蓝桥杯嵌入式学习笔记(7):ADC程序设计

目录

前言

1. ADC原理

1.1 主要特性

1.2 模拟输出电路图

2. 使用CubeMX进行源工程的配置

2.1 引脚配置

2.2 配置AD1 

2.3 配置AD2

2.4 配置时钟

3. 代码编程 

3.1 预备工作 

3.2 bsp_adc.h文件编写

3.3 bsp_adc.c文件编写

3.4 main.c编写

3.4.1 时钟函数配置

3.4.2 头文件引用

3.4.3 变量声明

3.4.4 子函数声明

3.4.5 函数定义

 3.4.6 main函数编写

4. 测试


前言

因本人备赛蓝桥杯嵌入式省赛,故编写此学习笔记进行学习上的记录。

上文我们实现了IIC程序设计,本文我们进行ADC程序设计。

1. ADC原理

STM32G431内部集成2个有最高12位ADC(ADC1、ADC2),它们是逐次逼近型模数转换器。

1.1 主要特性

可配置的转换精度:6位,8位,10位,12位

转换电压范围: 0 ~ VREF+(一般接到3.3V电源,不能超过STM32芯片电源电压)

19个转换通道:16个外部通道(IO引脚) + 3个内部通道(温度传感器、内部电压参考、电池供电监测)

采样时间可配置

扫描方向可配置

多种转换模式:单次,连续

数据存放对齐方式可配置:左对齐,右对齐(ADC的结果存储在一个左对齐或右对齐的 16 位数据寄存器中)

启动转换方式可配置:软件触发,硬件触发

可设置上下门限的模拟看门狗

DMA功能

在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断

1.2 模拟输出电路图

嵌入式实训平台CT117E-M4的模拟输出如下

其中PB15引脚连接R37电阻,PB12引脚连接R38电阻。

2. 使用IIC程序设计进行源工程的配置

2.1 引脚配置

将PB12配置为ADC1_IN11

将PB15配置为ADC2_IN15

2.2 配置AD1 

 

在Pinout&Configuration页面中的【Analog】目录中AD1的Mode选择IN11 Single。

在【Config】中配置【ADC_Settings】的【Clock Prescaler】选择【Asynchronous clock mode divided by 2】。

在【Config】中配置【ADC_Regular_Conversion Mode】的【Rank】选择【Sampling Time】为【640.5 Cycles】。

2.3 配置AD2

在Pinout&Configuration页面中的【Analog】目录中AD2的Mode选择IN15 Single。

在【Config】中配置【ADC_Settings】的【Clock Prescaler】选择【Asynchronous clock mode divided by 2】。

在【Config】中配置【ADC_Regular_Conversion Mode】的【Rank】选择【Sampling Time】为【640.5 Cycles】。

2.4 配置时钟

在【Clock Configuration】 中将ADC12的时钟源改为PLLP

配置完成后就生成代码

3. 代码编程 

3.1 预备工作 

接下来我们在Test_Project工程里的Src文件夹创建BSP\ADC\bsp_adc.c,同理,在Inc文件夹创建BSP\ADC\bsp_adc.h。这就是我们后面要编写的中间层代码文件。

打开Test_Project工程,进行文件Group的添加

在bsp_adc.c中添加依赖头文件

#include "ADC/bsp_adc.h"

 在Group中添加HAL库的相关文件,添加stm32g4xx_hal_adc.c和stm32g4xx_hal_adc_ex.c。

在stm32g4xx_hal_conf.h中去掉#define HAL_ADC_MODULE_ENABLED 的注释

#define HAL_ADC_MODULE_ENABLED  

3.2 bsp_adc.h文件编写

拷贝Source工程生成的adc.h代码,并定义获取ADC的函数

#include "main.h"

extern ADC_HandleTypeDef hadc1;
extern ADC_HandleTypeDef hadc2;

void ADC1_Init(void);
void ADC2_Init(void);


uint16_t getADC1(void);
uint16_t getADC2(void);

3.3 bsp_adc.c文件编写

 拷贝Source工程生成的adc.c代码。

#include "ADC/bsp_adc.h"

ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;


void ADC1_Init(void)
{
  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};

  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.GainCompensation = 0;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }
  sConfig.Channel = ADC_CHANNEL_11;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

void ADC2_Init(void)
{

  ADC_ChannelConfTypeDef sConfig = {0};

  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
  hadc2.Init.Resolution = ADC_RESOLUTION_12B;
  hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc2.Init.GainCompensation = 0;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc2.Init.LowPowerAutoWait = DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.NbrOfConversion = 1;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc2.Init.DMAContinuousRequests = DISABLE;
  hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc2.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_15;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

static uint32_t HAL_RCC_ADC12_CLK_ENABLED=0;

void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(adcHandle->Instance==ADC1)
  {
    HAL_RCC_ADC12_CLK_ENABLED++;
    if(HAL_RCC_ADC12_CLK_ENABLED==1){
      __HAL_RCC_ADC12_CLK_ENABLE();
    }

    __HAL_RCC_GPIOB_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  }
  else if(adcHandle->Instance==ADC2)
  {
    HAL_RCC_ADC12_CLK_ENABLED++;
    if(HAL_RCC_ADC12_CLK_ENABLED==1){
      __HAL_RCC_ADC12_CLK_ENABLE();
    }

    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  }
}

void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{

  if(adcHandle->Instance==ADC1)
  {
    HAL_RCC_ADC12_CLK_ENABLED--;
    if(HAL_RCC_ADC12_CLK_ENABLED==0){
      __HAL_RCC_ADC12_CLK_DISABLE();
    }
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_12);
  }
  else if(adcHandle->Instance==ADC2)
  {
    HAL_RCC_ADC12_CLK_ENABLED--;
    if(HAL_RCC_ADC12_CLK_ENABLED==0){
      __HAL_RCC_ADC12_CLK_DISABLE();
    }
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_15);

  }
}

 编写获取ADC1和ADC2的代码,首先进行ADC采集的开始,然后调用HAL库的获取并返回。

uint16_t getADC1(void)
{
	uint16_t adc = 0;
	
	HAL_ADC_Start(&hadc1);
	adc = HAL_ADC_GetValue(&hadc1);
	
	return adc;
}
uint16_t getADC2(void)
{
	uint16_t adc = 0;
	
	HAL_ADC_Start(&hadc2);
	adc = HAL_ADC_GetValue(&hadc2);
	
	return adc;
}

3.4 main.c编写

3.4.1 时钟函数配置

拷贝Source工程生成的时钟配置代码

void SystemClock_Config(void)
{
	RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;
  RCC_OscInitStruct.PLL.PLLN = 20;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the peripherals clocks
  */
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_ADC12;
  PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
  PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_PLL;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

3.4.2 头文件引用

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "LCD\bsp_lcd.h"
#include "ADC\bsp_adc.h"

3.4.3 变量声明

//变量声明
__IO uint32_t uwTick_Lcd_Set_Point;//LCD减速

//*LCD显示专用变量
unsigned char Lcd_Disp_String[22];

3.4.4 子函数声明

//***子函数声明区
void SystemClock_Config(void);
void Lcd_Proc(void);

3.4.5 函数定义

void Lcd_Proc(void)
{
	if((uwTick - uwTick_Lcd_Set_Point)<200)
		return;
	uwTick_Lcd_Set_Point = uwTick;
	
	sprintf((char*)Lcd_Disp_String,"R37:%4.2fV",(getADC2()*3.3)/4096);
	LCD_DisplayStringLine(Line3,Lcd_Disp_String);
	sprintf((char*)Lcd_Disp_String,"R38:%4.2fV",(getADC1()*3.3)/4096);
	LCD_DisplayStringLine(Line4,Lcd_Disp_String);
}

 3.4.6 main函数编写

int main(void)
{
	HAL_Init();
	
    SystemClock_Config();
	
	LCD_Init();
	LCD_Clear(White);
	LCD_SetBackColor(White);
	LCD_SetTextColor(Blue);
	
	
	ADC1_Init();
	ADC2_Init();

    while (1)
    {
		Lcd_Proc();
    }
}

4. 测试

将代码进行编译并下载到开发板上。效果如下图所示。

 

至此,本节就大功告成了! 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java子集(力扣Leetcode78)

子集 力扣原题链接 问题描述 给定一个整数数组 nums&#xff0c;数组中的元素互不相同。返回该数组所有可能的子集&#xff08;幂集&#xff09;。解集不能包含重复的子集。可以按任意顺序返回解集。 示例 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#x…

python3将exe 转支持库错误 AssertionError: None does not smell like code

exe -> pyc包(*.exe_extracted) 安装反编译工具 exe反编译工具&#xff1a;pyinstxtractor.py下载&#xff1a;https://sourceforge.net/projects/pyinstallerextractor/ python pyinstxtractor.py hello.exe包反编译 懒的写&#xff01;&#xff01;&#xff01; 这有详…

英语广场期刊投稿发表论文

《英语广场》是由国家新闻出版总署批准的正规期刊&#xff0c;杂志本着“轻松读原作&#xff0c;快乐学英语”的宗旨&#xff0c;倡导“寓学于乐”的学习理念&#xff0c;其活泼的办刊风格和优秀的文章选材受到读者特别是广大中学生的广泛欢迎&#xff0c;取得了良好的社会效益…

redis 的设计与实现(三)——对象

1. 前言&#xff1a; 在第一章节我们了解到了&#xff0c;redis底层所涉及的数据结构&#xff0c;但是这并非是离我们最近的一层&#xff0c;在此之上&#xff0c;redis实现了一层对象与我们交互。我们在本篇内容中将了解到&#xff1a; 对象对应的实现redis一些常用特性的实现…

数字化坚鹏:小熊电器面向数字化转型的大数据顶层设计实践培训

小熊电器面向数字化转型的大数据顶层设计实践培训圆满结束 ——努力打造“数据技术营销”三轮驱动的数字化领先企业 小熊电器股份有限公司由李一峰创立于2006年&#xff0c;是一家专业从事创意小家电研发、设计、生产和销售的实业型企业。2019年8月23日正式在深交所挂牌上市。…

rust使用Command库调用cmd命令或者shell命令,并支持多个参数和指定文件夹目录

想要在不同的平台上运行flutter doctor命令&#xff0c;就需要知道对应的平台是windows还是linux&#xff0c;如果是windows就需要调用cmd命令&#xff0c;如果是linux平台&#xff0c;就需要调用sh命令&#xff0c;所以可以通过cfg!实现不同平台的判断&#xff0c;然后调用不同…

【21-40】操作系统基础知识(非常详细)从零基础入门到精通,看完这一篇就够了

【21-40】操作系统基础知识&#xff08;非常详细&#xff09;从零基础入门到精通&#xff0c;看完这一篇就够了 以下是本文参考的资料 欢迎大家查收原版 本版本仅作个人笔记使用44、程序从堆中动态分配内存时&#xff0c;虚拟内存上怎么操作的45、常见的几种磁盘调度算法1. 先来…

codesys通过moudbus TCP连接西门子1214c,西门子做客户端

思路在codesys中发送数据到西门子&#xff0c;西门子原封不动的将数据传回。 1.首先配置codesys; 我设置了500个&#xff0c;但是好像发不这么多&#xff0c;只能120多个。因为什么来我忘了。但是这里不影响。 2.配置映射&#xff1a; 3.写代码 PROGRAM PLC_PRG VARarySendDa…

URL编码:原理、应用与安全性

title: URL编码&#xff1a;原理、应用与安全性 date: 2024/3/29 18:32:42 updated: 2024/3/29 18:32:42 tags: URL编码百分号编码特殊字符处理网络安全应用场景标准演变未来发展 在网络世界中&#xff0c;URL&#xff08;统一资源定位符&#xff09;是我们访问网页、发送请求…

Laya1.8.4 UI长按选择对应位置释放技能

需求&#xff1a; 需要实现拖拽摇杆选择技能释放位置&#xff0c;释放技能。 原理&#xff1a;首先拆分需求&#xff0c;分为两部分&#xff0c;UI部分和场景部分&#xff0c;UI部分需要实现长按效果&#xff0c;长按后又要有拖动效果&#xff0c;将官方文档的示例代码改了改…

HANA-公司间销售ICS-IDOC系统配置-保姆级配置文档

HANA公司间销售ICS-IDOC系统配置—保姆级配置文档 在项目实施过程中经常会遇到关联方交易的问题,有公司间采购的业务场景,也会存在公司间销售的业务场景,本文将着重讲解公司间销售在SAP系统中的实现场景。很多公司会在香港设置一个公司用于对外的销售接单,然后将接到的销售…

企业微信知识库:从了解到搭建的全流程

你是否也有这样的疑惑&#xff1a;为什么现在的企业都爱创建企业微信知识库&#xff1f;企业微信知识库到底有什么用&#xff1f;如果想要使用企业微信知识库企业应该如何创建&#xff1f;这就是我今天要探讨的问题&#xff0c;感兴趣的话一起往下看吧&#xff01; | 为什么企业…

小白python爬虫基础教程(看这一篇就完了)

爬虫的五个步骤&#xff1a; 1&#xff09;需求分析&#xff0c;找到需求相关的网址 2&#xff09;获取网址的返回信息&#xff08;urllib,requests&#xff09; 3&#xff09;定位需要的信息所在位置&#xff08;re正则表达式,XPATH, CSS selector&#xff09; 4&#xff…

argo rollout使用

一、前言 argorollout是比argocd更高级的发布工具&#xff0c;其中包含自动化金丝雀发布、自动化蓝绿发布、还可以通过argo命令或者dashboard查看发布的过程 二、使用 需要先部署argo rollout服务 参考&#xff1a;https://github.com/argoproj/argo-rollouts/tree/master/m…

关于web_server项目的学习记录(自用)

主要参考资料&#xff1a; 我在地铁吃闸机 基础处理框架&#xff1a;Multi-reactor muduo库有三个核心组件实现持续监听reactor的fd&#xff1a;channel;epoll/poller/eventloop类 channel 事件监听器epoll_ctl监听到了fd发生了什么事件,channel类会封装每个fd和fd感兴趣的事…

036—pandas 按行将列名根据值由大到小排序

前言 数据处理中&#xff0c;按行排列的列名可以提供更直观的数据探索和分析方式。 你可以逐行查看列名&#xff0c;了解每列的含义和特征&#xff0c;有助于更好地理解数据集的结构和内容。 需求&#xff1a; 需要增加一列「分布方式」&#xff0c;每行的值是本行基金名称对…

C++多线程:thread构造源码剖析与detach大坑(三)

1、thread源码浅剖析 基于Ubuntu18.04版本64位操作系统下进行分析thread源码分析&#xff0c;与Window或者其他版本可能有出入。 1.1、thread线程id的源头 typedef pthread_t __gthread_t; typedef __gthread_t native_handle_type;/// thread::id class id {native_handl…

常用类(日期时间)

目录 一、JDK 8之前的日期时间API1.1、System类中获取时间戳的方法1.2、Java中两个Date类的使用1.3、SimpleDateFormat的使用1.4、Calendar日历类的使用 二、JDK8中日期时间API的介绍2.1、LocalDate、LocalTime、LocalDateTime的使用2.2、Instant类的使用2.3、DateTimeFormatte…

Abaqus模拟新能源汽车电池理论概念

在新能源汽车电池的分析过程中&#xff0c;存在众多典型问题&#xff0c;这些问题跨越了机械、热管理和电气三大关键领域。其中&#xff0c;结构仿真分析作为一种重要的技术手段&#xff0c;主要聚焦于解决机械和热管理方面的挑战&#xff0c;为电池系统的性能优化和安全性提升…

集合(未完。。。)

集合 例题引入1.java集合引入2.为什么要使用集合&#xff1f;3.List、Set、Queue和Map的区别4.ListList——ArrayList&#xff08;&#xff01;&#xff01;实用&#xff01;&#xff01;&#xff09;ArrayList常用方法 List——VectorList——LinkedList 5.Set6.MapHashMapHas…