目录
一、外部中断,实现LED的闪亮与熄灭
1.创建工程
然后自动生成程序。
2.编译与写程序
以看到生成的中断服务函数 void EXTI1_IRQHandler(void)
可以看到该函数又调用了HAL_GPIO_EXTI_IRQHandler(),于是继续跳转到下一函数
可以看到在这之中调用了HAL_GPIO_EXTI_Callback(),接下来的函数可以看到是_weak开头,则需要用户自己写函数。
此时到main.c中书写callback程序,用到的库函数是HAL_GPIO_TogglePin(),该函数的作用是翻转电平,即中断一产生,则翻转一次电平。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
{HAL_GPIO_TogglePin(led1_GPIO_Port,led1_Pin);
}
}
接着进行编译。
3.烧录
通电下将boot0置0,然后再次reset后可运行
4.实验结果
此处因为stm32c8t6没有按键,因此选择使用杜邦线代替开关,每接触一次接地,则产生一次中断
二、串口中断实现串口通信
1.创建工程
RCC和SYS和CLOCK设置如上面一样
下面设置串口USART1,在MODE下选择Asynchronous(异步通信模式),并且使得USART1中断使能
接着就可以直接生成工程了
2.重定向printf和scanf
#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中添加
#define RXBUFFERSIZE 256
char RxBuffer[RXBUFFERSIZE];
printf("hello world\n");
HAL_Delay(1000);
在target勾选Use MicroLIB,需要调用微型库
3.运行结果
4.UART接收中断
具体流程:
- 初始化串口
- 在main中第一次调用接收中断函数
- 进入接收中断,接收完数据 进入中断回调函数
- 修改HAL_UART_RxCpltCallback中断回调函数,处理接收的数据,
- 回调函数中要调用一次HAL_UART_Receive_IT函数,使得程序可以重新触发接收中
代码实现
并在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);
}
实验成果
可以看到串口回显。
三、串口DMA接收发数据
1.创建工程
其余设置参考上面串口设置
以下为不同点
进入DMA Settings
点击add,选择USART_RX USART_TX 传输速率设置为中速
DMA传输模式为正常模式
DMA内存地址自增,每次增加一个Byte(字节)
右侧点击System Core 点击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 接收空闲中断
STM32的IDLE的中断产生条件:在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断
代码:
uart.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 */
uart.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文件,需要在文件夹里寻找
main.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负担减轻,熟悉了串口通信的基本函数和DMA通信的基本函数。
五、参考链接
qq_60678931的博客_Laul Ken-Yi_CSDN博客