【STM32多路温控—第三章】ADC多通道采样,及数据上报

2023-10-12 20:39

本文主要是介绍【STM32多路温控—第三章】ADC多通道采样,及数据上报,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 头文件定义

/* Define to prevent recursive inclusion ----------------------------------------------------------------*/
#ifndef __ADC1_TRANS_H__
#define __ADC1_TRANS_H__/*********************************************************************************************************/
/* 包含头文件 -------------------------------------------------------------------------------------------*/
#include "application.h"/* 宏定义 -----------------------------------------------------------------------------------------------*/\
#define   ChannelNum                   13 
#define   SampNum                      10        //采样次数,使用平均滤波,采样10次取均值
#define   ArrayNum                     130        //缓存
#define   B                              3435           //B值,温度系数
#define   TN                             292.5         //额定温度(绝对温度加常温:273.15+25)
#define   RN                             10           //额定阻值(绝对温度下时的电阻值10K)
#define   R2                             100         //分压电阻100K
#define   BaseVol                        3.281        //ADC基准电压#define ad1  0
#define ad2  1
#define ad3  2
#define ad4  3 
#define ad5  4 
#define ad6  5 
#define ad7  6
#define ad8  7
#define ad9  8
#define ad10  9 
#define ad11  10 
#define ad12  11
#define ad13  12
#define ad14  13extern int16_t *adctmpcontrol;                        // 定义数组指针变量
extern float *adctmparray;
/* 函数声明 ---------------------------------------------------------------------------------------------*/
void ADC1_Init(void);
void Adc_Handle(void);
uint16_t Get_Tempture_Value(uint16_t chx);
void Output_Tempture(void); 
void Get_Bus_Current(void);/* 扩展变量 ---------------------------------------------------------------------------------------------*/#endif 

c文件定义

定义一系列数组存放数据

static uint32_t  AdcArrayLib[ArrayNum]={0};           // 13个ADC通道的10次采样缓存
static uint32_t  Adc2dArray[SampNum][ChannelNum];     // AD转换结果缓存,10行6列
static uint16_t  AdcFilterArray[ChannelNum];          // 滤波数组
static float     AdcVoltArray[ChannelNum];            // 用于保存转换计算后的电压值		

定义温度数组以及数组指针

static float     AdcTmpArray[ChannelNum];             // 用于保存转换计算后的温度值
float *adctmparray = AdcTmpArray;                     // 定义数组指针变量static int16_t   AdcTmpControl[ChannelNum];
int16_t *adctmpcontrol = AdcTmpControl;               // 定义数组指针变量

封装ADC初始化

void ADC1_Init(void)
{MX_ADC1_Init();
//   HAL_ADCEx_Calibration_Start(&hadc1);//ADC初始化后要进行校准,开启后不准确HAL_ADC_Start_DMA(&hadc1, AdcArrayLib, ArrayNum); 
}

处理DMA采集的数据

void Adc_Handle(void)
{uint8_t i,j,n,m;int sum = 0;float RT;for(n = 0; n < SampNum; n++){for(m = 0; m < ChannelNum; m++){Adc2dArray[n][m] = AdcArrayLib[n*ChannelNum+m]; }            }      for(i = 0; i < ChannelNum; i++){for(j = 0; j < SampNum; j++){sum += Adc2dArray[j][i];}AdcFilterArray[i] = sum/SampNum;sum = 0;}for(i = 0; i < ChannelNum; i++){AdcVoltArray[i] = (double)(AdcFilterArray[i] & 0xFFF) * BaseVol / 4096; // NTC电压RT = AdcVoltArray[i] * 100 / (BaseVol-AdcVoltArray[i]); // 求出当前温度下的阻值,由串联分压得AdcTmpArray[i] = 1 / (1 / TN + (log(RT / RN) / B)) - 273.15; // RT=RN*exp*B(1/T-1/TN)-273.5AdcTmpControl[i]=ROUND_TO_INT16_1(AdcTmpArray[i]);}    
}

输出温度值

我们利用上位机对MCU发送指令,意图就是根据发送的指令数值改变其中的参数值,或者执行相关函数。因此接收字节的处理这部分也是值得研究和学习的。

首先对数据结构进行确定。数据类型我们依然可以使用结构体进行定义。

typedef union {char Ch[2]; uint16_t Int;
}Format_UnionTypedef;typedef struct {__IO uint8_t  Code;  	__IO Format_UnionTypedef data[13];//数据帧有13个参数
}MSG_TypeDef;

我们首先定义一个结构体,该结构体里面有两个数据类型,一个是具有13个参数的共同体数组,另一个是一个字节的参数Code,使用来存放帧头,帧尾和校验和。13个参数对应的是要发送的13个16位的温度值,也就是占用两个字节。可以考到这个数据串共占用3+13*2=29个字节。

我们在定义一个共同体,我们再了解一下共同体的区别。

结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。

结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。

基于上述的数据类型的建成,我们就开始讨论一下对接收和发送数据的编排构建了。

首先将数据长度的数组进行填充覆盖,就是将定义的发送数据帧长度的数组进行填充,我们这里填充值是FILL_VALUE = 0x00。

填充完毕后再利用定义的数据规则对上述填充后的数组进行原位覆盖,我们要用到将之前定义的数据结构体变量,那么我们就要声明一个新的结构体变量。

MSG_TypeDef Msg;

  Msg.data[0].Int = Get_Tempture_Value(0);//ad1

我们将数据的第一位,转化成16进制数据存入uint16-t的数据类型中。

  sendBuffer[2] = Msg.data[0].Ch[0];
  sendBuffer[3] = Msg.data[0].Ch[1]; 

并将其拆分为一个低位和一个高位,装入输入的第2和第3位发送出去。

  sendBuffer[0] = FRAME_START;   // 帧头
  sendBuffer[1] = 0x80|0x08; // 指令码

帧头和指令码各占用一位。

sendBuffer[FRAME_CHECKSUM] = CheckSum((uint8_t*)&sendBuffer[FRAME_CHECK_BEGIN],FRAME_CHECK_NUM);  // 计算校验和
sendBuffer[FRAME_LENTH-1] = FRAME_END;   // 加入帧尾


HAL_UART_Transmit(&huart3,(uint8_t *)&sendBuffer,FRAME_LENTH,0xffff); // 发送数据帧

void Transmit_Feedback(void)
{uint8_t i = 0;for(i=0;i<FRAME_LENTH;i++){sendBuffer[i] = FILL_VALUE;  // 参数填充 0x00}Msg.data[0].Int = Get_Tempture_Value(0);//ad1Msg.data[1].Int = Get_Tempture_Value(1);//ad2  sendBuffer[0] = FRAME_START;   // 帧头sendBuffer[1] = 0x80|0x08; // 指令码sendBuffer[2] = Msg.data[0].Ch[0];sendBuffer[3] = Msg.data[0].Ch[1]; sendBuffer[FRAME_CHECKSUM] = CheckSum((uint8_t*)&sendBuffer[FRAME_CHECK_BEGIN],FRAME_CHECK_NUM);  // 计算校验和sendBuffer[FRAME_LENTH-1] = FRAME_END;   // 加入帧尾HAL_UART_Transmit(&huart3,(uint8_t *)&sendBuffer,FRAME_LENTH,0xffff); // 发送数据帧
}
uint16_t Get_Tempture_Value(uint16_t adx)
{switch(adx){case ad1:return ROUND_TO_INT16_10(AdcTmpArray[0]);case ad2:return ROUND_TO_INT16_10(AdcTmpArray[1]);case ad3:return ROUND_TO_INT16_10(AdcTmpArray[2]);default:break; 	}return 0;
}

输出

void Output_Tempture(void) 
{Adc_Handle();
#if     NOUSECOM == 0Transmit_Feedback();
#else   uint8_t i;      for(i = 0; i < ChannelNum; i++){    
//            printf("通道%d value = %d -> %fV -> %f℃\n",i,AdcFilterArray[i]&0xFFF,AdcVoltArray[i],AdcTmpArray[i]);printf("%d=%f\n;",i,AdcTmpArray[i]); 
//          printf("当前电压为=%f, 母线电流为=%f;",AdcVoltArray[6],BusCurrent);       }  
#endif
}

这篇关于【STM32多路温控—第三章】ADC多通道采样,及数据上报的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/198318

相关文章

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

C#使用iText获取PDF的trailer数据的代码示例

《C#使用iText获取PDF的trailer数据的代码示例》开发程序debug的时候,看到了PDF有个trailer数据,挺有意思,于是考虑用代码把它读出来,那么就用到我们常用的iText框架了,所... 目录引言iText 核心概念C# 代码示例步骤 1: 确保已安装 iText步骤 2: C# 代码程

Pandas处理缺失数据的方式汇总

《Pandas处理缺失数据的方式汇总》许多教程中的数据与现实世界中的数据有很大不同,现实世界中的数据很少是干净且同质的,本文我们将讨论处理缺失数据的一些常规注意事项,了解Pandas如何表示缺失数据,... 目录缺失数据约定的权衡Pandas 中的缺失数据None 作为哨兵值NaN:缺失的数值数据Panda

C++中处理文本数据char与string的终极对比指南

《C++中处理文本数据char与string的终极对比指南》在C++编程中char和string是两种用于处理字符数据的类型,但它们在使用方式和功能上有显著的不同,:本文主要介绍C++中处理文本数... 目录1. 基本定义与本质2. 内存管理3. 操作与功能4. 性能特点5. 使用场景6. 相互转换核心区别

python库pydantic数据验证和设置管理库的用途

《python库pydantic数据验证和设置管理库的用途》pydantic是一个用于数据验证和设置管理的Python库,它主要利用Python类型注解来定义数据模型的结构和验证规则,本文给大家介绍p... 目录主要特点和用途:Field数值验证参数总结pydantic 是一个让你能够 confidentl

JAVA实现亿级千万级数据顺序导出的示例代码

《JAVA实现亿级千万级数据顺序导出的示例代码》本文主要介绍了JAVA实现亿级千万级数据顺序导出的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 前提:主要考虑控制内存占用空间,避免出现同时导出,导致主程序OOM问题。实现思路:A.启用线程池

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

MyBatis-plus处理存储json数据过程

《MyBatis-plus处理存储json数据过程》文章介绍MyBatis-Plus3.4.21处理对象与集合的差异:对象可用内置Handler配合autoResultMap,集合需自定义处理器继承F... 目录1、如果是对象2、如果需要转换的是List集合总结对象和集合分两种情况处理,目前我用的MP的版本