找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 23782|回复: 7
打印 上一主题 下一主题
收起左侧

如何将下位机采集的浮点型数据发送给上位机

[复制链接]
跳转到指定楼层
楼主
本帖最后由 xiao_yp2014 于 2016-1-21 14:21 编辑

大家好!我叫肖亚平,从小热爱上了电子技术,读书时阴差阳错的选择了电子这方面的专业,学习电子技术、一直到今年毕业。对于我的理解来说,学校里面学到的技术不是全部实用,但是必须有用,所以一直奋斗在前线。我对学习总结出一句话“压力不是有人努力,而是比你牛X N倍的人依然在努力

在有些项目中,需要单片机把数据处理完后,返回给上位机,数据处理离不成计算,有计算就会涉及到小数,那么,如何将小数转换成十六进制数据呢,下面请看简便算法。

上位机发送命令:01 02 EB 00 55   
下位机返回-5.625 的十六进制数据 C0 B4 00 00


  1. #include "REG52.H"

  2. #define const_voice_short  40 //蜂鸣器短叫的持续时间
  3. #define const_rc_size  10     //接收串口中断数据的缓冲区数组大小

  4. #define const_receive_time  5 //如果超过这个时间没有串口数据过来,就认为一串数据已经全部接收完,这个时间根据实际情况来调整大小

  5. unsigned char xdata array[10];//缓存数组

  6. float value = -5.625;                  //模拟下位机采集经过计算后的数据
  7. /*
  8. -5.625 = C 0 B 4 0 0 0 0

  9. C    0     B   4     0   0    0    0
  10. 1100 0000 1011 0100 0000 0000 0000 0000
  11. 按照浮点数格式切割成相应的域 1 1000 0001 01101 000000000000000000
  12. 经分析:符号域1 意味着负数;指数域为129 意味着实际的指数为2 (减去偏差值127);
  13. 尾数域为01101 意味着实际的二进制尾数为1.01101 (加上隐含的小数点前面的1)。
  14. 所以,实际的实数为:
  15. = -1.01101 × 2^2 =- ( 1*2^0 + 1*2^(-2) + 1*2^(-3) + 1*2^(-5) ) × 2^2
  16. = -(1+0.25+0.125+0.03125)*4
  17. = -1.40625*4
  18. = -5.625
  19. */

  20. void initial_myself(void);   
  21. void initial_peripheral(void);
  22. void delay_long(unsigned int uiDelaylong);



  23. void T0_time(void);       //定时中断函数
  24. void usart_receive(void); //串口接收中断函数
  25. void usart_service(void); //串口服务程序,在main函数里

  26. sbit beep_dr=P3^6; //蜂鸣器的驱动IO口

  27. unsigned int  uiSendCnt=0;     //用来识别串口是否接收完一串数据的计时器
  28. unsigned char ucSendLock=1;    //串口服务程序的自锁变量,每次接收完一串数据只处理一次
  29. unsigned int  uiRcregTotal=0;  //代表当前缓冲区已经接收了多少个数据
  30. unsigned char ucRcregBuf[const_rc_size]; //接收串口中断数据的缓冲区数组
  31. unsigned int  uiRcMoveIndex=0;  //用来解析数据协议的中间变量


  32. unsigned int  uiVoiceCnt=0;  //蜂鸣器鸣叫的持续时间计数器

  33. void usart_send();                //串口发送程序
  34. void  Change();                        //将浮点弄转换成字符弄

  35. void main()
  36. {
  37.         initial_myself();  
  38.         delay_long(100);   
  39.         initial_peripheral();

  40.         while(1)  
  41.         {
  42.            usart_service();  //串口服务程序
  43.         }
  44. }

  45. union                            //联合体定义
  46. {
  47.     char a[4];
  48.         float b;
  49. }temp;

  50. void usart_service(void)  //串口服务程序,在main函数里
  51. {
  52. /* 注释一:
  53. * 识别一串数据是否已经全部接收完了的原理:
  54. * 在规定的时间里,如果没有接收到任何一个字节数据,那么就认为一串数据被接收完了,然后就进入数据协议
  55. * 解析和处理的阶段。这个功能的实现要配合定时中断,串口中断的程序一起阅读,要理解他们之间的关系。
  56. */
  57.      if(uiSendCnt>=const_receive_time&&ucSendLock==1) //说明超过了一定的时间内,再也没有新数据从串口来
  58.      {

  59.             ucSendLock=0;    //处理一次就锁起来,不用每次都进来,除非有新接收的数据

  60.                     //下面的代码进入数据协议解析和数据处理的阶段
  61.                uiRcMoveIndex=uiRcregTotal; //由于是判断数据尾,所以下标移动变量从数组的最尾端开始向0移动

  62.             while(uiRcMoveIndex>=5)   //如果处理的数据量大于等于5(2个有效数据,3个数据头)说明还没有把缓冲区的数据处理完
  63.             {
  64.                if(ucRcregBuf[uiRcMoveIndex-3]==0xeb&&ucRcregBuf[uiRcMoveIndex-2]==0x00&&ucRcregBuf[uiRcMoveIndex-1]==0x55)  //数据尾eb 00 55的判断
  65.                {
  66.                                         if(ucRcregBuf[uiRcMoveIndex-5]==0x01&&ucRcregBuf[uiRcMoveIndex-4]==0x02)  //有效数据01 02的判断
  67.                                         {
  68.                                                 usart_send();        //收到正确的数据后,开发发送采集到的数据
  69.                                                 uiVoiceCnt=const_voice_short; //蜂鸣器发出声音,说明数据发送完毕
  70.                                                 
  71.                                         }
  72.                   break;   //退出循环
  73.                }
  74.                uiRcMoveIndex--; //因为是判断数据尾,下标向着0的方向移动
  75.            }

  76.            uiRcregTotal=0;  //清空缓冲的下标,方便下次重新从0下标开始接受新数据
  77.      }

  78. }

  79. void usart_send()           //串口发送
  80. {
  81.     static unsigned int ucSendCnt = 0;

  82.         ES = 0; //关串口中断
  83.         TI = 0; //清零串口发送完成中断请求标志
  84.         Change();
  85.         
  86.         for(ucSendCnt = 0;ucSendCnt<4;ucSendCnt++)//发送四个字节,表示一个浮点数
  87.         {
  88.              SBUF = array[ucSendCnt];                        //将数据装入缓冲区
  89.              delay_long(50);                                        //延时一会儿
  90.         }
  91.         
  92.         TI = 0; //清零串口发送完成中断请求标志
  93.         ES = 1; //允许串口中断
  94. }

  95. /*
  96. 联合休:
  97. 当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。
  98.      在C Programming Language 一书中对于联合体是这么描述的:
  99.      1)联合体是一个结构;
  100.      2)它的所有成员相对于基地址的偏移量都为0;
  101.      3)此结构空间要大到足够容纳最"宽"的成员;
  102.      4)其对齐方式要适合其中所有的成员;

  103. 下面解释这四条描述:
  104.      由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量
  105.          的基地址的偏移量为0,即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存,
  106.          因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是
  107.          指其必须符合所有成员的自身对齐方式。
  108. */
  109. void  Change()
  110. {
  111.      temp.b   = value;                //将数据存入联合休中
  112.      array[0] = temp.a[0];        //一个字节一个字节的取出来
  113.      array[1] = temp.a[1];
  114.      array[2] = temp.a[2];
  115.      array[3] = temp.a[3];
  116. }

  117. void T0_time(void) interrupt 1    //定时中断
  118. {
  119.         TF0=0;  //清除中断标志
  120.         TR0=0; //关中断
  121.         
  122.          uiSendCnt++;    //表面上这个数据不断累加,但是在串口中断里,每接收一个字节它都会被清零,除非这个中间没有串口数据过来

  123.         if(uiSendCnt>=const_receive_time)   //如果超过这个时间没有串口数据过来,就认为一串数据已经全部接收完
  124.         {
  125.             ucSendLock=1;     //开自锁标志
  126.         }
  127.         
  128.         if(uiVoiceCnt!=0)
  129.         {
  130.             uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
  131.             beep_dr=0;  //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
  132.         }
  133.         else
  134.         {
  135.             ; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
  136.             beep_dr=1;  //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
  137.         }
  138.         
  139.         
  140.         TH0=0xfe;   //重装初始值(65535-500)=65035=0xfe0b
  141.         TL0=0x0b;
  142.         TR0=1;  //开中断
  143. }


  144. void usart_receive(void) interrupt 4                 //串口接收数据中断        
  145. {        

  146.         if(RI==1)  
  147.         {
  148.                 RI = 0;
  149.                 ++uiRcregTotal;

  150.                 if(uiRcregTotal>=const_rc_size)  //超过缓冲区
  151.                 {
  152.                     uiRcregTotal=const_rc_size;
  153.                 }
  154.                 ucRcregBuf[uiRcregTotal-1]=SBUF;   //将串口接收到的数据缓存到接收缓冲区里
  155.                 uiSendCnt=0;  //及时喂狗,虽然main函数那边不断在累加,但是只要串口的数据还没发送完毕,那么它永远也长不大,因为每个中断都被清零。
  156.         }
  157.         else  //我在其它单片机上都不用else这段代码的,可能在51单片机上多增加" TI = 0;"稳定性会更好吧。
  158.         {
  159.                 TI = 0;
  160.         }

  161. }                                


  162. void delay_long(unsigned int uiDelayLong)
  163. {
  164.    unsigned int i;
  165.    unsigned int j;
  166.    for(i=0;i<uiDelayLong;i++)
  167.    {
  168.       for(j=0;j<50;j++)  //内嵌循环的空指令数量
  169.           {
  170.              ; //一个分号相当于执行一条空语句
  171.           }
  172.    }
  173. }


  174. void initial_myself(void)  //第一区 初始化单片机
  175. {

  176.           beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
  177.         
  178.           //配置定时器
  179.           TMOD=0x01;  //设置定时器0为工作方式1
  180.           TH0=0xfe;   //重装初始值(65535-500)=65035=0xfe0b
  181.           TL0=0x0b;
  182.         
  183.         
  184.           //配置串口
  185.           SCON=0x50;
  186.           TMOD=0X21;
  187.           TH1=TL1=-(11059200L/12/32/9600);  //这段配置代码具体是什么意思,我也不太清楚,反正是跟串口波特率有关。
  188.           TR1=1;        
  189. }

  190. void initial_peripheral(void) //第二区 初始化外围
  191. {

  192.            EA=1;     //开总中断
  193.            ES=1;     //允许串口中断
  194.            ET0=1;    //允许定时中断
  195.            TR0=1;    //启动定时中断
  196. }
复制代码




分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏6 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:67796 发表于 2015-1-29 22:35 | 只看该作者
标题是不是写错了?
帖子内容讲的是联合体字节流。
这个方法比效率比sprintf高,但是代码较多,适合于大量源数据采集/传输。
回复

使用道具 举报

板凳
ID:69862 发表于 2015-1-29 23:01 | 只看该作者
exv 发表于 2015-1-29 22:35
标题是不是写错了?
帖子内容讲的是联合体字节流。
这个方法比效率比sprintf高,但是代码较多,适合于大 ...

标题没有错,我的主导思想是联合体共用内存首地址一个优点,也可以叫做是缺点来为我办事,你也可以晒晒你的方法。
回复

使用道具 举报

地板
ID:67796 发表于 2015-1-29 23:24 | 只看该作者
本帖最后由 exv 于 2015-1-29 23:27 编辑
xiao_yp2014 发表于 2015-1-29 23:01
标题没有错,我的主导思想是联合体共用内存首地址一个优点,也可以叫做是缺点来为我办事,你也可以晒晒你 ...

联合休==>联合体

这个别字太显眼了,看标题感觉很奇怪,点进来才知讲的联合体。

联合体的这个特点也不好说是缺点,就像一个可自由存取可分隔的盒子,在执行效率上有优势。

回复

使用道具 举报

5#
ID:69862 发表于 2015-1-30 00:57 | 只看该作者
exv 发表于 2015-1-29 23:24
联合休==>联合体

这个别字太显眼了,看标题感觉很奇怪,点进来才知讲的联合体。

回复

使用道具 举报

6#
ID:57234 发表于 2015-1-30 02:10 来自手机 | 只看该作者
现在正在回家的火车上面阅读楼主的帖子。刚刚从一个隧道里面出来。一看就知道楼主一定是一个代码高手,这个程序非常精辟,果断收藏了。我以前也是实现过类似的功能,也是需要把浮点数上传到上为机上位机里面。为了这个功能我头疼了很多天。后面还是在51hei论坛里面找到了一个函数先把浮点数转化为字符串,然后再把字符串到上位机。然后上位机程序再把字符串转化为浮点数据。勉为其难地解决了这个问题。楼主这个程序改天回去试一下。
回复

使用道具 举报

7#
ID:69862 发表于 2015-1-30 18:44 | 只看该作者
xiaoou 发表于 2015-1-30 02:10
现在正在回家的火车上面阅读楼主的帖子。刚刚从一个隧道里面出来。一看就知道楼主一定是一个代码高手,这个 ...

希望对你有所帮助,带着回家喜悦的心情,阅读如此精彩的程序,相信你会满载而归的。
回复

使用道具 举报

8#
ID:249808 发表于 2023-8-24 19:13 | 只看该作者
xiaoou 发表于 2015-1-30 02:10
现在正在回家的火车上面阅读楼主的帖子。刚刚从一个隧道里面出来。一看就知道楼主一定是一个代码高手,这个 ...

我也是这么操作的……
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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