目录
前言
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. 测试
将代码进行编译并下载到开发板上。效果如下图所示。
至此,本节就大功告成了!