串口通信中的int float型數據的處理和發送

在做下位機通信時往往會用到串口,包括下位機將數據傳輸給上位機,或者是下位機與下位機之間進行數據傳輸,這時候就會遇到發送數據的問題,單片機通過串口發送數據時往往是一次一個字節(8位),如果傳輸char(8位)型數據則很好辦,只需要直接發送就可以了,但是在發送int型數據和float型數據時就會稍微有些複雜。

    下面就以常用的8位單片機89c51爲例來進行說明。

    當發送int型或long型數據時還比較簡單,一個int型數據是16位,long是32位,把int型/long型數據變成2/4個char型數據發送出去就可以了,程序如下

void long_char(unsigned long l,unsigned char *s)
{
    *s = l>>24;
    *(s+1) = l>>16;
    *(s+2) = l>>8;
    *(s+3) = l;  
}

在串口助手上就可以接收到相應的long型數據了。

    當發送float型數據時稍微有些複雜。下面簡單介紹下float型數據在內存中的存儲方式(double類似,以下部分參考了別人的博客)。

    float遵從的是IEEE R32.24 在存儲中都分爲三個部分:
1.符號位(Sign) : 0代表正,1代表爲負 
2.指數位(Exponent):用於存儲科學計數法中的指數數據,並且採用移位存儲 
3.尾數部分(Mantissa):尾數部分

float的存儲方式如下圖所示:

    R32.24和R64.53的存儲方式都是用科學計數法來存儲數據的,比如8.25用十進制的科學計數法表示就爲:8.25*clip_image0021,而120.5可以表示爲:1.205*clip_image0022

    而計算機根本不認識十進制的數據,他只認識0,1,所以在計算機存儲中,首先要將上面的數更改爲二進制的科學計數法表示,8.25用二進制表示可表示爲1000.01,120.5用二進制表示爲:1110110.1。用二進制的科學計數法表示1000.01可以表示爲1.00001*clip_image002[2],1110110.1可以表示爲1.1101101*clip_image002[3],任何一個數都的科學計數法表示都爲1.xxx*clip_image002[1],尾數部分就可以表示爲xxxx,第一位都是1,所以可以將小數點前面的1省略,所以23bit的尾數部分,可以表示的精度卻變成了24bit,道理就是在這裏,那24bit能精確到小數點後幾位呢,我們知道9的二進制表示爲1001,所以4bit能精確十進制中的1位小數點,24bit就能使float能精確到小數點後6位,而對於指數部分,因爲指數可正可負,8位的指數位能表示的指數範圍就應該爲:-127-128了,所以指數部分的存儲採用移位存儲,存儲的數據爲元數據 127,下面就看看8.25和120.5在內存中真正的存儲方式。

     首先看下8.25,用二進制的科學計數法表示爲:1.00001*clip_image002[2]

按照上面的存儲方式,符號位爲:0,表示爲正,指數位爲:3 127=130 ,位數部分爲,故8.25的存儲方式如下圖所示:

而單精度浮點數120.5的存儲方式如下圖所示:

 

那麼如果給出內存中一段數據,並且告訴你是單精度存儲的話,你如何知道該數據的十進制數值呢?其實就是對上面的反推過程,比如給出如下內存數據:0100001011101101000000000000,首先我們現將該數據分段,0 10000 0101 110 1101 0000 0000 0000 0000,在內存中的存儲就爲下圖所示:

根據我們的計算方式,可以計算出,這樣一組數據表示爲:1.1101101*clip_image002[3]=120.5而雙精度浮點數的存儲和單精度的存儲大同小異,不同的是指數部分和尾數部分的位數。

    介紹完了float型數據在內存中的存儲方式後能夠知道如何發送float型數據了,直接按照int型類似的發送肯定是不行的,這就需要採用指針的方法(在keil中數據的排放格式是大端模式):

void float_char(float f,unsigned char *s)
{
    unsigned char *p;
    p = (unsigned char *)&f;
    *s = *p;
    *(s+1) = *(p+1);
    *(s+2) = *(p+2);
    *(s+3) = *(p+3);
}

    通過這種方法把數組s發送出去,在接受端接受到的就是標準的IEEE754結構的原始數據,也就是float型數據在內存中存放的值,如果需要得到這個float型數據的值還需要進行一個轉換。

    這種方法比較簡單明瞭,這時候的串口接收端可以用現成的,不需要自己編寫。

    還可以採用共用體的方法,如果採用共用體時串口的接收端軟件需要自己編寫。

    我們知道共用體可以使不同的數據類型來共享相同的地址空間,所以程序如下:

void float_char(float f,unsigned char *s)
{
    union change
    {
        float d;
        unsigned char dat[4];
    }r1;
    r1.d = f;
    *s = r1.dat[0];
    *(s+1) = r1.dat[1];
    *(s+2) = r1.dat[2];
    *(s+3) = r1.dat[3];  
}

接收端採用同樣的程序編寫就可以得到float型數據的值了,不再需要其他的轉換。

類似的,傳輸long型或int型時也可以採用共用體的方法:

void long_char(unsigned long l,unsigned char *s)
{
    union change
    {
        long d;
        unsigned char dat[4];
    }r1;
    r1.d = l;
    *s = r1.dat[0];
    *(s+1) = r1.dat[1];
    *(s+2) = r1.dat[2];
    *(s+3) = r1.dat[3];  
}

    同時,還有一種內存操作函數/調用庫函數的方法,但這種方法對編譯器要求過高,在這裏就不介紹了。


轉載自:串口通信中的int float型數據的處理和發送

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章