STM32F103(二十五)完美解决USART发送接收floatu16u32数据

Posted 独独白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F103(二十五)完美解决USART发送接收floatu16u32数据相关的知识,希望对你有一定的参考价值。

学习板:STM32F103ZET6

参考:

STM32F103(二十三)通用同步异步收发器(USART)

STM32F103(二十四)一篇博客精通《485通信》

前言

前面两篇博客总结了USART、485通信,这两篇都是用USART直接进行u8数据的传送和接收。心里的完美主义又在作怪,想写一下怎么处理u16、u32、float型数据传输。

这个代码差不多一个小时写完,但是…花了整整一天的时间去调试。后来发现不是算法思想错误,程序中的一点点小问题可能会导致通信不成功。发送数据还好,直接发送就行;麻烦的是接收数据,接收数据以及接收到后数据转换的每一个环节都要一步步去调试。

本博的代码会直接以资源的方式上传,代码下载下就可以使用。接下来讲一下代码思想。分为带字符‘a’的和不带字符‘a’的,加入这个字符‘a’就是防止持续发送信号是,接收端数据串位。

加入字符‘a’的版本可以持续不断的发送接收信号。

未加入字符‘a’的版本在发送一个u16、u32、float数据后,需要给个延时,防止接收端数据串位,导致数据乱七八糟的。

这里留个空,资源正在审核,通过后加个链接

一、注意事项

(1)为了方便调试代码,我使用的USART2写的代码,用USART1串口打印来调试。

(2)调试代码时,分别用F1发信号、F4收信号;F4发信号、F1收信号。对每个函数都进行了测试。

(3)连线问题,F1的TX(PA2)接F4的RX(PA3)、F1的RX(PA3)接F4的TX(PA2)

(4)注意两块板子通信时,一定要共地!!!两块板子除了TX、RX连接外,GND引脚也需要连接!两块板子互相连接的GND是给芯片供电的GND,对于四层板,应该都是打孔连接负片,所以两板子的任意GND引脚飞线连接即可。

(5)发送和接收U8数据的函数前面两篇博客总结过了,也有源代码。本博的U8数据主要服务于u16、u32、float型数据传输,没有写专门的u8数据传输的代码。

二、代码讲解

1、所有函数的合影

先看一下所有函数,定义在usart1.h头文件中:

#ifndef _USART_
#define _USART_
#include "stm32f10x.h"
void  Myusart2_Init(u32 baud);//串口2初始化
void  USART2_Send_One_U8_Data(u8 data);//串口2发送一个u8数据
void  USART2_Send_Len_U8_data(u8 *,u16 );//串口2发送一长串u8数据
void USART2_Receive_U8_data(u8 len);//串口2接收u8数据,参数为接收len个u8数据

void U16_To_U8(u16);//u16转u8数据
void U32_To_U8(u32);//u32位数转u8数据
void Float_To_U8(float);//float数据转u8数据

u16 U8_To_U16(u8 *);//u8数据转u16数据
u32 U8_To_U32(u8 *);//u8数据转u32数据
float U8_To_float(u8 *);//u8数据转float

void USART2_Send_One_U16_Data(u16);//串口2发送一个u16数据
u16  USART2_Receive_One_U16_Data(void);//串口2接收一个u16数据

void USART2_Send_One_U32_Data(u32);//串口2发送一个u32数据
u32  USART2_Receive_One_U32_Data(void);//串口2接收一个u32数据

void USART2_Send_One_Float_Data(float);//串口2发送一个u32数据
float  USART2_Receive_One_Float_Data(void);//串口2接收一个u32数据
#endif

2、三个基础函数

首先讲解下面的三个基础函数:

void  USART2_Send_One_U8_Data(u8 data);//串口2发送一个u8数据
void  USART2_Send_Len_U8_data(u8 *,u16 );//串口2发送一长串u8数据
void USART2_Receive_U8_data(u8 len);//串口2接收u8数据,参数为接收len个u8数据

发送一个u8数据,只需等待上一次发送完成后发送本次数据即可:

void  USART2_Send_One_U8_Data(u8 data)
{
	while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);//等待上一次数据发送完成
	//delay_ms(10);//给对方一个处理数据的时间(经调试,没必要)
	USART_SendData(USART2,data);
}

发送一串u8数据,先将u8数据存入一个u8数组,然后传递过来即可,参数len为需要传递的数量。

void  USART2_Send_Len_U8_data(u8 data[],u16 len)
{
	u16 t=0;
	for(t=0;t<len;t++)
	USART2_Send_One_U8_Data(data[t]);
}

接收u8数据还是沿用了上一篇博客STM32F103(二十四)一篇博客精通《485通信》的思想,在函数中只使能接收中断,接收数据在中断服务函数中处理。中断服务函数代码后面着重讲解:(注意优先级,别和其他地方的优先级一样了)

void  USART2_Receive_U8_data(u8 len)
{
	NVIC_InitTypeDef  NVIC_InitStructure;
	NUM=0;
	USART2_Receive_Num=len;
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
	NVIC_Init(&NVIC_InitStructure); 
	
	USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
}

3、思想(数据转换与发送)

(1)u16数据转换与发送

发送u16数据时,将16位数转为2个8位数,即一个u16数据转换两个u8数据,分别储存在u8类型数组的0位和1位,发送时,传递这个全局的数组即可。

void U16_To_U8(u16 data)  //结果存入arr1[2]数组
{
	arr1[0]=(u8)((data>>8)&0xff);//u16的高8位存入arr1[0]
	arr1[1]=(u8)(data&0xff);//u16的低8位存入arr1[1]
}

重点说一下:
因为u16、u32、float类型数据由多个u8数据组成,如果不做特殊处理,不能判断接收的数据那一次是高位。如发送端一直发送u16的数据,而接收端每接收2个u8数据,才能转换为u16数据;但是问题就出在这里:如果先传送u16数据的高8位,但是接收端没有接收到,传送u16的低8位却接收到了,那么不做处理的话,这次接收到的低8位会被当做高八位,把下一个u16数据的高8位当做这次数据的低八位,新组成的数会出错,同时会导致连锁反应,后面接收的数据全部“串位”。经调试,发送数据时给个延时(2ms~20ms)基本不会出现串位的情况,所以关于字符‘a’的代码可以去掉,去掉后的代码也会放在资源里。

所以我发送一个非U8数据时,都会先发送一个字符“a”,接收端接收到“a”后,后面的数才是真正的数据。虽然通信速率降低了,但是准确性却提高了。当然字符‘a’=(u8)97=(u16)97=(u32)97,所以如果传输的数据中有数据是97,会出错,可以把‘a’改为其他字符。

代码:

void USART2_Send_One_U16_Data(u16 data)
{
	U16_To_U8(data);//此时arr1[0]和arr1[1]分别储存data数据的高8位和低八位
	
	USART2_Send_One_U8_Data('a');
	USART2_Send_Len_U8_data(arr1,2);//传递arr1数组,长度为2
}

(2)u32数据转换与发送

u32数据可以转换为4个u8数据,分别储存在全局数组的0~4位中,发送时传递这个数组即可:

void U32_To_U8(u32 data)//结果存入arr2[4]数组
{
	arr2[0]=(u8)((data>>24)&0xff);//u32的位31:24存入arr2[0]
	arr2[1]=(u8)((data>>16)&0xff);//u32的位23:16存入arr2[1]
	arr2[2]=(u8)((data>>8)&0xff);//u32的位15:8存入arr2[2]
	arr2[3]=(u8)(data&0xff);//u32的位7:0存入arr2[3]
}

发送数据:

void USART2_Send_One_U32_Data(u32 data)
{
	U32_To_U8(data);//此时arr2[4]数组储存u32 data的每一位,arr2[0]储存的是高8位
	USART2_Send_One_U8_Data('a');
	USART2_Send_Len_U8_data(arr2,4);//传递arr2数组,长度为4
}

(3)float数据转换与接收

float数据转换比较麻烦,所以我直接把它乘1000000再强制转为u32,按照u32数据发送和接收,接收到后再除以1000000即可,就算有误差,也在0.000001,而计算机默认处理小数点后6位,所以这个误差完全不算误差。

void Float_To_U8(float data)//转为u32,传过去后再除1000000
{
	u32 temp=(u32)(data*1000000);//float型扩大到U32
	arr3[0]=(u8)((temp>>24)&0xff);//u32的位31:24存入arr3[0]
	arr3[1]=(u8)((temp>>16)&0xff);//u32的位23:16存入arr3[1]
	arr3[2]=(u8)((temp>>8)&0xff);//u32的位15:8存入arr3[2]
	arr3[3]=(u8)(temp&0xff);//u32的位7:0存入arr3[3]
}

发送数据还是按照u32数据发送:

void USART2_Send_One_Float_Data(float data)
{
	Float_To_U8(data);//此时arr3[4]储存float变为u32数据的每一位,arr3[0]储存高8位
	USART2_Send_One_U8_Data('a');
	USART2_Send_Len_U8_data(arr3,4);//传递arr3数组,长度为4
}

4、思想(数据接收与转换)

程序中定义了以下全局变量:

u8 buffer[20];//接收的数据存储在这个数组
u8 USART2_Receive_Num;//储存需要接收的数据个数
u8 NUM; //统计现在接收到几个数据

u8 arr1[2];//储存一个u16数的高8位和低八位
u8 arr2[4];//储存一个u32数的每8位
u8 arr3[4];//存储float型变为u32后的每8位

u8 flag=0;//标记传输哪种类型的数据
//flag=1 传递u8数据
//flag=2 传递的是u16数据
//flag=3 传递的是u32数据(与float数据共用)

①buffer[]储存接收到的数据,其中buffer[0]储存接收到的字符‘a’,buffer[1]…开始顺序储存接下来接收到的u8数,例如要接收一个u16数据,buffer[0]=‘a’;buffer[1]=高8位;buffer[2]=低8位。下一次传输会重新覆盖buffer[]数组前几位

②arr1、arr2、arr3数组存储的是发送数据的转换结果

③flag为标记,用来标记是传递接收哪种类型数据

(1)u16数据接收与转换

接收函数主要是使能串口读中断、等待接收完成、清除变量状态:

u16 USART2_Receive_One_U16_Data(void)
{
	flag=2;//传递u16数
	USART2_Receive_U8_data(2);//接收的数据存储在buffer[]数组中,其中buffer[0]储存高8位
	while(NUM<3)//完成2次接收(8位)后,前者=2,后者=3  USART2_Receive_Num>=NUM
	{
		//等待2个u8数据接收结束
	}
	flag=0;
	NUM=0;
	USART2_Receive_Num=0;
	buffer[0]=0;
	return U8_To_U16(buffer);
}

重新写一下上面代码的USART2_Receive_U8_data(2);

void  USART2_Receive_U8_data(u8 len)
{
	NVIC_InitTypeDef  NVIC_InitStructure;
	NUM=0;
	USART2_Receive_Num=len;
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级 3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
	NVIC_Init(&NVIC_InitStructure); 
	
	USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
}

所以这个函数进来后,先将flag标记设为2,表示传递的是u16数,再设置NVIC优先级、使能读中断,然后等待数据接收完毕。等待过程中,如果收到数据,在中断服务函数中会储存数据。读取完成后清除状态,同时将buffer[0]为的字符‘a’清掉,便于下一次接收判断(可不带字符)。完成后将接收到两个u8数据转换为u16数据返回即可。

先看一下中断服务函数:


void USART2_IRQHandler(void)
{
	u8 temp;
 	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
	{	 
	 	temp=USART_ReceiveData(USART2); 
		
		if(temp=='a')//相当于自定义一个开始符
		{
			buffer[0]='a';
			NUM=0;
		}
		if(flag==2)//接收u16数据
		{
			if((buffer[0]=='a')&&(NUM==1||NUM==2))
			{
				buffer[NUM]=temp;
				//USART_SendData(USART1,temp);//调试用
				if(NUM==2)
				USART_ITConfig(USART2,USART_IT_RXNE,DISABLE);//接收结束后使读中断失能	
			}
			NUM++;	
		}
		
		if(flag==3)//接收u32数据、float数据
		{
			if((buffer[0]=='a')&&(NUM==1||NUM==2||NUM==3||NUM==4))
			{
				buffer[NUM]=temp;
				//USART_SendData(USART1,temp);//调试用
				if(NUM==4)
				USART_ITConfig(USART2,USART_IT_RXNE,DISABLE);//接收结束后使读中断失能	
			}
			NUM++;	
		}
		
	}  											 
} 

当接收到字符‘a’后,下一此传输的数据才能储存在buffer[1]中,防止数据串位。

再看一下数据转换,接收到两个u8 数据后转换为u16数据返回:

u16 U8_To_U16(u8 data[])
{
	//buffer[0]储存'a',buffer[1]储存高八位
	u16 temp;
	temp=(u16)(data[1]&0xff);
	temp<<=8;
	temp+=(u16)(data[2]&0xff);
	return temp;
}

传递的是中断服务函数中的buffer数组,其中buffer[0]为字符‘a’、buffer[1]为u16的高8位、buffer[2]为u16的低8位。

(2)u32数据接收与转换

u32数据处理与u16类似,不过需要接收4次u8数据才能合成完整的u32数据,直接附代码:

u32  USART2_Receive_One_U32_Data(void)
{
	flag=3;//传递u32数
	USART2_Receive_U8_data(4);//接收的数据存储在buffer[]数组中,其中buffer[4]储存高8位
	while(NUM<5)//完成4次接收(8位)后,USART2_Receive_Num>=NUM 前者=4,后者=5 
	{
		//等待4个u8数据接收结束
	}
	flag=0;
	NUM=0;
	USART2_Receive_Num=0;
	buffer[0]=0;
	return U8_To_U32(buffer);
}
u32 U8_To_U32(u8 data[])
{
	u32 temp;
	temp=(data[1]<<24);
	temp+=(data[2]<<16);
	temp+=(data[3]<<8);
	temp+=data[4];
	return temp;
}

(3)float数据接收与转换

与u32类似,不同的是最后数据转换时,需要除1000000,直接附代码:

float  USART2_Receive_One_Float_Data(void)
{
	flag=3;//传递u32数
	USART2_Receive_U8_data(4);//接收的数据存储在buffer[]数组中,其中buffer[4]储存高8位
	while(NUM<5)//完成4次接收(8位)后,USART2_Receive_Num>=NUM 前者=4,后者=5 
	{
		//等待4个u8数据接收结束
	}
	flag=0;
	NUM=0;
	USART2_Receive_Num=0;
	buffer[0]=0;
	return U8_To_float(buffer);
}
float U8_To_float(u8 data[])
{
	u32 temp;
	temp=U8_To_U32(data);
	return (float)temp*1.0/1000000;
}

三、测试(带字符‘a’、F1发送u32、F4接收U32)

1、注意

①F1的USART2(TX PA2)接F4的USART2(RX PA3 )

②F1的USART2(RX PA3)接F4的USART2(TX PA2 )

③两块板子GND飞线连接

2、F1完整代码(发送)

usart1.h代码:

#ifndef _USART_
#define _USART_
#include "stm32f10x.h"
void  Myusart2_Init(u32 baud);//串口2初始化
void  USART2_Send_One_U8_Data(u8 data);//串口2发送一个u8数据
void  USART2_Send_Len_U8_data(u8 *,u16 );//串口2发送一长串u8数据
void USART2_Receive_U8_data(u8 len);//串口2接收u8数据,参数为接收len个u8数据

void U16_To_U8(u16);//u16转u8数据
void U32_To_U8(u32);//u32位数转u8数据
void Float_To_U8(float);//float数据转u8数据

u16 U8_To_U16(u8 *);//u8数据转u16数据
u32 U8_To_U32(u8 *);//u8数据转u32数据
float U8_To_float(u8 *);//u8数据转float

void USART2_Send_One_U16_Data(u16);//串口2发送一个u16数据
u16  USART2_Receive_One_U16_Data(void);//串口2接收一个u16数据

void USART2_Send_One_U32_Data(u32);//串口2发送一个u32数据
u32  USART2_Receive_One_U32_Data(void);//串口2接收一个u32数据

void USART2_Send_One_Float_Data(float);//串口2发送一个u32数据
float  USART2_Receive_One_Float_Data(void);//串口2接收一个u32数据
#endif

usart1.c代码:

#include "usart1.h"
#include "stm32f10x.h"
#include "delay.h"
void Myusart2_Init(u32 baud)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef  USART_InitStructure;
	 NVIC_InitTypeDef  NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure); //TX
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure); //RX
	
	USART_InitStructure.USART_BaudRate=baud;
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART_InitStructure.USART_Parity=USART_Parity_No;
	USART_InitStructure.USART_StopBits=USART_StopBits_1;
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;
	USART_Init(USART2,&USART_InitStructure);
	

	USART_Cmd(USART2, ENABLE);
	
}


u8 buffer[200];//接收的数据存储在这个数组
u8 USART2_Receive_Num;//储存需要接收的数据个数
u8 NUM; //统计现在接收到几个数据

u8 arr1[2]STM32F103(二十五)完美解决USART发送接收floatu16u32数据

STM32F103(二十三)通用同步异步收发器(USART)

STM32F103(二十三)通用同步异步收发器(USART)

STM32F103(二十三)通用同步异步收发器(USART)

STM32F103(二十四)一篇博客精通《485通信》

stm32f103c8t6 usart1不工作