BETFX 简化版通信原理 Release:2020.12.24
描述:
不论哪种传输方式,都是采用一问一答方式进行的, 外部设备作为主机,BETFX是从机,
读取BETFX时,BETFX会在接收完数据后的5ms内作出回应; 写数据时, BETFX同样在收完数据
后的5ms内回复固定字符串”OK\r\n”, 数据帧最长256 Byte.
UART 数据帧格式:
读取数据格式:
说明 |
起始符 |
数据长度[LEN] |
内容[CMD+首地址addr+读取数据长度n] |
校验位 |
结束符 |
|---|---|---|---|---|---|
读命令 |
0x24 |
0x03 CMD(1bytes)+addr(1bytes)+n(1bytes) |
CMD:0x81读取系统信息 |
||
CMD:0x82读取系统配置 |
|||||
CMD:0x83读取当前效果器寄存器 |
|||||
CMD:0x84读取效果器默认参数 |
CS |
0x0d+0x0a |
|||
CMD:0x85读取当前EQ参数 |
|||||
CMD:0x86读取EQ默认参数 |
|||||
首地址:1bytes 查数据结构表 |
|||||
读取数据长度:1bytes 查数据结构表 |
|||||
回复 |
0x24 |
n |
para1+para2…paran |
CS |
0x0d+0x0a |
写数据格式
说明 |
起始符 |
数据长度[LEN] |
内容[CMD+首地址addr+写数据长度n+para1+para2…paran] |
校验位 |
结束符 |
|---|---|---|---|---|---|
写命令 |
0x24 |
0x03+n |
CMD:0x01 保留 |
CS |
0x0d+0x0a |
CMD:0x02 设置系统配置 |
|||||
CMD:0x03 设置效果器寄存器 |
|||||
CMD:0x05 设置EQ寄存器 |
|||||
addr:1bytes 查数据结构表 |
|||||
n:1bytes 查数据结构表 |
|||||
para1+para2…para3:nbytes 设置参数值 |
|||||
回复 |
OK\r\n |
说明: 数据长度+内容 数据必须转义成2个字符 分别转换成16进制 高4位 跟地4位的ASSCII码 例如 0x81 –> 0x38+0x31
CS:是起始符+LEN+内容所有值累加和低8位转换成2个字符
例如: 读取系统信息:制造商名称 占用0x20个字节 地址为0
$03810020B2\\r\\n
起始符:$
LEN:03
CMD:81 addr:00 n:20
CS:B2 (0x24+0x30+0x38+0x31+0x30+0x30+0x32+0x30)%0xFF=0xB2
结束符:\\r\\n
注意事项
UART 物理格式: 115200-8-N-1, 从机响应时间<5mS;
为了将来扩展应用, 不要对所有保留字段进行写操作;
传输采用ASCII, 允许的字符仅有字符0-9, A-Z, $, 以及rn, 其他所有字符都是不合法的.
传输中多字节字段,高字节在前.
数据结构
系统信息(只读, 读): CMD:0x81
首地址 |
长度[字节] |
内容 |
|---|---|---|
0x00 |
0x08 |
H’,’A’,’N’,’M’,’U’,’S’, 0x00, 0x00// 技术供应商名称 |
0x08 |
0x08 |
‘D’,’E’,’1’,’0’,’0’,0x00, 0x00, 0x00 // 主控型号 |
0x10 |
0x04 |
软件发布日期, BCD格式, YYMMDDxx, xx代表当天发布序号 |
0x14 |
0x0C |
保留 |
0x20 |
0x02 |
DELAY最长时间,高字节在前;//应用程序首先要读取当前字段,以确认DELAY数据范围 |
0x22 |
0x01 |
BIT0=1 支持REVERB, 否则不支持; BIT1=1 支持CHORUS, 否则不支持; BIT2=1 支持哇音,否则不支持; BIT3=1 支持颤音, 否则不支持; BIT4=1 支持DELAY, 否则不支持; BIT5=1 支持FBC, 否则不支持; BIT6=1 支持IN EQ, 否则不支持; BIT7=1 支持DRY VOL, 否则不支持; |
0x23 |
0x01 |
EQ 支持最大段数, 一般为6,代表1个HPF, 一个LPF, 4个参量均衡 |
0x24 |
0x02 |
保留 |
例子:读取技术供应商信息
+---------+------+--------+------------------------+
| PageLen | CMD | 首地址 | 读取长度 |
+=========+======+========+========================+
| 0x03 | 0x81 | 0x00 | 0x08[读取全部数据长度] |
+---------+------+--------+------------------------+
起始符 PageLen cmd 首地址 读取长度 校验位 结束符
编码格式如下:字符: $03810008B8\\r\\n
十六进制: 0x24 + 0x30+0x33 + 0x38+0x31 + 0x30+0x30 + 0x30+0x38 + 0x42+0x38 + 0x0D+0x0A
CS:=(0x24+0x30+0x33+0x38+0x31+0x30+030+0x+0x38)%255 =0xB8 转义2位 0x42+0x38
回复:$0848414E4D55530000E0\\r\\n 返回 HANMUS
08: 数据长度为8
48414E4D55530000 数据内容
E0: CS
读取主控型号:DE100 Send:$03810808C0\\r\\n
REV :$084445313030000000A7\r\n
读取发布日期:20120801 2020年12月8号01版本
Send:$03811004B5\r\n
REV :$042012080116\r\n
读取系统DELAY TIME最大值 0x1F4
Send:$03812002B4
REV :$0201F461
读取系统支持配置项
Send:$03812201B5
REV :$01D3FC :读取值为0xD3 :
读取EQ支持最大段数
Send:$03812301B6
REV :$0106EB 读取值为 0x06
系统配置(可读写): CMD: 0x82[读]/ 0x02[写]
首地址 |
长度[字节] |
内容 |
|---|---|---|
0x00 |
0x20 |
‘H’,0x00,’A’,0x00,’N’,0x00,’M’,0x00,’U’,0x00,’S’,0x00, 0x00,0x00, 0x00,0x00, |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00// 制造商名称, 例如: BMW [unicode编码] |
||
0x20 |
0x20 |
‘A’,0x00,’F’,0x00,’-‘,0x00,’I’,0x00,’I’,0x00,’R’,0x00,‘C’,0x00, 0x00,0x00, |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 // 产品型号, 例如: 740Li [unicode编码] |
||
0x40 |
0x04 |
生产日期, BCD格式, YYMMDDxx, xx代表当天发布序号 |
0x44 |
0x04 |
产品序列号, HEX格式 |
0x48 |
0x01 |
0x55h: 不锁定, 解锁定后, 效果器参数将自动恢复为HANMUS提供的默认参数, |
0xAA 锁定, 锁定后, 效果器参数将不可读出, |
||
其他数据忽略; |
||
0x49 |
0x36 |
保留 |
读取制造商信息:
Send:$03820020B3\\r\\n
REV :$20480041004E004D00550053000000000000000000000000000000000000000000DA\\r\\n
读取产品型号
Send:$03822020B5\\r\\n
REV :$20410046002D00490049002D005200430044000000000000000000000000000000F1\\r\\n
读取生产日期:20120801
Send:$03824004B9
REV :$042012080116
读取产品序列号
Send:$03824404BD
REV :$040000000008
读取锁定参数
Send:$03824801BE
REV :$0100E5
:
写入制造商信息为: 声天下科技有限公司 Send:$23020020F05829590B4ED1798062096750966C51F8530000000000000000000000000000B8 REV :OKrn
效果器参数(可读写): CMD:0x83[读]/0x03[写] / 0x84[读取默认值]
首地址 |
长度[字节] |
内容 |
|---|---|---|
0x00 |
0x01 |
保留 |
0x01 |
0x01 |
[Delay Volume],Type: UINT8, 200: 0dB, 最小值: 0(负无穷大); 最大值:200(0dB), 步进0.5dB |
0x02 |
0x01 |
[Delay Repeat], Type: UINT8, 0-20, Def: 7 |
0x03 |
0x02 |
[Delay Time], Type: UINT16, DEF: 220mS, 高字节在前. |
0x05 |
0x02 |
保留 |
0x07 |
0x01 |
[Reverb Volume], Type: UINT8, 200: 0dB, 最小值: 0(负无穷大); 最大值:200(0dB), 步进0.5dB |
0x08 |
0x01 |
保留 |
0x09 |
0x01 |
[Decay time], Type: UINT8, 0-100, 单位 :% |
0x0A |
0x01 |
[Room Size], Type: UINT8, 0-100, 单位 :% |
0x0B |
0x01 |
[Dramp], Type: UINT8, 0-100, 单位 :% |
0x0C |
0x01 |
[CHORUS Volume], Type: UINT8, 200: 0dB, 最小值: 0(负无穷大); 最大值:200(0dB), 步进0.5dB |
0x0D |
0x01 |
[Chorus speed], Type: UINT8, 0-10HZ, 单位 :0.1HZ |
0x0E |
0x01 |
[Chorus Depth], Type: UINT8, 0-100, 单位 :% |
0x0F |
0x01 |
[Chorus Feedback],Type: UINT8, 0-100, 单位 :% |
0x10 |
0x01 |
[FX volume],总音量, Type: UINT8, 200: 0dB, 最小值: 0(负无穷大); 最大值:200(0dB), 步进0.5dB |
0x11 |
0x02 |
[REVERB HPF FREQ], Type: UINT16, DEF: 20HZ, 范围: 20HZ-20KHZ, 高字节在前. |
0x13 |
0x02 |
[REVERB LPF FREQ], Type: UINT16, DEF: 20000HZ, 范围: 20HZ-20KHZ, 高字节在前. |
0x15 |
0x02 |
[REVERB PEAK FREQ], Type: UINT16, DEF: 20000HZ, 范围: 20HZ-20KHZ, 高字节在前. |
0x17 |
0x01 |
[REVERB PEAK GAIN], Type: UINT8, DEF: 0dB, 范围: 100 +-12dB, 100代表0dB. |
0x18 |
0x01 |
[FBC ENABLE],0: DISABLE, 1:ENABLE |
0x19 |
0x01 |
[DRY volume],总音量, Type: UINT8, 200: 0dB, 最小值: 0(负无穷大); 最大值:200(0dB), 步进0.5dB |
0x1A |
0x02 |
保留 |
读取效果器参数默认值
Send:$0384001CC7\\r\\n
REV :$1C08C80700DC0000C800265D63C8043200C801181F4003E86400C8000057\\r\\n
读取效果器参数 地址0开始到0x1B的值
Send:$0383001CC6\\r\\n
REV :$1C08C80700DC0000C800265D63C8043200A501181F4003E864E8A800006D\\r\\n
设置Delay Volume 音量为-20.5db 设置值为 200-41=159 =0x9F
Send:$040301019F2C\\r\\n
REV :OK\\r\\n
设置Delay time 为200ms 设置值为 0x00C8
$0503030200C88C|\r|\n
REV :OK\r\n
设置混响高通频率 HPF = 1000hz =0x03E8
$0503110203E890\r\n
REV :OK\r\n
EQ寄存器(可读写):CMD :0x85[读]/0x05[写]/0x86[读取默认值]
首地址 |
长度 |
内容 |
|---|---|---|
0x00 |
0x02 |
高通滤波器中心频率, Type: UINT16, DEF: 20HZ, 范围: 20HZ-20KHZ, 高字节在前; 例如:1000hz:0x03E4 |
0x02 |
0x02 |
低通滤波器中心频率, Type: UINT16, DEF: 20HZ, 范围: 20HZ-20KHZ, 高字节在前; |
0x04 |
0x02 |
参量均衡1 中心频率, Type: UINT16, DEF: 20HZ, 范围: 20HZ-20KHZ, 高字节在前; |
0x06 |
0x01 |
参量均衡1 增益,Type: UINT8, DEF:100, 数值100代表0dB, 最小值: 88(-12dB); 最大值:112(12dB), 步进1dB; |
0x07 |
0x01 |
参量均衡1 Q值,Type: UINT8, DEF:10, 最小值: 1(0.1); 最大值:255(25.5), 步进1 |
实际Q值等于该值除以10, 例如数值10代表滤波器Q值为1, 数值50代表Q值为5; |
||
0x08 |
0x02 |
参量均衡2 中心频率,Type: UINT16, DEF: 20HZ, 范围: 20HZ-20KHZ, 高字节在前; |
0x0A |
0x02 |
参量均衡2 增益,Type: UINT8, DEF:100, 数值100代表0dB, 最小值: 88(-12dB); 最大值:112(12dB), 步进1dB; |
0x0B |
0x01 |
参量均衡2 Q值,Type: UINT8, DEF:10, 最小值: 1(0.1); 最大值:255(25.5), 步进1 |
0x0C |
0x02 |
参量均衡3 中心频率, Type: UINT16, DEF: 20HZ, 范围: 20HZ-20KHZ, 高字节在前; |
0x0E |
0x01 |
参量均衡3 增益,Type: UINT8, DEF:100, 数值100代表0dB, 最小值: 88(-12dB); 最大值:112(12dB), 步进1dB; |
0x0F |
0x01 |
参量均衡3 Q值,Type: UINT8, DEF:10, 最小值: 1(0.1); 最大值:255(25.5), 步进1 |
实际Q值等于该值除以10, 例如数值10代表滤波器Q值为1, 数值50代表Q值为5; |
||
0x10 |
0x02 |
参量均衡4 中心频率, Type: UINT16, DEF: 20HZ, 范围: 20HZ-20KHZ, 高字节在前; |
0x12 |
0x01 |
参量均衡4 增益,Type: UINT8, DEF:100, 数值100代表0dB, 最小值: 88(-12dB); 最大值:112(12dB), 步进1dB; |
0x13 |
0x01 |
参量均衡4 Q值,Type: UINT8, DEF:10, 最小值: 1(0.1); 最大值:255(25.5), 步进1 |
实际Q值等于该值除以10, 例如数值10代表滤波器Q值为1, 数值50代表Q值为5; |
||
0x14 |
0x0C |
保留 |
读取EQ寄存器所有参赛的默认值
Send:$0386001ECB\\r\\n
REV :$1E00144E2001F4640A03E8640A0BB8640A1F40640A00000000000000000000E8\\r\\n
读取EQ寄存器所有值
Send:$03850020B6\\r\\n
REV :$2000144E2001F4640A03E8640A0BB8640A1F40640A00000000000000000000000094\\r\\n
设置EQ低通滤波器频率为100 = 0x0064
$0505020200647C\\r\\n
REV :OK\\r\\n
设置均衡2中心频率 为2000 = 0x7D0
$0505080207D093\\r\\n
REV :OK\\r\\n
设置均衡4增益为-5db 设置值为 100-5=95 =0x5F
$050512025F3091\\r\\n
REV :OK\\r\\n
设置均衡1的Q值为 0.9 设置值为 0.9*10 = 0x09
$04050701091E\\r\\n
REV :OK\\r\\n
编解码参考
#define RPC_BUF_MAX 256
#define RPC_BUF_MASK (RPC_BUF_MAX-1)
//RPC 命令集
#define RPC_RESERVE 0x01
#define RPC_WRITE_STSTEM_CONFIG 0x02
#define RPC_WRITE_FX_REG 0x03
#define RPC_WRITE_INEQ_REG 0x05
#define RPC_READ_SYSTEM_INFOR 0x81
#define RPC_READ_SYSTEM_CONFIG 0x82
#define RPC_READ_FX_REG 0x83
#define RPC_READ_FX_DEFAULT_REG 0x84
#define RPC_READ_INEQ_REG 0x85
#define RPC_READ_DEFAULT_INEQ_REG 0x86
typedef struct {
unsigned char rpcPackageLen;
unsigned char cmd,add,len;
unsigned char buf[RPC_BUF_MAX];
}frame_ctrl;
typedef struct {
int fi, fo;
unsigned char buf[RPC_BUF_MAX];
}fifo_t;
unsigned char hex2Assci(unsigned char hc) {
hc = hc & 0x0f;
if ((hc >= 0) && (hc <= 9))return (hc + '0');
return ((hc - 10) + 'A');
}
unsigned char assci2hex(unsigned char a, unsigned char b) {
if (a >= '0' && a <= '9')a = a - '0';
else if (a >= 'a' && a <= 'f') a = a - 'a' + 10;
else if (a >= 'A' && a <= 'F') a = a - 'A' + 10;
if (b >= '0' && b <= '9')b = b - '0';
else if (b >= 'a' && b <= 'f') b = b - 'a' + 10;
else if (b >= 'A' && b <= 'F') b = b - 'A' + 10;
return a * 16 + b;
}
void pushFifo(fifo_t *pfifo, unsigned char *pBuf, int len) {
while (len-- > 0) {
pfifo->buf[pfifo->fi++] = *pBuf++;
pfifo->fi &= RPC_BUF_MASK;
}
/*
编码函数:返回值 pBufOut 发送数据的长度
cmd: 命令
addr :首地址
len:读写数据长度
*pBufIn: 写数据的内容首地址 /读模式为NULL
*PBufOut: 编码后保持数据的首地址
write: false 读 true 写
*/
int encode(unsigned char cmd, unsigned char addr, unsigned char len,
unsigned char *pBufIn, unsigned char *pBufOut, bool write) {
int i;
unsigned char cs, d, d1, d2;
int offset = 0;
pBufOut[offset++&RPC_BUF_MASK] = 0x24;
cs = 0x24;
// pageageLen
d1 = hex2Assci((unsigned char)((write?(len+3):3)>> 4));
pBufOut[offset++&RPC_BUF_MASK] = d1;
cs += d1;
d1 = hex2Assci((unsigned char)((write ? (len + 3) : 3) >>0));
pBufOut[offset++&RPC_BUF_MASK] = d1;
cs += d1;
// cmd
d1 = hex2Assci((unsigned char)(cmd >> 4));
pBufOut[offset++&RPC_BUF_MASK] = d1;
cs += d1;
d1 = hex2Assci((unsigned char)(cmd >> 0));
pBufOut[offset++&RPC_BUF_MASK] = d1;
cs += d1;
// addr
d1 = hex2Assci((unsigned char)(addr >> 4));
pBufOut[offset++&RPC_BUF_MASK] = d1;
cs += d1;
d1 = hex2Assci((unsigned char)(addr >> 0));
pBufOut[offset++&RPC_BUF_MASK] = d1;
cs += d1;
//len
d1 = hex2Assci((unsigned char)(len >> 4));
pBufOut[offset++&RPC_BUF_MASK] = d1;
cs += d1;
d1 = hex2Assci((unsigned char)(len >> 0));
pBufOut[offset++&RPC_BUF_MASK] = d1;
cs += d1;
// date
for (i = 0; i< (write ? len:0); i++) {
d = pBufIn[i];
d1 = hex2Assci((unsigned char)(d >> 4));
d2 = hex2Assci(d);
pBufOut[offset++&RPC_BUF_MASK] = d1;
pBufOut[offset++&RPC_BUF_MASK] = d2;
cs += (d1 + d2);
}
pBufOut[offset++&RPC_BUF_MASK] = hex2Assci((cs >> 4));
pBufOut[offset++&RPC_BUF_MASK] = hex2Assci(cs);
pBufOut[offset++&RPC_BUF_MASK] = 0x0d;
pBufOut[offset++&RPC_BUF_MASK] = 0x0a;
pBufOut[offset&RPC_BUF_MASK] = 0x00; // only for printf.
//pfifo->fi = offset&RPC_BUF_MASK;
return write?(len*2 + 13):13;
}
/*
解码函数:返回值
0:解码成功
-1:
-2:没有新的数据
-3:数据长度不足
fifo_t *pfifo: 接收数据缓存
frame_ctrl *pRpc:解码结构体
*/
int decode(fifo_t *pfifo, frame_ctrl *pRpc)
{
int i, len;
unsigned char d1, d2;
unsigned short d;
unsigned char rpcPackageLen;
int offset;
unsigned char cs;
int ofs1=0;
if (pfifo->fo == pfifo->fi)return -2;
while (pfifo->fo != pfifo->fi) {
if (pfifo->buf[pfifo->fo] == 0x24) {
ofs1 = pfifo->fo;
break;
}
pfifo->fo++;
pfifo->fo &= RPC_BUF_MASK;
}
if (pfifo->buf[pfifo->fo] == 0x24) {
len = ((pfifo->fi + RPC_BUF_MAX) - ofs1)&RPC_BUF_MASK;
if (len<9)return -3;// must wait for "\r\n".
d1 = pfifo->buf[(ofs1 + 1)&RPC_BUF_MASK];
d2 = pfifo->buf[(ofs1 + 2)&RPC_BUF_MASK];
rpcPackageLen = assci2hex(d1, d2);
if (len<(rpcPackageLen * 2 + 7)) return -4;
cs = 0x24 + d1 + d2;
pRpc->len = rpcPackageLen;
offset = (ofs1 + 3)&RPC_BUF_MASK;
for (i = 0; i<rpcPackageLen; i++) {
d1 = pfifo->buf[offset++&RPC_BUF_MASK];
d2 = pfifo->buf[offset++&RPC_BUF_MASK];
pRpc->buf[i&RPC_BUF_MASK] = assci2hex(d1, d2);
cs += (d1 + d2);
}
pfifo->buf[ofs1] |= 0x80;
d1 = pfifo->buf[offset++&RPC_BUF_MASK];
d2 = pfifo->buf[offset++&RPC_BUF_MASK];
if (cs == assci2hex(d1, d2)) {
d1 = pfifo->buf[offset++&RPC_BUF_MASK];
d2 = pfifo->buf[offset++&RPC_BUF_MASK];
if ((d1 == 0x0d) && (d2 == 0x0a)) {
pRpc->len = rpcPackageLen;
return 0;
}
}else {
printf("CS check error:%x,%x", cs, assci2hex(d1, d2)));
}
return -1;
}