ADC基本功能
基本功能框图
STM32G431内部集成了2个最高12位的ADC(ADC1和ADC2)。在对这两个ADC配置的时候,其精度可以配置为:六位、八位、十位、十二位。
转换电压的范围是:0~Vref+;(一般都是接到3.3v电压上)。
一共有19个转换通道:分别为16个外部通道(IO引脚)和3个内部通道。
其采样时间、扫描方向都可以进行软件配置。
ADC的两个通道
在使用的开发板上,某外部引脚可以选择用ADC1或者是ADC2进行转换。ADC1和ADC2对应的通道和引脚如下图所示:
ADC通道一共分为两种,一种是规则通道,另外一种是注入通道。规则通道就是像字面意思一样,规规矩矩的按着顺序来转换,我们平时用到的也是这种通道。注入通道就相当于优先级更高的通道,他的原理和规则通道是一样的,只是在转换的过程中,它可以“插队”在规则通道当中。
通道转换时序
通道转换的时序图如下所示:
触发源
在底层配置的时候,对于触发源来说;
使用控制寄存器启动时,很简单,为ADON位写1开始转换;写0停止转换。
使用外部事件来触发转换,
这个触发包括内部定时器触发和外部I0触发。
触发源的选择由ADC_ CFGR的EXTSEL[3:0]和ADC_ JSQR的JEXTSEL[3:0]位 来控制,
EXTSEL[3:0]用于规则通道的触发源,
JEXTSEL[3:0]用于选择注入通道的触发源。
选择好触发源后,控制EXTTRIG和JEXTTRIG这两位来激活触发源。
采样和转换的时间
ADC_ SMPR1和ADC_ SMPR2的SMP[2:0]位设置每个通道可以分别设置成不同的时间进行采样
采样的周期最小是3个
周期等于1/ADC _CLK
TCONV=采样时间+12个周期
当ADC _CLK=30MHz,ADC为3周期,那么总的转换时间为: TCONV=3+12=15个周期=0.5uS。
数据寄存器
注入数据寄存器是4*16位,规则数据寄存器是16位,这就意味着,规则数据寄存器要一个转换结果一个转换结果的等待,而注入数据寄存器就可以一次性存四个转换结果。
对于转换结果来说,是12位的,这样的话在寄存器中就出现了左对齐和右对齐。
模拟看门狗和中断
此部分具体内容如下图所示:
ADC工作模式
ADC的工作模式大体上可以分为八种;
1.单次转换模式:在单词转换模式下,ADC执行一次转换。
2.不连续采样模式(触发一次,n个转换)
对于这种模式示例如下所示:
3.常规扫描模式(连续采样)
每次转换结果后,会自动转换该组中的下一个通道。
4.注入转换扫描模式
5.DMA应用模式
6.多重ADC同时模式(一次触发,多个ADC同时转换通道数据)
7.多重ADC交替模式(两个/三个ADC交替对一个通道采样)
8.多重ADC交替触发模式(两个/三个ADC交替对序列采样)
ADC程序设计
在本次的ADC程序设计中,是改变电位器组织,使得电位器两侧电压值发生改变。
//ADC1和ADC2的初始化程序
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();
}
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();
/**ADC1 GPIO Configuration
PB12 ------> ADC1_IN11
*/
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();
/**ADC2 GPIO Configuration
PB15 ------> ADC2_IN15
*/
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
}
初始化完成后,需要对两个通道的电压值进行采集,然后进行转换显示在液晶屏上。
// 获取通道采集值
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;
}
//液晶屏显示
sprintf((unsigned char *)Lcd_Disp_String,"R38_Vol:%6.3fV",getADC1()*3.3/4096);
LCD_DisplayStringLine(Line5,Lcd_Disp_String);
sprintf((unsigned char *)Lcd_Disp_String,"R37_Vol:%6.3fV",getADC2()*3.3/4096);
LCD_DisplayStringLine(Line6,Lcd_Disp_String);
需要注意的是,在时钟配置时需要加上如下代码,在STM32CubeMx中直接会生成
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_ADC12;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}