历程详解
详解包括:
中断原理讲解
例程流程详解
库函数分析详解
对应寄存器介绍
对应函数介绍
对应注释详解
本篇文章提供两种方法:
一种是 :IDLE 接收空闲中断+DMA
一种是: IDLE 接收空闲中断+RXNE接收数据中断
都可完成串口数据的收发
知识点介绍:
STM32 IDLE 接收空闲中断
功能:
在使用串口接受字符串时,可以使用空闲中断(IDLEIE置1,即可使能空闲中断),这样在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。在中断处理函数,我们可以解析这个字符串。
接受完一帧数据,触发中断
STM32的IDLE的中断产生条件:
在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断
STM32 RXNE接收数据中断
功能:
当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断
接受到一个字节的数据,触发中断
比如给上位机给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。
串口CR1寄存器
对bit4写1开启IDLE接受空闲中断
,对bit5写1开启RXNE接收数据中断。
串口ISR寄存器
此寄存器为串口状态查询寄存器
当串口接收到数据时,bit5 RXNE就会自动变成1,当接收完一帧数据后,bit4就会变成1.
清除RXNE中断标志位的方法为:
只要把接收到的一个字节读出来,就会清除这个中断
在STM32F1 /STM32F4 系列中清除IDLE中断标志位的方法为:
先读SR寄存器,
再读DR寄存器。
memset()函数
extern void *memset(void *buffer, int c, int count)
buffer:为指针或是数组
c:是赋给buffer的值
count:是buffer的长度.
USART采用DMA接收时,如何读取当前接收字节数?
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR);
DMA接收时该宏将返回当前接收空间剩余字节
实际接受的字节= 预先定义的接收总字节 - __HAL_DMA_GET_COUNTER()
其本质就是读取NTDR寄存器,DMA通道结构体中定义了NDTR寄存器,读取该寄存器即可得到未传输的数据数呢,
NTDR寄存器
实现方法:
两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:
方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这次接受的数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区数组里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);函数计算出当前DMA接收存储空间剩余字节
本次的数据接受长度=预先定义的接收总字节-接收存储空间剩余字节
比方说 本次串口接受200个字节,
HAL_UART_Receive_DMA(&huart1,rx_buffer,200);//打开DMA接收
然后我发送了Zxiaoxuan 9个字节的数据长度
那么此时 GET_COUNTER函数读出来 接收存储空间剩余字节 就是191个字节
实际接受的字节(9) = 预先定义的接收总字节(200) - __HAL_DMA_GET_COUNTER()(191)
9 = 200-191
应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。
方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。每次接收到一个字节的数据,触发RXNE中断 将该字节数据存放到数组里,传输完成之后,触发一次IDLE中断,对已经获取到的数据进行处理
例程1
本例程功能:
使用DMA+串口接受空闲中断实现将接收的数据完整发送到上位机的功能
接收数据的流程:
首先在初始化的时候打开DMA接收,当MCU通过USART接收外部发来的数据时,在进行第①②③步的时候,DMA直接将接收到的数据写入缓存rx_buffer[100] //接收数据缓存数组,程序此时也不会进入接收中断,在软件上无需做任何事情,要在初始化配置的时候设置好配置就可以了。
数据接收完成的流程:
当数据接收完成之后产生接收空闲中断④
在中断服务函数中做这几件事:
判断是否为IDLE接受空闲中断
在中断服务函数中将接收完成标志位置1
关闭DMA防止在处理数据时候接收数据,产生干扰。
计算出接收缓存中的数据长度,清除中断位,
while循环 主程序流程:
主程序中检测到接收完成标志被置1
进入数据处理程序,现将接收完成标志位置0,
将接收到的数据重新发送到上位机
重新设置DMA下次要接收的数据字节数,使能DMA进入接收数据状态。
例程代码:
uart.c
volatile uint8_t rx_len = 0; //接收一帧数据的长度
volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
uint8_t rx_buffer[100]={0}; //接收数据缓存数组
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
//下方为自己添加的代码
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
}
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]; //接收数据缓存数组
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接收
}
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_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); // 停止DMA传输,防止
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);
}
注释详解:
注释1:
temp = UartHandle.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
temp = UartHandle.Instance->DR; //读取数据寄存器中的数据
这两句被屏蔽的原因是它们实现的功能和这下面串口IDLE状态寄存器SR标志位清零的宏定义实现的功能是一样的:
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
我们可以点击这个宏定义进去看看,它实现的功能和上面两句是一样的:
#define __HAL_UART_CLEAR_IDLEFLAG(HANDLE) __HAL_UART_CLEAR_PEFLAG(HANDLE)
注释2:
temp = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面
同理, 这句被屏蔽的原因是因为他和上面的__HAL_DMA_GET_COUNTER的作用也是一样的,都可以获取DMA中未传输的数据个数
测试正常:
此程序可以进
关键字:
STM32
HAL
CubeMX
串口
IDLE接收
空闲中断
DMA
编辑:什么鱼 引用地址:
/mcu/ic507322.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。