stm32和python串口数据收发

0 串口基础配置(stm32F103)

c 复制代码
void uart_init(u32 bound){
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//使能USART1时钟
  RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, ENABLE); //复位串口1
  RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, DISABLE);
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟
	//USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //TX 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;//RX PA.10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//子优先级1
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
  //USART 初始化设置
	USART_InitStructure.USART_BaudRate = 921600;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	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(USART1, &USART_InitStructure); //初始化串口1
	
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断 
  USART_Cmd(USART1, ENABLE);                    //使能串口1 
}

1-1 串口发送端(stm32)

1字符串发送

c 复制代码
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_DATA(Data)); 
    
  /* Transmit Data */
  USARTx->DR = (Data & (uint16_t)0x01FF);
}
/*****************  发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}
/************状态:电机速度、位置和角位移传感器的速度、位置*****************/	 
    #define LPF(x, f, c) (c*x + (1-c)*f)  // 滤波
    motor_position = Read_Encoder_Angle(Encoder);
    sensor_position = Get_Adc_Average_Angle(Adc);
    motor_velocity = Read_Encoder_Speed(Encoder);
    sensor_velocity = Get_Adc_Average_Speed();
    pc_fil = LPF(motor_position, pc_fil,0.2f); 
    vc_fil = LPF(motor_velocity, vc_fil,0.2f);
    ec_fil = LPF(sensor_position,ec_fil,0.2f);
    wc_fil = LPF(sensor_velocity,wc_fil,0.2f);
		 
/************** 串口发送数据方式一:使用字符串传输数据(整型和浮点型) **********************/				 
    sprintf(data_str, "%-8.4f, %-8.4f, %-8.4f, %-8.4f\n", pc_fil, ec_fil, vc_fil, wc_fil);
    Usart_SendString(USART1, data_str);

2 16进制传输(整型)

c 复制代码
/************** 串口发送数据方式二:传输数据打包-16进制传输(整型) **********************/	
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_DATA(Data)); 
    
  /* Transmit Data */
  USARTx->DR = (Data & (uint16_t)0x01FF);
}

	
 uint8_t data_array[10];
 data_array[0] =  0x12;  // 帧头1
 data_array[1] =  0x34;  // 帧头2
 data_array[2] = (int)Encoder & 0xFF;         // 编码器低8位
 data_array[3] = ((int)Encoder >> 8) & 0xFF;  // 编码器高8位
 data_array[4] = (int)Adc & 0xFF;             // 编码器低8位
 data_array[5] = ((int)Adc >> 8) & 0xFF;      // 编码器低8位
 data_array[6] =  0x56;  // 帧尾1
 data_array[7] =  0x78;  // 帧尾2
 for(uint8_t  i = 0 ; i < 8; i++)
 {
     USART_SendData(USART1, *(data_array + i));
     while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);  
 }
	

3 16进制传输(整型和浮点型)

c 复制代码
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_DATA(Data)); 
    
  /* Transmit Data */
  USARTx->DR = (Data & (uint16_t)0x01FF);
}
/************** 串口发送数据方式三: 串口传输数据打包-16进制传输(整型和浮点型) **********************/		
 //函数功能:将一个浮点数转换为字节数组   倒序  大小端的问题
//入口参数:浮点数   字节数组
void FloatToByte(float floatNum, unsigned char* byteArry) {
    char* pchar = (char*)&floatNum;
    for (int i = 0; i < sizeof(float); i++) {
        *byteArry = *pchar;
        pchar++;
        byteArry++;
    }
}
    FloatToByte(motor_velocity, byteArry); // 8个字节数据
		data_array[0] =  0x12;  // 帧头1
		data_array[1] =  0x34;  // 帧头2
		data_array[2] = (int)motor_position & 0xFF;          // 电机位置低字节
		data_array[3] = ((int)motor_position >> 8) & 0xFF;   // 电机位置高字节
		/*电机速度为浮点型数据,将其十进制数转换为单精度浮点数是4个字节(32位),转换网站:http://www.styb.cn/cms/ieee_754.php*/
		data_array[4] = byteArry[0];	// 单精度浮点数第4个字节							
		data_array[5] = byteArry[1];	// 单精度浮点数第3个字节			 	
		data_array[6] = byteArry[2];	// 单精度浮点数第2个字节							
		data_array[7] = byteArry[3];	// 单精度浮点数第1个字节				
		data_array[8] =  0x56;  // 帧尾1
		data_array[9] =  0x78;  // 帧尾2
		for(uint8_t  i = 0 ; i < sizeof(data_array); i++) // 一个字节一个字节发送数据
			{
				USART_SendData(USART1, *(data_array + i));
                while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);  // 注意:此句是为防止数据还来不及发送,数据就被后面来的数据覆盖;等待发送完后继续发送下一个数据,没有过多的等待。
			}

4 仅发送浮点型小数

c 复制代码
/**************串口发送数据方式四: 仅发送浮点型小数 **********************/
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_DATA(Data)); 
    
  /* Transmit Data */
  USARTx->DR = (Data & (uint16_t)0x01FF);
}
 //函数功能:将一个浮点数转换为字节数组   倒序  大小端的问题
//入口参数:浮点数   字节数组
void FloatToByte(float floatNum, unsigned char* byteArry) {
    char* pchar = (char*)&floatNum;
    for (int i = 0; i < sizeof(float); i++) {
        *byteArry = *pchar;
        pchar++;
        byteArry++;
    }
}
FloatToByte(motor_velocity, byteArry); // 8个字节数据
for(uint8_t  i = 0 ; i < sizeof(float); i++) // 一个字节一个字节发送数据
{
    USART_SendData(USART1, *(byteArry + i));
    while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); // 注意:此句是为防止数据还来不及发送,数据就被后面来的数据覆盖;等待发送完后继续发送下一个数据,没有过多的等待。
}

1-2 串口接收端-python

接收数据:编码器(整型)、角位移传感器(整型)[对应1-1中的2]

python 复制代码
# 从串口接收的数据为:编码器(整型)、角位移传感器(整型)
def read_serial_one_data_encoder_adc(ser):
    global receive_result
    BUF_SIZE = 8
    buf = bytearray([0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x56, 0x78])
    c1 = ib = flag = 0

    while True:
        R = ser.read(1)
        # print("data", R)
        if R == b'':
            print("Read Fail")
            ser.close()
            break
        c = int.from_bytes(R, byteorder='big') # 将接收的十六进制数转换为整数;注意,接收的十六进制会进行自动转换,如0x56是十六进制表示法,表示的是十进制数值86。而V是英文字母,它在ASCII码中的十进制表示是86。所以,0x56和V表示的是同一个字符。串口接收时会将0x56转化成V,它们实际上是同一个字符。
        if flag > 0:
            if ib < BUF_SIZE: # 位置标记ib要小于缓冲数组的最大值
                buf[ib] = c   # 将接收的数据保存在缓冲数组当中
                ib += 1       # 位置+1
            if ib == 8: # 如果当前位置为8,则说明数据缓冲区已满
                if buf[6] == 0x56 and buf[7] == 0x78: # 判断帧尾1和2是否满足规定值
                    Encoder = (buf[3] << 8) + buf[2]  # 进行位操作。注意:buf[2]和buf[3]实际为十进制的数值,当进行位操作时,系统自动将其转换为十六进制进行位操作,最后结果仍为十进制数
                    Adc= (buf[5] << 8) + buf[4]
                    receive_result = [Encoder, Adc]  # 返回接收结果
                    break
                else:
                    print("CRC Fail")
                flag = 0
        if flag == 0:
            if c1 == 0x12 and c == 0x34: # 判断:是否接收到的帧头1和帧头2,且帧头1和2是否连续(帧头1在帧头2前面),若满足条件则开始接收
                flag = 1 # 接收数据标志位
                ib = 2   # 标记数据缓冲数据从第3位开始,即下标为2
        c1 = c
    return receive_result  # 返回接收到的结果

接收数据:编码器(整型)、角位移传感器(浮点型)[对应1-1中的3]

python 复制代码
# 函数功能:将一个字节数组转换为浮点数
# 入口参数:  字节数组
def Byte2Float(byteArry):
    floatNum = struct.unpack('f', byteArry)[0]
    return floatNum

# 从串口接收的数据为:电机位置(整型)、电机速度(浮点型)
def read_serial_one_data_motor_position_velocity(ser):
    global receive_result
    BUF_SIZE = 10
    buf = bytearray([0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x78]) # 定义一个字节数组,用于将数据打包,进行串口传输
    c1 = c2 = ib = flag = 0

    while True:
        R = ser.read(1) # 从串口接收一个字节
        if R == b'':
            print("Read Fail")
            ser.close()
            break
        c = int.from_bytes(R, byteorder='big') # 将接收的十六进制数转换为整数;注意,接收的十六进制会进行自动转换,如0x56是十六进制表示法,表示的是十进制数值86。而V是英文字母,它在ASCII码中的十进制表示是86。所以,0x56和V表示的是同一个字符。串口接收时会将0x56转化成V,它们实际上是同一个字符。
        if flag > 0:
            if ib < BUF_SIZE: # 位置标记ib要小于缓冲数组的最大值
                buf[ib] = c   # 将接收的数据保存在缓冲数组当中
                ib += 1       # 位置+1
            if ib == 10:       # 如果当前位置为10,则说明数据缓冲区已满
                if buf[8] == 0x56 and buf[9] == 0x78:
                    motor_position = (buf[3] << 8) + buf[2]
                    motor_veclocity = Byte2Float(buf[4:8])
                    receive_result = [motor_position, motor_veclocity]
                    break
                else:
                    print("CRC Fail")
                flag = 0
        if flag == 0:
            if c1 == 0x12 and c == 0x34:# 判断:是否接收到的帧头1和帧头2,且帧头1和2是否连续(帧头1在帧头2前面),若满足条件则开始接收
                flag = 1  # 接收数据标志位
                ib = 2    # 标记数据缓冲数据从第3位开始,即下标为2
        c1 = c
    return receive_result  # 返回接收到的结果

2-1 串口发送端python

python 复制代码
action = bytearray([0x12, 0x34, 0x00, 0x00, 0x00, 0x56, 0x78])
action_ = int(control_motor(result[0], result[1]))
print("action_", action_)
# 将action转化成字符串
action[0] = 0x2D   # 帧头
action[1] = 0x01   # 校验位,具体未实现,用0x01替代
if action_ < 0:
    action[2] = 0x45   # 符号位
    action[3] = action_  & 0xFF    # 数据位
    action[4] = (action_ >>8) & 0xFF  # 数据位
    action[5] = 0x0d   # 0x56是十六进制表示法,表示的是十进制数值86。而V是英文字母,它在ASCII码中的十进制表示是86。所以,0x56和V表示的是同一个字符。
    action[6] = 0x0a   # 0x78是十六进制表示法,表示的是十进制数值120。而x是英文字母,它在ASCII码中的十进制表示是120。所以,0x78和x表示的是同一个字符。

    for byte in action: # 一个一个字节发送 ,一次发送多个字节容易出错
        ser.write(byte.to_bytes(1, 'big'))

2-2 串口接收端stm32

c 复制代码
void USART1_IRQHandler(void)                	//串口1中断服务程序
	{
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res = USART_ReceiveData(USART1);	//读取接收到的数据 0x2D,0x01,0x30,0x30,0x30,0x0D,0x0A
		printf("%02X", Res);
				if((USART_RX_STA&0x8000)==0)//接收未完成。当接收完成时,最高位会被置为1,表示接收结束。 
                                            
					{
					if(USART_RX_STA&0x4000)//接收到了0x0D,开始执行以下代码。USART_RX_STA&0x4000判断USART_RX_STA的第14位(bit 14)是否为1。
						{
							if(Res!=0x0A){
								USART_RX_STA=0;//接收错误,重新开始
								memset(USART_RX_BUF,0,USART_REC_LEN);// 将数据缓冲区清0
							}
							else{ //接收到了0x0A,开始执行以下代码
								USART_RX_STA|=0x8000;	//接收完成了。将 USART_RX_STA 的第15位设置为1。这样做的目的是在接收到完整的数据时,将接收完成标志置为1,以便后续的处理程序可以知道数据已经接收完成,进行后序操作。
								if(USART_RX_BUF[0] == 0x2D && USART_RX_BUF[1] == 0x01 ) // 判断帧头是否正确、判断奇偶校验位是否正确 || USART_RX_BUF[1] == 0x01(USART_RX_BUF[1]为校验位,此处未定义,使用0x01替代,简化)
								{
									float value = 0;
									int16_t sign = 1;  // 符号标记,默认为'+'
                                    if(USART_RX_BUF[2] == 0x45) // USART_RX_BUF[2]为符号位,当其等于0x45,即'-',表示其为负数
                                    {
                                        sign = -1;
                                    }
                                    value = (USART_RX_BUF[4] << 8) + USART_RX_BUF[3];// 将接收的十六进制数转换为十进制	
                                    action = sign * value; // 与符号标记做乘法,等到真实值
                                    USART_RX_STA = 0; 	
                                    memset(USART_RX_BUF,0,USART_REC_LEN); // 将数据缓冲区清0
							}
							else{
								USART_RX_STA = 0;	
								memset(USART_RX_BUF,0,USART_REC_LEN);// 将数据缓冲区清0
							}
						}								
						}
					else //还没收到0X0D
						{	
							if(Res==0x0D)
								USART_RX_STA|=0x4000; //当接收缓冲区溢出时,第14位会被置为1,表示接收缓冲区已满。|0x4000 = 0100 0000 0000 0000
							else
								{					
										USART_RX_BUF[USART_RX_STA&0X3FFF]=Res; // 将接收到的值存放在数据缓冲区中,和0X3FFF(16383)进行与运算。这样做的目的是将索引值限制在0到16383之间,避免超出数组的范围。
										USART_RX_STA++; // 一个16位的无符号整数变量,用于接收数据的状态变量。
                                                        // USART_RX_STA 的第15位通常用于表示接收完成标志。
										if(USART_RX_STA>(USART_REC_LEN-1)) // 接收数据的状态变量USART_RX_STA > 串口接收长度USART_REC_LEN时,则接收数据错误,重新开始接收
										{
											USART_RX_STA=0;//接收数据的状态变量归0
											memset(USART_RX_BUF,0,USART_REC_LEN); // 清空接收数据缓冲区USART_RX_BUF
										}					
								}		 
						}
					}

				 } 
}
	

调试过程中的问题总结

1、串口传输数据需按字节传输,不可一次性发送多个字节,如下所示:

python 复制代码
action = bytearray([0x12, 0x34, 0x31, 0x32, 0x00, 0x56, 0x78])
ser.write(action )

在实际调试过程中,因为数据还来不及发送,数据就被后面来的数据覆盖了,导致实际接收的结果为0x12, 0x34,或0x12, 0x34, 0x32, 0x78。总之数据不完整。

因此改为每次发送一个字节,如下所示:

python 复制代码
action = bytearray([0x12, 0x34, 0x31, 0x32, 0x00, 0x56, 0x78])
for byte in action: 
        ser.write(byte.to_bytes(1, 'big'))

2、串口接收数据丢失,如发送固定数据为6900,偶尔会跳变为244或1等其他数据

经排查,此类情况为波特率设置过低导致的,将波特率设为较大值可解决此类情况。

相关推荐
Ws_2 小时前
leetcode LCR 068 搜索插入位置
数据结构·python·算法·leetcode
lx学习2 小时前
Python学习26天
开发语言·python·学习
qq_273900233 小时前
pytorch register_buffer介绍
人工智能·pytorch·python
大今野3 小时前
python习题练习
开发语言·python
q567315234 小时前
用 PHP或Python加密字符串,用iOS解密
java·python·ios·缓存·php·命令模式
winfredzhang5 小时前
如何使用 python 中的 Pillow 创建可自定义的图标生成器
python·pillow·图标·png
qq_273900236 小时前
pytorch detach方法介绍
人工智能·pytorch·python
虞书欣的66 小时前
Python小游戏24——小恐龙躲避游戏
开发语言·python·游戏·小程序·pygame
FHYAAAX6 小时前
【机器学习】任务十:从函数分析到机器学习应用与BP神经网络
开发语言·python
PyAIGCMaster6 小时前
python环境中,敏感数据的存储与读取问题解决方案
服务器·前端·python