■ 問題提出
在 帶有128KB緩存的AD7606模擬採集板 後面更新了不帶板內緩衝區的C51程序。採集板與計算機之間存在兩種實時數據通道:
- 通過USB-RS232轉接串口,波特率爲460800
- 通過WiFi-UART模塊,波特率爲460800,但是實際傳輸速率要小於460800
爲了能夠利用 STC WiFi下載程序 界面作爲接受數據的緩衝界面,這樣就需要能夠利用ASCII碼來高效傳輸二進制數據。
傳統的方式通過C51的printf函數,將二進制轉換成ASCII字符串通過串口發送出去,雖然這樣有很強的可讀性,但是存在着很大的轉換效率低下:
- 通常一個兩字節(16bit)的二進制,最大需要6個字節(帶有符號位)。比如有符號整數 0xd8ef : -10000。
- 爲了分割連續兩個數字,其間還需增加一個空格(0x20)
因此,最大需要是踹死7個字節來傳輸一個16bit的數據。傳輸效率浪費了:
▲ ISP下載軟件界面
■ Base64編碼方式
在網頁 What is Base64 Encoding and How does it work? | Base64Encoder 給出了Base64編碼的具體方式。它將連續三個字節(24bit)轉換成四個6bit組,按照下面的表格找到對應的ASCII碼。
▲ Base64編碼字符表格
這樣的轉換效率爲
通過Base64的轉換比普通的ASCI字符效率提高了:
在實際實現中,將原來的 “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。再乘以編碼增益,則實際上傳速率爲:21.3kByte/秒.
▲ 通過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]);
}
C51工程文件:C51\STC\Tools\AD7606\Sample8G1KNoBuffer\Sample8G1KNoBuffer.uvproj ↩︎