目录
前言
本文主要介绍标准RDM协议中的控制端组建RDM包及解析RDM包的接口使用方法,底层驱动请看上一篇文章。
一、控制端解包接口
1、辅助函数解释
首先是一个辅助函数,为的是清除已经解析了的包
static void MU_Move_Arr(uint8_t arr[],int size,int n)
{
if (n >= size)
{
memset(arr,0,size*sizeof(uint8_t));
return;
}
// 将剩余元素移动到数组前面
for(int i = 0; i < size - n; i++)
{
arr[i] = arr[i+n];
}
memset(arr+size-n,0,n*sizeof(uint8_t));
}
包类型定义和解包结构定义,解包结构体只保留想要的数据
//解析包定义 搜寻设备回应 哑音回应 解除哑音回应 获取设备状态回应 获取DMX地址回应 获取版本回应 获取设备信息回应
// 设置DMX地址回应 设置设备状态回应 校验错误包 无回应包
enum rdm_package {DISC_UNIQUE,DISC_MUTE = 0,DISC_UN_MUTE, GET_DRIVER_FLAG,GET_DRIVER_DMX_ADDR,GET_DRIVER_VERSION,GET_DRIVER_INFO,
SET_DRIVER_DMX_ADDR, SET_DRIVER_FLAG,ERROR_PACKAGE ,NULL_PACKAGE};
typedef struct rdm_package_prase_t
{
enum rdm_package rdm_package;
//最大允许填入数据
uint8_t data[231];
}rdm_package_prase_t;
2、解包接口解析
用于定位包头,跳过包头的不稳定数据和起始信号,标准RDM协议有两种包,一种是0xCC开头的标准包,另一种是0xFE * 7开头的特殊包
//定位包头
uint8_t disc_unique = 0;
//用于确定包长
int i = 0;
for(i = 0; i < rx_buf.index; i++)
{
if(rx_buf.buf[i] == 0xCC && rx_buf.buf[i+1] == 0x01)
break;
if(rx_buf.buf[i] == 0xFE)
{
disc_unique++;
if(disc_unique == 5)
break;
}
}
判断是没有包,还是特殊包,还是标准包
rdm_package_prase_t package_prase;
//空包
if(i == rx_buf.index)
{
//...
}
//广播包
else if(disc_unique == 5)
{
//...
}
//其他包
else
{
//...
}
特殊包,空包解析
//空包
if(i == rx_buf.index)
{
package_prase.rdm_package = NULL_PACKAGE;
}
else if(disc_unique == 5)
{
package_prase.rdm_package = DISC_UNIQUE;
//跳过0xFE
while(rx_buf.buf[++i] == 0xFE);
//再次确认
if(rx_buf.buf[i++] == 0xAA)
{
uint8_t driver_uid[6] = {0};
for(int j = 0; j < 6; j++)
{
// | 0XAA
driver_uid[j] |= rx_buf.buf[i]&0x55;
// | 0x55
driver_uid[j] |= rx_buf.buf[i+1]&0xAA;
i+=2;
}
uint32_t sum = 0;
for(int j = 0; j < 6; j++)
{
sum += driver_uid[j]|0xAA;
sum += driver_uid[j]|0x55;
}
uint8_t checksum[4] = {0};
checksum[0] = (sum >> 8)|0xAA;
checksum[1] = (sum >> 8)|0x55;
checksum[2] = sum|0xAA;
checksum[3] = sum|0x55;
if(rx_buf.buf[i] == checksum[0] && rx_buf.buf[i+1] == checksum[1] &&
rx_buf.buf[i+2] == checksum[2] && rx_buf.buf[i+3] == checksum[3])
{
uint64_t uid = 0;
for(int j = 0; j < 5; j++)
{
uid |= driver_uid[j];
uid <<= 8;
}
//最后一位不用左移
uid |= driver_uid[5];
RDM_Uid_Add(uid);
}
else
package_prase.rdm_package = ERROR_PACKAGE;
i+=4;
}
}
标准包解析
//其他包
else
{
switch(rx_buf.buf[i+20])
{
case 0x11:
if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0x02)
{
package_prase.rdm_package = DISC_MUTE;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0x03)
{
package_prase.rdm_package = DISC_UN_MUTE;
}
break;
case 0x21:
if(rx_buf.buf[i+21] == 0x10 && rx_buf.buf[i+22] == 0x00)
{
package_prase.rdm_package = GET_DRIVER_FLAG;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0xF0)
{
package_prase.rdm_package = GET_DRIVER_DMX_ADDR;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0xC0)
{
package_prase.rdm_package = GET_DRIVER_VERSION;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0x60)
{
package_prase.rdm_package = GET_DRIVER_INFO;
}
break;
case 0x33:
if(rx_buf.buf[i+21] == 0x10 && rx_buf.buf[i+22] == 0x00)
{
package_prase.rdm_package = SET_DRIVER_FLAG;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0xF0)
{
package_prase.rdm_package = SET_DRIVER_DMX_ADDR;
}
break;
}
for(int j = 0; j < rx_buf.buf[i+23]; j++)
{
package_prase.data[j] = rx_buf.buf[i+24+j];
}
//检测校验位
uint32_t sum = 0;
for(int j = i; j < i+24+rx_buf.buf[i+23]; j++)
{
sum += rx_buf.buf[j];
}
i += 24+rx_buf.buf[i+23];
//!!! & 优先级小于 ==
if((((sum >> 8) & 0xFF) == rx_buf.buf[i]) && ((sum & 0xFF) == rx_buf.buf[i+1]));
else
package_prase.rdm_package = ERROR_PACKAGE;
i += 2;
}
解析完清除包
//清除处理完的数据
MU_Move_Arr(rx_buf.buf,rx_buf.index,i);
rx_buf.index -= i;
return package_prase;
3、完整代码
rdm_package_prase_t RDM_Package_Prase(void)
{
//定位包头
uint8_t disc_unique = 0;
//用于确定包长
int i = 0;
for(i = 0; i < rx_buf.index; i++)
{
if(rx_buf.buf[i] == 0xCC && rx_buf.buf[i+1] == 0x01)
break;
if(rx_buf.buf[i] == 0xFE)
{
disc_unique++;
if(disc_unique == 5)
break;
}
}
rdm_package_prase_t package_prase;
//空包
if(i == rx_buf.index)
{
package_prase.rdm_package = NULL_PACKAGE;
}
//广播包
else if(disc_unique == 5)
{
package_prase.rdm_package = DISC_UNIQUE;
//跳过0xFE
while(rx_buf.buf[++i] == 0xFE);
//再次确认
if(rx_buf.buf[i++] == 0xAA)
{
uint8_t driver_uid[6] = {0};
for(int j = 0; j < 6; j++)
{
// | 0XAA
driver_uid[j] |= rx_buf.buf[i]&0x55;
// | 0x55
driver_uid[j] |= rx_buf.buf[i+1]&0xAA;
i+=2;
}
uint32_t sum = 0;
for(int j = 0; j < 6; j++)
{
sum += driver_uid[j]|0xAA;
sum += driver_uid[j]|0x55;
}
uint8_t checksum[4] = {0};
checksum[0] = (sum >> 8)|0xAA;
checksum[1] = (sum >> 8)|0x55;
checksum[2] = sum|0xAA;
checksum[3] = sum|0x55;
if(rx_buf.buf[i] == checksum[0] && rx_buf.buf[i+1] == checksum[1] && rx_buf.buf[i+2] == checksum[2] && rx_buf.buf[i+3] == checksum[3])
{
uint64_t uid = 0;
for(int j = 0; j < 5; j++)
{
uid |= driver_uid[j];
uid <<= 8;
}
//最后一位不用左移
uid |= driver_uid[5];
RDM_Uid_Add(uid);
}
else
package_prase.rdm_package = ERROR_PACKAGE;
i+=4;
}
}
//其他包
else
{
switch(rx_buf.buf[i+20])
{
case 0x11:
if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0x02)
{
package_prase.rdm_package = DISC_MUTE;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0x03)
{
package_prase.rdm_package = DISC_UN_MUTE;
}
break;
case 0x21:
if(rx_buf.buf[i+21] == 0x10 && rx_buf.buf[i+22] == 0x00)
{
package_prase.rdm_package = GET_DRIVER_FLAG;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0xF0)
{
package_prase.rdm_package = GET_DRIVER_DMX_ADDR;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0xC0)
{
package_prase.rdm_package = GET_DRIVER_VERSION;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0x60)
{
package_prase.rdm_package = GET_DRIVER_INFO;
}
break;
case 0x33:
if(rx_buf.buf[i+21] == 0x10 && rx_buf.buf[i+22] == 0x00)
{
package_prase.rdm_package = SET_DRIVER_FLAG;
}
else if(rx_buf.buf[i+21] == 0x00 && rx_buf.buf[i+22] == 0xF0)
{
package_prase.rdm_package = SET_DRIVER_DMX_ADDR;
}
break;
}
for(int j = 0; j < rx_buf.buf[i+23]; j++)
{
package_prase.data[j] = rx_buf.buf[i+24+j];
}
//检测校验位
uint32_t sum = 0;
for(int j = i; j < i+24+rx_buf.buf[i+23]; j++)
{
sum += rx_buf.buf[j];
}
i += 24+rx_buf.buf[i+23];
//!!! & 优先级小于 ==
if((((sum >> 8) & 0xFF) == rx_buf.buf[i]) && ((sum & 0xFF) == rx_buf.buf[i+1]));
else
package_prase.rdm_package = ERROR_PACKAGE;
i += 2;
}
//清除处理完的数据
MU_Move_Arr(rx_buf.buf,rx_buf.index,i);
rx_buf.index -= i;
return package_prase;
}
推荐的用法是发送一个包后延时一段时间后立马解包,不要堆积太多包导致错误发生。
二、控制端组包发送实例
组包接口的实现在上一篇有介绍,这里主要介绍接口的用法
1、广播包
//广播包
uint8_t RDM_Disc_Driver(uint64_t low_uid,uint64_t high_uid)
{
rdm_package_t package = {0};
RDM_Package_Init(&package);
//广播地址
RDM_Package_Set_Uid(&package,0x0000FFFFFFFFFFFF);
//寻找设备
RDM_Package_Set_Cmd(&package,0x10,0x0001);
//设置数据
uint8_t data[12];
data[0] = (low_uid >> 40) & 0xFF;
data[1] = (low_uid >> 32) & 0xFF;
data[2] = (low_uid >> 24) & 0xFF;
data[3] = (low_uid >> 16) & 0xFF;
data[4] = (low_uid >> 8) & 0xFF;
data[5] = low_uid & 0xFF;
data[6] = (high_uid >> 40) & 0xFF;
data[7] = (high_uid >> 32) & 0xFF;
data[8] = (high_uid >> 24) & 0xFF;
data[9] = (high_uid >> 16) & 0xFF;
data[10] = (high_uid >> 8) & 0xFF;
data[11] = high_uid & 0xFF;
RDM_Package_Set_Data(&package,0x0C,data);
RDM_Package_Send(&package);
HAL_Delay(20);
//解包
rdm_package_prase_t package_prase = RDM_Package_Prase();
if(package_prase.rdm_package == ERROR_PACKAGE)
return 0;
else if(package_prase.rdm_package == NULL_PACKAGE)
return 1;
else
return 2;
}
2、设置地址包
//设置设备DMX地址
uint8_t RDM_Set_DMX_Addr(uint64_t uid,uint16_t addr)
{
rdm_package_t package = {0};
RDM_Package_Init(&package);
RDM_Package_Set_Uid(&package,uid);
RDM_Package_Set_Cmd(&package,0x30,0x00F0);
uint8_t data[2] = {0};
data[0] = (addr >> 8) & 0xFF;
data[1] = addr & 0xFF;
RDM_Package_Set_Data(&package,2,data);
RDM_Package_Send(&package);
HAL_Delay(20);
//解包
rdm_package_prase_t package_prase = RDM_Package_Prase();
if(package_prase.rdm_package == ERROR_PACKAGE)
return 0;
else
return 1;
}
3、设置状态包
//设置设备状态
uint8_t RDM_Set_Flag(uint64_t uid,uint8_t flag)
{
rdm_package_t package = {0};
RDM_Package_Init(&package);
RDM_Package_Set_Uid(&package,uid);
RDM_Package_Set_Cmd(&package,0x30,0x1000);
RDM_Package_Set_Data(&package,1,&flag);
RDM_Package_Send(&package);
HAL_Delay(20);
//解包
rdm_package_prase_t package_prase = RDM_Package_Prase();
if(package_prase.rdm_package == ERROR_PACKAGE)
return 0;
else
return 1;
}
4、其他包
//哑音包
uint8_t RDM_Disc_Mute(uint64_t uid)
{
rdm_package_t package = {0};
RDM_Package_Init(&package);
RDM_Package_Set_Uid(&package,uid);
RDM_Package_Set_Cmd(&package,0x10,0x0002);
RDM_Package_Set_Data(&package,0,NULL);
RDM_Package_Send(&package);
HAL_Delay(20);
//解包
rdm_package_prase_t package_prase = RDM_Package_Prase();
if(package_prase.rdm_package == ERROR_PACKAGE)
return 0;
else
return 1;
}
//解除哑音包
uint8_t RDM_Disc_Un_Mute(uint64_t uid)
{
rdm_package_t package = {0};
RDM_Package_Init(&package);
RDM_Package_Set_Uid(&package,uid);
RDM_Package_Set_Cmd(&package,0x10,0x0003);
RDM_Package_Set_Data(&package,0,NULL);
RDM_Package_Send(&package);
HAL_Delay(20);
//解包
rdm_package_prase_t package_prase = RDM_Package_Prase();
if(package_prase.rdm_package == ERROR_PACKAGE)
return 0;
else
return 1;
}
//查设备状态
uint8_t RDM_Get_Flag(uint64_t uid,uint8_t *flag)
{
rdm_package_t package = {0};
RDM_Package_Init(&package);
RDM_Package_Set_Uid(&package,uid);
RDM_Package_Set_Cmd(&package,0x20,0x1000);
RDM_Package_Set_Data(&package,0,NULL);
RDM_Package_Send(&package);
HAL_Delay(20);
//解包
rdm_package_prase_t package_prase = RDM_Package_Prase();
if(package_prase.rdm_package == ERROR_PACKAGE)
{
*flag = 255;
return 0;
}
else
{
*flag = package_prase.data[0];
return 1;
}
}
//查设备DMX地址
uint8_t RDM_Get_DMX_Addr(uint64_t uid,uint16_t *addr)
{
rdm_package_t package = {0};
RDM_Package_Init(&package);
RDM_Package_Set_Uid(&package,uid);
RDM_Package_Set_Cmd(&package,0x20,0x00F0);
RDM_Package_Set_Data(&package,0,NULL);
RDM_Package_Send(&package);
HAL_Delay(20);
//解包
rdm_package_prase_t package_prase = RDM_Package_Prase();
if(package_prase.rdm_package == ERROR_PACKAGE)
{
*addr = 0;
return 0;
}
else
{
*addr = package_prase.data[0];
*addr <<= 8;
*addr |= package_prase.data[1];
return 1;
}
}
//查设备软件版本
uint8_t RDM_Get_Version(uint64_t uid,uint8_t *str)
{
rdm_package_t package = {0};
RDM_Package_Init(&package);
RDM_Package_Set_Uid(&package,uid);
RDM_Package_Set_Cmd(&package,0x20,0x00C0);
RDM_Package_Set_Data(&package,0,NULL);
RDM_Package_Send(&package);
HAL_Delay(20);
//解包
rdm_package_prase_t package_prase = RDM_Package_Prase();
uint8_t str_len = 0;
if(package_prase.rdm_package == ERROR_PACKAGE)
return str_len;
else
{
while(package_prase.data[str_len] != '\0')
{
str[str_len]=package_prase.data[str_len];
str_len++;
}
str[str_len]=package_prase.data[str_len];
return str_len;
}
}
//查设备信息
uint8_t RDM_Get_Info(uint64_t uid,uint8_t *str)
{
rdm_package_t package = {0};
RDM_Package_Init(&package);
RDM_Package_Set_Uid(&package,uid);
RDM_Package_Set_Cmd(&package,0x20,0x0060);
RDM_Package_Set_Data(&package,0,NULL);
RDM_Package_Send(&package);
HAL_Delay(20);
//解包
rdm_package_prase_t package_prase = RDM_Package_Prase();
uint8_t str_len = 0;
if(package_prase.rdm_package == ERROR_PACKAGE)
return str_len;
else
{
while(package_prase.data[str_len] != '\0')
{
str[str_len]=package_prase.data[str_len];
str_len++;
}
str[str_len]=package_prase.data[str_len];
return str_len;
}
}
注意:设置发送数据没有额外分配空间,当函数调用结束,数据内存空间释放后包还未发送就会出错。延时20ms是为了留有足够的时间让接收设备处理RDM包并回应
三、二分搜寻设备算法
使用了递归实现,调用RDM_Disc_Driver接口,如果返回正确的包表示没有冲突,即可以确定唯一设备,确定后将设备哑音。如果没有回应表示这个分支没有设备,即结束分支。如果检测到有冲突则分为左右两个分支分别搜寻设备,即二分。
static uint8_t RDM_Binary_Search(uint64_t low_uid,uint64_t high_uid)
{
uint8_t disc_return = RDM_Disc_Driver(low_uid,high_uid);
//确定唯一设备
if(disc_return == 2)
{
//新加入的uid在头节点后面
RDM_Disc_Mute(uid_list->next->uid);
return 2;
}
//无设备
else if(disc_return == 1)
{
return 0;
}
//冲突
else
{
uint64_t mid_uid = (high_uid - low_uid)/2;
RDM_Binary_Search(low_uid,mid_uid);
RDM_Binary_Search(mid_uid,high_uid);
return 1;
}
}
再一次二分结束后,理想状态是搜寻到所有设备,但是中途加进来的设备可能无法确定。所以需要再次搜寻,之前搜寻到的设备进入哑音不会回应,如果这次搜寻也没有回应表示所有设备均已确定。所以以完整算法如下。
void RDM_Driver_Search(uint64_t low_uid,uint64_t high_uid)
{
//销毁之前的链表
RDM_Uid_Distory();
//创建新的链表
RDM_Uid_Init();
//搜寻设备直到无设备回应
while(RDM_Binary_Search(low_uid,high_uid) != 0);
//解除所有哑音的设备
uid_t *p = uid_list->next;
while(p != NULL)
{
RDM_Disc_Un_Mute(p->uid);
p = p->next;
}
}
总结
本文介绍了控制端的组包和解包函数,接收端的组包和解包也类似,相信你也能独立完成接收端的组包和解包函数。下面是接口实现结果,本文结束。