Bootstrap

蓝桥杯嵌入式学习笔记(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. 使用CubeMax进行源工程的配置

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. 测试

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

​ 

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

;