OpenEdv-开源电子网

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

带帧头、校验和的串口数据处理

[复制链接]

19

主题

107

帖子

0

精华

初级会员

Rank: 2

积分
195
金钱
195
注册时间
2015-3-5
在线时间
44 小时
发表于 2015-5-28 22:36:41 | 显示全部楼层 |阅读模式

今天要做一个串口取数据然后叠加OSD的项目,串口发过来的数据一次8个字节,0x5A开头,最后一个字节校验和,大约100ms发一次,波特率115200。
然后就想直接在原子哥的串口教程里改改,先把串口收数据这一块搞定。但发现每次进中断,读到的数据都只是0x5A,后边的7个数据数据都读不出来了。苦思冥想了一会,再加上这边帖子里的灵感
http://www.openedv.com/posts/list/41525.htm
我也觉得在高波特率下,在串口接收中断函数里做过多处理的话,有可能会把字头后边的数据漏掉,造成混乱。所以,
个人建议,中断服务程序我们只做存储数据就好,处理数据尽量去main里面处理,这样不容易产生混乱。
USART_RX_BUF和seri_count都是全局变量。
然后在main函数中处理数据,核心问题是如果传输过程中出现乱码该怎么办。我的思路是:
  
若数组USART_RX_BUF中的数据超过8字节,则开始找帧头,若找不到,丢掉数据,重新从USART_RX_BUF[0]开始写数据。 若找到帧头,则从帧头开始,检查和校验,校验通过就可以刷新数据,校验不通过的话,丢掉该组数据。如果帧头在数据的中间,也是一样处理。这样即使有乱码过来,程序也能很快找到帧头。


[mw_shl_code=c,true] [/mw_shl_code]


[mw_shl_code=c,true]#if EN_USART1_RX //如果使能了接收 void USART1_IRQHandler(void) //串口1中断服务程序 { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 { USART_RX_BUF[seri_count]=USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据 LED1=!LED1; seri_count++; } } #endif [/mw_shl_code]
[mw_shl_code=c,true][/mw_shl_code] [mw_shl_code=c,true]
[mw_shl_code=c,true]#include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "lcd.h" #include "stdio.h" //ALIENTEK Mini STM32开发板范例代码11 //TFTLCD显示实验 //技术支持:www.openedv.com //广州市星翼电子科技有限公司 typedef struct osd_Data { int16_t roll; // 滚转角,高八位在前,第八位在后,,除以10得到角度值,精度为0.1度 int16_t pitch; // 俯仰角,高八位在前,第八位在后,除以10得到角度值,精度为0.1度 u16 V_bat; // 电池电压,除以10得到电压值,精度为0.1V } OSD_Data; OSD_Data OSD_250; int main(void) { u8 x=0; u8 sum_check=0; // u8 UART_RX_Mid[10]; u8 lcd_id[12]; //存放LCD ID字符串 seri_count=0; //串口缓冲区数组个数,初始化为0 delay_init(); //延时函数初始化 uart_init(115200); //串口初始化为115200 LED_Init(); //初始化与LED连接的硬件接口 LCD_Init(); NVIC_Configuration();// 设置中断优先级分组 POINT_COLOR=RED; sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);//将LCD ID打印到lcd_id数组。 LCD_Clear(GRAY); LCD_ShowString(30,40,200,24,24,"Mini STM32 ^_^"); LCD_ShowString(30,70,200,16,16,"TFTLCD TEST"); LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(30,110,200,16,16,lcd_id); //显示LCD ID LCD_ShowString(30,130,200,12,12,"2014/3/7"); while(1) { if(seri_count>=Data_Length) //如果已经接收到超过字符串长度的字符 { u8 i=0; while(USART_RX_BUF!=0x5A) //找字头 { i++; if(i>(Data_Length-1)) { i=0; //如果在一个字符串的长度范围内,都没有接收到字头,则重新开始 seri_count=0; } } if(i==0) { for(x=0;x<Data_Length-1;x++) //将校验和之前的字符串相加 { sum_check=sum_check+USART_RX_BUF[x]; } if(sum_check==USART_RX_BUF[Data_Length-1]) //判断校验和 { seri_count=0; //为下一次进中断做好准备 sum_check=0; //校验和清零,准备下一次校验 OSD_250.roll=((u16)USART_RX_BUF[1])<<8 |USART_RX_BUF[2]; OSD_250.pitch=((u16)USART_RX_BUF[3])<<8|USART_RX_BUF[4]; OSD_250.V_bat=((u16)USART_RX_BUF[5])<<8|USART_RX_BUF[6]; LCD_Fill(30,150,300,210,GRAY); //填充单色,刷掉显示区域 sprintf((char*)lcd_id, "Roll:%-04.1f",(float)OSD_250.roll/10.0); //将数据打印到data数组。 LCD_ShowString(30,150,200,16,16,lcd_id); sprintf((char*)lcd_id,"pitch:%-04.1f",(float)OSD_250.pitch/10.0);//将数据打印到data数组。 LCD_ShowString(30,170,200,16,16,lcd_id); sprintf((char*)lcd_id,"votalge:%-04.1f",(float)OSD_250.V_bat/100.0);//将数据打印到data数组。 LCD_ShowString(30,190,200,16,16,lcd_id); } else {seri_count=0; //校验和不正确,则可能发生帧错误,重新开始 sum_check=0; } } else { for(x=0;x<Data_Length-1;x++) //将校验和之前的字符串相加个数据相加 { sum_check=sum_check+USART_RX_BUF[x+i]; } if(sum_check==USART_RX_BUF[i+Data_Length-1]) { seri_count=0; // 为下一次进中断做好准备 sum_check=0; //校验和清零,准备下一次校验 OSD_250.roll=((u16)USART_RX_BUF[i+1])<<8 |USART_RX_BUF[i+2]; OSD_250.pitch=((u16)USART_RX_BUF[i+3])<<8|USART_RX_BUF[i+4]; OSD_250.V_bat=((u16)USART_RX_BUF[i+5])<<8|USART_RX_BUF[i+6]; LCD_Fill(30,150,300,210,GRAY); //填充单色,刷掉显示区域 sprintf((char*)lcd_id, "Roll:%-04.1f",(float)OSD_250.roll/10.0); //将数据打印到data数组。 LCD_ShowString(30,150,200,16,16,lcd_id); sprintf((char*)lcd_id,"pitch:%-04.1f",(float)OSD_250.pitch/10.0);//将数据打印到data数组。 LCD_ShowString(30,170,200,16,16,lcd_id); sprintf((char*)lcd_id,"votalge:%-04.1f",(float)OSD_250.V_bat/100.0);//将数据打印到data数组。 LCD_ShowString(30,190,200,16,16,lcd_id); } else //校验和不正确,则可能发生帧错误,重新开始 {seri_count=0; sum_check=0; } } } x++; if(x==12)x=0; LED0=!LED0; delay_ms(1000); } } [/mw_shl_code]


[/mw_shl_code]


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

19

主题

107

帖子

0

精华

初级会员

Rank: 2

积分
195
金钱
195
注册时间
2015-3-5
在线时间
44 小时
 楼主| 发表于 2015-5-28 22:43:07 | 显示全部楼层
在原子哥的代码上改的,可以在mini板V3上直接跑,但如果串口收到的数据不是以0x5A开头的且校验和正确的话,LCD上是不会显示Roll,pitch和Votagle这三行数据的。。。。。。

DataOnLcd.zip

2.08 MB, 下载次数: 2375

回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165524
金钱
165524
注册时间
2010-12-1
在线时间
2116 小时
发表于 2015-5-28 23:15:02 | 显示全部楼层
谢谢分享.....
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

9

帖子

0

精华

新手上路

积分
42
金钱
42
注册时间
2015-7-20
在线时间
4 小时
发表于 2015-12-19 17:46:29 | 显示全部楼层
谢谢楼主,有收获
年轻不要吧钱看的太重要。
回复 支持 反对

使用道具 举报

13

主题

201

帖子

0

精华

高级会员

Rank: 4

积分
671
金钱
671
注册时间
2015-4-26
在线时间
173 小时
发表于 2015-12-29 15:28:57 | 显示全部楼层
如果要是用串口调试助手怎么搞?
回复 支持 反对

使用道具 举报

1

主题

3

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2015-1-18
在线时间
10 小时
发表于 2016-1-18 14:17:06 | 显示全部楼层
同样的问题感谢楼主分享
回复 支持 反对

使用道具 举报

2

主题

5

帖子

0

精华

新手上路

积分
43
金钱
43
注册时间
2015-11-23
在线时间
3 小时
发表于 2016-4-20 17:26:09 | 显示全部楼层
这个不能处理不定字节长度的数据,不知道有没有处理变字节长度的例子
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
24
金钱
24
注册时间
2014-9-20
在线时间
3 小时
发表于 2016-5-11 15:25:49 | 显示全部楼层
感谢楼主  ,我是32小白  在使用你的程序的时候发现读出来的数据不会变化  ,不知道是什么原因呢
回复 支持 反对

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
135
金钱
135
注册时间
2014-9-17
在线时间
20 小时
发表于 2016-5-16 17:14:34 | 显示全部楼层
氤朦湖 发表于 2016-4-20 17:26
这个不能处理不定字节长度的数据,不知道有没有处理变字节长度的例子

不知道你这个程序弄得怎么样了我最近也在弄这方面,我们可以互相探讨一下
回复 支持 反对

使用道具 举报

2

主题

43

帖子

0

精华

初级会员

Rank: 2

积分
196
金钱
196
注册时间
2015-12-8
在线时间
43 小时
发表于 2016-5-16 19:27:16 | 显示全部楼层
楼主可以在中端里面创建一个数组       for(i=0;i<seriou_count;i++)
{
       uart2recevedate[ i ]=USART_ReceiveData(USART1);
}
然后再 一位一位的从数组里面取出与协议对比 ;
回复 支持 反对

使用道具 举报

2

主题

1446

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
2246
金钱
2246
注册时间
2010-12-16
在线时间
202 小时
发表于 2016-5-16 19:57:19 | 显示全部楼层
加菲老爷 发表于 2016-5-16 17:14
不知道你这个程序弄得怎么样了我最近也在弄这方面,我们可以互相探讨一下

数据变长 , 长度值还是固定的 , 先将长度作为数据读取并校验 , 后面再附上数据和校验就可以 .
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
135
金钱
135
注册时间
2014-9-17
在线时间
20 小时
发表于 2016-5-17 15:28:36 | 显示全部楼层
shihantu 发表于 2016-5-16 19:57
数据变长 , 长度值还是固定的 , 先将长度作为数据读取并校验 , 后面再附上数据和校验就可以 .

我最近在弄一组数据,这个数据就是变化的
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2016-6-4
在线时间
0 小时
发表于 2016-6-4 23:44:47 | 显示全部楼层
首先谢谢楼主的分享,我最近在linux系统下弄串口,我的数据是int型的,我是不是需要转化成unsigned char,然后用write写数据到FT245BL中去。
回复 支持 反对

使用道具 举报

34

主题

178

帖子

0

精华

初级会员

Rank: 2

积分
142
金钱
142
注册时间
2013-11-1
在线时间
183 小时
发表于 2016-9-28 22:46:48 | 显示全部楼层
有参考意义
回复 支持 反对

使用道具 举报

8

主题

50

帖子

0

精华

初级会员

Rank: 2

积分
119
金钱
119
注册时间
2017-3-12
在线时间
27 小时
发表于 2017-6-25 03:15:23 | 显示全部楼层
大神   后面的x++;if(x==12)x=0;是否可以省略?
回复 支持 反对

使用道具 举报

6

主题

69

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
219
金钱
219
注册时间
2017-8-31
在线时间
45 小时
发表于 2017-11-30 17:29:35 | 显示全部楼层
非常具有参考,价值,感谢分享!!!
回复 支持 反对

使用道具 举报

1

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
67
金钱
67
注册时间
2016-8-19
在线时间
10 小时
发表于 2017-12-7 10:01:16 | 显示全部楼层
棒棒哒,谢楼主
回复 支持 反对

使用道具 举报

1

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
67
金钱
67
注册时间
2016-8-19
在线时间
10 小时
发表于 2017-12-7 10:02:16 | 显示全部楼层

我发现好多人都是要接收不定长数据。我也是
回复 支持 反对

使用道具 举报

0

主题

4

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2018-3-4
在线时间
4 小时
发表于 2018-6-12 21:37:48 | 显示全部楼层
mark一下
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
39
金钱
39
注册时间
2019-4-22
在线时间
11 小时
发表于 2019-10-22 15:59:11 | 显示全部楼层
为什么要使用WHILE去查找,大bug,
回复 支持 反对

使用道具 举报

0

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
52
金钱
52
注册时间
2019-6-28
在线时间
13 小时
发表于 2019-10-24 16:22:53 | 显示全部楼层
我记得论坛里面有个示例: 把串口通讯挂在DMA上 程序里面建立一个接收缓解区,然后启用串口接收空闲中断,调用库函数:DMA_GetCurrDataCounter 计算出当前帧的长度
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-1 02:23

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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