文章目录
ADC转换时长
STM32芯片数据手册的【Electrical characteristics】章节描述了硬件特性,下面的是F103C8系列硬件ADC原理图:
芯片内部采样电阻 Radc=1KB,Cadc大小为12pF,这两个器件决定了采样时长,官方给出了各电阻的对应关系表:
下面自行计算。
阻容串联,通过电阻对电容充电时,充电时长与电容充电量有如下关系:
当t= RC时,电容电压=0.63E;
当t= 2RC时,电容电压=0.86E;
当t= 3RC时,电容电压=0.95E;
当t= 4RC时,电容电压=0.98E;
当t= 5RC时,电容电压=0.99E;
假定采用分压电阻方式进行采样,则等效电路如下:
电阻R124电流一部分通过分压电阻R128流向地,另外一部分通过Radc向Cadc充电。对于MCU来说,其等效电阻为(R12+R128)/R128*Radc = 6.7K。
不同的输入阻抗对应的采样时长如下:
等效电阻(kohm) | 电容(pF) | 5RC时长(us) | ADC频率(MHz) | ADC采样时长(Tick) |
---|---|---|---|---|
1.5 | 12 | 0.09 | 10.67 | 0.96 |
4.7 | 12 | 0.28 | 10.67 | 3.01 |
6.7 | 12 | 0.40 | 10.67 | 4.29 |
10 | 12 | 0.60 | 10.67 | 6.40 |
15 | 12 | 0.90 | 10.67 | 9.60 |
20 | 12 | 1.20 | 10.67 | 12.80 |
47 | 12 | 2.82 | 10.67 | 30.09 |
本例中ADC采样时长为4.3Tick,所以最后使用 7.5Tick。
STM32 ADC功能
STM32提供16通道ADC采样,这16个通道可以通过划分到两种组中:
-
规则通道组:相当正常运行的程序,最多16个通道。规则通道和它的转换顺序在ADC_SQRx寄存器中选择,规则组转换的总数应写入ADC_SQR1寄存器的L[3:0]中。规则通道转换出来的数据统一存放在 ADC_DR 寄存器中,转换完成后需要立即将数据取走,否则就会被下次转换覆盖。所以一般使用规则组时,均是配合DMA一起使用。
-
注入通道组:相当于中断,最多4个通道。注入组和它的转换顺序在ADC_JSQR寄存器中选择。注入组里转化的总数应写入ADC_JSQR寄存器的L[1:0]中。注入通道转换后的数据存放在专有寄存器中(ADC_JDR1~ADC_JDR4),用户可以在收到注入组转换完成的中断中将数据取出来。
规则组和注入组的优先顺序不同,注入组的转换可以打断规则组转换,下图中左侧是规则组转换顺序,右图为注入组抢占规则组转换:
(图来自网络,找不到出处了,抱歉~~)
规则组转换范例
本例使用规则组进行转换,先创建创建ADC转换所需数据:
ADC_HandleTypeDef adcHandle;
static uint16_t adcConvData[M_AdcMaxChannels] = {0};
ADCInit
1、首先初始化ADC:
adcHandle.Instance = ADC1;
adcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
adcHandle.Init.ScanConvMode = ADC_SCAN_ENABLE;
adcHandle.Init.ContinuousConvMode = DISABLE;
adcHandle.Init.DiscontinuousConvMode= DISABLE;
adcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
adcHandle.Init.NbrOfConversion = M_AdcRegularChannels;
/* Initialize ADC peripheral according to the passed parameters */
HAL_ADC_Init(&adcHandle);
其中:
- ExternalTrigConv表示触发模式,本例使用软件触发:ADC_SOFTWARE_START;
- ScanConvMode 表示是否连续转换多通道,当设置为 ENABLE 时,ADC会一次性将规则组中的所有通道依次进行转换,如果设置为DISABLE则仅转换通道1;
- ContinuousConvMode 表示是否进行连续转换,如果用户希望启动完成后,ADC转换完成就开启下次转换,则将此字段设置为ENABLE,如果希望仅转换一次,那就设置为DISABLE;
- DiscontinuousConvMode 表示规则组内多个通道之间是否停顿,如果设置为ENABLE,则触发一次转换就转换一个通道;
- NbrOfConversion 表示规则组中转换的通道数。
2、设置规则组通道
将通道加入到规则组中,每个通道占一个BANK,通道数量与前文指定的 NbrOfConversion 参数一致:
ADC_ChannelConfTypeDef sConfig = {0};
uint32_t bank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; // 温度传感器采样时长推荐17.1us
sConfig.Rank = bank++;
HAL_ADC_ConfigChannel(&adcHandle, &sConfig);
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = bank++;
HAL_ADC_ConfigChannel(&adcHandle, &sConfig);
3、启动校准
ADC需要执行校准:
HAL_ADCEx_Calibration_Start(&adcHandle);
4、启动转换:
本文使用了DMA传输数据,通过下面的代码启动ADC转换:
HAL_ADC_Start_DMA(&adcHandle, (uint32_t *)adcConvData, M_AdcRegularChannels);
注意,HAL_ADC_Start_DMA 函数的最后一个参数表示ADC转换次数,不是缓冲区的大小。
完整代码:
void ADCInit(void)
{
adcHandle.Instance = ADC1;
adcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* Right-alignment for converted data */
adcHandle.Init.ScanConvMode = ADC_SCAN_ENABLE;
adcHandle.Init.ContinuousConvMode = DISABLE;
adcHandle.Init.DiscontinuousConvMode= DISABLE;
adcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
adcHandle.Init.NbrOfConversion = M_AdcRegularChannels;
/* Initialize ADC peripheral according to the passed parameters */
HAL_ADC_Init(&adcHandle);
/* ### Regular Channel configuration ######################################## */
ADC_ChannelConfTypeDef sConfig = {0};
uint32_t bank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; // 温度传感器采样时长推荐17.1us
sConfig.Rank = bank++;
HAL_ADC_ConfigChannel(&adcHandle, &sConfig);
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = bank++;
HAL_ADC_ConfigChannel(&adcHandle, &sConfig);
/* ### Start calibration ############################################ */
HAL_ADCEx_Calibration_Start(&adcHandle);
/* ### Start Regular conversion in DMA mode ################################# */
HAL_ADC_Start_DMA(&adcHandle, (uint32_t *)adcConvData, M_AdcRegularChannels);
}
MSP Init
使用HAL库时,执行ADC初始化时HAL会调用用户定义的 HAL_ADC_MspInit 函数,此函数用于初始化ADC使用到的端口资源、DMA配置等,下面是完整代码:
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
static DMA_HandleTypeDef DmaHandle;
/*## Enable peripherals and GPIO Clocks #################################*/
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
/*## Configure peripheral GPIO #########################################*/
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*## Configure DMA #####################################################*/
/*********************** Configure DMA parameters ***************************/
DmaHandle.Instance = DMA1_Channel1;
DmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;
DmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
DmaHandle.Init.MemInc = DMA_MINC_ENABLE;
DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
DmaHandle.Init.Mode = DMA_CIRCULAR;
DmaHandle.Init.Priority = DMA_PRIORITY_MEDIUM;
HAL_DMA_DeInit(&DmaHandle);
HAL_DMA_Init(&DmaHandle);
/* Associate the DMA handle */
__HAL_LINKDMA(hadc, DMA_Handle, DmaHandle);
/* NVIC configuration for DMA Input data interrupt */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, ADCDMA_IRQ_PRIO, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
需要说明的是DMAp配置:
- DmaHandle.Init.Direction 表示数据传输方向,本例是从外设到内存,取值 DMA_PERIPH_TO_MEMORY;
- DmaHandle.Init.PeriphInc 和 DmaHandle.Init.MemInc 表示传输完成后,外设和内存地址是否增加,内存需要增加,外设不需要;
- DmaHandle.Init.PeriphDataAlignment 表示外设数据对齐,ADC数据是12比特,所以对应的对齐模式为 DMA_PDATAALIGN_HALFWORD,内存对齐模式也是同样的要求;
- DmaHandle.Init.Mode 表示向内存写入数据的模式,DMA_CIRCULAR表示循环覆盖。
中断函数
中断函数比较简单,统一交给HAL库处理就行了,用户的代码全部写到回调中:
extern ADC_HandleTypeDef adcHandle;
void DMA1_Channel1_IRQHandler(void)
{
HAL_DMA_IRQHandler(adcHandle.DMA_Handle);
}
中断回调
DMA中断支持两种回调:
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc);
第一个是转换完成回调,第二个是转换一半回调。
如何理解?
我们在调用开始ADC转换是给定了数据长度 M_AdcRegularChannels,这个参数告诉DMA此内存有多少空间可以存储数据。
HAL_ADC_Start_DMA(&adcHandle, (uint32_t *)adcConvData, M_AdcRegularChannels);
以本次转换为例,规则通道中有2个数据需要转换,那么我们准备的缓冲器只要能够保存2个HALFWORD就可以了。如果是这样,DMA传输完成后会直接调用 HAL_ADC_ConvCpltCallback
完成回调。
某些场景下,我们希望将缓存区开的大一点,例如缓存区可以保存4个HALFWORD,也就是DMA需要两次传输才能将缓存区填满。DMA在完成第一次转换后,将数据填充到了前两个HALFWORD,此时刚好到一半,DMA会调用 HAL_ADC_ConvHalfCpltCallback
通知用户处理。下一轮ADC转换后,DMA会将数据填充到后两个HALFWORD,完成后会调用 HAL_ADC_ConvCpltCallback
回调。
也就是当用户需要双缓冲器乒乓操作时,这种方式非常方便。
注入组转换范例
注入组与规则组略有差异,直接参考代码:
ADCInit
1、首先初始化ADC:
adcHandle.Instance = ADC1;
adcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
adcHandle.Init.ScanConvMode = ADC_SCAN_ENABLE;
adcHandle.Init.ContinuousConvMode = DISABLE;
adcHandle.Init.DiscontinuousConvMode= DISABLE;
adcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
adcHandle.Init.NbrOfConversion = M_AdcRegularChannels;
/* Initialize ADC peripheral according to the passed parameters */
HAL_ADC_Init(&adcHandle);
这里的 NbrOfConversion 不是必须的了,可以填写为0。
2、设置注入组通道
将通道加入到注入组中,每个通道占一个BANK,通道数量写到 sConfigInjected.InjectedNbrOfConversio 参数中:
ADC_InjectionConfTypeDef sConfigInjected = {0};
bank = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_7CYCLES_5;
sConfigInjected.InjectedOffset = 0;
sConfigInjected.InjectedNbrOfConversion = M_AdcInjectChannels;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.ExternalTrigInjecConv = ADC_INJECTED_SOFTWARE_START;
sConfigInjected.InjectedChannel = ADC_CHANNEL_6;
sConfigInjected.InjectedRank = bank++;
HAL_ADCEx_InjectedConfigChannel(&adcHandle, &sConfigInjected);
sConfigInjected.InjectedChannel = ADC_CHANNEL_5;
sConfigInjected.InjectedRank = bank++;
HAL_ADCEx_InjectedConfigChannel(&adcHandle, &sConfigInjected);
3、启动校准
ADC需要执行校准,务必在配置完通道后再执行,否则ADC无法产生中断,可能是HAL库的问题:
HAL_ADCEx_Calibration_Start(&adcHandle);
4、启动转换:
注入组的启动函数与规则组不同:
HAL_ADCEx_InjectedStart_IT(&adcHandle);
下面是完整的代码:
void ADCInit(void)
{
adcHandle.Instance = ADC1;
adcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* Right-alignment for converted data */
adcHandle.Init.ScanConvMode = ADC_SCAN_ENABLE;
adcHandle.Init.ContinuousConvMode = DISABLE;
adcHandle.Init.DiscontinuousConvMode= DISABLE;
adcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
adcHandle.Init.NbrOfConversion = M_AdcRegularChannels;
/* Initialize ADC peripheral according to the passed parameters */
HAL_ADC_Init(&adcHandle);
/* ### Inject Channel configuration ######################################## */
ADC_InjectionConfTypeDef sConfigInjected = {0};
bank = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_7CYCLES_5;
sConfigInjected.InjectedOffset = 0;
sConfigInjected.InjectedNbrOfConversion = M_AdcInjectChannels;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.ExternalTrigInjecConv = ADC_INJECTED_SOFTWARE_START;
sConfigInjected.InjectedChannel = ADC_CHANNEL_6;
sConfigInjected.InjectedRank = bank++;
HAL_ADCEx_InjectedConfigChannel(&adcHandle, &sConfigInjected);
sConfigInjected.InjectedChannel = ADC_CHANNEL_5;
sConfigInjected.InjectedRank = bank++;
HAL_ADCEx_InjectedConfigChannel(&adcHandle, &sConfigInjected);
/* ### Start calibration ############################################ */
HAL_ADCEx_Calibration_Start(&adcHandle);
/* ### Start Regular conversion in DMA mode ################################# */
HAL_ADC_Start_DMA(&adcHandle, (uint32_t *)adcConvData, M_AdcRegularChannels);
}
MSP Init
完整代码:
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
static DMA_HandleTypeDef DmaHandle;
/*## Enable peripherals and GPIO Clocks #################################*/
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
/*## Configure peripheral GPIO #########################################*/
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* ADC中断 */
HAL_NVIC_SetPriority(ADC1_2_IRQn, ADC_IRQ_PRIO, 0);
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
}
与规则组最大的差异在于这里不需要开启DMA了,而是使用ADC中断。
中断函数
中断函数比较简单,统一交给HAL库处理就行了,用户的代码全部写到回调中:
extern ADC_HandleTypeDef adcHandle;
void ADC1_2_IRQHandler(void)
{
HAL_ADC_IRQHandler(&adcHandle);
}
中断回调
注入中断回调函数与规则回调不同,在此函数中,用户可以通过 HAL_ADCEx_InjectedGetValue
获取到已经转换得到的数据:
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc)
{
adcInjectConvTimes++;
for (uint32_t i=0; i<M_AdcInjectChannels; i++)
adcConvData[M_AdcRegularChannels+i] = HAL_ADCEx_InjectedGetValue(hadc, i+1);
}
双ADC模式
STM32系列MCU支持双ADC模式。
ADCInit
下面配置有个问题,注入组工作没有问题,但是规则组无法工作,目前没有查到原因。
1、初始化ADC1,设置双ADC模式
ADC_MultiModeTypeDef MultiModeInit;
/* ### - 1 - 初始化主ADC1 ######################################## */
adc1Handle.Instance = ADC1;
adc1Handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* Right-alignment for converted data */
adc1Handle.Init.ScanConvMode = ADC_SCAN_ENABLE;
adc1Handle.Init.ContinuousConvMode = DISABLE;
adc1Handle.Init.DiscontinuousConvMode = DISABLE;
adc1Handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGINJECCONV_T1_CC4;
adc1Handle.Init.NbrOfConversion = M_AdcRegularChannels;
HAL_ADC_Init(&adc1Handle);
/* ### - 1.1 - 设置使用双ADC模式 ######################################## */
MultiModeInit.Mode = ADC_DUALMODE_REGSIMULT_INJECSIMULT;
HAL_ADCEx_MultiModeConfigChannel(&adc1Handle, &MultiModeInit);
关键参数说明:
- adc1Handle.Init.ExternalTrigConv 设置为 ADC_EXTERNALTRIGINJECCONV_T1_CC4,即由T1的CC4触发;
- 双ADC模式设置为 ADC_DUALMODE_REGSIMULT_INJECSIMULT,即 【Combined regular/injected simultaneous mode】,此模式支持规则组和注入组双模式。
2、初始化ADC2
/* ### - 2 - 初始化从ADC2 ######################################## */
adc2Handle.Instance = ADC2;
adc2Handle.Init = adc1Handle.Init; /* Same configuration as ADC master */
adc2Handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 必须使用软触发
HAL_ADC_Init(&adc2Handle);
所有的初始化配置均拷贝自ADC1,唯一的差异就是 ADC2 的ExternalTrigConv必须设置为软件触发!
3、初始化规则组
/* ### - 3 - 初始化规则组 ######################################## */
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.SamplingTime = ADC_CONVERSIONCLOCKCYCLES_SAMPLETIME_239CYCLES5;
sConfig.Channel = ADC_CH_Tempr; // 温度传感器采样时长推荐17.1us
sConfig.Rank = ADC_REGULAR_RANK_1;
HAL_ADC_ConfigChannel(&adc1Handle, &sConfig);
sConfig.Channel = ADC_CH_Voltage;
HAL_ADC_ConfigChannel(&adc2Handle, &sConfig);
ADC1和ADC2规则组内的通道数量保持一致,参数也要一致!
4、初始化注入组
与初始化规则组非常类似:
/* ### - 4 - 初始化注入组 ######################################## */
ADC_InjectionConfTypeDef sConfigInjected = {0};
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_7CYCLES_5;
sConfigInjected.InjectedOffset = 0;
sConfigInjected.InjectedNbrOfConversion = M_AdcInjectChannels;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_CC4;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_7CYCLES_5;
sConfigInjected.InjectedChannel = ADC_CH_PhaseA;
sConfigInjected.InjectedRank = ADC_REGULAR_RANK_1;
HAL_ADCEx_InjectedConfigChannel(&adc1Handle, &sConfigInjected);
sConfigInjected.InjectedRank = ADC_REGULAR_RANK_1;
sConfigInjected.InjectedChannel = ADC_CH_PhaseB;
HAL_ADCEx_InjectedConfigChannel(&adc2Handle, &sConfigInjected);
...
5、启动校准
/* ### - 5 - 开始校准 ############################################ */
HAL_ADCEx_Calibration_Start(&adc1Handle);
HAL_ADCEx_Calibration_Start(&adc2Handle);
6、启动ADC2
ADC2需要提前开启,后续启动ADC1后会自动触发ADC2的转换:
/* ### - 6 - 启动从ADC2 ############################################ */
HAL_ADC_Start(&adc2Handle); // 启动ADC2
HAL_ADCEx_InjectedStart(&adc2Handle); // 启动ADC2
7、开启注入模式转换
/* ### - 7 - 启动注入组转换,中断模式 ################################# */
HAL_ADCEx_InjectedStart_IT(&adc1Handle);
8、开启规则模式转换
不知为何,上述配置规则组不生效,得到的数据是错误的!
HAL_ADCEx_MultiModeStart_DMA(&adc1Handle, (uint32_t *)adcRegularConvData, M_AdcRegularChannels);
完整代码如下:
void ADCInit(void)
{
printf("adc init.\n");
ADC_MultiModeTypeDef MultiModeInit;
/* ### - 1 - 初始化主ADC1 ######################################## */
adc1Handle.Instance = ADC1;
adc1Handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* Right-alignment for converted data */
adc1Handle.Init.ScanConvMode = ADC_SCAN_ENABLE;
adc1Handle.Init.ContinuousConvMode = DISABLE;
adc1Handle.Init.DiscontinuousConvMode = DISABLE;
adc1Handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGINJECCONV_T1_CC4;
adc1Handle.Init.NbrOfConversion = M_AdcRegularChannels;
HAL_ADC_Init(&adc1Handle);
/* ### - 1.1 - 设置使用双ADC模式 ######################################## */
MultiModeInit.Mode = ADC_DUALMODE_REGSIMULT_INJECSIMULT;
HAL_ADCEx_MultiModeConfigChannel(&adc1Handle, &MultiModeInit);
/* ### - 2 - 初始化从ADC2 ######################################## */
adc2Handle.Instance = ADC2;
adc2Handle.Init = adc1Handle.Init; /* Same configuration as ADC master */
adc2Handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 必须使用软触发
HAL_ADC_Init(&adc2Handle);
/* ### - 3 - 初始化规则组 ######################################## */
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.SamplingTime = ADC_CONVERSIONCLOCKCYCLES_SAMPLETIME_239CYCLES5;
sConfig.Channel = ADC_CH_Tempr; // 温度传感器采样时长推荐17.1us
sConfig.Rank = ADC_REGULAR_RANK_1;
HAL_ADC_ConfigChannel(&adc1Handle, &sConfig);
sConfig.Channel = ADC_CH_Voltage;
HAL_ADC_ConfigChannel(&adc2Handle, &sConfig);
/* ### - 4 - 初始化注入组 ######################################## */
ADC_InjectionConfTypeDef sConfigInjected = {0};
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_7CYCLES_5;
sConfigInjected.InjectedOffset = 0;
sConfigInjected.InjectedNbrOfConversion = M_AdcInjectChannels;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_CC4;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_7CYCLES_5;
sConfigInjected.InjectedChannel = ADC_CH_PhaseA;
sConfigInjected.InjectedRank = ADC_REGULAR_RANK_1;
HAL_ADCEx_InjectedConfigChannel(&adc1Handle, &sConfigInjected);
sConfigInjected.InjectedRank = ADC_REGULAR_RANK_1;
sConfigInjected.InjectedChannel = ADC_CH_PhaseB;
HAL_ADCEx_InjectedConfigChannel(&adc2Handle, &sConfigInjected);
sConfigInjected.InjectedChannel = ADC_CH_PhaseC;
sConfigInjected.InjectedRank = ADC_REGULAR_RANK_2;
HAL_ADCEx_InjectedConfigChannel(&adc1Handle, &sConfigInjected);
sConfigInjected.InjectedChannel = ADC_CH_Current;
sConfigInjected.InjectedRank = ADC_REGULAR_RANK_2;
HAL_ADCEx_InjectedConfigChannel(&adc2Handle, &sConfigInjected);
sConfigInjected.InjectedChannel = ADC_CH_Ref;
sConfigInjected.InjectedRank = ADC_REGULAR_RANK_3;
HAL_ADCEx_InjectedConfigChannel(&adc1Handle, &sConfigInjected);
sConfigInjected.InjectedRank = ADC_REGULAR_RANK_3;
sConfigInjected.InjectedChannel = ADC_CH_Voltage;
HAL_ADCEx_InjectedConfigChannel(&adc2Handle, &sConfigInjected);
/* ### - 5 - 开始校准 ############################################ */
HAL_ADCEx_Calibration_Start(&adc1Handle);
HAL_ADCEx_Calibration_Start(&adc2Handle);
/* ### - 6 - 启动从ADC2 ############################################ */
HAL_ADC_Start(&adc2Handle); // 启动ADC2
HAL_ADCEx_InjectedStart(&adc2Handle); // 启动ADC2
/* ### - 7 - 启动注入组转换,中断模式 ################################# */
HAL_ADCEx_InjectedStart_IT(&adc1Handle);
}
MSP Init
是规则组和注入组范例的融合。
完整代码如下:
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
static DMA_HandleTypeDef DmaHandle;
if (hadc->Instance==ADC1) {
/*##-1- Enable peripherals and GPIO Clocks #################################*/
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_ADC2_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
/*##- 2- Configure peripheral GPIO #########################################*/
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*##- 3- Configure DMA #####################################################*/
/*********************** Configure DMA parameters ***************************/
DmaHandle.Instance = DMA1_Channel1;
DmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;
DmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
DmaHandle.Init.MemInc = DMA_MINC_ENABLE;
DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
DmaHandle.Init.MemDataAlignment = DMA_PDATAALIGN_WORD;
DmaHandle.Init.Mode = DMA_CIRCULAR;
DmaHandle.Init.Priority = DMA_PRIORITY_MEDIUM;
HAL_DMA_DeInit(&DmaHandle);
HAL_DMA_Init(&DmaHandle);
/* Associate the DMA handle */
__HAL_LINKDMA(hadc, DMA_Handle, DmaHandle);
/* NVIC configuration for DMA Input data interrupt */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, ADCDMA_IRQ_PRIO, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* ADC中断 */
HAL_NVIC_SetPriority(ADC1_2_IRQn, ADC_IRQ_PRIO, 0);
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
}
}
中断&中断回调
中断函数和中断回调也是结合了规则组和注入组的配置。