非中断和非DMA方式可以用于特殊场景,比如其它外设占据DMA负担比较重而CPU又比较清闲的时候。
1. STM32F429的AD转换最高12bit
12位分辨率意味着我们采集电压的精度可以达到:Vref /4096。
采集电压 = Vref * ADC_DR / 4096;
Vref:参考电压
ADC_DR:读取到ADC数据寄存器的值
一般使用3.3V为参考电压,则采集到的电压 = ADC_DR*3.3/4096 。
2. STM32F4的ADC转换不需要做程序校准。
这在HAL库里也可以看出来,官方没有HAL_ADCEx_Calibration_Start();这个校准函数。
3. STM32F4的ADC时钟最大36M。
STM32F1为14M,STM32F4快了2倍多。
4. 两个以上通道必须开启scan扫描模式。
ADC_ScanConvMode = ENABLE。
因为转换结果都在同一个寄存器里,所以单个通道转换完成后必须读走数据再启动转换下一个通道。
5. 不使用DMA不需要开连续方式。
ADC_ContinuousConvMode = DISABLE。
连续转换ENABLE后,也就是只需要启动(触发)转换一次,后面就不用再次启动(触发)就可以连续工作了。
单次转换DISABLE:也就是根据一次转换完后需要再次启动(触发)才能工作。
6. 触发方式一般选用软件触发。
7. 对齐方式一般选右对齐。
8. 通道数就是你要转换的通道数量
通道数要和后面的规格组相符合。
9. 设置规则组通道
下面例子是设置 channel5 和 channel8连个通道,开启触发AD转换后,通道5是首先转换的,转换完成后,再次触发转换,就会自动转换通道8。一轮完成后,不需要STOP ADC,可以直接再次触发转换,此时又回到了转换通道5,开始新的循环。 - /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
- */
- sConfig.Channel = ADC_CHANNEL_5;
- sConfig.Rank = 1;
- sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
- */
- sConfig.Channel = ADC_CHANNEL_8;
- sConfig.Rank = 2;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
复制代码
10. 规则组间断模式配置。
如果不使用DMA,这里必须要配置为1,也就是说每触发一次转换一条通道。否则,结果寄存器里只会是本组最后一次转换的通道电压值。具体可以看下图:
11. 其他ADC的基础配置见下面程序代码。
例子中单片机型号为STM32F429VIT6,代码全部经过实测,通过算法滤波,转换精度足够平常使用。而且有人提出多个通道之间会有干扰,此例未发现。选用的未PA5(ADC channel5)和PB0(ADC channel8)两个通道。
一、ADC配置 - /* ADC1 init function */
- void MX_ADC1_Init(void)
- {
- ADC_ChannelConfTypeDef sConfig;
- /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
- */
- hadc1.Instance = ADC1;
- hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
- hadc1.Init.Resolution = ADC_RESOLUTION_12B;
- hadc1.Init.ScanConvMode = ENABLE;
- hadc1.Init.ContinuousConvMode = DISABLE;
- hadc1.Init.DiscontinuousConvMode = ENABLE;
- hadc1.Init.NbrOfDiscConversion = 1;
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
- hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
- hadc1.Init.NbrOfConversion = 2;
- hadc1.Init.DMAContinuousRequests = DISABLE;
- hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
- */
- sConfig.Channel = ADC_CHANNEL_5;
- sConfig.Rank = 1;
- sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
- */
- sConfig.Channel = ADC_CHANNEL_8;
- sConfig.Rank = 2;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- }
- void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- if(adcHandle->Instance==ADC1)
- {
- /* USER CODE BEGIN ADC1_MspInit 0 */
- /* USER CODE END ADC1_MspInit 0 */
- /* ADC1 clock enable */
- __HAL_RCC_ADC1_CLK_ENABLE();
- /**ADC1 GPIO Configuration
- PA5 ------> ADC1_IN5
- PB0 ------> ADC1_IN8
- */
- GPIO_InitStruct.Pin = GPIO_PIN_5;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- GPIO_InitStruct.Pin = GPIO_PIN_0;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- /* USER CODE BEGIN ADC1_MspInit 1 */
- /* USER CODE END ADC1_MspInit 1 */
- }
- }
复制代码
二、滤波算法处理 - uint16_t adc_values_ch5[14] = {0};
- uint16_t adc_values_ch8[14] = {0};
- /*进行单次AD转换,50ms超时*/
- uint32_t Perform_single_AD_conversion(void)
- {
- HAL_ADC_Start(&hadc1);
- HAL_ADC_PollForConversion(&hadc1, 50);
- uint8_t i=255;
- while (!HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
- {
- i--;
- if (i ==0)
- {
- break; //防止AD死循环
- }
- }
- uint32_t ad_Temp = HAL_ADC_GetValue(&hadc1);
- return ad_Temp;
- }
- /*对14次采样的电压按照从小到大进行排序*/
- void Sort_ADC_Values(uint16_t *adc_nums)
- {
- int i, j, temp, isSorted;
- //优化算法:最多进行 n-1 轮比较
- for(i=0; i<14-1; i++)
- {
- isSorted = 1; //假设剩下的元素已经排序好了
- for(j=0; j<14-1-i; j++)
- {
- if(adc_nums[j] > adc_nums[j+1])
- {
- temp = adc_nums[j];
- adc_nums[j] = adc_nums[j+1];
- adc_nums[j+1] = temp;
- isSorted = 0; //一旦需要交换数组元素,就说明剩下的元素没有排序好
- }
- }
- if(isSorted) break; //如果没有发生交换,说明剩下的元素已经排序好了
- }
- }
- /*去掉2个最大值,2个最小值,剩余10个值求平均数*/
- uint16_t Find_Average_ADC_Values(uint16_t *adc_nums)
- {
- uint16_t sum = 0;
- for(uint8_t i=2; i<12; i++)
- {
- sum = sum + adc_nums[i];
- }
- return sum/10;
- }
- /*软件滤波求出ADC转换平均值*/
- uint16_t Software_filtering_ADC(uint16_t *adc_nums)
- {
- uint16_t adc_avg = 0;
- Sort_ADC_Values(adc_nums);
- adc_avg = Find_Average_ADC_Values(adc_nums);
- return adc_avg;
- }
- void Get_Two_Channel_ADC_Votage(void)
- {
- float votage = 0.0;
- for(uint8_t i=0; i<14; i++)
- {
- adc_values_ch5[i] = Perform_single_AD_conversion();
- printf("\r\nadc_values_ch5[%d] = %d\r\n", i,adc_values_ch5[i]);
- adc_values_ch8[i] = Perform_single_AD_conversion();
- printf("\r\nadc_values_ch8[%d] = %d\r\n", i,adc_values_ch8[i]);
- }
- HAL_ADC_Stop(&hadc1);
- votage = Software_filtering_ADC(adc_values_ch5)*3.3/4096;
- printf("\r\nPA5采集电压: %f \r\n",votage);
-
- votage = Software_filtering_ADC(adc_values_ch8)*3.3/4096;
- printf("\r\nPB0采集电压: %f \r\n",votage);
- }
复制代码
三、打印输出结果
在main函数的主循环while里调用Get_Two_Channel_ADC_Votage(),即可输出打印结果。 - int main(void)
- {
- /* USER CODE BEGIN 1 */
- /* USER CODE END 1 */
- /* MCU Configuration----------------------------------------------------------*/
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
- HAL_Init();
- /* USER CODE BEGIN Init */
- /* USER CODE END Init */
- /* Configure the system clock */
- SystemClock_Config();
- /* USER CODE BEGIN SysInit */
- /* USER CODE END SysInit */
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_DMA_Init();
- MX_IWDG_Init();
- MX_ADC1_Init();
- MX_USART1_UART_Init();
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- Get_Two_Channel_ADC_Votage();
- HAL_Delay(2000);
- /* USER CODE END WHILE */
- /* USER CODE BEGIN 3 */
- HAL_IWDG_Refresh(&hiwdg); //看门狗喂狗
- }
- /* USER CODE END 3 */
- }
复制代码
|