目录
一.中断实现LED亮灭
1.新建工程
1.1选择芯片
1.2 设置led,PA5引脚和外部中断EXTI1,PB1
1.3给中断设置下降沿触发,并且上拉电阻。
1.4 配置中断优先级,因为此处只有一个中断,因此它的优先级为0,为最高
1.5配置时钟,设时钟频率为72MHZ
1.6自动生成程序
2.编译写程序
可以看到生成的中断服务函数 void EXTI1_IRQHandler(void)
可以看到在这之中调用了HAL_GPIO_EXTI_Callback(),接下来的函数可以看到是_weak开头,则需要用户自己写函数。
此时到main.c中书写callback程序,用到的库函数是HAL_GPIO_TogglePin(),该函数的作用是翻转电平,即中断一产生,则翻转一次电平。然后编译。
3. 烧录
通电下将boot0置0,然后再次reset后可运行。
4.实验结果
此处因为stm32c8t6没有按键,因此选择使用杜邦线代替开关,每接触一次接地,则产生一次中断。
二.串口通信
1.创建工程
RCC和SYS和CLOCK设置如上面一样
下面设置串口USART1,在MODE下选择Asynchronous(异步通信模式),并且使得USART1中断使之能够
接着就可以直接生成工程了。
2.重定向printf和scanf
之后可以直接使用重定向的函数
- 在 stm32f1xx_hal.c中包含#include <stdio.h> #include <stdio.h>
extern UART_HandleTypeDef huart1; //声明串口
在 stm32f1xx_hal.c 中重写fget和fput函数
/**
* 函数功能: 重定向c库函数printf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
在main.c中添加
在target勾选Use MicroLIB,需要调用微型库
3. 运行结果
4. UART接收中断
4.1 代码实现:
并在main.c中添加下列定义:
#include <string.h>
#define RXBUFFERSIZE 256 //最大接收字节数
char RxBuffer[RXBUFFERSIZE]; //接收数据
在main()主函数中,调用一次接收中断函数
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, RxBuffer, 1);
/* USER CODE END 2 */
在main.c下方添加中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)
{
HAL_UART_Transmit(&huart1,RxBuffer,1,0xff);
}
HAL_UART_Receive_IT(&huart1, RxBuffer, 1);
}
4.2实验成果
此处需要使用XCOM调试助手
三、串口DMA接收发数据
1.创建工程
其余设置参考上面串口设置
以下为不同点
进入DMA Settings
点击add,选择USART_RX USART_TX 传输速率设置为中速
DMA传输模式为正常模式
DMA内存地址自增,每次增加一个Byte(字节)
右侧点击System view 点击DMA
点击add添加MENTOMEN
Normal:正常模式
当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次
DMA指针递增设置
Increment Address:地址指针递增(上方有介绍)。
左侧Src Memory 表示外设地址寄存器
功能:设置传输数据的时候外设地址是不变还是递增。如果设置 为递增,那么下一次传输的时候地址加 Data Width个字节,
右侧Dst Memory 表示内存地址寄存器
功能:设置传输数据时候内存地址是否递增。如果设置 为递增,那么下一次传输的时候地址加 Data Width个字节,
这个Src Memory一样,只不过针对的是内存。然后即可生成工程
2. 测试例程1
在main.C中添加:
/* USER CODE BEGIN Init */
uint8_t Senbuff[] = "HELLO WORLD"; //定义数据发送数组
/* USER CODE END Init */
while循环:
while (1)
{
/* USER CODE END WHILE */
HAL_UART_Transmit_DMA(&huart1, Senbuff, sizeof(Senbuff));
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
3. 测试结果
4. 测试例程2
STM32的IDLE的中断产生条件:在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断
usart.c
volatile uint8_t rx_len = 0; //接收一帧数据的长度
volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
uint8_t rx_buffer[100]={0}; //接收数据缓存数组
/* USER CODE BEGIN USART1_Init 2 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
/* USER CODE END USART1_Init 2 */
usart.h
extern UART_HandleTypeDef huart1;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
/* USER CODE BEGIN Private defines */
#define BUFFER_SIZE 100
extern volatile uint8_t rx_len ; //接收一帧数据的长度
extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
extern uint8_t rx_buffer[100]; //接收数据缓存数组
工程里没有.h文件,需要在文件夹里寻找
mian.c
/*
*********************************************************************************************************
* 函 数 名: DMA_Usart_Send
* 功能说明: 串口发送功能函数
* 形 参: buf,len
* 返 回 值: 无
*********************************************************************************************************
*/
void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装
{
if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数
{
Error_Handler();
}
}
/*
*********************************************************************************************************
* 函 数 名: DMA_Usart1_Read
* 功能说明: 串口接收功能函数
* 形 参: Data,len
* 返 回 值: 无
*********************************************************************************************************
*/
void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装
{
HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收
}
这里需要定义在main函数前面,否则在main函数里调用会报错,也可以在main之前先声明函数。
while循环
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(recv_end_flag == 1) //接收完成标志
{
DMA_Usart_Send(rx_buffer, rx_len);
rx_len = 0;//清除计数
recv_end_flag = 0;//清除接收结束标志位
// for(uint8_t i=0;i<rx_len;i++)
// {
// rx_buffer[i]=0;//清接收缓存
// }
memset(rx_buffer,0,rx_len);
}
HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
}
stm32f1xx_it.c中
#include "usart.h"
void USART1_IRQHandler(void)
{
uint32_t tmp_flag = 0;
uint32_t temp;
tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
if((tmp_flag != RESET))//idle标志被置位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
//temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
//temp = huart1.Instance->DR; //读取数据寄存器中的数据
//这两句和上面那句等效
HAL_UART_DMAStop(&huart1); //
temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
//temp = hdma_usart1_rx.Instance->NDTR;//读取NDTR寄存器 获取DMA中未传输的数据个数,
//这句和上面那句等效
rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
recv_end_flag = 1; // 接受完成标志位置1
}
HAL_UART_IRQHandler(&huart1);
}
5测试程序
四.心得
本次实验,从延时的方式转换到了中断与串口DMA通信,可以提高程序的运行效率,从而不浪费芯片的内存,DMA可以直接访问存储器,而直接绕过CPU,为cpu减负。但是感觉还是迷迷糊糊的,没有搞明白他的原理。
五.参考文献
【STM32】HAL库 STM32CubeMX教程三----外部中断(HAL库GPIO讲解)_Z小旋-CSDN博客
【STM32】HAL库 STM32CubeMX教程十一---DMA (串口DMA发送接收)_Z小旋-CSDN博客
【STM32】HAL库 STM32CubeMX教程四---UART串口通信详解_Z小旋-CSDN博客_hal_uart_transmit stm32cubemx下stm32中断与串口DMA通信_Laul Ken-Yi的博客-CSDN博客