天天看點

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

文章目錄

  • 1. 所用硬體
  • 2. 生成工程
    • 2.1. 建立工程選擇主要
    • 2.2. 系統配置
    • 2.3. 配置工程目錄
    • 2.4. 配置用到的外設
  • 3. ADC配置(四選一)
    • 3.1. 單通道輪詢
    • 3.2. 單通道中斷
    • 3.3. 多通道輪詢
    • 3.4. DMA模式

====>>> 文章彙總(有代碼彙總) <<<====

1. 所用硬體

正點原子Mini闆,主要 STM32F103RCT6.

用到的外設:

  1. 序列槽1(PA9、PA10)
  2. 任意幾個GPIO口(這裡用PA1、PA2、PA3,對應ADC通道1、2、3)。

2. 生成工程

2.1. 建立工程選擇主要

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

2.2. 系統配置

配置時鐘源

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

配置debug模式(如果需要ST-Link下載下傳及調試可以勾選)

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

配置時鐘樹(可以直接在HCLK那裡輸入72,然後敲回車會自動配置)

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)
注意最後的ADC時鐘,時鐘頻率最大14MHZ,是以這裡設定6分頻,剛好小于14。

2.3. 配置工程目錄

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)
STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

2.4. 配置用到的外設

序列槽1配置(用于輸出結果)

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)
STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

3. ADC配置(四選一)

有如下情況:

  1. 單通道輪詢
  2. 單通道中斷
  3. 多通道輪詢
  4. DMA模式(單通道、多通道都能用)

設定說明:

  • ADC_Settings:
    • Data Alignment:
      • Right alignment

        :轉換結果資料右對齊,一般我們選擇右對齊模式。
      • Left alignment 轉換結果資料左對齊。
    • Scan Conversion Mode:
      • Disabled 禁止掃描模式。如果是單通道 AD 轉換使用 DISABLE。
      • Enabled 開啟掃描模式

        。如果是多通道 AD 轉換使用 ENABLE。
    • Continuous Conversion Mode:
      • Disabled 單次轉換

        。轉換一次後停止需要手動控制才重新啟動轉換。
      • Enabled 自動連續轉換。
    • DiscontinuousConvMode:
      • Disabled 禁止間斷模式

        。這個在需要考慮功耗問題的産品中很有必要,也就是在某個事件觸發下,開啟轉換。
      • Enabled 開啟間斷模式。
  • ADC_Regular_ConversionMode:
    • Enable Regular Conversions

      是否使能規則轉換。
    • Number Of Conversion

      ADC轉換通道數目,有幾個寫幾個就行。
    • External Trigger Conversion Source

      外部觸發選擇。這個有多個選擇,一般采用軟體觸發方式。
  • Rank:
    • Channel

      ADC 轉換通道
    • Sampling Time

      采樣周期選擇,采樣周期越短,ADC 轉換資料輸出周期就越短但資料精度也越低,采樣周期越長,ADC 轉換資料輸出周期就越長同時資料精度越高。
  • ADC_Injected_ConversionMode:

    Enable Injected Conversions 是否使能注入轉換。注入通道隻有在規則通道存在時才會出現。

  • WatchDog:Enable Analog WatchDog Mode 是否使能模拟看門狗中斷。當被 ADC 轉換的模拟電壓低于低門檻值或者高于高門檻值時,就會産生中斷。

3.1. 單通道輪詢

第一步:配置ADC

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

第二步:點選生成代碼

第三步:序列槽重定向,在usart.c中添加如下代碼。具體的參考上一篇文章序列槽使用

// 需要調用stdio.h檔案
#include <stdio.h>
//取消ARM的半主機工作模式
#pragma import(__use_no_semihosting)//标準庫需要的支援函數                 
struct __FILE 
{ 
	int handle; 
}; 
FILE __stdout;       
void _sys_exit(int x) //定義_sys_exit()以避免使用半主機模式
{ 
	x = x;
} 

int fputc(int ch, FILE *f)
{  
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
	return ch;
}
           

第四步:編寫 main.c 代碼 其他的什麼都不用改

while (1)
  {
		// 開啟ADC
		HAL_ADC_Start(&hadc1);
		// 開始輪詢轉換
		HAL_ADC_PollForConversion(&hadc1,100);
		// 存儲轉換的值
		float value = 0;
		// 查詢ADC狀态
		uint32_t state = HAL_ADC_GetState(&hadc1);
		if (( state & HAL_ADC_STATE_REG_EOC) == HAL_ADC_STATE_REG_EOC)
		{
			// 擷取ADC轉換結果
			value = HAL_ADC_GetValue(&hadc1);
			printf("adc value:%f \r\n",value/4096.0*3.3);
		}
		else
		{
			printf("adc state %d \r\n",state);
		}
		// 關閉ADC
		HAL_ADC_Stop(&hadc1);
		HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
           

效果驗證

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)
單通道輪詢 在轉換時會阻塞直到轉換完成。

3.2. 單通道中斷

第一步:配置上:在“單通道輪詢”實作配置基礎上再打開ADC全局中斷。

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

第二步:點選生成代碼

第三步:序列槽重定向,在usart.c中添加如下代碼。具體的參考上一篇文章序列槽使用

// 需要調用stdio.h檔案
#include <stdio.h>
//取消ARM的半主機工作模式
#pragma import(__use_no_semihosting)//标準庫需要的支援函數                 
struct __FILE 
{ 
	int handle; 
}; 
FILE __stdout;       
void _sys_exit(int x) //定義_sys_exit()以避免使用半主機模式
{ 
	x = x;
} 

int fputc(int ch, FILE *f)
{  
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
	return ch;
}
           

第四步:編寫 main.c 代碼

生成後檢視代碼,在

stm32f1xx_it.c

檔案中有 ADC1通道2的中斷函數

ADC1_2_IRQHandler

,這個中斷函數又調用了

HAL_ADC_IRQHandler(&hadc1);

/**
  * @brief This function handles ADC1 and ADC2 global interrupts.
  */
void ADC1_2_IRQHandler(void)
{
  /* USER CODE BEGIN ADC1_2_IRQn 0 */

  /* USER CODE END ADC1_2_IRQn 0 */
  HAL_ADC_IRQHandler(&hadc1);
  /* USER CODE BEGIN ADC1_2_IRQn 1 */

  /* USER CODE END ADC1_2_IRQn 1 */
}
           

HAL_ADC_IRQHandler(&hadc1);

函數在

stm32f1xx_hal_adc.c

中,這個函數考慮了很多情況,其中調用了

HAL_ADC_ConvCpltCallback(hadc);

,還是在同一個檔案中,這是一個弱函數。根據翻譯,很好了解,我們直接重新定義這個方法即可。

/**
  * @brief  Conversion complete callback in non blocking mode 
  * @param  hadc: ADC handle
  * @retval None
  */
__weak void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hadc);
  /* NOTE : This function should not be modified. When the callback is needed,
            function HAL_ADC_ConvCpltCallback must be implemented in the user file.
   */
}
           

main.c

/* USER CODE BEGIN PFP */
// 重定義ADC轉換完成回調函數
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	if(hadc == &hadc1)
	{
		uint16_t adc_value = HAL_ADC_GetValue(hadc);
		printf("refresh adc value:%f \r\n", adc_value/4096.0*3.3);
		// 重新開啟ADC中斷
		HAL_ADC_Start_IT(&hadc1);
	}
}
/* USER CODE END PFP */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  
  /* USER CODE BEGIN WHILE */
	// 開啟ADC中斷
	HAL_ADC_Start_IT(&hadc1);
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
           

效果和輪詢的一樣,不過這個會一直執行,而且很快

實際上,這裡設定的是單次轉換,是以中斷隻會觸發一次,需要再次使用HAL_ADC_Start_IT開啟中斷。如果需要實時的轉換,可以将轉換設為連續模式,這樣的話ADC轉換器便會實時的持續的進行轉換,那将是非常消耗CPU的,以至于main将不能正常執行(采樣時間太短的話)。
開啟中斷後,一般需要實作HAL_ADC_ConvCpltCallback函數,在callback中GetValue,也可以在程式其他地方像輪詢那樣先判斷ADC狀态,再GetValue。

3.3. 多通道輪詢

第一步:ADC配置

多通道時掃描模式會自動打開。要開啟“Discontinuous Conversion Mode”。

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

第二步:點選生成代碼

第三步:序列槽重定向,在usart.c中添加如下代碼。具體的參考上一篇文章序列槽使用

// 需要調用stdio.h檔案
#include <stdio.h>
//取消ARM的半主機工作模式
#pragma import(__use_no_semihosting)//标準庫需要的支援函數                 
struct __FILE 
{ 
	int handle; 
}; 
FILE __stdout;       
void _sys_exit(int x) //定義_sys_exit()以避免使用半主機模式
{ 
	x = x;
} 

int fputc(int ch, FILE *f)
{  
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
	return ch;
}
           

第四步:編寫 main.c 代碼

HAL_ADCEx_Calibration_Start(&hadc1);
const uint8_t kNbrOfPin = 3;
  while (1)
  {
	for(int i = 0; i < kNbrOfPin; i++)
	{
		HAL_ADC_Start(&hadc1);
		HAL_ADC_PollForConversion(&hadc1, 100);
		float value = 0;
		uint32_t state = HAL_ADC_GetState(&hadc1);
		if (( state & HAL_ADC_STATE_REG_EOC) == HAL_ADC_STATE_REG_EOC)
		{
			value = HAL_ADC_GetValue(&hadc1);
			printf("adc value [%d]:%f\r\n", i,value/4096.0*3.3);
		}
		else
		{
			printf("adc state[%d]:%d\r\n", i, state);
		}
	}
	HAL_ADC_Stop(&hadc1);
	HAL_Delay(200);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
           

效果驗證

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

3.4. DMA模式

第一步:ADC配置

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)
STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)

第二步:點選生成代碼

第三步:序列槽重定向,在usart.c中添加如下代碼。具體的參考上一篇文章序列槽使用

// 需要調用stdio.h檔案
#include <stdio.h>
//取消ARM的半主機工作模式
#pragma import(__use_no_semihosting)//标準庫需要的支援函數                 
struct __FILE 
{ 
	int handle; 
}; 
FILE __stdout;       
void _sys_exit(int x) //定義_sys_exit()以避免使用半主機模式
{ 
	x = x;
} 

int fputc(int ch, FILE *f)
{  
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
	return ch;
}
           

第四步:編寫 main.c 代碼

/* USER CODE BEGIN PFP */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	if(hadc == &hadc1)
	{
		// 使用DMA其實也會運作到這裡,也可以将結果在這裡輸出。
		// 當然此函數也可以不寫。
	}
}
/* USER CODE END PFP */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	uint16_t adc_value[3] = {0};
  /* USER CODE END 1 */

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  
  /* Configure the system clock */
  SystemClock_Config();
  
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  
  /* USER CODE BEGIN WHILE */
	HAL_ADCEx_Calibration_Start(&hadc1);
	// enable DMA通道
	// 參數:ADC1、目标緩沖區位址、從ADC外圍裝置傳輸到記憶體的資料長度
	/*
	 * 此處有個大坑,經過測試,DMA中斷非常容易進(具體的不知道)
	 *
	 * 如果ADC采樣周期短的話,一直在執行中斷,
	 * 導緻無法執行主程式,是以會卡死在這個函數裡面出不去。
	 *
	 * 是以,ADC的采用周期需要長一點。
	 */
	HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_value, 3);
	
  while (1)
  {
	printf("-------------------- \r\n");
	printf("adc value[0]:%f \r\n", adc_value[0]/4096.0*3.3);
	printf("adc value[1]:%f \r\n", adc_value[1]/4096.0*3.3);
	printf("adc value[2]:%f \r\n", adc_value[2]/4096.0*3.3);
	HAL_Delay(1000);
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
           

注意

用到 DMA 的外設,MX_DMA_Init(); 一定要在外設初始化前面,比如這裡的 MX_ADC1_Init(); 。

效果驗證

STM32CubeMX系列05——ADC(輪詢、中斷、DMA)1. 所用硬體2. 生成工程3. ADC配置(四選一)
多通道DMA和單通道DMA配置基本相同,隻需注意存儲AD轉換結果的數組,如果有兩個通道,數組長度為2,則每個通道的值分别對應數組的每一位;如果數組長度為2的整數倍,如10,則數組内[0] [2] [4] [6] [8]的值對應其中一個通道的AD值,即存儲了連續采集5次的AD值,這樣可以用多個AD值求平均值。