打印

电脑(PC)通过串口向单片机发送浮点数(float、double)的方法

[复制链接]
8508|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
maswell_xiao|  楼主 | 2013-6-4 15:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
电脑(PC)通过串口向单片机发送浮点数(floatdouble)的方法
version1.0
maswellxiao
         在实际应用中我们可能会遇到这样一个问题,PC机要向单片机发送一个浮点数,float类型、double类型也好。maswell我曾经做过AD转换并发送数据到MATLAB中做FFT、滤波器等数字信号处理。处理完毕以后maswell我遇到一个非常棘手的问题……MATLAB数据处理完毕以后得到的数据类型是小数类型,就算忽略后几位精度,最起码也是一个float数据类型的数,怎么把这个数发送到单片机呢?
         经过几天的折腾。maswell我一不小心就实现PC通过串口向单片机发送浮点小数。下面把这种方法分享给大家。
         首先我们需要一个预备知识。也就是float数据类型在单片机中的存储方式。
         float数据类型总共占据32个位bit,其中第一个位为数据符号(Symbol,在下面简称为S)该位表示数据的正负性。接下来8个位是阶码(Expoent,下面简称为E),这8个位表示浮点数的小数点的位置。最后有23位的尾数(mantissaM),这23个位表示数据。下面做个示意图
  
1S
  
8个位E
23个位M
         例如:十进制的数据N
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png
将之换成二进制表示
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image006.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image008.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image010.png
因此file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image012.png
并由公式file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image014.pngfile:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image016.png
组合起来就是
  
S
  
E
M
0
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image018.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image020.png
4bit一格隔开
  
0100
  
0010
1111
0110
1110
1001
0111
1001
4
2
F
6
E
9
7
9
因此十进制浮点小数N = 123.456在单片机里存储的数据为0x42F6E979
         

电脑(PC)向单片机发送浮点小数(float、double)的方法.pdf

449.88 KB, 阅读权限: 1

相关帖子

沙发
maswell_xiao|  楼主 | 2013-6-4 15:59 | 只看该作者
至此,我们的思路有了一点了。PC机向单片机发送十六进制代码0x42F6E979就意味着向单片机发送浮点小数123.456了。
下面是MATLAB向单片机发送数据的一段代码
function pushbutton7_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton7 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
global s;
global TxDataToMSP430;
TxDataHex = num2hex(single(TxDataToMSP430));
fwrite(s,'dcba','uchar');%协议密码
t1 =clock;
while 1
    if fread(s,1) == hex2dec('af');
        break;
    end
    if etime(clock , t1) > 1%1秒钟内未收到数据则放弃
        break ;
    end
end
TxDataHexTemp(1) = TxDataHex(1);
TxDataHexTemp(2) = TxDataHex(2);
fwrite(s,base2dec(TxDataHexTemp,16),'uchar');%协议数据
         我当初做的是一个GUI界面,函数名是MATLAB自动生成的,我的下位机用的是MSP430G2553单片机。编译环境CCS5.2
volatile float DisplayData1,DisplayData2,DisplayData3,DisplayData4;
volatile long DisplayData;//接收到的数据转换前数据
unsigned char TXVolFlag = 0;
static char FrameHeaderFlag = 1;//识别帧头标志位
static char StopTxFlag = 0;//上位机发送停止发送标志
unsigned char ReadDataClassFlag = 0x00;//读数据包格式
         以上为全局变量。注意DisplayData为long数据类型
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
    static char k = 0;
    if(FrameHeaderFlag)
    {
        ReadDataClassFlag = IdentifyHeader();
        if(ReadDataClassFlag != 0x00)//识别帧头
            FrameHeaderFlag = 0;
    }
    else//上一次读到的temp不是0,说明握手成功
    {
        switch(ReadDataClassFlag)
        {
        case 0xaf:
        case 0xbe:
        case 0xcc:
        case 0xdb:
            switch(k)
            {
            case 0:
                DisplayData = UCA0RXBUF;
                DisplayData <<= 8;
                k = 1;
                break;
            case 1:
                DisplayData += UCA0RXBUF;
                DisplayData <<= 8;
                k = 2;
                break;
            case 2:
                DisplayData += UCA0RXBUF;
                DisplayData <<= 8;
                k = 3;
                break;
            case 3:
                DisplayData += UCA0RXBUF;
                k = 0;
                switch(ReadDataClassFlag)
                {
                ///////////////////////////////////接收数据,以下是接收数据的控制字
                case 0xaf:DisplayData1 = *((float *)&DisplayData);break;
                case 0xbe:DisplayData2 = *((float *)&DisplayData);break;
                case 0xcc:DisplayData3 = *((float *)&DisplayData);break;
                case 0xdb:DisplayData4 = *((float *)&DisplayData);break;
                default:;
                }
                FrameHeaderFlag = 1;//帧数据接收完毕,32个bit数据为一帧
                break;
            default:;
            }
            break;
        case 0xfa://停止发送的密码
            IdentifyHeader();
            break;
        default:;
        }
    }
}
我这个函数是MSP430G2553单片机的接受中断函数,单片机串口接收到数据后进入中断,首先判断是不是协议的帧头(我这里自行定义了一个协议)如果不是帧头,则是数据,进入数据的判断,由于PC机发送的是32位的数据,而UART只支持接收8位的数据,故此用了四个case语句判断。接收完32位的数据以后,问题又来了,我们用来存储接收来的数据是long类型(volatile long DisplayData;)的。而我们需要接收的数据是float数据类型的。故此还需要一个转换。其实这个转换很简单。一个语句就可以了。
DisplayData1 = *((float *)&DisplayData);
首先将DisplayData的地址强制转换为float数据类型的地址,然后再对地址取值就可以了。至此,就可以完整地将long数据类型转换为float数据类型。
         那么一整个过程就可以完整地实现,从PC机发送1、0代码到MSP430单片机接收为long数据类型,再由long数据类型强制转换为float数据类型。
         那么同样道理,我们可以用同样的方法将long long数据类型转换为double数据类型。

PS :maswell我建了个全国大学生电子设计竞赛的交流群,群号171486219,1000人群,还没满,大家可以加进去一起交流哦。
这个文档我做成了PDF,PDF下载见附件哦~






maswell xiao
于桂林电子科技大学信息与通信学院
科技协会科技楼实验室
2013/3/30
maswell_xiao@foxmail.com
桂电二院科协出品
本文版权所有,转载不究

使用特权

评论回复
板凳
electric-lover| | 2015-10-3 15:16 | 只看该作者
lz你好  ,你的程序我有一个点不明白。我们每次接受一个float数据,只需把这个数据从long转换成float就行了,为什么这里要分四种情况         
                case 0xafisplayData1 = *((float *)&DisplayData);break;
                case 0xbeisplayData2 = *((float *)&DisplayData);break;
                case 0xccisplayData3 = *((float *)&DisplayData);break;
                case 0xdbisplayData4 = *((float *)&DisplayData);break;
               希望楼主解释一下

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

17

帖子

0

粉丝