串口普通/中断/DMA发收方式
串口中断函数总结:
参数不多说了,问问百度或者AI大致知道
一、串口发送
1、普通发送
HAL_UART_Transmit(&huartx,send_buf,strlen(send_buf),100);
一输入指令就发送固定的数组或是字符串(这里的send_buf是一个数组)
2、中断发送
HAL_UART_Transmit_IT(&huartx, (uint8_t*)send_buf,strlen(send_buf));
一输入指令就发送固定的数组或是字符串,每发送一次就会进入中断函数USARTx_IRQHandler内(由硬件进行触发),
并且发送完固定的字符长度就会进入回调函数HAL_UART_TxCpltCallback
3、DMA发送(会进行中断操作)
HAL_UART_Transmit_DMA(&huartx, (uint8_t*)send_buf,strlen(send_buf));
和中断发送类似,发送完数据后就会进入中断函数USARTx_IRQHandler内,也会进入回调函数
至于调用也是非常简单,初始化配置好以后直接在主函数while(1){ 串口/IT方式/DMA 发送 }
发送模式就不多说了,由于我在项目过程中串口发送很顺利的实现,所以也没有遇到太多问题
今天重点来讲讲接收函数的用法
二、串口接收
1、使用最常规的串口接收中断
HAL_UART_Receive(&huartx,USART_RX_BUF,USART_RX_LEN,200)
首先我们要接收数据总不可能先发送数据那样一直接收吧,这样子除了极大占用了CPU资源以外,还会存在接收数据丢失的情况。
就比如说我在main函数一直接收数据HAL_UART_Receive,但是在接收的过程中如果其他硬件资源申请了中断,那么我们的数据接收到了一半就终止了,会不会导致数据的丢失呢?大家只要细细一想就知道这种接收方式肯定不对。
事实上要使用第一种接收模式,先要开启中断使能__HAL_UART_ENABLE_IT(&huartx,UART_IT_RXNE),
开启了使能之后,这样子我们单片机每次检测到又数据需要接收时,就会申请中断进行数据接收
这里以uart3为例
int main(void)
{
Init(); .....
__HAL_UART_ENABLE_IT(&huart3,UART_IT_RXNE);//开启中断使能
}
接收到了数据进入下面的中断函数。。。
void USART3_IRQHandler(void)
{
if((__HAL_UART_GET_FLAG(&huart3,UART_FLAG_RXNE)!=RESET)) //判断是否接收中断标志位为1
{
HAL_UART_Receive(&huart3,USART_RX_BUF,USART_RX_LEN,100); //开始接收我们要的数据
对数据进行处理。。。。
}
HAL_UART_IRQHandler(&huart3);
}
2、中断接收模式
HAL_UART_Receive_IT(&huartx,USART_RX_BUF,USART_RX_LEN)
我查了一下资料,这种中断方式不需要开启使能(__HAL_UART_ENABLE_IT(&huartx,UART_IT_RXNE))
这种接收方式只需在主函数调用一次,之后在中断或回调函数中重新开启即可
也是以uart3为例
int main(void)
{
Init(); .....
HAL_UART_Receive_IT(&huart3,USART_RX_BUF,USART_RX_LEN)//开启中断接收
}
!!!注意:这里是每次接收到了数据进入下面的中断函数
void USART3_IRQHandler(void)
{
对数据进行处理。。。。。
HAL_UART_Receive(&huart3,USART_RX_BUF,USART_RX_LEN,100); //重新开启中断接收我们要的数据
这里如果不开启的话,接收够了长度USART_RX_LEN,就不会再进来这个中断函数里面了
}
HAL_UART_IRQHandler(&huart3);
}
当然也可以调用回调函数
回调函数的区别就在于,只有接收完USART_RX_LEN长度,才会进来
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart==&huart3)
{
HAL_UART_Receive(&huart3,USART_RX_BUF,USART_RX_LEN,100); //重新开启中断接收我们要的数据,这样下次就能在进来了
}
}
这里我在来详细给大家说明一下回调函数和中断函数两种的区别:
比如我在主函数main调用了函数
HAL_UART_Receive_IT(&huartx,USART_RX_BUF,10) //接收10个字节长度才终止中断
假如我们现在每次只发送“1”,方便我们区分回调函数和中断函数的区别。
按理说我们每次接收到了数据都会进入中断函数
也就是说我们发送10次“1”,就会进入10次中断函数USART3_IRQHandler里,
发送完了10次“1”才会进入一次回调函数HAL_UART_RxCpltCallback,相信这样子大家都明了了吧
所以一般我都习惯在回调函数里面重新开启中断,也就是直接在回调函数编写
3、高效的DMA接收
最后一种就是最为高效的DMA接收了
也是先来说说使用方法
int main(void)
{
Init(); .....
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
我这里开启的是空闲中断模式,只要DMA接收完了数据进入空闲状态就会发生中断
(当然也可以用其他方式进入中断函数)
}
void USART3_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart3);// 清除标志
将接受到的数据进行处理
HAL_UART_Receive_DMA(&huart3,USART_RX_BUF,USART_RX_LEN);
重新开启DMA接收数据
}
}
HAL_UART_IRQHandler(&huart3);
}
由于我这里实际发送的字长和接收到的字长是一样的,就没有用回调函数了,
如果需要用回调函数的,这个就交给读者自己去挖掘吧
(嘻嘻,其实就是想偷偷懒,因为我实在给这个接收模式折腾好几天了,有点累了)
本人猜测应该和第二种方式差不多
这里在配置上提个醒
我在DMA接收模式选择的是Normal并不是循环模式,所以才需要每次进中断函数重新开启DMA接收数据HAL_UART_Receive_DMA(&huart3,USART_RX_BUF,USART_RX_LEN);
三种接收模式选择总结
那么我为什么会要把这三种模式都说了一下呢?
表面上看三种接收模式都没有什么问题,都可以接收到数据。但是在实际运用时我发现了问题:
1、对于第一种接收模式,他存在等待操作,也就是说会阻塞程序,如果把他放进中断函数里面进行接收,频繁的接收数据,等待的时间过长,会导致程序卡死(死机)
当然他可以完整的接收较长的数据不会出错,唯一缺点就是阻塞程序,如果接收不频繁CPU资源充足的情况下还是可以用的。在我的项目里面,前几次的接收还是很成功的,但是后面就会死机。
2、对于第二中接收模式呢,相比第一种,没有阻塞情况,但是有一个很大的问题。
当我发送的数据较长时,经常会出现丢包问题,我实测了一下,实际发送15个字节,实际接收到的
只有10个字节,然后接收出现了异常情况(不再接收我发的数据)
我猜测可能是我的CPU比较繁忙而漏接收了数据,导致缓冲区溢出,数据接收异常
目前我是没有找到解决方法,如果有高手有解决方法可以在评论区分享一下
当然这种方法接收短一点长度的数据是完全没有问题的,我实测过不会出现漏帧卡顿问题
3、第三种方法就完美的解决了我上面遇到的两种情况
既不会阻塞程序,也不会占用CPU资源。如果你的单片机资源不够用了,或者执行的东西过多,
那最好还是选择DMA模式了!!!
以上都是我做项目遇到的一些问题的解决方法,我在这里分享其实也是怕我自己哪天忘记了,这样子就可以回来翻文章看看了。感谢大家观看!!!
更多推荐
所有评论(0)