STM32自定义串口通信协议

目录

前沿

1 命令格式

2 应用举例

2.1 读寄存器

2.2 写寄存器  

3 编写代码

3.1 main函数代码

3.2 中断函数代码

3.3 串口函数代码

3.4 事件处理函数代码

3.5 串口发送代码

4 代码演示

4.1 读寄存器

4.2 写寄存器


前沿

        这段时间在写一个自动化测试平台的下位机软件,借此机会给大家分享一下自定义的串口通信协议。

1 命令格式

帧格式:[数据头码] [类型码] [长度码] [ID 码] [参数码1] [参数码2]... [参数码n] [校验码] [数据尾码] 注意:参数码最少为1

  • 数据头码:‘$’ '#' [0x24 0x23](主到从) ,‘@’ '#' [0x40 0x23](从到主)

  • 类型码:用于定于具体操作的类型,可以是读写寄存器、控制电机,也可以是控制摄像头等

  • 数据尾码:'#' '@'[0x23 0x40](主到从),‘#’ ‘$’[0x23 0x24](从到主)长度码:1byte[类型码] +1byte [ID 码] + nbyte[参数码] (参数码最少为1)+1byte[校验码]

  • ID码:ID码根据类型码的不同含义有所不同

  • 参数码:参数码可表示地址、数据、子类型等

  • 校验码:等于和校验^0xFF,计算公式: [类型码] + [长度码] + [ID 码] + [参数码1] + [参数码2] +... + [参数码n](参数码最少为1

2 应用举例

      这里举一个寄存器的读写操作命令格式。寄存器数据位为8位。

类型码:0x01

ID码:读(0x00),写(0x01)

2.1 读寄存器

通信方向数据头码长度码类型码ID码参数码1参数码2参数码3参数码n校验码数据尾码
主-->从"$#"0x050x010x00addrlenxx"#@"
从-->主"@#"xx0x010x00lendatadatadataxx"#$"

addr:读寄存器的地址

  • 0x00:寄存器1地址

  • 0x01:寄存器2地址

  • 0x02:寄存器3地址

len:读取的长度

data:读取的数据,地址依次递增

举例:

主-->从:0x24 0x23 0x05 0x01 0x00 0x01 0x02 0xxx 0x23 0x40

表示从0x01地址开始,读取2个连续的地址的值

从-->主:0x40 0x23 0x06 0x01 0x00 0x02 0xxx 0xxx 0xxx 0x23 0x24

返回0x01和0x02地址的值

2.2 写寄存器  

通信方向数据头码长度码类型码ID码参数码1参数码2参数码3参数码n校验码数据尾码
主-->从"$#"xx0x010x01addrlendatadataxx"#@"
从-->主"@#"xx0x010x01addrlendatadataxx"#$"

addr:写寄存器的地址

  • 0x00:寄存器1地址

  • 0x01:寄存器2地址

  • 0x02:寄存器3地址

len:写的长度,字节为单位

data:写的数据,地址依次递增

举例:

主-->从:0x24 0x23 0x07 0x01 0x01 0x01 0x02 0x01 0x02 0xxx 0x23 0x40

表示向0x01地址写入0x01值,0x02地址写入0x02值

从-->主:0x24 0x23 0x07 0x01 0x01 0x01 0x02 0x01 0x02 0xxx 0x23 0x40

返回状态

3 编写代码

3.1 main函数代码

        main函数主要实现协议的解析,根据不同的类型码进行具体的操作。cmdSet是一个函数指针数组,里面存放不同类型的操作函数。

typedef void (*p_cmdType)(CommDataWithHost_Typedef *commdata_struct);

/* 不同类型码执行不同的函数 */
p_cmdType cmdSet[MAX_CMD_SUPPORT]=
{
    Function1,             /* 其他功能函数 */
    RW_RegFunction         /* 读写寄存器函数 */
    
};

/* 参数码的大小 */
#define PARA_CODE_LEN 32

/* 帧结构 */
typedef struct
{
    __IO uint8_t framehead1;
    __IO uint8_t framehead2;
    __IO uint8_t length;
    __IO uint8_t type;
    __IO uint8_t id;
    __IO uint8_t function[PARA_CODE_LEN];
    __IO uint8_t check;
    __IO uint8_t frameend1;
    __IO uint8_t frameend2;
}CommDataWithHost_Typedef;

CommDataWithHost_Typedef RxDataWithHost_Structure;
__IO uint8_t ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEHEAD1_FLAG;
extern p_cmdType cmdSet[MAX_CMD_SUPPORT];

/*******************************************************************************
* Function Name  : main.
* Description    : Main routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int main(void)
{
    void (*pfun)(CommDataWithHost_Typedef *);
    
    USART_Config();

    /* Infinite loop */
    while (1)
    {
        if(ReceiveData_InformFlag == RXDATA_FROMHOST_COMPLETED_FLAG)
        {
            ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEHEAD1_FLAG;
            
            /* 防止数组越界 */
            if (RxDataWithHost_Structure.type < MAX_CMD_SUPPORT)
            {
                //p_cmdType pfun = cmdSet[RxDataWithHost_Structure.type];
                pfun = cmdSet[RxDataWithHost_Structure.type];
                
                if (pfun != 0)
                {
                    pfun(&RxDataWithHost_Structure);
                }                 
            }
          
        }  
    }
}

3.2 中断函数代码

        中断函数主要是接收串口收到的数据,并进行逐一处理。因为串口是一个字节一个字节的接收数据,所以在每次串口接收到数据之后,都会置位下一次将要接收的标志。

        这里也对数据的越界做了相应的处理,处理代码:

if ((tempData > 3) && (tempData <= (PARA_CODE_LEN + 3)))

extern CommDataWithHost_Typedef RxDataWithHost_Structure;
extern __IO uint8_t ReceiveData_InformFlag;

/* Definition for information flag of receive data from pc host  */
typedef enum
{
    RXDATA_FROMHOST_FRAMEHEAD1_FLAG = 0,
    RXDATA_FROMHOST_FRAMEHEAD2_FLAG,
    RXDATA_FROMHOST_LENGTH_FLAG,   
    RXDATA_FROMHOST_TYPE_FLAG,      
    RXDATA_FROMHOST_ID_FLAG,        
    RXDATA_FROMHOST_FUNCTION_FLAG,  
    RXDATA_FROMHOST_CHECKDATA_FLAG, 
    RXDATA_FROMHOST_FRAMEEND1_FLAG, 
    RXDATA_FROMHOST_FRAMEEND2_FLAG, 
    RXDATA_FROMHOST_COMPLETED_FLAG 
    
}RXDATA_FROMHOST_ENUM;

/* RX帧头帧尾 */
#define RXDATA_WITHHOST_FRAMEHEAD1        '$'
#define RXDATA_WITHHOST_FRAMEHEAD2        '#'
#define RXDATA_WITHHOST_FRAMEEND1         '#'
#define RXDATA_WITHHOST_FRAMEEND2         '@'

/* TX帧头帧尾 */
#define TXDATA_WITHHOST_FRAMEHEAD1        '@'
#define TXDATA_WITHHOST_FRAMEHEAD2        '#'
#define TXDATA_WITHHOST_FRAMEEND1         '#'
#define TXDATA_WITHHOST_FRAMEEND2         '$'

/**
  * @brief  This function handles USART1 interrupt request.
  * @param  None
  * @retval None
  */
void DEBUG_USART_IRQHandler(void)
{
    uint8_t tempData = 0x00;
    static uint8_t tempcnt = 0;

    if (USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)
    {
        tempData = USART_ReceiveData(DEBUG_USARTx);

        switch (ReceiveData_InformFlag)
        {
            /* Received 1 byte : frame head data 1 = '$' */
            case RXDATA_FROMHOST_FRAMEHEAD1_FLAG:
                if (tempData == RXDATA_WITHHOST_FRAMEHEAD1)
                {
                    RxDataWithHost_Structure.framehead1 = tempData;
                    ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEHEAD2_FLAG;
                }
                else
                {
                    ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEHEAD1_FLAG;
                }

                break;

            /* Received a frame head data 2 : '#' */
            case RXDATA_FROMHOST_FRAMEHEAD2_FLAG:
                if (tempData == RXDATA_WITHHOST_FRAMEHEAD2)
                {
                    RxDataWithHost_Structure.framehead2 = tempData;
                    ReceiveData_InformFlag = RXDATA_FROMHOST_LENGTH_FLAG;
                }
                else
                {
                    ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEHEAD1_FLAG;
                }

                break;

            /* Received data length */
            case RXDATA_FROMHOST_LENGTH_FLAG:

                /* 判断长度段的数据是否满足要求,至少为4,不能大于PARA_CODE_LEN+3 */
                if ((tempData > 3) && (tempData <= (PARA_CODE_LEN + 3)))
                {
                    RxDataWithHost_Structure.length = tempData;
                    ReceiveData_InformFlag = RXDATA_FROMHOST_TYPE_FLAG;
                    tempcnt = 0;
                }
                else
                {
                    ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEHEAD1_FLAG;
                }

                break;

            /* Received data type information */
            case RXDATA_FROMHOST_TYPE_FLAG:
                RxDataWithHost_Structure.type = tempData;
                ReceiveData_InformFlag = RXDATA_FROMHOST_ID_FLAG;
                break;

            /* Received id information */
            case RXDATA_FROMHOST_ID_FLAG:
                RxDataWithHost_Structure.id = tempData;
                ReceiveData_InformFlag = RXDATA_FROMHOST_FUNCTION_FLAG;
                break;

            /* Received function information */
            case RXDATA_FROMHOST_FUNCTION_FLAG:
                RxDataWithHost_Structure.function[tempcnt] = tempData;
                tempcnt++;

                if ((RxDataWithHost_Structure.length - 3) == tempcnt)
                {
                    ReceiveData_InformFlag = RXDATA_FROMHOST_CHECKDATA_FLAG;
                }

                break;

            /* Received check information */
            case RXDATA_FROMHOST_CHECKDATA_FLAG:
                RxDataWithHost_Structure.check = tempData;
                ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEEND1_FLAG;
                break;

            /* Received frame end data 1 = '#' */
            case RXDATA_FROMHOST_FRAMEEND1_FLAG:
                if (tempData == RXDATA_WITHHOST_FRAMEEND1)
                {
                    RxDataWithHost_Structure.frameend1 = tempData;
                    ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEEND2_FLAG;
                }
                else
                {
                    ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEHEAD1_FLAG;
                }

                break;

            /* Received frame end data 2 = '@' */
            case RXDATA_FROMHOST_FRAMEEND2_FLAG:
                if (tempData == RXDATA_WITHHOST_FRAMEEND2)
                {
                    RxDataWithHost_Structure.frameend2 = tempData;
                    ReceiveData_InformFlag = RXDATA_FROMHOST_COMPLETED_FLAG;
                }
                else
                {
                    ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEHEAD1_FLAG;
                }

                break;

            default:
                ReceiveData_InformFlag = RXDATA_FROMHOST_FRAMEHEAD1_FLAG;
                break;
        }
    }
}

3.3 串口函数代码

        串口初始化代码主要初始化串口的波特率、奇偶校验、停止位等信息。并使能接收中断。

/**
  * @brief  Configure NVIC.
  * @param  None
  * @retval None
  */
static void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/**
  * @brief  Initializes and config USART.
  * @param  None
  * @retval None
  */
void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    /* Enable USART and GPIO clock */
    DEBUG_USART_TX_GPIO_AHBClkCmd(DEBUG_USART_TX_GPIO_CLK, ENABLE);
    DEBUG_USART_RX_GPIO_AHBClkCmd(DEBUG_USART_RX_GPIO_CLK, ENABLE);
    DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

    /* Configure the GPIO of USART_TX to AF-PP mode */
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

    /* Configure the GPIO of USART_RX to floating input mode */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

    /* Configure the working parameters of the USART  */
    USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(DEBUG_USARTx, &USART_InitStructure);

    NVIC_Configuration();

    USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);

    USART_Cmd(DEBUG_USARTx, ENABLE);
}

3.4 事件处理函数代码

        处理读写事件,读主要是读出寄存器的内容返回给主机,写主要是把主机下发的内容写入到从机的寄存器中,读写这两个函数需要自己实现。

/**
  * @brief  读写寄存器
  * @param  RxData: 接收的串口数据.
  * @retval None
  */
void RW_RegFunction(CommDataWithHost_Typedef *RxData)
{
    CommDataWithHost_Typedef TxData;
    uint8_t i = 0;

#ifdef DATA_CHECK  
    uint8_t tempCheckValue = 0;
       
    tempCheckValue = CalcuCheckValue_DataWithHost(RxData);
        
    if (tempCheckValue == RxData->check)
#endif
    {
        /* 读 */
        if (RxData->id == 0x00)
        {
            TxData.length = RxData->length+RxData->function[1]-1;  //总长度
            TxData.type = RxData->type;
            TxData.id = RxData->id;
            TxData.function[0] = RxData->function[1];             //数据长度

            /* 依次读出寄存器的值 */
    //        pEEPROMReg->Read((uint8_t*)&TxData.function[1+i], RxData->function[0], RxData->function[1]);
            
            TransmitData_CommunicateWithHost(&TxData); 
        }
        /* 写 */
        else if (RxData->id == 0x01)
        {
    //        pEEPROMReg->Write((uint8_t*)&RxData->function[2], RxData->function[0], RxData->function[1]);
            TxData.length = RxData->length;  //总长度
            TxData.type = RxData->type;
            TxData.id = RxData->id;
            /* 依次读出寄存器的值,并反馈给TxData */
            for (i=0; i<(RxData->length-3); i++)
            {
                TxData.function[i] = RxData->function[i];
            }
            
            TransmitData_CommunicateWithHost(&TxData);         
        }          
    }
}

3.5 串口发送代码

        串口发送代码分为校验发送和不带校验发送,校验通过CalcuCheckValue_DataWithHost函数实现。串口发送通过TransmitData_CommunicateWithHost实现。

        校验与否可通过宏定义决定,因为串口助手进行验证的时候,校验码不好计算,所以在测试阶段不校验,在交付阶段需要把校验打开。

/**
  * @brief  Calculate the data check value of communication with pc host.
  * @param  TxData: Communication data structure.
  * @retval None
  */
uint8_t CalcuCheckValue_DataWithHost(CommDataWithHost_Typedef* TxData)
{
    uint8_t i;
    uint8_t tempValue = 0x00;
  
    for(i=0; i<(TxData->length-3); i++)
    {
        tempValue = (uint8_t)(tempValue + TxData->function[i]);
    }
    tempValue = (uint8_t)(tempValue                 + \
                          TxData->length   + \
                          TxData->type     + \
                          TxData->id );
    tempValue = tempValue ^ 0xFF;
    
    return tempValue;
}

/**
  * @brief  Communicate and transmit data with pc host.
  * @param  TxData: Communication data structure.
  * @retval None
  */
void TransmitData_CommunicateWithHost(CommDataWithHost_Typedef* TxData)
{
    uint8_t i = 0;
    uint8_t tempCheckValue = 0;

    /* 防止数组越界 */
    if ((TxData->length-3) <= PARA_CODE_LEN)
    {
        TxData->framehead1 = TXDATA_WITHHOST_FRAMEHEAD1;
        TxData->framehead2 = TXDATA_WITHHOST_FRAMEHEAD2;
        TxData->frameend1 = TXDATA_WITHHOST_FRAMEEND1;
        TxData->frameend2 = TXDATA_WITHHOST_FRAMEEND2;
      
        tempCheckValue = CalcuCheckValue_DataWithHost(TxData);
        
        usartWithHost_SendByte(TxData->framehead1);
        usartWithHost_SendByte(TxData->framehead2);
        usartWithHost_SendByte(TxData->length);
        usartWithHost_SendByte(TxData->type);
        usartWithHost_SendByte(TxData->id);
      
        for(i=0; i<(TxData->length-3); i++)
        {
            usartWithHost_SendByte(TxData->function[i]);
        }
        
        usartWithHost_SendByte(tempCheckValue);
        usartWithHost_SendByte(TxData->frameend1);
        usartWithHost_SendByte(TxData->frameend2);                   
    }
}

4 代码演示

4.1 读寄存器

4.2 写寄存器

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

binhaoPro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值