cubeIDE开发, stm32的ADC(模数转换器) 开发要点

news2024/9/23 9:31:14

一、ADC模数转换简介

        ADC(Analog-to-Digital Converter,模数转换器) 是将连续变化的模拟信号转换为离散的数字信号的器件。真实世界的模拟信号,例如温度、压力、声音或者图像等,需要转换成更容易储存、处理和发射的数字形式。模数转换器可以实现这个功能,在各种不同的产品中都可以找到它的身影。与之相对应的 DAC(Digital-to-Analog Converter),它是 ADC 模数转换的逆向过程。

        我们在采集外界信号转换为电气(电平)信号时,通常有两种输入方式:逻辑电平输入和ADC输入。例如现有输入电压值采集,前者只能读取高、低电平(0或1)的二态信号,该态势信号是与某阀值比较输出的真假数字信号,而ADC输入方式可以读取出电压值大小的离散信号,这个将输入的连续模拟量电压转换为数字离散电压的过程就叫模数转换,模数转换一般要经过采样、保持和量化、编码这几个步骤。

        【1】采样,将时间上连续变化的模拟信号转换为时间上离散的模拟信号。

        【2】保持,模拟信号转换为数字信号都需要一定时间,为了给后续处理过程提供一个稳定的值,在采样电路后要求将所采样的模拟信号保持一段时间。

        【3】量化,采样保持电路的输出电压还需要按照某种近似方式归化到与之相应的离散电平上,任何数字量只能是某个最小数量单位的整数倍。

        【4】编码,量化后的数值其后还需要编码过程,也就是 A/D 转换器输出的数字量。

二、ADC工程创建

        【1】本文采用STM32L496VGTx的ali联合上海诺行的开发板,其支持ADC功能的原理框图及引脚描述如下:

         引脚描述:

         本文不按开发板资料设定来配置ADC,只采用到PC2、PC3两个引脚。

        【2】本文基于原有的已经实现串口lpusart调试输出和LCD屏幕输出的就工程为基础新建一个adc工程,并将旧工程已经实现的功能迁移到新功能工程。

        完成新工程创建后,进入cubeMX配置界面,配置ADC功能

        开启ADC1的IN3:

         开启ADC2的IN4

         前两部配置开启ADC功能时,时钟会自动开启ADC时钟源

         【3】点击保存输出

        在ICore目录下新建adc文件夹,并在该目录下创建adc.h和adc.c源文件,项目整体目录结构如下:

 三、轮询读取单路ADC数值代码设计

        目前只配置了ADC轮询模式读取数据,在stm32l4xx_hal_adc.c中关于轮询模式读取ADC数据说明如下:

        (++) ADC conversion by polling:
          (+++) Activate the ADC peripheral and start conversions
                using function HAL_ADC_Start()
          (+++) Wait for ADC conversion completion
                using function HAL_ADC_PollForConversion()
          (+++) Retrieve conversion results
                using function HAL_ADC_GetValue()
          (+++) Stop conversion and disable the ADC peripheral
                using function HAL_ADC_Stop()

       因此现读取adc数据驱动: adc.h

#ifndef ADC_ADC_H_
#define ADC_ADC_H_

#include "stm32l4xx_hal.h" //HAL库文件声明
extern ADC_HandleTypeDef hadc1;
extern ADC_HandleTypeDef hadc2;

uint16_t ADC_IN_1(void);
uint16_t ADC_IN_2(void);

#endif /* ADC_ADC_H_ */

        adc.c

#include "adc.h"

uint16_t ADC_IN_1(void) //ADC采集程序
{
	HAL_ADC_Start(&hadc1);//开始ADC采集
	HAL_ADC_PollForConversion(&hadc1,500);//等待采集结束
	if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))//读取ADC完成标志位
	{
		return HAL_ADC_GetValue(&hadc1);//读出ADC数值
	}
	return 0;
}

uint16_t ADC_IN_2(void) //ADC采集程序
{
	HAL_ADC_Start(&hadc2);//开始ADC采集
	HAL_ADC_PollForConversion(&hadc2,500);//等待采集结束
	if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc2), HAL_ADC_STATE_REG_EOC))//读取ADC完成标志位
	{
		return HAL_ADC_GetValue(&hadc2);//读出ADC数值
	}
	return 0;
}

        在main.c文件中,添加acd.h驱动文件

/* USER CODE BEGIN Includes */
#include "../../ICore/key/key.h"
#include "../../ICore/led/led.h"
#include "../../ICore/print/print.h"
#include "../../ICore/usart/usart.h"
#include "../../ICore/oled/oled.h"
#include "../../ICore/adc/adc.h"
/* USER CODE END Includes */

       在主函数内初始化两个独立ADC引脚

  /* USER CODE BEGIN 2 */
  ResetPrintInit(&hlpuart1);
  HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
  HLPUSART_RX_STA = 0;
  //ADC相关,ADC_SINGLE_ENDED=单端模式,ADC_DIFFERENTIAL_ENDED=差分模式,CubeMX配置了单端
  HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);//ADC采样校准
  HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);//ADC采样校准
  //LCD
  OLED_init();
  //设置OLED蓝色背景显示
  BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
  printf("OLED_Clear_DMA\r\n");
  /* USER CODE END 2 */

        在主函数循环体内,通过按键2获取两个ADC数值

 /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
		  //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
		  OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
		  HLPUSART_RX_STA=0;//接收错误,重新开始
		  HAL_Delay(100);//等待
	  }
	  if(KEY_0())
	  {
		  BSP_LCD_login(24,108);
	  }
	  if(KEY_1())
  	  {
		  BSP_LCD_img_DMA();
  	  }
	  if(KEY_2())
  	  {
		  printf("ADC1=%04X  ADC2=%04X \r\n",ADC_IN_1(),ADC_IN_2());
		  OLED_printf(10,108,"ADC1=%04X  ADC2=%04X",ADC_IN_1(),ADC_IN_2());//向lcd发送字符串
  	  }
    /* USER CODE END WHILE */

        编译及下载效果如下,目前两个ADC引脚没有外接设备,每次取得数值是一定范围随机的:

 四、DMA方式读取单路ADC代码设计

        回到cubeMX配置界面,开启ADC的连续转换功能

        开启ADC的DMA功能 

         确认ADC的DMA的终端功能是否开启。

         确保DMA初始化排序在ADC前面

         点击保存输出生成代码,设置ADC1支持DMA模式,ADC2依然是轮询模式。

        关于ADC的DMA读取数据,HAL标准库描述如下:

        (++) ADC conversion with transfer by DMA:
          (+++) Activate the ADC peripheral and start conversions
                using function HAL_ADC_Start_DMA()
          (+++) Wait for ADC conversion completion by call of function
                HAL_ADC_ConvCpltCallback() or HAL_ADC_ConvHalfCpltCallback()
                (these functions must be implemented in user program)
          (+++) Conversion results are automatically transferred by DMA into
                destination variable address.
          (+++) Stop conversion and disable the ADC peripheral
                using function HAL_ADC_Stop_DMA()

        现在来实现ADC的DMA读取数据,在main.c主函数内,调整如下:

  /* USER CODE BEGIN 2 */
  ResetPrintInit(&hlpuart1);
  HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
  HLPUSART_RX_STA = 0;
  //ADC相关,ADC_SINGLE_ENDED=单端模式,ADC_DIFFERENTIAL_ENDED=差分模式,CubeMX配置了单端
  HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);//ADC采样校准
  HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);//ADC采样校准
  //本次新调整
  uint16_t a1 = 0, a2 = 0;	//缓存ADC1数值
  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&a1,1);//启动DMA,采集数据存入的变量地址,长度1
  //LCD
  OLED_init();
  //设置OLED蓝色背景显示
  BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
  printf("OLED_Clear_DMA\r\n");
  /* USER CODE END 2 */

        在主函数循环体内调整如下:

  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
		  //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
		  OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
		  HLPUSART_RX_STA=0;//接收错误,重新开始
		  HAL_Delay(100);//等待
	  }
	  if(KEY_0())
	  {
		  BSP_LCD_login(24,108);
	  }
	  if(KEY_1())
  	  {
		  BSP_LCD_img_DMA();
  	  }
	  if(KEY_2())
  	  {
        //本次调整
		  a2 = ADC_IN_2();
		  printf("ADC1=%04X  ADC2=%04X \r\n",a1,a2);
		  OLED_printf(10,108,"ADC1=%04X  ADC2=%04X",a1,a2);//向lcd发送字符串
  	  }
    /* USER CODE END WHILE */

        编译及下载,也能实现数据读取:

 五、DMA模式读取ADC多路数据

        再回到cubeMX配置界面,关闭ADC2

         再开启ADC1的4通道对PC3的支持,参数设置页面,设置通道数量为2,rank1支持3通道,rank2支持4通道

         确保扫描模式已经自动开启(设置多通道会自动开启)

         点击保存生成输出代码

        DMA读取多路ADC数据和单路数据步骤几乎一致,只是给出不同大缓存空间而已,调整代码如下:

        在main.c主函数调整ADC初始化

  /* USER CODE BEGIN 2 */
  ResetPrintInit(&hlpuart1);
  HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
  HLPUSART_RX_STA = 0;
  //ADC相关,ADC_SINGLE_ENDED=单端模式,ADC_DIFFERENTIAL_ENDED=差分模式,CubeMX配置了单端
  HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);//ADC采样校准
//本次调整,注释hadc2相关,给hadc1的DMA读取2个缓存空间
//  HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);//ADC采样校准
//  uint16_t a1 = 0, a2 = 0;	//缓存ADC1数值
//  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&a1,1);//启动DMA,采集数据存入的变量地址,长度1
  uint16_t adc_val[2] = {0};
  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&adc_val,2);//启动DMA,采集数据存入的变量地址,长度2
  //LCD
  OLED_init();
  //设置OLED蓝色背景显示
  BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
  printf("OLED_Clear_DMA\r\n");
  /* USER CODE END 2 */

        在main.c的主函数循环体中,调整如下:

 /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
		  //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
		  OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
		  HLPUSART_RX_STA=0;//接收错误,重新开始
		  HAL_Delay(100);//等待
	  }
	  if(KEY_0())
	  {
		  BSP_LCD_login(24,108);
	  }
	  if(KEY_1())
  	  {
		  BSP_LCD_img_DMA();
  	  }
	  if(KEY_2())
  	  {
        //本次调整,直接读取数组数据打印输出
//		  a2 = ADC_IN_2();
		  printf("ADC1=%04X  ADC2=%04X \r\n",adc_val[0],adc_val[1]);
		  OLED_printf(10,108,"ADC1=%04X  ADC2=%04X",adc_val[0],adc_val[1]);//向lcd发送字符串
  	  }
    /* USER CODE END WHILE */

        编译及下载,同样能顺利读取到数据:

 六、ADC中断读取模式

        再次回到cubeMX配置界面,再次开启ADC2支持PC3引脚,参数保持默认并开启中断功能。

         保存输出生成代码

        在自定义的驱动文件adc.c(非图形配置生成输出代码),添加对回调函数的处理:

extern uint16_t Adc_Value;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)//回调函数
{
	if(&hadc2==hadc){
		Adc_Value = HAL_ADC_GetValue(&hadc2);
	}
}

         在main.c文件中,加入全局变量声明:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint16_t Adc_Value = 0;
/* USER CODE END 0 */

        在mian.c文件的主函数中,改写ADC初始化

  /* USER CODE BEGIN 2 */
  ResetPrintInit(&hlpuart1);
  HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
  HLPUSART_RX_STA = 0;
  //ADC相关,ADC_SINGLE_ENDED=单端模式,ADC_DIFFERENTIAL_ENDED=差分模式,CubeMX配置了单端
  HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);//ADC采样校准
//  HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);//ADC采样校准
//  uint16_t a1 = 0, a2 = 0;	//缓存ADC1数值
//  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&a1,1);//启动DMA,采集数据存入的变量地址,长度1
  uint16_t adc_val[2] = {0};
  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&adc_val,1);//启动DMA,采集数据存入的变量地址,长度1
  //本次改写
  Adc_Value = 0;
  HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);
  HAL_ADC_Start_IT(&hadc2);
  //LCD
  OLED_init();
  //设置OLED蓝色背景显示
  BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
  printf("OLED_Clear_DMA\r\n");
  /* USER CODE END 2 */

 在主函数循环体内,调整输出显示

  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
		  //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
		  OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
		  HLPUSART_RX_STA=0;//接收错误,重新开始
		  HAL_Delay(100);//等待
	  }
	  if(KEY_0())
	  {
		  BSP_LCD_login(24,108);
	  }
	  if(KEY_1())
  	  {
		  BSP_LCD_img_DMA();
  	  }
	  if(KEY_2())
  	  {
//		  a2 = ADC_IN_2();
		  HAL_ADC_Start_IT(&hadc2);//无外接设备,需master端自驱动刷新数据
		  printf("ADC1=%04X  ADC2=%04X ADC2=%04X \r\n",adc_val[0],adc_val[1],Adc_Value);
		  OLED_printf(10,108,"ADC1=%04X  ADC2=%04X ADC2=%04X ",adc_val[0],adc_val[1],Adc_Value);//向lcd发送字符串
  	  }
    /* USER CODE END WHILE */

        编译下载,数据只有中断读取的变更了,而DMA读取的不刷新变化,显然PC3引脚共用带来的问题(先留个疑问,哈哈):

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/67502.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

cesium切片底图正常出来但控制台一直报错的方法

1、部署ngnix服务 2、修改ngnix配置 在location 下的路径配置如下内容 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET, POST, OPTIONS; add_header Access-Control-Allow-Headers DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Re…

程序员需知的8个视频教程网站,建议收藏

上一篇介绍的是在线教程网站以文字性内容为主,同样收藏了一些以视频为主的一些网站,相比较而言,更容易上手,当然,也更加耗时间。 1、B站 简 介:没错,bilibili也可以用来学习,除了番…

吉林优美姿文化:抖音小店店铺评分怎么提升?

现在大家都在抖音平台上购物,只用等商家发货就行了,但是抖音发货也是有一定相关规定的,也需要各位商家去遵守的,那么抖音的发货时效方面有着什么样的规定呢?跟着吉林优美姿小编来看一下吧!发货时间&#xf…

TPE-3-CHO;CAS:2351847-81-7;AIE聚集诱导发光

中文名 4,4,4,4-(乙烯-1,1,2,2-四基)四(([1,1-联苯]-3-甲醛)) 英文名 4,4,4,4-(Ethene-1,1,2,2-tetrayl)tetrakis(([1,1-biphenyl]-3-carbaldehyde)) 中文别名 四(3-甲醛基联苯基)乙烯 | 4,4,4,4-(乙烯-1,1,2,2-四基)四(([[1,1-联苯] -3 -甲醛)) 物理化学性质 密度 1.2200.06 g…

spring的BeanPostProcessor分析

spring常见的set注入,就是通过配置文件给变量赋值,这属于第一步注入。BeanPostProcessor还能进行第二次注入,简单来说就是第二是给变量赋值 1、首先定义一个实体类EntityObject,声明get和set方法 package beanPost;public class…

C语言---函数---总结

🚀write in front🚀 📝个人主页:认真写博客的夏目浅石. 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝​ 📣系列专栏:鹏哥带我学c带我飞 💬总结:希望你看…

一文了解Linux内核网络设备驱动

1. 接收数据包过程概述 介绍数据包收包过程,有助于我们了解Linux内核网络设备在数据收包过程中的位置,下面从宏观的角度介绍数据包从被网卡接收到进入 socket 接收队列的整个过程: 加载网卡驱动,初始化数据包从外部网络进入网卡…

月子会所管理系统| 月子会所小程序| 数字化门店转型

随着二孩三孩政策的相继开放,中国母婴市场呈现出稳定增长的局面,据相关数据显示,2019年中国母婴市场规模达34950亿元,预计2024年将增长到70000亿元。母婴行业的细分类高,同时还可与多行业进行对接。 母婴月子会所近些年…

Vue3 事件处理

Vue3 事件处理1.基本使用2.事件修饰符3.按键修饰符1.基本使用 我们可以使用 v-on 指令来监听 DOM 事件,从而执行 JavaScript 代码。 v-on 指令可以缩写为 符号。 语法格式: v-on:click"methodName" 或 click"methodName"一个最…

基于jsp+ssm的驾校预约管理系统-计算机毕业设计

项目介绍 驾校预约管理系统是一个高校用来管理教员和学员的授课信息并存储档案必需的一个管理系统,由于时代的进步,它成为了一个现代化管理不可缺少的一部分。它的查询的方便简洁,可以为一个驾校经营者节约足够的时间,为驾校迅速…

QWebEngine集成Netron可视化模型

Netron是一个用于可视化深度学习网络模型的工具软件,主体以JavaScript语言实现,源码在: https://github.com/lutzroeder/netron 。用户可以使用各系统平台的安装包进行安装之后使用,也可以用浏览器使用在线版本: Netron。 Netron支持几乎所有…

ChatGPT 和 Midjourney 将改变我们的生活,日常工作流程将完全改变并与这些新型工具集成

上周末我花了很多时间先玩 Open AI ChatGPT,然后玩 Midjourney。起初我笑了,然后我开始完全被各种可能性所困扰,然后我终于意识到了它的潜力,并开始将其用于更有成效的工作。 注意:我本可以用它来制作一个引人入胜的点击诱饵标题,但我没有. 这是我问 Open AI 聊天的第一…

在python中调用ChatGPT,并使用tkinter打包成exe

在python中调用ChatGpt一、前提1. 安装库2. 获取key3. 调用示例二、tkinter桌面应用网页使用与python使用的对比用它来搜题你将会知道什么叫爽一、前提 小伙伴们都知道,最近这两天ChatGpt最近很火爆,更重要的是他对中文的兼容性很好,比如我问…

以“社交和品质”打通长线运营,UTONMOS打造真正的Web3.0链游破圈之作

元宇宙(Metaverse),这个来源于科幻小说的概念已成为真实世界中的流行语。在大众对元宇宙的构想中," 游戏 " 是优先级最高的落地场景之一。《头号玩家》《赛博朋克 2077》等作品中," 游戏 " 也多次…

【20221206】【每日一题】01背包的基础

思路: 二维数组 动规五部曲 1、确定dp数组以及下标含义:二维数组dp[i][j]表示从下标为0-i的物品里任意取,放入容量为j的背包,价值总和最大为多少; 2、确定递推关系式:从两个方向推dp[i][j],没…

CSS 之 渐变色边框

一、渐变色边框 如果我们前端最亲爱的UI设计师,让我们给盒子绘制一个渐变色的边框,而且盒子的宽高还需要随着内容变化而变化,那我们就不能通过切图来解决问题,所以我们可以这么说: 但是我相信优秀的你肯定不会说做不…

如何删除密码?知道密码和不知道密码的情况

压缩包设置了密码,就需要输入压缩包密码才能顺利解压文件。 有些时候我们加密了压缩包之后,过了一段时间可能就不需要再加密压缩包里的文件了。 有些时候我们加密了压缩包之后,长时间没有使用,又没有将密码记录在一个地方&#…

基于 LSTM 的分布式能源发电预测(Matlab代码实现)

💥💥💥💞💞💞欢迎来到本博客❤️❤️❤️💥💥💥 🎉作者研究:🏅🏅🏅主要研究方向是电力系统和智能算法、机器学…

CNAME记录和A记录

文章目录定义CNAME使用说明总结定义 A记录:A (Address) 记录是用来指定主机名(或域名)对应的IP地址记录。CNAME记录:CName记录是Canonical Name的简称,通常称别名指向,CNAME记录可用于将一个域名别名为另一…

太强了!GitHub大佬白嫖的SpringCloud微服务进阶宝典,啃完感觉能吊锤面试官!

自 2014 年起,微服务技术一直火热至今。随着越来越完善的微服务技术栈的发布,以及越来越多的微服务项目实际的落地和上线,使用 Java 技术栈的企业应该都在尝试或者已经落地了各自的微服务项目。同时,通过招聘网站的信息和每次面试…