文章目录
前言
这个暑假参加了电赛,做的是飞机题,因此经常需要使用到串口通信,完成各个模块之间的通信。但是,在使用串口的时候却发现很多的问题,因此这里记录一下一些串口通信方式。主要参考匿名飞控的串口的通信代码。
一、STM32与Openmv之间的串口通信(通信数据长度已知)
1.STM32
首先,先给出匿名飞控之间的串口通信的协议。
由上图可知,匿名飞控之间的串口通信都是数据长度已知的。
下面开始分析代码
首先是串口的配置(STM32F103C8T6)
//USART1
void DrvUart1Init(u32 br_num)
{
USART_InitTypeDef USART_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_StructInit(&USART_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
// GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);
// GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
USART_InitStructure.USART_BaudRate = br_num;
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_Tx | USART_Mode_Rx;
USART_ClockInitStruct.USART_Clock = USART_Clock_Disable;
USART_ClockInitStruct.USART_CPOL = USART_CPOL_Low;
USART_ClockInitStruct.USART_CPHA = USART_CPHA_2Edge;
USART_ClockInitStruct.USART_LastBit = USART_LastBit_Disable;
USART_Init(USART1, &USART_InitStructure);
USART_ClockInit(USART1, &USART_ClockInitStruct);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
u8 Tx1Buffer[256];
u8 Tx1Counter = 0;
u8 count1 = 0;
void DrvUart1SendBuf(unsigned char *DataToSend, u8 data_num)
{
u8 i;
for (i = 0; i < data_num; i++)
{
Tx1Buffer[count1++] = *(DataToSend + i);
}
if (!(USART1->CR1 & USART_CR1_TXEIE))
{
USART_ITConfig(USART1, USART_IT_TXE, ENABLE); //打开发送中断
}
}
u8 U1RxDataTmp[100];
u8 U1RxInCnt = 0;
u8 U1RxoutCnt = 0;
void drvU1GetByte(u8 data)
{
U1RxDataTmp[U1RxInCnt++] = data;
if(U1RxInCnt >= 100)
U1RxInCnt = 0;
}
void drvU1DataCheck(void)
{
while(U1RxInCnt!=U1RxoutCnt)
{
U1GetOneByte(U1RxDataTmp[U1RxoutCnt++]);
if(U1RxoutCnt >= 100)
U1RxoutCnt = 0;
}
}
void Usart1_IRQ(void)
{
u8 com_data;
if (USART1->SR & USART_SR_ORE) //ORE中断
{
com_data = USART1->DR;
}
//接收中断
if (USART_GetITStatus(USART1, USART_IT_RXNE))
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除中断标志
Res_Data = USART1->DR;
drvU1GetByte(com_data);
}
//发送(进入移位)中断
if (USART_GetITStatus(USART1, USART_IT_TXE))
{
USART1->DR = Tx1Buffer[Tx1Counter++]; //写DR清除中断标志
if (Tx1Counter == count1)
{
USART1->CR1 &= ~USART_CR1_TXEIE; //关闭TXE(发送中断)中断
}
}
}
void USART1_IRQHandler(void)
{
Usart1_IRQ();
}
//USART3
void DrvUart3Init(u32 br_num)
{
USART_InitTypeDef USART_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_StructInit(&USART_InitStructure);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);
// GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = br_num;
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_Tx | USART_Mode_Rx;
USART_ClockInitStruct.USART_Clock = USART_Clock_Disable;
USART_ClockInitStruct.USART_CPOL = USART_CPOL_Low;
USART_ClockInitStruct.USART_CPHA = USART_CPHA_2Edge;
USART_ClockInitStruct.USART_LastBit = USART_LastBit_Disable;
USART_Init(USART3, &USART_InitStructure);
USART_ClockInit(USART3, &USART_ClockInitStruct);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
USART_Cmd(USART3, ENABLE);
}
u8 Tx3Buffer[256];
u8 Tx3Counter = 0;
u8 count3 = 0;
void DrvUart3SendBuf(unsigned char *DataToSend, u8 data_num)
{
u8 i;
for (i = 0; i < data_num; i++)
{
Tx3Buffer[count3++] = *(DataToSend + i);
}
if (!(USART3->CR1 & USART_CR1_TXEIE))
{
USART_ITConfig(USART3, USART_IT_TXE, ENABLE); //打开发送中断
}
}
u8 U3RxDataTmp[100];
u8 U3RxInCnt = 0;
u8 U3RxoutCnt = 0;
void drvU3GetByte(u8 data)
{
U3RxDataTmp[U3RxInCnt++] = data;
if(U3RxInCnt >= 100)
U3RxInCnt = 0;
}
void drvU3DataCheck(void)
{
while(U3RxInCnt!=U3RxoutCnt)
{
U3GetOneByte(U3RxDataTmp[U3RxoutCnt++]);
if(U3RxoutCnt >= 100)
U3RxoutCnt = 0;
}
}
void Usart3_IRQ(void)
{
u8 com_data;
if (USART3->SR & USART_SR_ORE) //ORE中断
com_data = USART3->DR;
//接收中断
if (USART_GetITStatus(USART3, USART_IT_RXNE))
{
USART_ClearITPendingBit(USART3, USART_IT_RXNE); //清除中断标志
com_data = USART3->DR;
drvU3GetByte(com_data);
}
//发送(进入移位)中断
if (USART_GetITStatus(USART3, USART_IT_TXE))
{
USART3->DR = Tx3Buffer[Tx3Counter++]; //写DR清除中断标志
if (Tx3Counter == count3)
{
USART3->CR1 &= ~USART_CR1_TXEIE; //关闭TXE(发送中断)中断
}
}
}
void DrvUartDataCheck(void)
{
drvU3DataCheck();
drvU1DataCheck();
}
void USART3_IRQHandler(void)
{
Usart3_IRQ();
}
需要注意的事,匿名飞控的Usart3_IRQ();Usart1_IRQ();都是封装好的串口处理函数,使用时,必须把它们放到对应的中断函数处理函数void USART3_IRQHandler(void),否则程序会卡死。同时,将void DrvUartDataCheck(void)这个函数需要放入1ms的中断(建议用一个定时器作为系统的定时,1ms进入1次),这样串口收发的数据就会1ms处理一次。
void DrvUartDataCheck(void)中调用drvU3DataCheck()就是对应串口的数据接收处理的部分。
void drvU3DataCheck(void)
{
while(U3RxInCnt!=U3RxoutCnt)
{
U3GetOneByte(U3RxDataTmp[U3RxoutCnt++]);
if(U3RxoutCnt >= 100)
U3RxoutCnt = 0;
}
}
drvU3DataCheck()中调用了用户自定义的串口接收函数U3GetOneByte(),U3GetOneByte可以通过宏定义修改为自定义函数名。例如下图
同时,这个时候自己就可以在另外的.c文件中定义自己的函数接收函数 ,下面以OPENMV_GetOneByte为例。
下面为代码
/***********接受数据定义区***************/
#define BB_data0 opmv.lt.shape_flag
#define BB_data1 opmv.lt.err_X
#define BB_data2 opmv.lt.shape_cx
#define BB_data3 opmv.lt.err_Dis
#define BB_data4 opmv.lt.ultrasonic_distance
#define BB_data5 opmv.lt.fire_flag
#define BB_data6 opmv.lt.color_flag
#define BB_data7 opmv.lt.y_vel_out//openmv x方向速度
#define BB_data8 opmv.lt.x_vel_out//超声波前后 控制速度
#define CC_data0 opmv.lt.found_flag//光源寻找到的标志位
#define CC_data1 opmv.lt.red_cx
#define CC_data2 opmv.lt.red_cy
#define CC_data3 opmv.lt.green_pid_vel_v
#define CC_data4 opmv.lt.red_pid_vel_v
#define CC_data5 opmv.lt.ultrasonic_distance
#define CC_data6 opmv.lt.ultrasonic_vel_v
#define CC_data7 opmv.lt.y_vel_out//
#define CC_data8 opmv.lt.x_vel_out
#define DD_data0 opmv.lt.start_flag
#define DD_data1 opmv.lt.start_flag2
#define DD_data2 nouse
#define DD_data3 nouse
#define DD_data4 nouse
#define DD_data5 nouse
#define DD_data6 nouse
#define DD_data7 nouse
#define DD_data8 nouse
/***********接受数据处理***************/
static void OPENMV_DataAnl(uint8_t *data, uint8_t len1)
{
u8 check_sum3 = 0, check_sum4 = 0;
if (*(data + 3) != (len1 - 6)) //判断数据长度是否正确
return;
for (u8 i = 0; i < len1 - 2; i++)
{
check_sum3 += *(data + i);
check_sum4 += check_sum3;
}
if ((check_sum3 != *(data + len1 - 2)) || (check_sum4 != *(data + len1 - 1))) //判断sum校验
return;
//================================================================================
if(*(data + 2) == 0XBB)//底面的openmv
{
BB_data0 = *(data + 4)<<8|*(data + 5);//opmv.lt.shape_flag
BB_data1 = *(data + 6)<<8|*(data + 7);//opmv.lt.color_flag
BB_data2 = *(data + 8)<<8|*(data + 9);//opmv.lt.shape_cx
BB_data3 = *(data + 10)<<8|*(data + 11);//opmv.lt.shape_cy
BB_data4 = *(data + 12)<<8|*(data + 13);//Nouse
BB_data5 = *(data + 14)<<8|*(data + 15);//Nouse
BB_data6 = *(data + 16)<<8|*(data + 17);//Nouse
BB_data7 = *(data + 18)<<8|*(data + 19);//opmv.lt.shape_cy
BB_data8 = *(data +20)<<8|*(data + 21);//opmv.lt.shape_cy
}
else if(*(data + 2) == 0XCC)//顶面的openmv
{
CC_data0 = *(data + 4)<<8|*(data + 5);//Nouse
CC_data1 = *(data + 6)<<8|*(data + 7);//Nouse
CC_data2 = *(data + 8)<<8|*(data + 9);//Nouse
// CC_data3 = *(data + 10)<<8|*(data + 11);//Nouse
// CC_data4 = *(data + 12)<<8|*(data + 13);//Nouse
// CC_data5 = *(data + 14)<<8|*(data + 15);//Nouse
// CC_data6 = *(data + 16)<<8|*(data + 17);//Nouse
CC_data7 = *(data + 18)<<8|*(data + 19);//Nouse
CC_data8 = *(data +20)<<8|*(data + 21);//Nouse
}
else if(*(data + 2) == 0XDD)//zigbee数传
{
DD_data0 = *(data + 4);//opmv.lt.start_flag
DD_data1 = *(data + 5);//Nouse
// DD_data2 = *(data + 8)<<8|*(data + 9);//Nouse
// DD_data3 = *(data + 10)<<8|*(data + 11);//Nouse
// DD_data4 = *(data + 12)<<8|*(data + 13);//Nouse
// DD_data5 = *(data + 14)<<8|*(data + 15);//Nouse
// DD_data6 = *(data + 16)<<8|*(data + 17);//Nouse
// DD_data7 = *(data + 18)<<8|*(data + 19);//Nouse
// DD_data8 = *(data +20)<<8|*(data + 21);//Nouse
}
}
# define HW_TYPE 0x61
/***********接受数据解析,参考匿名飞控通信协议***************/
void OPENMV_GetOneByte(uint8_t data)
{
static u8 _data_len = 0, _data_cnt = 0;
static u8 rxstate = 0;
if (rxstate == 0 && data == 0xAA)
{
rxstate = 1;
uart3_datatemp[0] = data;
}
else if (rxstate == 1 && (data == HW_TYPE || data == HW_ALL))
{
rxstate = 2;
uart3_datatemp[1] = data;
}
else if (rxstate == 2)
{
rxstate = 3;
uart3_datatemp[2] = data;
}
else if (rxstate == 3 && data < 250)
{
rxstate = 4;
uart3_datatemp[3] = data;
_data_len = data;
_data_cnt = 0;
}
else if (rxstate == 4 && _data_len > 0)
{
_data_len--;
uart3_datatemp[4 + _data_cnt++] = data;
if (_data_len == 0)
rxstate = 5;
}
else if (rxstate == 5)
{
rxstate = 6;
uart3_datatemp[4 + _data_cnt++] = data;
}
else if (rxstate == 6)
{
rxstate = 0;
uart3_datatemp[4 + _data_cnt] = data;
// DT_data_cnt = _data_cnt+5;
//
OPENMV_DataAnl(uart3_datatemp, _data_cnt + 5); //
}
else
{
rxstate = 0;
}
}
void OPENMV_GetOneByte(uint8_t data)就是按照匿名协议进行数据接收,接受完一帧数据后调用OPENMV_DataAnl(uart3_datatemp, _data_cnt + 5)这个函数,进行数据解析,通过自己的宏定义与数据对应上,这里采用宏定义主要是为了与openmv那边的数据位对应上(方便与视觉队友对接),同时在1对多的通信也比较容易区分。
2.OPENMV
from pyb import UART
uart = UART(3,500000)
class Receive(object):
uart_buf = []
_data_len = 0
_data_cnt = 0
state = 0
R=Receive()
class Ctrl(object):
Data1 = 0
Data2 = 0
Data3 = 0
Data4 = 0
Data5 = 0
Data6 = 0
Data7 = 0
Data8 = 0
Data9 = 0
Data10 = 0
IsDebug = 1
T_ms = 0
Ctr=Ctrl()
def UartSendData(Data):
if uart.write(Data)==None:
print('send error')
def ReceiveAnl(data_buf):
Ctr.Data1=data_buf[2]
Ctr.Data2=data_buf[3]
Ctr.Data3=data_buf[4]
Ctr.Data4=data_buf[5]
Ctr.Data5=data_buf[6]
Ctr.Data6=data_buf[7]
Ctr.Data7=data_buf[8]
Ctr.Data8=data_buf[9]
Ctr.Data9=data_buf[10]
Ctr.Data10=data_buf[11]
def ReceivePrepare(data):
if R.state==0:
if data == 0xAA:
R.uart_buf.append(data)
R.state = 1
else:
R.state = 0
elif R.state==1:
if data == 0x32:
R.uart_buf.append(data)
R.state = 2
else:
R.state = 0
elif R.state==2:
R.uart_buf.append(data)
R.state = 3
elif R.state==3:
R.state = 4
R.uart_buf.append(data)
elif R.state==4:
R.uart_buf.append(data)
R.state = 5
elif R.state==5:
R.state = 6
R.uart_buf.append(data)
elif R.state==6:
R.uart_buf.append(data)
R.state = 7
elif R.state==7:
R.state = 8
R.uart_buf.append(data)
elif R.state==8:
R.state = 9
R.uart_buf.append(data)
elif R.state==9:
R.state = 10
R.uart_buf.append(data)
elif R.state==10:
R.state = 11
R.uart_buf.append(data)
elif R.state==11:
R.state = 12
R.uart_buf.append(data)
elif R.state==12:
if data == 0xdd:
R.state = 0
R.uart_buf.append(data)
ReceiveAnl(R.uart_buf)
R.uart_buf=[]#清空缓冲区,准备下次接收数据
else:
R.state = 0
else:
R.state = 0
def UartReadBuffer():
i = 0
Buffer_size = uart.any()
while i<Buffer_size:
ReceivePrepare(uart.readchar())
i = i + 1
def UserDataPack(data0,data1,data2,data3,data4,data5,data6,data7,data8):
UserData=bytearray([0xAA,0x61,0xBB,0x00
,data0,data1,data2,data3,data4,data5,data6,data7,data8
,0x00,0x00])
lens = len(UserData)
UserData[3] = lens-6;
i = 0
sum = 0
sum1 = 0
while i<(lens-2):
sum = sum + UserData[i]
sum1 = sum1 + sum
i = i+1
UserData[lens-2] = sum;
UserData[lens-1] = sum1;
return UserData
这里也是参照匿名openmv的代码,这边的代码就参考上图进行修改。但是注意openmv在接受数据的时候存在丢包的现象,目前还不清楚为什么。飞控这边为了减少处理的压力,把一部分的PID运算放到了Openmv端,但是在初始化PID参数的时候发现经常会有某个PID无法传参,存在丢包现象。因此,为保险起见,飞控在传输数据到openmv时,通常是连续发个几百毫秒。
3.小结
以上便是数据长度已知的串口通信方式,之后在运用的时候可以参照这种方式修改,调了一个暑假这种方式还是挺稳定的。
void Uart_Send_Str(unsigned char * p)
{
while(*p)
{
Uart_Send_byte(*p++);
}
}
值得注意的事,进行串口发送的时候,采用上述代码的方式一旦数据有0便会结束,调用void DrvUart1SendBuf(unsigned char *DataToSend, u8 data_num),这个函数进行发送的时候就不会。
二、STM32与UWB之间的通信(通信数据长度未知)
UWB的通信协议
UWB配置完成后会,自动按照上述例子将数据发送过来,按照字符进行发送,数据不定长。当时比赛的时候是第一次用这个UWB,虽然协议很简单,但是为了读UWB的数据也花费了一个下午的时间。
1.STM32
这里也是采用匿名飞控的程序的数据接收,但是数据解析函数有些不相同。
#define UART2_BUFFER_SIZE 100 // 假设uart2_datatemp的大小为100
void UWB_GetOneByte(char data)
{
static u8 rxstate_2 = 0;
static u8 i = 0;
// 增加数据长度检查,防止数据长度超出缓冲区大小
if (i >= UART2_BUFFER_SIZE - 1)
{
// 缓冲区已满,进行错误处理或清空操作
rxstate_2 = 0;
i = 0;
// 这里可以根据实际情况进行处理,比如错误提示、丢弃当前数据等
return;
}
if (rxstate_2 == 0 && data == '[')
{
rxstate_2 = 1;
uart2_datatemp[0] = data;
}
else if (rxstate_2 == 1)
{
if (data != ']')
{
i++;
uart2_datatemp[i] = data;
}
else
{
i++;
uart2_datatemp[i] = data;
// 增加对数据长度的检查,防止传递错误的数据长度
if (i + 2 <= UART2_BUFFER_SIZE)
{
UWB_Data_Anl(uart2_datatemp, i + 2);
}
// 处理完一组数据后,重置接收状态和缓冲区
rxstate_2 = 0;
i = 0;
// 清空缓冲区
// memset(uart2_datatemp, 0, sizeof(uart2_datatemp));
}
}
else
{
// 处理未知状态,重置接收状态和缓冲区
rxstate_2 = 0;
i = 0;
// 清空缓冲区
//memset(uart2_datatemp, 0, sizeof(uart2_datatemp));
}
}
注意这这rxstate_2、i两个变量在接受完成或者数据接收错误的时候都要清0。
三、总结
目前这就更新到这里,以后在遇到坑再过来填!