AD7606數據採集板使用ASCII碼傳遞BINARY數據

 

■ 問題提出


帶有128KB緩存的AD7606模擬採集板 後面更新了不帶板內緩衝區的C51程序。採集板與計算機之間存在兩種實時數據通道:

  • 通過USB-RS232轉接串口,波特率爲460800
  • 通過WiFi-UART模塊,波特率爲460800,但是實際傳輸速率要小於460800

爲了能夠利用 STC WiFi下載程序 界面作爲接受數據的緩衝界面,這樣就需要能夠利用ASCII碼來高效傳輸二進制數據。

傳統的方式通過C51的printf函數,將二進制轉換成ASCII字符串通過串口發送出去,雖然這樣有很強的可讀性,但是存在着很大的轉換效率低下:

  • 通常一個兩字節(16bit)的二進制,最大需要6個字節(帶有符號位)。比如有符號整數 0xd8ef : -10000。
  • 爲了分割連續兩個數字,其間還需增加一個空格(0x20)

因此,最大需要是踹死7個字節來傳輸一個16bit的數據。傳輸效率浪費了:ηascii=72\eta _{ascii} = {7 \over 2}

▲ ISP下載軟件界面

▲ ISP下載軟件界面

 

■ Base64編碼方式


在網頁 What is Base64 Encoding and How does it work? | Base64Encoder 給出了Base64編碼的具體方式。它將連續三個字節(24bit)轉換成四個6bit組,按照下面的表格找到對應的ASCII碼。

▲ Base64編碼字符表格

▲ Base64編碼字符表格

這樣的轉換效率爲ηbase64=43\eta _{base64} = {4 \over 3}
通過Base64的轉換比普通的ASCI字符效率提高了:
ηΔ=ηasciiηbase64=7/24/3=218=2.625\eta _\Delta = {{\eta _{ascii} } \over {\eta _{base64} }} = {{7/2} \over {4/3}} = {{21} \over 8} = 2.625

在實際實現中,將原來的 “62”編碼爲$+$,修改爲$*$,這樣在下載程序界面可以避免換行的。整個顯示是鋪滿Mem0界面。

 

■ C51編碼程序和Python解碼程序


1. C51的編碼1

/*
**==============================================================================
** BASE64ASCII.C:             -- by Dr. ZhuoQing, 2020-06-04
**
**==============================================================================
*/
//------------------------------------------------------------------------------
#define BASE64ASCII_GLOBALS        1              // Define the global variables
#include "BASE64ASCII.H"
#include "c51basic.H"
//------------------------------------------------------------------------------
void Base64ASCIISendChar(unsigned char ucChar) {
    SendChar(ucChar);
}
//------------------------------------------------------------------------------
void Base64ASCIIInit(void) {
    g_ucBase64ASCIIBufferPoint = 0;
}
//------------------------------------------------------------------------------
void Base64ASCIIPushByte(unsigned char ucByte) {
    g_ucBase64ASCIIBuffer[g_ucBase64ASCIIBufferPoint ++] = ucByte;
    if(g_ucBase64ASCIIBufferPoint >= 3) {
        Base64ASCIIFlushBuffer();
    }
}
unsigned char Base64ASCII6BitChar(unsigned char ucBit) {
    if(ucBit < 26) return 'A' + ucBit;
    if(ucBit < 52) return (ucBit - 26) + 'a';
    if(ucBit < 62) return (ucBit - 52) + '0';
    if(ucBit == 62) return '*';
    if(ucBit == 63) return '/';
    return '=';
}
//------------------------------------------------------------------------------
void Base64ASCIIFlushBuffer(void) {
    unsigned char ucDim[4];
    unsigned char ucChar, ucNibble;
    if(g_ucBase64ASCIIBufferPoint == 0) return;
    ucChar = g_ucBase64ASCIIBuffer[0];
    ucNibble = ucChar << 4;
    ucDim[0] = Base64ASCII6BitChar(ucChar >> 2);
    ucChar = g_ucBase64ASCIIBuffer[1];
    ucDim[1] = Base64ASCII6BitChar((ucNibble & 0x30) | (ucChar >> 4));
    ucNibble = (ucChar & 0xf) << 2;
    ucChar = g_ucBase64ASCIIBuffer[2];
    ucDim[2] = Base64ASCII6BitChar(ucNibble | (ucChar >> 6));
    ucDim[3] = Base64ASCII6BitChar(ucChar & 0x3f);
    if(g_ucBase64ASCIIBufferPoint == 1) {
        ucDim[2] = '=';
        ucDim[3] = '=';
    } else if(g_ucBase64ASCIIBufferPoint == 2) {
        ucDim[3] = '=';
    }
    Base64ASCIISendChar(ucDim[0]);
    Base64ASCIISendChar(ucDim[1]);
    Base64ASCIISendChar(ucDim[2]);
    Base64ASCIISendChar(ucDim[3]);
    g_ucBase64ASCIIBufferPoint = 0;
}
//==============================================================================
//                END OF THE FILE : BASE64ASCII.C
//------------------------------------------------------------------------------
/*
**==============================================================================
** BASE64ASCII.H:            -- by Dr. ZhuoQing, 2020-06-04
**
**  Description:
**
**==============================================================================
*/
#ifndef __BASE64ASCII__
#define __BASE64ASCII__
//------------------------------------------------------------------------------
#ifdef BASE64ASCII_GLOBALS
   #define BASE64ASCII_EXT
#else
   #define BASE64ASCII_EXT extern
#endif // BASE64ASCII_GLOBALS
//------------------------------------------------------------------------------
//==============================================================================
void Base64ASCIISendChar(unsigned char ucChar);
//------------------------------------------------------------------------------
void Base64ASCIIInit(void);
BASE64ASCII_EXT unsigned char g_ucBase64ASCIIBuffer[3];
BASE64ASCII_EXT unsigned char g_ucBase64ASCIIBufferPoint;
void Base64ASCIIPushByte(unsigned char ucByte);
void Base64ASCIIFlushBuffer(void);
unsigned char Base64ASCII6BitChar(unsigned char ucbit);
//==============================================================================
//             END OF THE FILE : BASE64ASCII.H
//------------------------------------------------------------------------------
#endif // __BASE64ASCII__

2. Python解碼程序

def Base64Ascii2Bit(ascii):
    ascii = int(ascii)
    if ascii >= ord('A') and ascii <= ord('Z'): return ascii - ord('A')
    if ascii >= ord('a') and ascii <= ord('z'): return ascii - ord('a') + 26
    if ascii >= ord('0') and ascii <= ord('9'): return ascii - ord('0') + 52
    if ascii == ord('*'): return 62
    if ascii == ord('/'): return 63
    return 0
def Base64Ascii2Byte(fourb):
    data = bytearray()
    bits0 = Base64Ascii2Bit(fourb[0])
    bits1 = Base64Ascii2Bit(fourb[1])
    bits2 = Base64Ascii2Bit(fourb[2])
    bits3 = Base64Ascii2Bit(fourb[3])
    data.append(bits0 * 4 + int(bits1 / 16))
    data.append((bits1 & 0xf) * 16 + int(bits2 / 4))
    data.append((bits2 & 0x3) * 64 + bits3)
    if fourb[2] == ord('=') and fourb[3] == ord('='):
        data = data[0:1]
        return data
    if fourb[3] == ord('='):
        return data[0:2]
    return data
def Base64Ascii2Data(ascii):
    data = bytearray()
    length = len(ascii)
    for i in range(int(length / 4)):
        bytedata = Base64Ascii2Byte(ascii[i*4:i*4+4])
        if len(bytedata) > 0:
            data.extend(bytedata)
    valdim = [x*256+y for x,y in zip(data[0::2], data[1::2])]
    valdim = [(d & 0x7fff) - (d & 0x8000) for d in valdim]
    return valdim

 

■ 最大采樣速率測試結果


經過測試,在 帶有128KB緩存的AD7606模擬採集板 上,對八個通道進行週期爲1毫秒的採集,實際上傳速率爲 16kByte/s。再乘以編碼增益ηbase64=43\eta _{base64} = {4 \over 3},則實際上傳速率爲:21.3kByte/秒.

最大速率上載數據

▲ 通過AD7606採集板採集到的兩個通道的信號波形

▲ 通過AD7606採集板採集到的兩個通道的信號波形

#------------------------------------------------------------
stm32cmd('ad5v')
stm32cmd('CLEAR')
stm32cmd('adascii 1000 8 1')
val = stm32val()[10]
while True:
    time.sleep(.2)
    valnew = stm32val()[10]
    if valnew == val: break
    val = valnew
tspbeep(1200, 200)
stm32cmd("COPY")
time.sleep(.5)
pastestr = bytes(clipboard.paste(), 'utf-8')
data = Base64Ascii2Data(pastestr)
plt.plot(data[0::8], label='Channel1')
plt.plot(data[1::8], label='Channel2')
plt.xlabel("sample")
plt.ylabel("Voltage(V)")
plt.legend(loc='upper right')
plt.grid(True)
plt.show()

 

■ 結論


通過Base64編碼,將原來的3個字節的數據編碼成4個字節的ASCII字符,可以在適合ASCII存儲顯示的界面完成二進制數據的傳遞。這大大提高數據的傳輸效率。

//==============================================================================
//                    PROCESS THE DEBUG BUFFER
//------------------------------------------------------------------------------
void SerialDebugProcessBuffer(void) {
    unsigned int nNumber, nChannel, nWaitTime, i, j;
    int nData[8];
    unsigned int nNum;
    SerialDebugBuffer2Argument();
    if(g_ucSDANumber == 0) return;
    if(strcmp("hello",                (char *)STD_ARG[0]) == 0)
        printf("%s is ready !\r\n", VERSION_STRING);
    else IFARG0("ad") {
        sscanf(SDA(1), "%d", &nNumber);
        sscanf(SDA(2), "%d", &nChannel);
        sscanf(SDA(3), "%d", &nWaitTime);
        g_ucWaitMS = 0;
        for(i = 0; i < nNumber; i ++) {
            while(g_ucWaitMS != nWaitTime);
            g_ucWaitMS = 0;
            AD7606Convert();
            AD7606ReadData(nData, 4);
            for(j = 0; j < nChannel; j ++) {
                nNum = nData[j];
                SendChar((unsigned char)(nNum >> 8));
                SendChar((unsigned char)nNumber);
            }
        }
    }
    else IFARG0("adtext") {
        sscanf(SDA(1), "%d", &nNumber);
        sscanf(SDA(2), "%d", &nChannel);
        sscanf(SDA(3), "%d", &nWaitTime);
        g_ucWaitMS = 0;
        for(i = 0; i < nNumber; i ++) {
            while(g_ucWaitMS != nWaitTime);
            g_ucWaitMS = 0;
            AD7606Convert();
            AD7606ReadData(nData, 4);
            for(j = 0; j < nChannel; j ++) {
                nNum = nData[j];
                printf("%d ", nNum);
            }
        }
    } else IFARG0("adascii") {
        sscanf(SDA(1), "%d", &nNumber);
        sscanf(SDA(2), "%d", &nChannel);
        sscanf(SDA(3), "%d", &nWaitTime);
        g_ucWaitMS = 0;
        Base64ASCIIInit();
        for(i = 0; i < nNumber; i ++) {
            while(g_ucWaitMS != nWaitTime);
            g_ucWaitMS = 0;
            AD7606Convert();
            AD7606ReadData(nData, 4);
            for(j = 0; j < nChannel; j ++) {
                nNum = nData[j];
                Base64ASCIIPushByte((unsigned char)(nNum >> 8));
                Base64ASCIIPushByte((unsigned char)nNum);                
            }
        }
        Base64ASCIIFlushBuffer();
    } else IFARG0("ad5v") {
        OFF(AD7606_RANGE);        
    } else IFARG0("ad10v") {
        ON(AD7606_RANGE);
    }
    else printf("Error command : %s !\r\n", STD_ARG[0]);
}

  1. C51工程文件:C51\STC\Tools\AD7606\Sample8G1KNoBuffer\Sample8G1KNoBuffer.uvproj ↩︎

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