标准RDM协议详解及代码实现篇二 —— 控制端组包解包及二分搜寻算法

目录

前言

一、控制端解包接口

1、辅助函数解释

 2、解包接口解析

3、完整代码

二、控制端组包发送实例

1、广播包

2、设置地址包

3、设置状态包

4、其他包

三、二分搜寻设备算法

总结


前言

        本文主要介绍标准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;
    }
}

总结

        本文介绍了控制端的组包和解包函数,接收端的组包和解包也类似,相信你也能独立完成接收端的组包和解包函数。下面是接口实现结果,本文结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值