上位机与下位机的交流:编码与字节序的实验

上位机与下位机的交流:编码与字节序的实验
下午在南区的讨论班居然提前结束了,而且,到预定吃晚饭的时候还剩两个半小时,按包师弟的说法,真是个尴尬的时间啊。干做两个半小时,太无聊,如果赶回所编程序,似乎时间又不太够的样子。我甚至想提议去暴走吧,但是想到冷得冻肺子的天气,想到包师弟和我都正感冒,也就只是想想。
包师弟说:要编这程序的想法,真是搅得人…
我忘了他原来的措词,为避免他跑上来纠正,只好留了空白。大意是,热血沸腾啊,夜不能寐啊之类的。现场表现是坐立不安。
好吧。
我们迅速用了十来分钟重新回顾了一下以前做过的计划,然后跑出来打车去所里。
1. 实验计划及基本原理
我们要做的是一个原型实验,确定STM32某芯片 (意法半导体)
的浮点型变量的编码方案与PC[http://en.wikipedia.org/wiki/IEEE_754-2008]是相同的,且大小头
(Endianness) 也是一样的。
几天来,包师弟已经把下位机的板子都整完了,昨天半夜的时候邮件说USB芯片及驱动都整完了。
我们计划的技术路线是:由上位机通过USB向下位机发送一个浮点数的二进制形式,下位机接收到以后,写到串口,然后用PC从串口读了显示出来。
如果PC下发的和最后显示的如果不一致,即可判定STM32与PC使用了不同的浮点变量编码。我们希望一致。
为初学者同学们解释一下,这并非是为了验证PC-STM32-串口的链路是正常的,而是为了验证上位机与下位机使用相同的方式表示一个浮点。
上位机就是PC,下位机是单片机或者ARM芯片的机器。
有的同学可能会问:难道上位机和下位机表示同一个数,会使用不同的方式,不都是"3.14"这样的形式么?
可能不相同,一定不是"3.14"这样的形式。因为我们知道,计算机只接受二进制,而"3.14"显示不是二进制。所以"3.14"是以一种有点诡异的方式表示的。
(1) 编码
在上位机中,一个浮点数,比如3.14,需要经过归一化、求符号、求指数、求有效数字、求尾数……一系列的步骤,终于变成了下面的形式。
如果是32位系统,用十六进制表示就是: 4048F5C3
具体的步骤和4048F5C3中每个二进制位的含义略过,感兴趣的同学请去文后的两个地址自行观赏。
我和包师弟打算用实验确认,上位机与下位机使用了相同的计算方法,或者说,相同的编码方法。即,当上位机发送3.14的时候,它发送4048F5C3这四个字节;当下位机接收4048F5C3这四个字节的时候,它认为这是3.14。
上位机与下位机使用相同的语言,才能通信。如果一方对另一方一无所知,还要指导,情况就可能比较糟糕了。
比如下面的对话。
上位机:你最喜欢莎士比亚的什么作品。
请注意上位机知道莎士比亚是一个人,而是有作品的人,不是意法半导体生产的某个芯片型号。
下位机:英雄双行体。
上位机:……
如果上位机在日本动画里,估计会说"那呢?"。我文学基本白痴,以前从来只知道莎士比亚还写十四行诗,从来没听说过英雄双行体这种东西,不过上位机似乎更白痴一些,他似乎认定莎士比亚就只写过戏剧。
恩,我比较喜欢李清照的词。
上位机:你读过莎士比亚的哪部作品?
下位机:我演过麦克白中的巫婆。
上位机:你是演员?
上位机显然不知道,而不是在调笑,他不知道英文专业学习英文的重要方法之一,从高中的时候就是这样,就是表演英文经典名著或者非名著。每个学英语的好像还都有剧照什么的。
不幸的是,下位机也不知道上位机如此无知。
上位机:你英语这么好,为什么要学英语呢?
下位机:我们为什么要学汉语呢?
我补充一下:因为英语系根本不是学英语的,就像下位机的回答所暗示的,中文系不是学中文的,历史系不是学历史演义的,而计算机系也不是学计算机的。很多英语系还有个全称,叫英语言文学,中文系一般还有个名字,叫汉语言文学,历史系……历史系好像叫历史文化学院,计算机系则根本不学怎么修理计算机拆壳子什么的。
深入地研究一个东西,跟简单的使用,根本就是天壤之别。
有人可能误认为:上位机误会了自己的身份,那些指责只有BOSS们才有资格做。
不,不是这样。没有人有资格指责别人,尤其,尤其,尤其是你身居高位的时候。身为BOSS,被安排居高临下,你对看不惯的人唯一能说的话也就是:我们不适合你。被你指责的那个人,没有花钱请你做他的顾问。
上位机下位机,不过是为了称呼方便给的一个名字,你真以为自己就高人一等了,你当小资的时候口口声声赞扬的独立宣言里有一句"人生而平等"不知道是不是忘光了。你花钱买到的不过是劳动时间,并不包括人的尊严。
所以,如果编码不同,还是承认不同罢了,少扯别人低级自己高级什么的。
又,故做平易近人和蔼可亲低下头来和你说话这样的姿态的,也不是什么好鸟,因为他们假设自己高人一等。
(2)大小头
目前有两种Endianness在计算机中普遍存在,一种是,在内存里,4048F5C3就表示为4048F5C3,这个叫做 big endian,大头儿。
有同学说,还能有啥表示方法?
还有一种叫 little endian,小头儿,4048F5C3会表示成C3F54840,正好按逆序排列每个字节。
有同学可能说,居然还有小头儿这么诡异的表示方法。是啊,事实上,我们一般用的PC,x86体系的,都是小头儿,是诡异的那一方。而ARM,是大头那一方。
大头小头,这个名字来自格列弗游记,两个小人国战争的原因是,到底吃鸡蛋应该从大头还是小头先打碎。这个问题也困扰计算机网络,发送数据前,也可能需要重排一下字节的顺序。
说到这里插一句,有意思的是,在小头的x86体系中,浮点数恰恰很诡异地使用了大头方案。这是由于历史的原因,你看,我们已经是一门有历史的学科了。
以上,我和包师经要验证的是上位机 (并非多么尊贵) 与下位机 (并非理应谦卑)使用了相同的 (1)编码、 (2)大小头。
可能有的同学会说:查下手册不就完了么。大多数时候我乐意相信手册,不过昨天马利同学刚刚抱怨了Thinkphp手册之不咋地,还是小心为妙。
2. 实验环境
上位机一台,跑windows7,64位CPU。杨注:可能运行在32位模式,或者应用程序工作在32位模式。
上位机的应用程序包括:
* labview
* symbolink 可能是这个名字,不能确认了。windows驱动开发工具之一,用来显  示驱动提供用来读写的文件名的。
* 虚机里跑了个windows xp,里面有个visual studio.
* 下位机的交叉编译和烧写软件
* 串口通信工具,用来看下位机发到串口的东西
* bushond
下位机:
* 意法办导体STM32某芯片,USB驱动已完成,串口驱动已完成
其他的硬件环境包括:我和包师弟各据一个显示器,连接在同一台上位机上,两套鼠标两套键盘。
两套鼠标两套键盘的结对编程方式,是ZHUMAO同学最先采用的,我们两个似乎当时调的是个JAVA程序。两套输入设备,结对时就不必抢键盘了,只要说一句"放着我来",就行了。论起争执和争夺,嘴还是比手快。
其他的硬件环境,我在实验其间还征用了关同学的座机一会,用来拷出symbolink,包师弟其间用过他的笔记本一阵,查资料。
还用了一个不知道什么软件,为了计时工作,西红柿时间管理。一共两次迭代,第二次严重超时,刚好在最后一分钟结束所有工作。
2. 实验过程
* 下位机输出到串口,浮点变量
改下位机程序,把浮点数,比如3.14,忘记当时用的是什么了,输出到串口。用上位机的串口通信软件读,符合3.14。
这步是为了确认串口通信及下位机的浮点数格式化输出正确。
float a = 3.14;printf("%f", a);
我们观察到了单精度浮点数精度不够的情况,略。
* labview,不知道如何把浮点写下去
用labview,打算把浮点数对应的二进制四个字节写到USB口。遇到个问题,不知道应该怎么取得浮点数对应的二进制那四个字节。
这时,我们转到了另一个技术路线。
请某些同学注意,这里的另一个技术路线,原本就是我们计划的技术路线之一,而不是因为前一路线"可能"不通,而临时想到的。同时,我们非常确认另一条技术路线可能,而且我和包师弟各喜欢一条路线,所以才会转向。这与某些同学"一遇困难就转向,再遇困难再转回来,始终也不知道毛病在哪里,这个方法到底行不行"是完全不同的。事实上,我们基本确认labview有这样的能力,只是不打算再花时间。前面提过,我们原计划加交通也只有两个半小时。
* C,不知道symbolic link name
然后,开始用C写USB口,但是CreateFile的时候不知道驱动给提供的文件名(symbolic
link)是什么,因为正使用labview的驱动,不是我们自己写的那个版本。
自己马上写个驱动,也是方法,但是我们需要马上架起驱动开发的环境。原来有个驱动,但当前的这块板子vid,pid,endpoint设置全不一样。时间关系,也放到一边。请还是那部分同学注意,我们仍随时可以开始这一路线。
* 用symbolink (?) ,不能确定文件名是正确的,CreateFile没有发出数据
开关同学的机器,找到了能查驱动提供的文件名的工具。
这里有个插曲,我们拥有N多个虚拟机,以致于找到需要的那个,花了不少时间。后来全盘搜索才找到。
当时我想起了之前某次"结对"编程,我的身后有四位同学,包独坐一边,还是上面的硬件环境,我们把众多文件在真实的和虚拟的共达七台计算机间来回挪腾,最终一切都跑了起来。大家说,这很盗梦空间。
不过,这一次比较不幸,我按symbolikn查到的文件名发数据,下位机没有接收到。
* 包接受用C写用文件,然后用labview读
这时包提到一个之间的方案:我用C语言把float变量的二进制写到一个文件中,然后包用labview读进来,按字节读。
我说:咱们还是直接用手写吧。
* 用C输出float的4个字节
我们用C程序,把float变量对应的十六进制,显示在控制台上。这样:
#include <stdio.h>main(){    float a = 3.14;    printf("%x", *
(unsigned char*) &a);    printf("%x", * ((unsigned char*) &a + 1) );
printf("%x", * ((unsigned char*) &a + 2) );    printf("%x", *
((unsigned char*) &a + 3) );}
* 用bushound,而不是labview写到下位机
然后,我们没有用labview,而是用另一个更轻量级的工具,bushound,直接向usb的endpoint中写入了十六进制。
* 下位机,强制转换指针类型
在下位机中,我们按收到数据以后,把这四个字节十六进制转换成了浮点型变量,然后printf到串口。大致这样:
printf("%f", * (float*) buf);
* 上位机显示下位机串口
上位机显示,下位机的串口输出了 3.14。
我们又改了几个浮点数,由上位机把C算出来的十六进制通过USB口写给下位机,下位机又通过串口把这四个字节当成浮点型变量格式化输出。上位机显示下位机的串口输出,与我们预期的值是相同的。
* 然后,我们去东方肉馆吃肉去了。
虽然我的挂钩还疼着,尤其嚼脆骨的时候,不过心情不错。当你知道这个世界上有一些人与你用相同的语言交流,有些相同的习惯,比如大半夜的上网发贴子看贴子,怎么能心情不好。
就让那些 听不懂别人说话,还命令别人按自己的方式说话的 上位机孤独去吧。
3. 结论
浮点型单精度变量,PC与ARM使用了相同的编码和字节序。
参考文献:
[http://en.wikipedia.org/wiki/IEEE_754-2008][http://babbage.cs.qc.cuny.edu/IEEE-754/]

Leave a Reply