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

2023-05-16

单片机通过串口向串口助手或者其他器件发送数据时是不能直接发送浮点类型数据的,通过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);
	}
}

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

 

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

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

单片机通过串口发送浮点类型数据 的相关文章

  • sip response 摘要认证

    详解摘要认证 1 什么是摘要认证 摘要认证与基础认证的工作原理很相似 xff0c 用户先发出一个没有认证证书的请求 xff0c Web服务器回复一个带有WWW Authenticate头的响应 xff0c 指明访问所请求的资源需要证书 但是
  • Prim算法实现最小生成树

    Prim算法实现最小生成树 1 最小生成树是什么2 最小生成树的用途3 Prim算法描述4 Prim算法演示最小生成树过程5 Prim算法实现END 1 最小生成树是什么 对连通图进行遍历 过程中所经过的边和顶点的组合可看做是一棵普通树 通
  • 哈夫曼树,哈夫曼编码及应用——(代码实现)

    哈夫曼树 xff0c 哈夫曼编码及应用 1 哈夫曼树1 1 什么是哈夫曼树 2 如何构造哈夫曼树 xff08 哈夫曼算法 xff09 2 1 举例实现哈夫曼树2 1 1手动实现具体步骤2 1 2代码实现具体步骤 3 哈夫曼编码3 1 什么是
  • 二叉排序树详解及实现

    二叉排序树详解及实现 1 什么是二叉排序树2 二叉排序树的数据结构2 1二叉排序树的节点类型2 2二叉排序树中插入某个元素2 3 二叉排序树中按值查找元素2 4 找排序二叉树中的最小值2 5返回排序二叉树中ptr中序遍历的后续节点2 6 寻
  • 平衡二叉树的一系列操作:删除、插入(在二叉排序树中插入新结点后,如何保持平衡)、调整平衡等等等

    平衡二叉树的插入 xff08 在二叉排序树中插入新结点后 xff0c 如何保持平衡 xff09 1 平衡二叉树的定义2 平衡二叉树的插入 xff08 调整最小不平衡子树A xff09 2 1LL xff08 在A的左孩子的左子树中插入导致不
  • 网络 UDP协议(C++|代码通过udp协议实现客户端与服务端之间的通信)

    这里写目录标题 udp通信编程各端的操作流程 xff1a 服务端操作流程 xff1a 客户端操作流程 xff1a 第2 3步与服务端不同 socket接口介绍udp客户服务端代码实现 推荐阅读 socket套接字编程就是在网络程序中编写代码
  • 网络 TCP协议(C++代码|通过tcp协议实现客户端与服务端之间的通信)

    目录 TCP通信编程各端的操作流程 xff1a 服务端操作流程 xff1a 客户端操作流程 xff1a 推荐先学习UDP协议在学习TCP协议 在UDP协议博客中讲解得更详细 xff0c 看懂UDP协议就很容易理解TCP了 网络 UDP协议
  • Matlab学习-箱型图绘制

    1 箱型图简介 xff1a 参考链接 xff1a boxplot函数用法详解 箱型图简介 箱型图主要包括的数据有 xff1a 最大值 最小值 上四分位数 下四分位数和中位数 xff0c 以及异常值 2 箱型图绘制 X span class
  • Matlab学习-CDF(累积分布函数图)绘制

    累积分布函数图绘制 参考链接 xff1a 1 Matlab官方说明 2 参考链接 3 属性设置 CDF xff1a 累积分布函数图 xff0c 顾名思义就是能够直观的反应某组数列分布的概率情况 xff0c 能够非常直观的反应误差精度大小 图
  • Matlab学习-频率分布直方图绘制

    参考链接 xff1a hist xff08 xff09 函数用法 频率分布直方图 xff1a 在数理统计中 xff0c 会经常使用到频率分布直方图 xff0c 能够直观的反应频率分布的范围大小 xff0c 在直角坐标系中 xff0c 横轴为
  • Matlab学习-经纬度在matlab内置地图显示

    已知经纬度坐标 xff0c 将其显示是地图上 参考链接 xff1a 使用matlab绘制世界地图并根据经纬度绘制点位 附m map的下载与安装说明 wm span class token operator 61 span webmap sp
  • ARM存储格式的“大小端”解析

    ARM储存 大端格式和小端格式 所谓的大端模式 xff0c 是指数据的高位 xff0c 保存在内存的低地址中 xff0c 而数据的低位 xff0c 保存在内存的高地址中 xff0c 这样的存储模式有点儿类似于把数据当作字符串顺序处理 xff
  • UBLOX板卡基础设置--F9P板卡配置(基准站和流动站)

    UBLOX F9P板卡配置 基准站 流动站 UBX F9P模块为双频定位芯片 xff0c 是市场上目前最常用的高精定位模块 xff0c 差分定位精度可达厘米级 xff0c 具体参数详见官方文档 官方文档下载链接 xff1a UBX F9P模
  • GIT学习-常用命令

    2 GIT学习 常用命令 在学习git前首先需要对相关名词和概念有基本了解 xff0c git基础知识学习可参考以下资料 xff1a git基础知识 xff1a GIT学习 1 基础知识git下载与配置 xff1a GIT学习 xff08
  • ROS常用命令

    ROS常用命令 1 将话题数据单独导出 将话题数据单独导出为一个文件 rostopic echo b name name p topic name gt save file name ex rostopic echo b test bag
  • Linux常用命令

    Linux常用命令 1 查看电脑IP地址 ifconfig 2 远程连接其他电脑 xff0c 查看是否连接成功 ping IP address 3 通过IP地址远程连接电脑 ssh lcl 64 IP address 4 文件传输 4 1
  • opencv-3.4.1-x86编译安装 -- 超详细

    相关链接 xff1a opencv 3 4 1 arm编译安装 超详细 opencv 3 4 1 x86编译安装 环境1 安装依赖库2 OpenCV源码获取与解压2 1 获取源码2 2 工作目录准备2 3 解压 3 OpenCV配置编译3
  • Qt编程之单例模式——代码复用,一个类供多个类调用

    什么是单例模式 单例模式是一种对象创建模式 xff0c 用于生产一个对象的实例 xff0c 它可以确保系统中一个类只产生一个实例 xff0c 这样做有两个好处 xff1a 1 对于频繁使用的对象 xff0c 可以省略创建对象所花费的时间 x
  • STM32串口数据接收处理,数据分割为整形浮点型数据。

    简介 通过stm32的串口接收数据 xff0c 通过strstr函数分割数据 xff0c 再将字符数据转化为整形数据或浮点数据 比如 xff1a stm32接收到数据 s555s xff0c 分割数据为 555 xff0c 然后转化为int
  • 抛出异常时将异常信息返给前端

    全局异常处理器负责将抛出的异常 xff0c 以统一的格式返给前端 在这里起主要作用的注解是 64 RestControllerAdvice 64 RestControllerAdvice主要配合 64 ExceptionHandler使用

随机推荐