模擬信號(hào)的讀取是我們?cè)谧龊芏囗?xiàng)目是都要用到的,而模擬量的讀取就要依賴于ADC數(shù)模轉(zhuǎn)換器。對(duì)于初學(xué)者,學(xué)習(xí)使用ADC可以很大的幫助以后的STM32學(xué)習(xí)。
目錄
ADC簡介 :
DMA簡介:?
工程開始:
STM32CubeMX配置區(qū):
配置外部時(shí)鐘:
配置調(diào)試:
配置ADC:
配置DMA:
配置串口:
配置工程文件:?
?KEIL編程:
?開啟MicroLIB:
添加庫函數(shù):?
串口重定向:
定義變量:
while:?
回調(diào)函數(shù):
成果展示:?
總結(jié):
ADC簡介 :
? ? ? ? ADC可以將模擬信號(hào)轉(zhuǎn)換為數(shù)字信號(hào),用于采集和處理模擬信號(hào)。ADC在嵌入式系統(tǒng)中應(yīng)用廣泛,應(yīng)用場景包括但不限于電池電量檢測、音頻數(shù)據(jù)采集、波形捕獲。
DMA簡介:?
? ? ? DMA全稱Direct Memory Access,即直接存儲(chǔ)器訪問。
? ? ? DMA傳輸可以將數(shù)據(jù)從一個(gè)地址空間直接復(fù)制到另一個(gè)地址空間,提供外設(shè)與儲(chǔ)存器或者儲(chǔ)存器之間的高速數(shù)據(jù)傳輸。
? ? ? DMA傳輸不需要CPU的參與,可以節(jié)省大量CPU資源以使程序更加快速高效。
工程開始:
STM32CubeMX配置區(qū):
? ? ? 打開STM32CubeMX,新建工程
配置外部時(shí)鐘:
?
??輸入需要的頻率,敲擊回車,STM32CubeMX會(huì)自動(dòng)配置。
配置調(diào)試:
?這里一定要配置好,不然會(huì)導(dǎo)致芯片自鎖
配置ADC:
?開啟ADC后時(shí)鐘樹可能會(huì)報(bào)錯(cuò),點(diǎn)進(jìn)去選擇Yes自動(dòng)配置就好了;
?這里要先更改通道數(shù)才可以更改其他的參數(shù);
配置DMA:
?
配置串口:
串口的參數(shù)使用默認(rèn)設(shè)置就好了。
配置工程文件:?
?然后點(diǎn)擊右上角的GENERATE CODE生成文件,點(diǎn)擊打開文件,進(jìn)入keil中
?KEIL編程:
?開啟MicroLIB:
關(guān)于MicroLIB大家可以自行查找,在此不過多贅述
添加庫函數(shù):?
打開main.c,添加庫函數(shù),不加會(huì)報(bào)錯(cuò)
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
串口重定向:
/* USER CODE BEGIN PTD */
/*串口重定向*/
int fputc(int ch, FILE *f){
HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END PTD */
定義變量:
/* USER CODE BEGIN 0 */
uint32_t ADC1_1, ADC1_2; //兩個(gè)通道的ADC
uint32_t ADC1_Value[10]; //ADC數(shù)據(jù)存放數(shù)組
uint8_t ADC1_Flag; //ADC采集完畢標(biāo)志位
/* USER CODE END 0 */
?這里把ADC數(shù)據(jù)存放數(shù)組長度定義為了10,因?yàn)橛袃蓚€(gè)通道的ADC,所以每個(gè)通道各采樣10次,輪詢模式交錯(cuò)進(jìn)行(即[ADC1_0; ADC1_1; ADC1_0; ADC1_1…]),最后兩個(gè)通道累加后求平均值,實(shí)際使用如果需要更高的精度,可以采樣更多次后求平均值。
初始化 :
/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,10);
/* USER CODE END 2 */
while:?
在while循環(huán)中編寫主程序(這里直接把一整個(gè)while貼上了)
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(ADC1_Flag==1)
{
ADC1_Flag=0; //清空標(biāo)志位
ADC1_1=0;
ADC1_2=0;
for(int i=0;i<10;){
ADC1_1+=ADC1_Value[i++];
ADC1_2+=ADC1_Value[i++];
//讀取ADC值
}
printf("\n");
printf("ADC_IN0(PA0)=%4.0d,Voltage0=%1.4f\r\n",ADC1_1/5,ADC1_1/5*3.3f/4096);
printf("ADC_IN1(PA1)=%4.0d,Voltage1=%1.4f\r\n",ADC1_2/5,ADC1_2/5*3.3f/4096);
//串口打印
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,10);
}
HAL_Delay(1000); //延時(shí)1s
}
/* USER CODE END 3 */
回調(diào)函數(shù):
主程序?qū)懲旰筮€要在下面的用戶代碼區(qū)寫回調(diào)函數(shù),不然程序?qū)o法運(yùn)行
/* USER CODE BEGIN 4 */
/*回調(diào)函數(shù)*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1){
ADC1_Flag=1;
HAL_ADC_Stop_DMA(&hadc1); //關(guān)閉DMA
}
}
/* USER CODE END 4 */
這里加了一句關(guān)閉DMA的語句,因?yàn)榘l(fā)現(xiàn)實(shí)際使用中如果不加會(huì)出現(xiàn)數(shù)值跑飛的情況?
最后貼上完整的main.c代碼:?
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 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 "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/*串口重定向*/
int fputc(int ch, FILE *f){
HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* 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);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint32_t ADC1_1, ADC1_2; //兩個(gè)通道的ADC
uint32_t ADC1_Value[10]; //ADC數(shù)據(jù)存放數(shù)組
uint8_t ADC1_Flag; //ADC采集完畢標(biāo)志位
/* 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();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,10); //開啟DMA傳輸
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(ADC1_Flag==1)
{
ADC1_Flag=0; //清空標(biāo)志位
ADC1_1=0;
ADC1_2=0;
for(int i=0;i<10;){
ADC1_1+=ADC1_Value[i++];
ADC1_2+=ADC1_Value[i++];
//讀取ADC值
}
printf("\n");
printf("ADC_IN0(PA0)=%4.0d,Voltage0=%1.4f\r\n",ADC1_1/5,ADC1_1/5*3.3f/4096);
printf("ADC_IN1(PA1)=%4.0d,Voltage1=%1.4f\r\n",ADC1_2/5,ADC1_2/5*3.3f/4096);
//串口打印
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,10);
}
HAL_Delay(1000); //延時(shí)1s
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/*回調(diào)函數(shù)*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1){
ADC1_Flag=1;
HAL_ADC_Stop_DMA(&hadc1); //關(guān)閉DMA
}
}
/* 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 */
這次的程序沒有更改任何的頭文件和STM32CubeMX生成的初始化函數(shù),全部在main.c中實(shí)現(xiàn),只要在規(guī)定的用戶代碼區(qū)寫代碼,就不會(huì)影響后面再次使用STM32CubeMX添加外設(shè)或者做更改
成果展示:?
?編譯后下載到MCU中,PA9連接USB轉(zhuǎn)串口模塊的RXD,PA10連接TXD(因?yàn)榇谕ㄐ乓唇樱?,如果MCU與USB轉(zhuǎn)串口用的不是同一路電源,還要把兩電源共地(不要把正極也接了,不然可能會(huì)燒)。作為試驗(yàn),我們把PA0接3.3v,PA1接地,打開串口調(diào)試助手應(yīng)該可以看到上面接收到的信息:
一定要注意,ADC的最大輸入電壓為3.6V,超過會(huì)燒MCU(親身體會(huì),燒了一個(gè)ZET6)
總結(jié):
?此方法可以實(shí)現(xiàn)快速讀取模擬量的同時(shí)占用較少的資源,泛用性比較強(qiáng)。
都看到這里了點(diǎn)點(diǎn)關(guān)注再走唄文章來源:http://www.zghlxwxcb.cn/news/detail-697759.html
?交流Q群:659512171文章來源地址http://www.zghlxwxcb.cn/news/detail-697759.html
到了這里,關(guān)于STM32初學(xué)入門筆記(3):STM32CubeMX配置STM32實(shí)現(xiàn)多通道ADC+DMA讀取模擬量的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!