一、基礎知識
1. 模擬信號(Analog?Signal)
- 定義:模擬信號是連續變化的信號,可以取任意數值。
- 特點:幅值和時間都是連續的,沒有“跳變”。
- 舉例:
- 聲音(麥克風采集到的電壓)
- 溫度傳感器輸出的電壓
- 光敏電阻輸出的電壓
- 正弦波、三角波等
2. 數字信號(Digital Signal)
- 定義:數字信號是離散的,只能取有限個特定數值(通常是0和1)。
- 特點:幅值和時間都是“跳變”的,只有高低電平(如3.3V/0V)。
- 舉例:
- 單片機的GPIO輸出
- 計算機中的二進制數據
- 數碼管顯示的數字
3. 轉換器
3.1 ADC(模數轉換器,Analog to?Digital?Converter)
- 作用:把模擬信號轉換為數字信號,讓單片機、計算機等數字系統能夠“理解”模擬世界的信息。
- 舉例:STM32的ADC模塊可以把0~3.3V的電壓轉換為0~4095的數字值(12位ADC)。
3.2 DAC(數模轉換器,Digital to?Analog?Converter)
- 作用:把數字信號轉換為模擬信號,讓數字系統輸出連續變化的電壓或電流。
- 舉例:STM32的DAC模塊可以把數字值(如0~4095)轉換為0~3.3V的模擬電壓,用于音頻輸出、波形發生等。
二、ADC(模數轉換器)?
1. 概念
ADC(Analog?to?Digital Converter,模數轉換器)是將模擬信號(如電壓)轉換為數字信號(如二進制數值)的硬件模塊。
在STM32等單片機中,ADC模塊可以讓你采集外部傳感器、信號源等的模擬電壓,并在程序中進行處理。
2. ADC的主要作用(應用場景)
采集傳感器信號(如溫度、光照、濕度、壓力等)
檢測電池電壓、電流等模擬量
實現音頻采集、波形分析等功能
3. STM32 ADC的基本特性
(1) 多通道:STM32的ADC通常有多個輸入通道(如16、18、24等),可采集多個模擬信號。
(2) 分辨率:常見有14位、12位、10位、8位、6位等,12位ADC的輸出范圍是0~4095。
(3) 采樣速率:支持高速采樣,適合音頻、波形等應用。
(4) 多種采樣模式:單次采樣、連續采樣、掃描模式、間斷模式等。
(5) 支持DMA:可用DMA自動搬運數據,減輕CPU負擔。
(6) 內部通道:可采集芯片內部溫度、參考電壓等。
(7) 轉換時間:是指ADC從開始采樣到輸出數字結果所需的總時間。包含采樣時間(ADC轉采集輸入電壓的時間)和轉換時間(ADC內部將采樣到的模擬信號轉換成數字值的時間)。
(8)量程:能測量的電壓范圍 0 ~ 3.6V(VSS ~VDD) (單片機供電范圍是1.71到3.6)
4.ADC工作原理
(1).?多路輸入與通道選擇
- IN0~IN7:8路模擬輸入信號。
- 通道選擇開關:通過外部的地址線(ADDA、ADDB、ADDC)和地址鎖存允許(ALE)信號,選擇其中一路模擬信號輸入到后續電路。
(2).?地址鎖存和譯碼
- ADDA、ADDB、ADDC:三根地址線,選擇輸入通道(000~111對應IN0~IN7)。
- ALE:地址鎖存允許信號,鎖存當前地址,防止轉換過程中地址變化。
(3).?逐次逼近寄存器(SAR, Successive?Approximation?Register)
- 作用:控制DAC輸出的電壓,逐步逼近輸入的模擬電壓,實現A/D轉換。
- 原理:
- SAR內部有8位寄存器(對應8位ADC)。
- 轉換過程從最高位(MSB)到最低位(LSB)依次判斷每一位是1還是0。
- 每判斷一位,SAR就把該位暫時置1,其余低位為0,然后控制DAC輸出對應的電壓,與輸入模擬電壓比較。
- 如果DAC輸出電壓小于輸入電壓,則該位保留為1;否則清零為0。
- 依次判斷8位,最終得到8位數字量。
(4).?DAC(數模轉換器)
- 作用:把SAR當前寄存器的內容(數字量)轉換成模擬電壓,供比較器與輸入模擬電壓比較。
- 原理:
- DAC接收SAR輸出的8位數字量,輸出對應的模擬電壓。
- 這個電壓和輸入的模擬電壓一起送到比較器。
- 比較器輸出高低電平,反饋給SAR,決定當前位是1還是0。
(5).?逐次逼近A/D轉換過程舉例(八位)
假設要把某一路輸入電壓轉換為數字量,過程如下:
1. SAR先把最高位(D7)設為1,其余為0,DAC輸出對應電壓。
2. 比較器比較:
????????如果DAC電壓?< 輸入電壓,D7保留為1;
????????否則D7清零為0。
3. SAR再把次高位(D6)設為1,其余低位為0,DAC輸出新電壓。
4. 重復比較,依次判斷D6、D5……直到D0。
5. 8位全部判斷完畢,SAR寄存器內容就是輸入電壓的數字表示。
6. 把數據存入八位三態鎖存緩沖器,等待主機獲取。
?5. U5(STM32U575RIT6)的ADC
6. 工作模式
6.1 工作模式劃分
1. 單次轉換模式(Single?Conversion?Mode)
- 說明:ADC只進行一次模數轉換,轉換完成后自動停止。
- 應用:適合對某一通道偶爾采樣的場合,比如需要時才采集一次數據。
2. 連續轉換模式(Continuous?Conversion?Mode)
- 說明:ADC會不斷地進行模數轉換,轉換完成后自動開始下一次轉換,循環往復。
- 應用:適合需要實時、連續采集數據的場合,如電壓、電流實時監測。
3. 掃描轉換模式(Scan?Conversion Mode)
- 說明:ADC可以按照預先設定的通道順序,依次對多個通道進行采樣和轉換。
- 應用:適合多路信號采集,如多傳感器數據采集。
4. 間斷轉換模式(Discontinuous?Conversion?Mode)
- 說明:在掃描模式基礎上,每次只轉換設定數量的通道,然后暫停,等待下次觸發再繼續轉換剩余通道。
- 應用:適合對多通道分批采集,減少瞬時采集壓力。
5. 插隊轉換模式(Injected?Conversion Mode)
- 說明:在常規轉換過程中,可以被高優先級的“插隊”轉換打斷,優先采集插隊通道的數據,插隊轉換完成后再恢復常規轉換。
- 應用:適合需要對某些重要信號進行優先采集的場合,如故障檢測、緊急信號采集。
6.2 指示采集過程不同階段的兩個重要標志位EOC和EOS:
1. EOC(End Of Conversion,轉換結束)
- 含義:EOC?表示“單次轉換結束”。
- 作用:當ADC完成對某一個通道的模數轉換后,EOC標志會被置位,表示本次采樣的數據已經轉換完成,可以讀取轉換結果了。
- 應用場景:適用于單通道采集或多通道逐個采集時,判斷每個通道的數據是否轉換完成。
- 舉例:假如你配置ADC采集通道1,通道1的電壓轉換完成后,EOC標志置位,軟件可以讀取通道1的ADC值。
2. EOS(End?Of?Sequence,序列結束)
- 含義:EOS?表示“序列采集結束”。
- 作用:當ADC配置為掃描模式(即一次采集多個通道,形成一個采集序列)時,只有當本輪所有通道都采集并轉換完成后,EOS標志才會被置位,表示整個采集序列結束。
- 應用場景:適用于多通道掃描采集,判斷一輪所有通道的數據是否都已采集完成。
- 舉例:假如你配置ADC依次采集通道1、2、3,只有當3個通道都采集完畢后,EOS標志才會置位,軟件可以批量讀取所有通道的ADC值。
7. 使用ADC進行單通道采集
使用ADC采集VBAT的電壓,并且打印到串口。
在底板上,紐扣電池BT1的作用是在外部電源斷電時為RTC(實時時鐘)模塊持續供電,確保保證RTC時鐘和時間信息不丟失;而S1是一個與單片機連接的開關,其在原理圖中的作用是切換RTC的供電來源,當S1向上撥時,RTC通過外部電源供電,適用于系統正常工作時;當S1向下撥時,RTC則由紐扣電池BT1供電,適用于外部電源斷開時保證RTC繼續運行,這樣設計可以在系統掉電時依然保持實時時鐘的計時功能。
此外,S1的狀態還可以通過連接到單片機的引腳進行檢測,單片機可以根據該引腳的電平判斷當前RTC的供電狀態,從而在軟件上做出相應的處理,比如提示用戶更換電池或記錄掉電事件,實現對系統供電狀態的智能管理。
所以當我們外部不接電并且想讓紐扣電池給單片機內部RTC供電就要求我們按鈕撥到BAT方向,讓紐扣電池供電。
1. 使用ADC4采集紐扣電池BT1的電壓,設置ADC4檢測的通道為Vbat/4通道,這樣ADC4的一個對應的通道就和單片機引腳VBAT連接,可以進行代碼編輯采集電壓。
其他屬性補充,單通按照上面配置:
?2.編輯代碼:
U5特有步驟:(只需要執行一次,放在主函數內while外面,注釋中詳細介紹了)
HAL_PWREx_EnableVddA(); //這個函數用于啟用VDDA電壓域。VDDA是指處理器的模擬電源電壓域,用于供電模擬功能模塊,例如ADC,DAC等。通過HAL_PWREx_EnableVddA()函數,可以使處理器的VDDA電壓域處于啟用狀態,以供給模擬功能模塊所需的電源。
HAL_PWREx_EnableVddIO2(); //這個函數用于啟用VDDIO2電壓域。VDDIO2是指處理器的I/O引腳電源域,用于供電處理器的I/O引腳。通過調用HAL_PWREx_EnableVddIO2()函數,可以使處理器的VDDIO2電壓域處于啟用狀態,以供給處理器的I/O引腳所需的電源。
HAL_ADCEx_Calibration_Start(&hadc4,ADC_CALIB_OFFSET,ADC_SINGLE_ENDED); //校準單端ADC采樣
所有類型芯片采樣都要做的事:(放在while循環多次采樣,也可以只測一次看個人需要)
HAL_ADC_Start(&hadc4); //啟動adc轉換
HAL_ADC_PollForConversion(&hadc4,100); //等待轉換完成,第二個參數表示超時時間,單位ms
int value=HAL_ADC_GetValue(&hadc4); //獲取ADC轉換結果
3. 接通好燒錄引腳和數據線后,編譯后使用debug功能。
把測的的value一定要放到全局變量中,不然不好觀測,局部變量每次都重新創建影響測試。
一定要確定線連接無誤,右鍵可視窗口的變量可以取消十六進制顯示:
?8. 使用ADC進行多通道單次數據采集
找到擴展板上的電壓采集:
確定連接的引腳后,選擇引腳模式為ADC4,后面就是通道數,可以在左邊ADC4,后面就不用設置,因為我們已經設置采集順序,上面圖片已經標注,這樣的好處是方便。
?完整代碼:
多通道單次采集特點是每次采集都要重新打開ADC,接收數據后就需要關閉再次打開采集下一次數據。
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
#include "stdio.h"
int fputc(int ch,FILE *p)
{while(!(USART1->ISR &(1<<7))){} //等待發送寄存器空//ISR的第七位是1 ,說明發送寄存器是空,這個時候再去寫入要發送的數據//當發送寄存器是空,跳出循環,把數據放好發送數據寄存器里面USART1->TDR = ch;return ch;
}/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void SystemPower_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int value = 0;
int value2 = 0;
float a = 0;
/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
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();/* Configure the System Power */SystemPower_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_ADC4_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */HAL_PWREx_EnableVddA(); //這個函數用于啟用VDDA電壓域。VDDA是指處理器的模擬電源電壓域,用于供電模擬功能模塊,例如ADC,DAC等。通過HAL_PWREx_EnableVddA()函數,可以使處理器的VDDA電壓域處于啟用狀態,以供給模擬功能模塊所需的電源。HAL_PWREx_EnableVddIO2(); //這個函數用于啟用VDDIO2電壓域。VDDIO2是指處理器的I/O引腳電源域,用于供電處理器的I/O引腳。通過調用HAL_PWREx_EnableVddIO2()函數,可以使處理器的VDDIO2電壓域處于啟用狀態,以供給處理器的I/O引腳所需的電源。HAL_ADCEx_Calibration_Start(&hadc4,ADC_CALIB_OFFSET,ADC_SINGLE_ENDED); //校準單端ADC采樣 /* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){HAL_ADC_Start(&hadc4);//啟動adc轉換 HAL_ADC_PollForConversion(&hadc4,100); //等待轉換完成,第二個參數表value = HAL_ADC_GetValue(&hadc4); //獲取ADC第一個轉換結果HAL_ADC_PollForConversion(&hadc4,100); //等待轉換完成,第二個參數表value2 = HAL_ADC_GetValue(&hadc4); //獲取ADC第二個轉換結果HAL_ADC_Stop(&hadc4); //停止ADCa = 3.3*(value*4)/4096; //計算電壓值//printf("%fV",a);HAL_Delay(2000);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE4) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_MSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.MSIState = RCC_MSI_ON;RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_4;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2|RCC_CLOCKTYPE_PCLK3;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK){Error_Handler();}
}/*** @brief Power Configuration* @retval None*/
static void SystemPower_Config(void)
{/** Disable the internal Pull-Up in Dead Battery pins of UCPD peripheral*/HAL_PWREx_DisableUCPDDeadBattery();
/* USER CODE BEGIN PWR */
/* USER CODE END PWR */
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
?9. 使用ADC1進行多通道數據單次采集
我們新建項目,熟悉一下ADC1,使用ADC1實現。
(1) 配置通道,使能ADC1:
(2) 調節轉換通道數量,設置基本屬性:
等待ADC轉換結束信號EOC兩種方式:
while等待EOC:適合所有ADC,直接操作寄存器,靈活但需要自己處理細節。
HAL_ADC_PollForConversion:HAL庫推薦用法,安全、易用、帶超時,但部分老型號或特殊用法下可能不適用。
實際選擇:如果HAL庫支持,建議用HAL_ADC_PollForConversion;如果遇到兼容性或特殊需求,可以用while輪詢。
(3) 編寫代碼:
?結果:
注意:ADC1采集的BAT電壓不需要乘以4,ADC4采集BAT電壓需要乘以四?