STM32 串口中断总结
原文:https://blog.csdn.net/weixin_42480952/article/details/82981409
最近在学习使用dma传输方式进行串口通讯,感觉这个很详细,存一下
.定义了三种传输方式:阻塞传输,中断传输、DMA传输
HAL_UART_Transmit; HAL_UART_Receive
HAL_UART_Transmit_IT; HAL_UART_Receive_IT
HAL_UART_Transmit_DMA; HAL_UART_Receive_DMA
此外还定义了两个中断回调函数,供中断和DMA使用,分别在数据传输一半和完成时使用
voidHAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef*huart);
voidHAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
voidHAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
2.阻塞传输
阻塞传输是调用这个函数并在等待时间内一直等待操作完成。
uint8_t aTxbuffer[]="enter 10 characters:\n";
uint8_t aRxBuffer;
uint8_t Usart1_RxBuff[10];
uint8_t Usart1_Rx_Cnt = 0;
int main(void)
{
HAL_Init();
Sysclk_config();
USART1_UART_Init(19200);
printf("input your string:\n");
HAL_UART_Transmit(&huart1 ,(uint8_t*)aTxbuffer,sizeof(aTxbuffer),0xFFF);
HAL_UART_Receive(&huart1,(uint8_t*)Usart1_RxBuff,10,10);
HAL_UART_Transmit(&huart1 ,(uint8_t*)Usart1_RxBuff,10,10);
}
可以添加循环语句,循环输入输出。
3.中断传输
配置串口,开启中断,在中断处理函数中进行输入语句的输出。
通过查看源代码,可以看到HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)这个函数只是用来开启中断用的,并不能真正接收数据。开启中断后,在中断处理函数HAL_UART_IRQHandler(&huart1)中,会先调用UART_Receive_IT(huart)函数进行数据输入的接收,此为静态全局函数,代码如下:
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
uint16_t* tmp;
uint16_t uhMask = huart->Mask;
/* Check that a Rx process is ongoing */
if(huart->RxState == HAL_UART_STATE_BUSY_RX)
{
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
tmp = (uint16_t*) huart->pRxBuffPtr ;
*tmp = (uint16_t)(huart->Instance->RDR & uhMask);
huart->pRxBuffPtr +=2;
}
else
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
}
if(--huart->RxXferCount == 0)
{
/* Disable the UART Parity Error Interrupt and RXNE interrupt*/
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Rx process is completed, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
HAL_UART_RxCpltCallback(huart);
return HAL_OK;
}
return HAL_OK;
}
else
{
/* Clear RXNE interrupt flag */
__HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
return HAL_BUSY;
}
}
可以看到该函数的作用是将接收到的数据存入结构体huart内的pRxBuffPtr指针中,当待传输数据长度RxXferCount为0时,便调用回调函数HAL_UART_RxCpltCallback(huart);因此可以考虑更改此函数的源代码,当待传输数据的字符为空格或回车时,判断为输入结束,将RxXferCount置为0,然后调用回调函数进行数据处理。
a.单字节循环接收数据
HAL_UART_Receive_IT通过设置接收缓冲区和需要接收的数据个数。当数据接收达到设定个数后引发一次中断调用回调函数HAL_UART_RxCpltCallback。由于只引发一次中断,如果需要连续接收,则需要在HAL_UART_RxCpltCallback再调用HAL_UART_Receive_IT。这种定长的接收可能并不是想要的,往往传输的数据都是不定长的,我想这需要将HAL_UART_Receive_IT长度设置为1,然后自己根据接收的数据判断。此外由于回调函数没有指明是哪个串口引发的中断,因此有必要在回调函数中做判断,如if(huart==&huart1){ }。
int main(void)
{
HAL_Init();
Sysclk_config();
USART1_UART_Init(19200);
//HAL_UART_Transmit(&huart1,aTxBuffer,sizeof(aTxBuffer),0xFFF);
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);//开启接收中断
while(1)
{
if(flag==1)
{
HAL_UART_Transmit(&huart1,RxBuff,count,0xFF);
printf("\n");
for(uint8_t i=0;i<LENTH;i++)
{
RxBuff[i]=0;
}//清空数组
flag=0;
count=0;
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);//重新开启接收中断
}
}
}
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(count<LENTH-1)
{
if(aRxBuffer!=0x0d)//输入非回车
{
flag=0;
RxBuff[count]=aRxBuffer;
count++;
HAL_UART_Transmit(&huart1,&aRxBuffer,count,0x20);
printf("\n");
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);//重新开启接收中断
}
else flag=1;
}
else
flag=1;
}
//接收中断回调函数
b.长字节数据循环输入
设置输入缓冲数组(长度可设置100),利用单次中断,将接收到的数据元素轮流存入数组中,数据元素为0或回车时判断数据输入完成,进行数据输入完成标志位置位。
void Buffer_reset(void);
int fputc(int ch, FILE *f);
uint8_t aTxBuffer[]="this is a test message!\n";
uint16_t flag=0;
uint8_t RxBuff[LENTH]; //接收缓冲数组
uint16_t count = 0; //接收缓冲计数
uint8_t aRxBuffer[LENTH]={0}; //USART接收Buffer
int main(void)
{
HAL_Init();
Sysclk_config();
Buffer_reset();
USART1_UART_Init(19200);
HAL_UART_Receive_IT(&huart1,aRxBuffer,LENTH);//开启接收中断
while(1)
{
if(flag==1)
{
HAL_UART_Transmit(&huart1,RxBuff,count,0xFF);
printf("\n");
Buffer_reset();
flag=0;
count=0;
HAL_UART_Receive_IT(&huart1,aRxBuffer,LENTH);//重新开启接收中断
}
}
}
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
do
{
RxBuff[count]=aRxBuffer[count];
printf("aRxBuffer[%d]=%d\n",count,aRxBuffer[count]);
count++;
}
while((aRxBuffer[count]!=0)&&(count<LENTH)); //将接收到的字节存进数组,直到元素为0或溢出为止。
flag=1;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
}
//接收中断回调函数
void Buffer_reset(void){
for(uint8_t i=0;i<LENTH;i++)
{
RxBuff[i]=0;
aRxBuffer[i]=0;
}//清空数组
}
此代码暂时有问题,调试中。
4.DMA传输
DMA可以解放CPU,同时可以利用DMA+空闲中断实现任意字节的串口输入输出。
空闲中断:接收到一条完整的数据,就会产生空闲中断,同时空闲标志位置位。
串口接收中断:每接收到一个字符,就会产生一个串口接收中断。
原理:利用DMA配置,将串口读入的数据存储在DMA缓冲区的接收数组中。当检测到一帧数据(即数据输入完成)时,产生空闲中断,此时可以将所接收的数据进行处理或输出。
步骤:
a.串口配置(时钟使能,引脚配置,串口配置,中断配置,使能空闲中断,串口全局中断,开启DMA接收数据)
b.DMA配置(时钟使能,usart_tx和usart_rx通道配置,中断配置,关联usart和DMA通道)
c.重写串口中断函数(检测到空闲中断时,清除空闲中断标志位,停止DMA传输,获取输入数据的长度,置位输入完成标志位)
d.主函数处理(检测到输入完成标志位时,进行数据处理或输入,然后清空数组,清除数据长度和输入完成标志位)
DMA配置:
void usart_dma_init(void){
__HAL_RCC_DMA2_CLK_ENABLE();
huart1_dma_rx.Instance=DMA2_Stream2;
huart1_dma_rx.Init.Channel=DMA_CHANNEL_4;
huart1_dma_rx.Init.Direction=DMA_PERIPH_TO_MEMORY;
huart1_dma_rx.Init.PeriphInc=DMA_PINC_DISABLE;
huart1_dma_rx.Init.MemInc=DMA_MINC_ENABLE;
huart1_dma_rx.Init.MemDataAlignment= DMA_MDATAALIGN_BYTE;
huart1_dma_rx.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;
huart1_dma_rx.Init.Mode=DMA_NORMAL;
huart1_dma_rx.Init.Priority=DMA_PRIORITY_LOW;
huart1_dma_rx.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&huart1_dma_rx);
//RX_DMA_config
huart1_dma_tx.Instance=DMA2_Stream7;
huart1_dma_tx.Init.Channel=DMA_CHANNEL_4;
huart1_dma_tx.Init.Direction=DMA_MEMORY_TO_PERIPH;
huart1_dma_tx.Init.PeriphInc=DMA_PINC_DISABLE;
huart1_dma_tx.Init.MemInc=DMA_MINC_ENABLE;
huart1_dma_tx.Init.MemDataAlignment= DMA_MDATAALIGN_BYTE;
huart1_dma_tx.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;
huart1_dma_tx.Init.Mode=DMA_NORMAL;
huart1_dma_tx.Init.Priority=DMA_PRIORITY_HIGH;
huart1_dma_tx.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&huart1_dma_rx);
//TX_DMA_config
__HAL_LINKDMA(&huart1, hdmarx, huart1_dma_rx);
__HAL_LINKDMA(&huart1, hdmatx, huart1_dma_tx);
//关联USART1和DMA
HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 1, 1);
HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 1, 1);
HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
//配置DMA通道的中断
}
中断处理函数:
void USART1_IRQHandler(void)
{
uint32_t tmp_flag = 0;
uint32_t temp;
tmp_flag= __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
if(tmp_flag==1)//当产生空闲中断时(及接收到一帧数据)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除空闲中断标志位
HAL_UART_DMAStop(&huart1); //停止串口的DMA传输
temp = __HAL_DMA_GET_COUNTER(&huart1_dma_rx);// 获取DMA中未传输的数据个数
rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,即得到已经接收的数据个数
flag=1;
}
HAL_UART_IRQHandler(&huart1);
}
5.利用定时器实现串口的不定长字节输入
当接收到第一个字符时,打开定时器。经过延时后,进入定时器中断回调函数,在该回调函数中进行数据的处理和输出。
int main(void)
{
HAL_Init();
Sysclk_config();
USART1_UART_Init(19200);
Basic_Tim_Config();
printf("input strings:\n");
num_reset();
HAL_UART_Receive(&huart1,RxBuff,LENTH,0xFFF);
if(RxBuff[0])
{
HAL_TIM_Base_Start_IT(&Basic_Tim6);//有数据输入的时候就开启定时器
}
while(1)
{
if(flag)
{
printf("input strings again:\n");
flag=0;
count=0;
num_reset();
HAL_UART_Receive(&huart1,RxBuff,LENTH,0xFFF); //开启串口输入
if(RxBuff[0])
{
HAL_TIM_Base_Start_IT(&Basic_Tim6);//重新开启定时器
}
}
}
}
void TIM6_DAC_IRQHandler(TIM_HandleTypeDef *htim)
{
HAL_TIM_IRQHandler(&Basic_Tim6);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
HAL_TIM_Base_Stop(&Basic_Tim6);
huart1.RxState = HAL_UART_STATE_READY;
huart1.Lock=HAL_UNLOCKED;
printf("your input:");
do
{
aRxBuff[count]=RxBuff[count];
count++;
}
while(RxBuff[count]);
if((HAL_UART_Transmit(&huart1,aRxBuff,count+1,0xFFF))==HAL_OK)//串口输出
{
flag=1;
}
}
void num_reset(void)
{
for(uint8_t i=0;i<LENTH;i++)
{
RxBuff[i]=0;
aRxBuff[i]=0;
}
}
STM32 串口中断总结的更多相关文章
- STM32串口中断实例二
int main(void) { uint8_t a=;//LED高低电压控制 /* System Clocks Configuration */ RCC_Configuration(); //系统时 ...
- STM32串口中断的一些资料
在研究STM32串口接收发送中断的时候找到不少不错的资料,现在备份在这里.以供自己查阅,以及方便其他人. TC ====TXE 顺便预告下最近会写个有关串口处理数据的帖子,从查询和中断方面以及数据处理 ...
- STM32串口——中断方式的一般配置方法
#include "stm32f10x.h" /************************************************ 该程序讲解串口程序的一般配置方法: ...
- stm32串口中断总结
串口文件uart.c需要被用到; 串口通信是对GPIO端口引脚的功能复用,因此需要用到gpio.c; 因为中断的产生,因此中断文件也是需要用到的: 中断响应函数需要自己编写: 接收中断:在接收移位寄存 ...
- STM32串口中断
在打开串口1中断后 待机唤醒功能,(PA0唤醒)等 系统挺运行问题? ??(2014.11.13)
- stm32串口接收中断协议解析
借鉴了文章:<stm32串口中断接收方式详细比较> 文章地址:http://blog.csdn.net/kevinhg/article/details/40186169 串口的配置这里不做 ...
- stm32串口接收完整的数据包
参考了文章:<stm32串口中断接收方式详细比较> 文章地址:http://bbs.elecfans.com/jishu_357017_1_1.html 借鉴了第四种中断方式 串口的配置这 ...
- STM32单片机串口中断+DMA使用(含CUBE配置)
最近又要重新用32做点东西,发现一两年没怎么碰的结果就是,曾经熟得不行的东西都变得极度陌生,这种重新学习记忆的过程过于痛苦,果然还是要留下一些记录给之后失忆的自己的. 1.STM32CUBE配置 1. ...
- stm32的串口中断
下面有很多问题没有验证: 在设置USART_CR1中的TE位时,会发送一个空闲帧作为第一次数据发送, 目前我所了解的串口中断发送,有两种方式: 一个是:TC 一个是:TXE 这是判断两个标志位, 第一 ...
随机推荐
- grafana启用mysql作为的后台数据库
vim grafana-5.4.2/conf/defaults.ini grafana默认使用sqlite3使用后台数据库,可选用mysql,postgres,sqlite3. 重新启动 注意: gr ...
- javascript 数组求交集/差集/并集/过滤重复
最近在小一个小程序项目,突然发现 javscript 对数组支持不是很好,连这些基本的功能,都还要自己封装.网上查了下,再结合自己的想法,封装了一下,代码如下. //数组交集 Array.protot ...
- equals与hashcode区别
哈希码: hashCode的作用是用来获取哈希码,也可以称作散列码.实际返回值为一个int型数据.用于确定对象在哈希表中的位置. Object中有hashcode方法,也就意味着所有的类都有has ...
- 《Spring实战》-- 'cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element' 错误的解决办法
在Eclipse中新建了一个maven项目学习Spring,在 service.xml 中配置 Spring,想要学习'面向切面的Spring',service.xml 内容如下: <beans ...
- Fedora16的双显卡切换问题
症状:笔记本是Acer 4745G,安装了Fedora16+Win7 x64的双系统,每次开机后,独立显卡的风扇就开始狂转,同时笔记本的发热量极大,左侧出风口简直烫手.... 问题:Acer 4745 ...
- sqlserver sql优化案例及思路
始sql: SELECT TOP 100 PERCENT ZZ.CREW_NAME AS 机组, ZZ.CREW_ID, AA.年度时间, CC.当月时间, DD.连续七天时间 AS 最近七天 FRO ...
- PFX文件提取公钥私钥
jks是JAVA的keytools证书工具支持的证书私钥格式.pfx是微软支持的私钥格式. cer是证书的公钥. 如果是你私人要备份证书的话记得一定要备份成jks或者pfx格式,否则恢复不了. 简单来 ...
- DLL导出类避免地狱问题的完美解决方案
DLL动态链接库是程序复用的重要方式,DLL可以导出函数,使函数被多个程序复用,DLL中的函数实现可以被修改而无需重新编译和连接使用该DLL的应用程序.作为一名面向对象的程序员,希望DLL可以导出类, ...
- git bash安装和基本设置
Part1:安装步骤 1.下载地址: https://git-for-windows.github.io/ 2.下载完成后安装,安装路径自己选择,其他的选项参照下图: 其他的一步一步往下即可,最后Fi ...
- Camstar MES 5.8 發現Ajax事件失效
從Camstar4.5升級到5.8後,發現原來用戶在4.5下可以正常使用的不良信息收集功能,列出的不良只有第一頁可以顯示,無法自動裝載下一頁. 嘗試發出,IE以下這個選項沒有選中,選中後,就可以正常工 ...