在调试中,常常需要把数据发送到PC端,而串口通常以字节为单位传送数据,这就需要将各种不同类型的数据转化成字符串的形式。我在调试485程序时,遇到几个问题。
我打算将一个浮点型数据传送到PC,用串口助手观察其数值,所以我定义了一个字符数组:长度为8(我的数据取值有限,精确度要求也不高);前4元素为整数部分(千位、百位、十位、个位);第五个元素为小数点;后三个元素是小数。这样,调用RS485_Write函数就可以将这个数组发送到485。这个函数定义如下:
void RS485_Write(u8* data,u16 len){ u16 i; DIR485_H ; for (i=0; i问题1:发送数据的长度问题
在调用RS485_Write函数时,我的字符数组长度是8,所以想到形参len的值应为8,但是,这么做经常会出错,最后一个字节总是不能显示出来,很郁闷。后来经俞老师点播:字符串的结尾有一个空字符作为结束,正是对这个空字符的处理,造成了错误。
事实上,像RS485_Write((u8*)"abc",sizeof("abc"));这样的应用是没有错的,但字符串的size是包含末尾的空字符的,所以这个函数将发送‘a’、‘b’、‘c’和‘\0’共4个字节,在PC接收端,也是这么默认的,在显示的时候,最后一个字符是舍弃的(实际上他也不可显示)。这就解释了我的千分位为什么消失了。将AsciiBuff数组的长度初始化为9,并给AsciiBuff[8]赋值为‘\0’,RS485_Write((u8*)AsciiBuff,9);就能解决问题。
但是,即使是原方法,我的串口也确实是完整发送了八个字符的,只不过是串口助手将我最后一个字节舍弃,这个做法应该是有点不妥的,我要不要为这个特性修改代码,还要看我所服务的上位机的程序到底怎么理解从串口上接收的字符。
问题2:数据的转换
之前,我的代码为:
u8 AsciiBuff[9];int HexToASCII(float data){ AsciiBuff[0] =( u8 )(data/1000)%10 + 0x30; AsciiBuff[1] =( u8 )(data/100)%10 + 0x30; AsciiBuff[2] =( u8 )(data/10)%10 + 0x30; AsciiBuff[3] =( u8 )((int)(data)%10) + 0x30; AsciiBuff[4] = 46; //小数点 AsciiBuff[5] = ( u8 )(data*10)%10 + 0x30; AsciiBuff[6] = (( u8 )(data*100))%10 + 0x30; AsciiBuff[7] = ((u8)(data*1000.0))%10 + 0x30; AsciiBuff[8] = (u8)'\0'; if(AsciiBuff[0]==48 && AsciiBuff[1]==48 && AsciiBuff[2]==48) { AsciiBuff[0] =32; AsciiBuff[1] =32; AsciiBuff[2] =32; } if(AsciiBuff[0]==48 && AsciiBuff[1]==48 ) { AsciiBuff[0] =32; AsciiBuff[1] =32; } if(AsciiBuff[0]==48) { AsciiBuff[0] =32; }}
关键在于这两句:
AsciiBuff[6] = ( u8 )(data*100)%10 + 0x30;
AsciiBuff[7] = ( u8 )((u8)(data*1000)%10) + 0x30;
之前总是出错,症状是小数最后两位(百分位、千分位)不准。想了很多可能性,包括考虑data*1000是不是超过了u8类型的最大值(确实可能超过255,但是c语言不会直接对字符数据进行运算,都是转换为整型处理,所以不会出问题)。都没有效果
后来改为:
AsciiBuff[0] =( u8 )(data/1000)%10 + 0x30; AsciiBuff[1] =( u8 )(data/100)%10 + 0x30; AsciiBuff[2] =( u8 )(data/10)%10 + 0x30; AsciiBuff[3] =( u8 )((int)(data)%10) + 0x30; AsciiBuff[4] = 46; AsciiBuff[5] = ( int )(data*10)%10 + 0x30; AsciiBuff[6] = (( int )(data*100))%10 + 0x30; AsciiBuff[7] = ((int)(data*1000.0))%10 + 0x30; AsciiBuff[8] = (u8)'\0';
这才实现。
也就是说,我u8必须改为int,这是为什么?难道u8的数据不能做取余的运算?不是有自动转化为整型么。我猜想可能是强制指派的类型,不会再自动变化,所以指派为u8后,不能再进行取余运算,但是编译器为什么不报错呢?
这仍是个疑问
问题3:二进制的精度
我要显示1121.553,显示出来的总是1121.552,这是二进制的精度问题,没有简单的办法解决,我的处理方法是不管他。
具体是这样的,AsciiBuff[7] = ((int)(data*1000))%10 + 0x30;就可以正确显示1152.553,问题出在1000.0上。汗一个
问题4:PC机的串口设置
之前传输485数据,波特率设为115200,但是接收总出问题,在发送数据之间需要很大的延时,需要5ms左右,这太长了。后来更改了串口的设置(设备管理器-端口),可以将延时缩小到500us。(这个延时对485是必要的)
总结
在发生问题后, 要全局考虑问题,判断问题出在哪,第一个、第四个问题就是这么定位原因的
要分析症状,将问题分解,比如数值转换的问题,那个赋值语句很长,将其分解为多条语句,一步一步观察其运行,最后得到解
写代码的时候,不要去刻意省略括号,一些运算符的优先级可能对计算产生很大影响,加上括号可以使代码意图很明显,也不会出错。整理好并不会显得繁复。