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的数据。传输效率浪费了: η a s c i i = 7 2 \eta _{ascii} = {7 \over 2} ηascii=27

AD7606数据采集板使用ASCII码传递BINARY数据_第1张图片

▲ ISP下载软件界面

 

■ Base64编码方式


在网页 What is Base64 Encoding and How does it work? | Base64Encoder 给出了Base64编码的具体方式。它将连续三个字节(24bit)转换成四个6bit组,按照下面的表格找到对应的ASCII码。

AD7606数据采集板使用ASCII码传递BINARY数据_第2张图片

▲ Base64编码字符表格

这样的转换效率为 η b a s e 64 = 4 3 \eta _{base64} = {4 \over 3} ηbase64=34
通过Base64的转换比普通的ASCI字符效率提高了:
η Δ = η a s c i i η b a s e 64 = 7 / 2 4 / 3 = 21 8 = 2.625 \eta _\Delta = {{\eta _{ascii} } \over {\eta _{base64} }} = {{7/2} \over {4/3}} = {{21} \over 8} = 2.625 ηΔ=ηbase64ηascii=4/37/2=821=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。再乘以编码增益 η b a s e 64 = 4 3 \eta _{base64} = {4 \over 3} ηbase64=34,则实际上传速率为:21.3kByte/秒.

AD7606数据采集板使用ASCII码传递BINARY数据_第3张图片

▲ 通过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 ↩︎

你可能感兴趣的:(嵌入式系统,经验分享)