STM32 ADC使用记录

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.5120.0910.670.96
4.7120.2810.673.01
6.7120.4010.674.29
10120.6010.676.40
15120.9010.679.60
20121.2010.6712.80
47122.8210.6730.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);

	}
}

中断&中断回调

中断函数和中断回调也是结合了规则组和注入组的配置。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值