//引言
学校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 && 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(&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 && 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通信协议传输全部完成。