OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3986|回复: 5

关于STM32 串口收发数据的常用方法和讨论。

[复制链接]

11

主题

55

帖子

0

精华

初级会员

Rank: 2

积分
197
金钱
197
注册时间
2014-12-29
在线时间
45 小时
发表于 2018-8-2 23:40:15 | 显示全部楼层 |阅读模式
之所以想发这么个帖子,发现要想实际项目应用和学习玩玩有很大的区别,学习也是为了以后的实际项目应用。
所以越是基础的东西越需要透彻的理解,需要考虑到方方面面细节。在此,想和大家分享并讨论有关串口在实际项目应用时的做法和思路。欢迎拍砖~
ps:本人目前还没学习和接触过操作系统。全都是裸跑。

中断接收:接收完成中断里一个字节中断一次的收数据,每次接收中断里只把收到的一个字节放入一个环形队列里,主函数里再提取分析数据。遵循中断里尽量少做函数调用的原则。
[mw_shl_code=c,true]if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
        {
                USART_ClearFlag(USART1, USART_FLAG_RXNE);  
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
                        Res =USART_ReceiveData(USART1);                //(USART3->DR);           //读取接收到的数据
                        if (((usart1Rx.rear+1)%RX_BUFFER_SIZE) != usart1Rx.front)//接收缓冲未满
               {
                                usart1Rx.buf[usart1Rx.rear] = Res;
                                usart1Rx.rear              = (usart1Rx.rear+1)%RX_BUFFER_SIZE;
                    }       
        }[/mw_shl_code]

中断发送:发送空中断(TC),每发送一个字节,则中断一次传入下一个字节。
[mw_shl_code=c,true]if(USART_GetITStatus(USART1, USART_IT_TC) != RESET) //  发送空中断
        {       
                        if(usart1Tx.front != usart1Tx.rear)
                        {               
                                        USART_SendData(USART1,usart1Tx.buf[usart1Tx.front]);       
                                        usart1Tx.front=(usart1Tx.front+1)%TX_BUFFER_SIZE;
                                }
                        else
                        {
                                USART_ITConfig(USART1, USART_IT_TC, DISABLE);  //关闭发送空中断使能               
                        }               
        }[/mw_shl_code]

以上是我平时写串口数据收发主要用的方法,暂时也没有出现过什么问题。

/////////////////////////////////////////////////////////////////////////////////
最近想到用DMA尝试串口数据的收发,毕竟DMA收发一帧数据才触发一次中断(当然你也可以不触发DMA接收通道中断,通过查询接收数组中是否有数据的方法)。


下面是我用DMA方式收发串口数据的程序思路:
[mw_shl_code=c,true]void USART1_IRQHandler(void)                        //串口1中断服务程序
{
        u8 Res;
        if(USART_GetITStatus(USART1,USART_IT_IDLE)!= RESET)
        {
                Res=USART1->SR;//先读SR,然后读DR才能清除
                Res=USART1->DR;// 清除DR
                Res=Res; // 防止编译器警告
                receive.dmaRxCount =DMA_USART1_RxLEN - DMA1_Channel5->CNDTR;// 总的buf长度减去剩余buf长度,得到接收到数据的长度
                receive.dmaRxCompleteFlag=true;   //主循环 receive函数里根据这个接收完成的标志来提取处理一帧数据
                DMAUsartRxSetAddr();
                USART_ClearITPendingBit(USART1, USART_IT_IDLE); // 清除空闲中断
        }

        if(USART_GetITStatus(USART1,USART_IT_TC)!= RESET) // 全部数据发送完成,产生该标记  
        {
                USART_ClearITPendingBit(USART1, USART_IT_TC);   // 清除完成标记
                DMA_Cmd(DMA1_Channel4, DISABLE); // 关闭DMA
                DMA1_Channel4->CNDTR=0;          // 清除数据长度
         }
}
[/mw_shl_code]

测下来基本能用,没发现什么异常。但是我有几点困惑的地方,也查了一些资料,没有找到具体说法。
困惑1:这个串口空闲中断,到底是多久没有字节进来则触发空闲中断。比如上位机串口传送5个字节的数据给单片机,正常情况下5个字节连续传完了,这个“完了”具体指多长时间?
            如果上位机卡顿,发送5个字节的时候发到第三个字节卡了一会儿,则是不是会产生两次空闲中断,如果是的话,我上面这种程序则会出现问题,当我在主循环receive函数里根据接收完成的标志来提取处理一帧数据的时候,后半段数据又来了,就会覆盖掉前面刚收到的数据。
困惑2:DMA1 4 5两个通道可以同时进行吗?接收数据的同时DMA发送数据。

以上则是我对串口传输数据的一些理解和做法,欢迎大家拍砖讨论,有什么更好更加适合实际项目应用的方法帮忙指点一下。个人认为只有把基础的东西琢磨透了,才能走的更远一些。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

11

主题

55

帖子

0

精华

初级会员

Rank: 2

积分
197
金钱
197
注册时间
2014-12-29
在线时间
45 小时
 楼主| 发表于 2018-8-3 14:45:48 | 显示全部楼层
回复 支持 反对

使用道具 举报

7

主题

188

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
385
金钱
385
注册时间
2018-7-19
在线时间
126 小时
发表于 2018-8-3 15:54:41 | 显示全部楼层
首先IDLE这个中断标志代表一帧数据,就是说如果你发送5个数据但是上位机在第三个数据卡顿就说明之后的数据都没收到
你这里用IDLE没出问题么,如果上位机连续发送多个数据并且这些数据发送间隔在一个字节以内就代表这一整个帧数据都不会
进入中断,但是此时数据的确已经存入DR寄存器只不过每来一个数据都会将前一个数据覆盖了。

不知道楼主能不能明白我说的,这是我的理解

一般情况下都是使用接收中断标志USART_IT_RXNE,去判断是否收到一个字节数据,而IDLE是用来判断一帧数据是否接受完毕
以此来判断再有数据来时知道是新的一帧
回复 支持 反对

使用道具 举报

11

主题

55

帖子

0

精华

初级会员

Rank: 2

积分
197
金钱
197
注册时间
2014-12-29
在线时间
45 小时
 楼主| 发表于 2018-8-3 20:17:08 | 显示全部楼层
坐看风 发表于 2018-8-3 15:54
首先IDLE这个中断标志代表一帧数据,就是说如果你发送5个数据但是上位机在第三个数据卡顿就说明之后的数据 ...

1、你的意思就是产生IDLE空闲中断的时间间隔依据就是串口传送一个字节的时间是吧?如果一帧数据5个字节,传了三个字节上位机卡顿了一小会儿(超过一个字节时间),这时候就会产生一个IDLE空闲中断,后面两个字节又被重新接收并且再产生一个中断?

2、最后你说的一般做法是:RXNE中断来接收存储每个字节,配合IDLE空闲中断来判断一帧数据的结束,你说的这样做法貌似方便一些。
我一直以来的做法都是每一帧数据带帧头帧尾协议。接收数据的时候串口就一个RXNE中断不断地接收存储数据至环形队列数据池里,主函数循环里再慢慢提取每帧数据。

3、DMA串口接收数据不知道你有什么看法,毕竟这个产生中断少。除了DMA配合IDLE中断,有没有其他好的想法。

4、谢谢
回复 支持 反对

使用道具 举报

11

主题

55

帖子

0

精华

初级会员

Rank: 2

积分
197
金钱
197
注册时间
2014-12-29
在线时间
45 小时
 楼主| 发表于 2018-8-5 10:13:45 | 显示全部楼层
沉了
回复 支持 反对

使用道具 举报

31

主题

1955

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4524
金钱
4524
注册时间
2018-5-11
在线时间
947 小时
发表于 2018-8-5 11:55:30 | 显示全部楼层
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-7-13 07:56

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表