uart协议实现双stm32单片机通信

//引言

学校robomaster校内赛规定主控需使用c8t6,但因笔者想尝试引入编码器电机做简单的pid电机控制,而c8t6只有4个定时器,无法同时实现4路编码器均用定时器计数(电机速度快,采用中断模式有阻塞CPU风险)和pwm输出控制电机舵机等外设。

故思考许久采用双c8t6主从控模式,主控负责接收遥控器信号、电机方向发送、编码器速度接收、pid计算pwm输出,从控负责电机方向接收、编码器速度识别与发送。

本文主要讲述uart通信过程。

//主控Transmit

				
					void UART_Main_SendDirection(void)
{
    main_txbuf[0] = 0xAA;
    main_txbuf[1] = 0x55;

    main_txbuf[2] = Motor_Direction[0];
    main_txbuf[3] = Motor_Direction[1];
    main_txbuf[4] = Motor_Direction[2];
    main_txbuf[5] = Motor_Direction[3];

    main_txbuf[6] = main_txbuf[2] + main_txbuf[3] + main_txbuf[4] + main_txbuf[5];

    HAL_UART_Transmit_DMA(&huart3, main_txbuf, 7);
}

				
			

此处在发送的数据包前添加了帧头0xAA 0x55,数据包尾部添加了求和计算

//从控Receive

				
					void UART_Sub_RxParse(){
    uint8_t sub_rxbuf_temp[7];

    for(int i = 0; i < 7; i++)
    {
        if(sub_rxbuf[i] == 0xAA &#038;&#038; sub_rxbuf[i+1] == 0x55)
        {
            memcpy(sub_rxbuf_temp, sub_rxbuf, 7);
            for(int k = 0; k < i; k++)
            {
                int temp = sub_rxbuf_temp[0];
                for(int j = 1; j < 7; j++)
                {
                    sub_rxbuf_temp[j-1] = sub_rxbuf_temp[j];
                }
                sub_rxbuf_temp[6] = temp;
            }
        }

            int8_t sum = sub_rxbuf_temp[2] + sub_rxbuf_temp[3] + sub_rxbuf_temp[4] + sub_rxbuf_temp[5];
            if(sum == sub_rxbuf_temp[6])
            {
                Motor_Direction_From_Main[0] = sub_rxbuf_temp[2];
                Motor_Direction_From_Main[1] = sub_rxbuf_temp[3];
                Motor_Direction_From_Main[2] = sub_rxbuf_temp[4];
                Motor_Direction_From_Main[3] = sub_rxbuf_temp[5];
        }
    }
}
				
			

为防止uart传输中的不稳定导致丢包等情况,此处定义了一个temp数组。使用memcpy复制到temp中,并采用for循环处理数组,以保证0xAA帧头位于数组第一位

				
					if(sub_rxbuf[i] == 0xAA && sub_rxbuf[i+1] == 0x55)
        {
            memcpy(sub_rxbuf_temp, sub_rxbuf, 7);
            for(int k = 0; k < i; k++)
            {
                int temp = sub_rxbuf_temp[0];
                for(int j = 1; j < 7; j++)
                {
                    sub_rxbuf_temp[j-1] = sub_rxbuf_temp[j];
                }
                sub_rxbuf_temp[6] = temp;
            }
        }
				
			

//从控Transmit

				
					void UART_Sub_Send_RealSpeed(void)
{
    sub_txbuf[0] = 0xAA;
    sub_txbuf[1] = 0x55;

    // 将PID输出的 int16_t 转为字节流
    int16_t *speed = (int16_t*)Real_Speed;

    sub_txbuf[2] = speed[0] & 0xFF;
    sub_txbuf[3] = (speed[0] >> 8) & 0xFF;

    sub_txbuf[4] = speed[1] & 0xFF;
    sub_txbuf[5] = (speed[1] >> 8) & 0xFF;

    sub_txbuf[6] = speed[2] & 0xFF;
    sub_txbuf[7] = (speed[2] >> 8) & 0xFF;

    sub_txbuf[8] = speed[3] & 0xFF;
    sub_txbuf[9] = (speed[3] >> 8) & 0xFF;

    // 校验
    uint8_t sum = 0;
    for (int i = 2; i <= 9; i++)
        sum += sub_txbuf[i];
    sub_txbuf[10] = sum;

    HAL_UART_Transmit_DMA(&#038;huart1, sub_txbuf, 11);
}

				
			

此处依旧添加0xAA 0x55帧头和尾部求和校验。

				
					int16_t *speed = (int16_t*)Real_Speed;

    sub_txbuf[2] = speed[0] & 0xFF;
    sub_txbuf[3] = (speed[0] >> 8) & 0xFF;

    sub_txbuf[4] = speed[1] & 0xFF;
    sub_txbuf[5] = (speed[1] >> 8) & 0xFF;

    sub_txbuf[6] = speed[2] & 0xFF;
    sub_txbuf[7] = (speed[2] >> 8) & 0xFF;

    sub_txbuf[8] = speed[3] & 0xFF;
    sub_txbuf[9] = (speed[3] >> 8) & 0xFF;
				
			

因编码器输出的Real_Speed已包含方向,但我们的uart通信协议只能传输uint8_t类型的指针数据,所以这里采用常用的位运算把数据处理为高八位和低八位的格式。

//主控Receive

				
					void UART_Main_RxPrase(void)
{
    uint8_t main_rxbuf_temp[11];

    for(int i = 0; i < 11; i++)
    {
        if(main_rxbuf[i] == 0xAA &#038;&#038; main_rxbuf[i+1] == 0x55)
        {
            memcpy(main_rxbuf_temp, main_rxbuf, 11);
            for(int k = 0; k < i; k++){
                int temp = main_rxbuf_temp[0];
                for (int j = 1; j < 11; j++){
                    main_rxbuf_temp[j-1] = main_rxbuf_temp[j];
                }
                main_rxbuf_temp[10] = temp;
            }

            uint8_t sum = main_rxbuf_temp[2] + main_rxbuf_temp[3] + main_rxbuf_temp[4] + main_rxbuf_temp[5] + main_rxbuf_temp[6] + main_rxbuf_temp[7] + main_rxbuf_temp[8] + main_rxbuf_temp[9];
            if(sum == main_rxbuf_temp[10])
            {
                Real_Speed_From_Sub[0] = (int16_t)((main_rxbuf_temp[3] << 8) | main_rxbuf_temp[2]);
                Real_Speed_From_Sub[1] = (int16_t)((main_rxbuf_temp[5] << 8) | main_rxbuf_temp[4]);
                Real_Speed_From_Sub[2] = (int16_t)((main_rxbuf_temp[7] << 8) | main_rxbuf_temp[6]);
                Real_Speed_From_Sub[3] = (int16_t)((main_rxbuf_temp[9] << 8) | main_rxbuf_temp[8]);
            }
        }
    }
}
				
			

此处原理同上,采用for循环处理帧头,使其保持在第一位。再用按位或运算还原带方向的Real_Speed数据。

//总结

uart数据传输中应充分考虑传输的不稳定性、不确定性。采用帧头帧尾设定与处理校验,保证每组接收的数据均为正确的。

同时应注意有符号数据的高低八位传输。

至此,两块c8t6的uart通信协议传输全部完成。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注