单片机通过串口发送浮点类型数据

单片机通过串口向串口助手或者其他器件发送数据时是不能直接发送浮点类型数据的,通过printf("%f",1.28)发送浮点数据,在串口助手看似是小数,其实是字符串类型,它们是各符号对应的ASSCII码值(“1”的asscii是0x31,“.”的asscii是0x2E),使用printf发送的%d、%f、%lf等都是这样,是发送的对应数据的各asscii码值,除了%c之外。如下图所示是发送浮点类型串口助手的情况。

分别使用%d、%f、%c发送数据,如图(左边是程序,中间是hex类型(字符型)显示,右边是十六进制显示):

可以看到,发送的%d的100显示的是100,实际发送的是‘1’‘0’‘0’对应的assiic码十六进制值,发送的%f的1.25,实际发送的是‘1’‘.’‘2’‘5’对应的assiic码十六进制值,看%c发送的100,在hex显示的情况下是字符‘d’,在十六进制显示的情况下是0x64,而十进制100对应的十六进制就是0x64对应的assiic字符就是d。在真正通过串口向模块发送数据时,采用的应该是%c这种方式而不是%d、%f这些,这些都是方便我们看的字符串类型,在模块进行数据处理时用不了。%c在串口发送时可以等价于直接向串口寄存器写要发送的数据,这条语句:

    while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;   

把ch替换成需要发送的char类型数据。

通过串口发送浮点类型有下面几种方法:

方法1:将浮点类型乘以100或1000等值,将浮点值变成整型数据再发送。如1.28*100=128,128通过char(u8)就可以直接发送出来,在接收设备上再除以100就可以得到原来的浮点类型数据。

方法2:通过 共用体来发送浮点类型数据,通过共用体不仅可以发送浮点类型数据,还可以发送u16,u32等类型的数据。下面以发送浮点值为例。

共用体是共用体内的数据共用一段内存,内存的大小按共用体内数据长度最大的值算。

如下图所示,定义了一个共用体,共用体内有一个float类型数据和一个4个字节大小的char类型数组,因为float类型占用的是4个字节,所以是一个float,4个char的搭配,如果是2个char就是8个char,u16、u32以此类推。我们把1.28这个浮点值存储在f_data这个float类型中,因为1.28对应的十六进制是3F A3 D7 0A,所以在byte【4】数组中存储的就是3F A3 D7 0A,我们把byte这个数组通过串口发送出去,在接收端再以float类型来存储处理,这样就实现了浮点类型的传输。

 所有类型其实是内存里数据的不同展现方式,我们把该类型拆分成8bit通过发送出去,在接收方再按原来的排列方式(大小端)存储起来并按发送端的方式去读取就可以得到原数据。STM32的内存是小端模式。

 参考:STM32如何收发float类型数据? – 知乎

 例程:

使用DMA传输ADC1的通道6、通道8、通道9采样到的电压值到变量中,然后再将变量中的值计算出电压值后通过DMA传输给串口发送出来,这里ADC1的三个通道都接的是3.3V,电压值通过DMA传输给USART只传输一次(设置正常模式不循环,只发送12个数据)。

/**adc.h**/
#ifndef __ADC_H

#define __ADC_H
#include "stm32f4xx.h"
void ADC1_Init(void);
extern u16 adc_value[3];

typedef union adc_v{
	float adc_price[3];
	u8  adc_buff[12];
}ADC_VALUE;
extern  ADC_VALUE adc_data;

#endif
/**adc.c**/
#include "adc.h"

u16 adc_value[3]={0};

ADC_VALUE adc_data;

/**
*@funcktion: ADC1通道引脚初始化
						GPIOB0  |  ADC通道8 
						GPIOB1  |  ADC通道9 
						GPIOA6  |  ADC通道6 
**/
void ADC1_GPIO_Init(void)
{  	 
  GPIO_InitTypeDef  GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB, ENABLE);	//使能GPIOF时钟

  //GPIOB0、GPIOB1初始化设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;				//模拟输入
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;			//推挽(不影响)
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;		//无上下拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);							//初始化
	//GPIOA6初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//初始化

}

/**
注意,单片机是32位的,地址是32位的,所以你写的地址要用(32)强制转换成32位,16位8位的数都不能做地址,否则会出错
**/
void ADC1_DMA_Config(void)
{
	DMA_InitTypeDef DMA_InitStruct;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);	//开启DMA2时钟
	
	DMA_DeInit(DMA2_Stream0);
	DMA_StructInit(&DMA_InitStruct);	//用默认值填充结构体值
	DMA_InitStruct.DMA_BufferSize = 3;															//待传输数据数目
	DMA_InitStruct.DMA_Channel = DMA_Channel_0;											//DMA的通道选择
	DMA_InitStruct.DMA_DIR =DMA_DIR_PeripheralToMemory;							//传输方向
	DMA_InitStruct.DMA_Memory0BaseAddr = (u32)adc_value;//(u32)(adc_data.adc_price);	//存储器地址
	DMA_InitStruct.DMA_MemoryDataSize =DMA_MemoryDataSize_HalfWord;	//存储器数据大小
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;						//存储器地址递增
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;										//循环模式
	DMA_InitStruct.DMA_PeripheralBaseAddr = ((u32)ADC1+0x4c);				//外设地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	//外设数据大小
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设地址不递增
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;								//优先级
	DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;							//不使用FIFO模式
	DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
	DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
	DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
	DMA_Init(DMA2_Stream0,&DMA_InitStruct);
	
	DMA_Cmd(DMA2_Stream0,ENABLE);
} 

void ADC1_Init(void)
{
	ADC_InitTypeDef ADC_InitStruct;
	ADC_CommonInitTypeDef ADC_CommonInitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);	//开启ADC时钟
	
	ADC1_GPIO_Init();		//ADC的GPIO初始化
	ADC1_DMA_Config();	//ADC的DMA初始化
	//配置ADC系统相关参数
	ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;			//关闭DMA
	ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;											//独立模式
	ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div2;									//二分频
	ADC_CommonInitStruct.ADC_TwoSamplingDelay =ADC_TwoSamplingDelay_20Cycles;	//两个采样通道之间20个周期延时
	ADC_CommonInit(&ADC_CommonInitStruct);
	//配置ADC1相关参数
	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;			//开启连续转换
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;	//右对齐
	//ADC_InitStruct.ADC_ExternalTrigConv =;						//外部触发选择
	ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;	//不用上下沿触发
	ADC_InitStruct.ADC_NbrOfConversion= 3;								//通道数量
	ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;		//分辨率12位
	ADC_InitStruct.ADC_ScanConvMode = ENABLE;						//扫描模式关闭
	ADC_Init( ADC1, &ADC_InitStruct);

	ADC_RegularChannelConfig( ADC1,  ADC_Channel_8, 1 , ADC_SampleTime_3Cycles);		//配置规则通道:选择通道、通道转换顺序、采样周期
	ADC_RegularChannelConfig( ADC1,  ADC_Channel_9, 2 , ADC_SampleTime_3Cycles);		//配置规则通道:选择通道、通道转换顺序、采样周期
	ADC_RegularChannelConfig( ADC1,  ADC_Channel_6, 3 , ADC_SampleTime_3Cycles);		//配置规则通道:选择通道、通道转换顺序、采样周期
	
//	ADC_ITConfig( ADC1, ADC_IT_EOC,  ENABLE);	//使能传输完成中断

//	NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn;
//	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
//	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x02;
//	NVIC_InitStruct.NVIC_IRQChannelSubPriority= 0x02;
//	NVIC_Init( &NVIC_InitStruct);	//初始化中断

	ADC_DMARequestAfterLastTransferCmd( ADC1,ENABLE);
	ADC_DMACmd( ADC1, ENABLE);
	ADC_Cmd(ADC1,ENABLE);					//使能ADC

	ADC_SoftwareStartConv(ADC1);	//软件触发

}                                   

ADC中断
//void ADC_IRQHandler(void)
//{
//	if(ADC_GetITStatus(ADC1,ADC_IT_EOC)==SET)
//	{
//		adc_value = ADC_GetConversionValue( ADC1);
//	}
//	ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);

//}
/**usart1.c**/

#include "usart1.h"
#include "adc.h"

u8 buff[5] = {0x59,0x4A,0x4C,0x0D,0x0A};
void USART1_GPIO_Init(void)
{
	 //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟

	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
	
	//USART1端口配置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
}

/**
DMA2-Stream7-Channel4(USART1_TX)
**/
void USART1_DMA_Init(void)
{
	DMA_InitTypeDef DMA_InitStruct;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);	//开启DMA2时钟

	DMA_StructInit(&DMA_InitStruct);

	DMA_InitStruct.DMA_BufferSize = 12;															//待传输数据数目
	DMA_InitStruct.DMA_Channel = DMA_Channel_4;											//DMA的通道选择
	DMA_InitStruct.DMA_DIR =DMA_DIR_MemoryToPeripheral;							//传输方向
//	DMA_InitStruct.DMA_Memory0BaseAddr = (u32)buff;						//存储器地址
	DMA_InitStruct.DMA_Memory0BaseAddr = (u32)adc_data.adc_buff;						//存储器地址
	DMA_InitStruct.DMA_MemoryDataSize =DMA_MemoryDataSize_Byte;	//存储器数据大小
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;						//存储器地址递增
	DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;//DMA_Mode_Circular;										//循环模式
	DMA_InitStruct.DMA_PeripheralBaseAddr = ((u32)USART1+0x04);				//外设地址
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据大小
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		//外设地址不递增
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;								//优先级
	DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;							//不使用FIFO模式
	DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
	DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
	DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
	DMA_Init( DMA2_Stream7, &DMA_InitStruct);
	
	DMA_Cmd(DMA2_Stream7,ENABLE);
	
}

void USART1_Init(u32 bound){

	USART_InitTypeDef USART_InitStructure;
	
	USART1_GPIO_Init();
	USART1_DMA_Init();
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
	
  //USART1 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	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_DMACmd( USART1,  USART_DMAReq_Tx,  ENABLE);
  USART_Cmd(USART1, ENABLE);  //使能串口1 
}
/**main.c*/

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "beep.h"
#include "key.h"
#include "adc.h"
 
int main(void)
{ 
 u8 i;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);		//延时初始化 
//	uart_init(115200);	//串口初始化波特率为115200
	USART1_Init(115200);
	LED_Init();		  		//初始化与LED连接的硬件接口  
	ADC1_Init();

 
	while(1)
	{
			adc_data.adc_price[0]=(float)adc_value[0]*3.3/4096;
			adc_data.adc_price[1]=(float)adc_value[1]*3.3/4096;
			adc_data.adc_price[2]=(float)adc_value[2]*3.3/4096;

//		delay_ms(100);
	}
}

 结果(十六进制显示):注意单片机是小端模式,要从右往左看

 

 可以看到,发送出来的就是浮点电压值。

 

物联沃分享整理
物联沃-IOTWORD物联网 » 单片机通过串口发送浮点类型数据

发表评论