關(guān)于ADC的一些原理和實驗我們已經(jīng)有了2篇筆記,鏈接如下:
關(guān)于ADC的筆記1_Mr_rustylake的博客-CSDN博客
STM32-ADC單通道采集實驗_Mr_rustylake的博客-CSDN博客
實驗要求:通過ADC1通道1(PA1)采集電位器的電壓,并顯示ADC轉(zhuǎn)換的數(shù)字量和換算后的電壓值。
我們通過下表可以知道DMA1通道1的外設(shè)對應的就是ADC1的讀取。
首先確定我們的最小刻度,Vref = 3.3V,所以0V <= Vin <= 3.3V,所以最小刻度是3.3V / 4096(2^12)。
接下來確定轉(zhuǎn)換時間。采樣時間239.5個ADC時鐘周期為例,可以得到轉(zhuǎn)換時間為21us。
時間轉(zhuǎn)換公式參考如下公式:Tcvtmin=(12.5+X)周期=(12.5 + X)/(12MHz)=21us。
?因為使用的是DMA讀取,所以采取連續(xù)轉(zhuǎn)換模式,因為使用的是單通道,所以不掃描。
接下來我們編寫實驗代碼:
先編寫函數(shù)代碼adc.c:
#include "./BSP/ADC/adc.h"
ADC_HandleTypeDef g_adc_handle;
DMA_HandleTypeDef g_dma_handle;
uint8_t g_adc_dma_sta; //標志DMA的傳輸是否完成
void adc_dam_init(uint32_t mar){
ADC_ChannelConfTypeDef adc_ch_conf;
__HAL_RCC_DMA1_CLK_ENABLE();
g_dma_handle.Instance = DMA1_Channel1;
g_dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; //外設(shè)到內(nèi)存
g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE; //因為選取的是DMA1的數(shù)據(jù)寄存器,選擇不增量
g_dma_handle.Init.MemInc = DMA_MINC_ENABLE; //對于存儲器需要存儲多個數(shù)據(jù),所以選擇增量模式
g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外設(shè)數(shù)據(jù)位寬,我們選擇16位半字(全字可以理解為全角中文字符)
g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存儲器數(shù)據(jù)位寬,我們也選擇16位半字
g_dma_handle.Init.Mode = DMA_NORMAL; //選擇普通模式,因為在傳輸完成之后我們需要進行進一步操作現(xiàn)實我們獲取到的值,所以選擇normal
g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM; //只有1個DMA隨便選
HAL_DMA_Init(&g_dma_handle);
//聯(lián)系DMA和ADC的句柄
__HAL_LINKDMA(&g_adc_handle, DMA_Handle, &g_dma_handle); //第二個參數(shù)為第一個ADC句柄的第三個成員,指向?qū)腄MA句柄
g_adc_handle.Instance = ADC1;
g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右對齊
g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; //不掃描
g_adc_handle.Init.ContinuousConvMode = ENABLE; //連續(xù)模式
g_adc_handle.Init.NbrOfConversion = 1; //轉(zhuǎn)換通道數(shù)為1,單通道
g_adc_handle.Init.DiscontinuousConvMode = DISABLE; //不用間斷模式
g_adc_handle.Init.NbrOfDiscConversion = 0; //無間斷模式則無間斷通道
g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; //外部軟件觸發(fā)
HAL_ADC_Init(&g_adc_handle);
adc_ch_conf.Channel = ADC_CHANNEL_1;
adc_ch_conf.Rank = ADC_REGULAR_RANK_1; //轉(zhuǎn)換順序
adc_ch_conf.SamplingTime = ADC_SMAPLINGTIME_239CYCLES_5; //設(shè)置為最大值
HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 2, 3);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
HAL_ADCEx_Calibration_Start(&g_adc_handle);
HAL_DMA_Start_IT(&g_dma_nch_handle, (uint32_t)&ADC1->DR, mar, 0);
HAL_ADC_Start_IT(&g_adc_nch_handle, &mar, 0);
}
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){
if(hadc->Instance == ADC1){
GPIO_InitTypeDef gpio_init_struct;
RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能ADC時鐘
__HAL_RCC_ADC1_CLK_ENABLE(); //使能GPIO時鐘
gpio_init_struct.Pin = GPIO_PIN_1;
gpio_init_struct.Mode = GPIO_MODE_ANALOG; //模擬模式
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; //選擇ADC外設(shè)時鐘設(shè)置
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; //選擇6分頻,72/6=12MHz
HAL_RCCEx_PeriphCLKConfig(&adc_clk_init, &g_adc_handle);
}
}
uint32_t adc_get_result(void){
HAL_ADC_Start(&g_adc_handle);
HAL_ADC_PollForConversion(&g_adc_handle, 10); //第二個參數(shù)比1大就行
return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);
}
uint32_t adc_get_result_average(uint32_t ch, uint8_t times){
uint32_t temp_val = 0;
uint8_t t;
for(t = 0; t < times; t++){
temp_val += adc_get_result();
delay_ms(5);
}
return temp_val / times;
}
void adc_dma_enable(uint16_t cndtr){
/*
ADC1->CR2 &= ~(1 << 0); //關(guān)閉ADC
DMA1_Channel1->CCR &= ~(1 << 0);//關(guān)閉DMA
while(DMA1_Channel1->CCR & (1 << 0));
DMA1_Channel1->CNDTR = cndtr;
DMA1_Channel1->CCR |= (1 << 0); //開啟DMA
ADC1->CR2 |= (1 << 0); //開啟ADC
ADC1->CR2 |= (1 << 22); //觸發(fā)規(guī)則組轉(zhuǎn)換
*/
//hal庫法
__HAL_ADC_DISABLE(&g_adc_nch_handle);
__HAL_DNA_DISABLE(&g_dma_nch_handle);
while(__HAL_DMA_GET_FLAG(&g_dma_nch_handle, __HAL_DMA_GET_FLAG_INDEX(&g_dma_nch_handle)));
DMA1_Channel1->CNDTR = cndtr;
__HAL_DMA_ENABEL(&g_dma_nch_handle);
__HAL_ADC_ENABLE(&g_adc_nch_handle);
HAL_ADC_Start(&g_adc_nch_handle);
}
void DMA1_Channel1_IRQHandle(void){
if(DMA1->ISR & (1 << 1)){
g_adc_dma_sta = 1;
DMA1->IECR |= 1 << 1;
}
}
接下來編寫函數(shù)頭文件adc.h:
#ifndef __ADC_H
#define __ADC_H
#include "SYSTEM/sys/sys.h"
#include "BSP/DMA/dma.h"
extern ADC_HandleTypeDef g_adc_handle;
void adc_dam_init(uint32_t mar);
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc);
uint32_t adc_get_result(void);
uint32_t adc_get_result_average(uint32_t ch, uint8_t times);
void adc_dma_enable(uint16_t cndtr);
void DMA1_Channel1_IRQHandle(void);
#endif
接下來編寫主函數(shù)代碼main.c:文章來源:http://www.zghlxwxcb.cn/news/detail-595786.html
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/ADC/adc.h"
#define ADC_DMA_BUF_SIZE 100 /* ADC DMA采集 BUF大小 */
uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE]; /* ADC DMA BUF */
extern uint8_t g_adc_dma_sta; /* DMA傳輸狀態(tài)標志, 0,未完成; 1, 已完成 */
int main(void)
{
uint16_t i;
uint16_t adcx;
uint32_t sum;
float temp;
HAL_Init(); /* 初始化HAL庫 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 設(shè)置時鐘, 72Mhz */
delay_init(72); /* 延時初始化 */
usart_init(115200); /* 串口初始化為115200 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
adc_dma_init((uint32_t)&g_adc_dma_buf); /* 初始化ADC DMA采集 */
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "ADC DMA TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH1_VAL:", BLUE);
lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH1_VOL:0.000V", BLUE); /* 先在固定位置顯示小數(shù)點 */
adc_dma_enable(ADC_DMA_BUF_SIZE); /* 啟動ADC DMA采集 */
while (1)
{
if (g_adc_dma_sta == 1)
{
/* 計算DMA 采集到的ADC數(shù)據(jù)的平均值 */
sum = 0;
for (i = 0; i < ADC_DMA_BUF_SIZE; i++) /* 累加 */
{
sum += g_adc_dma_buf[i];
}
adcx = sum / ADC_DMA_BUF_SIZE; /* 取平均值 */
/* 顯示結(jié)果 */
lcd_show_xnum(134, 110, adcx, 4, 16, 0, BLUE); /* 顯示ADCC采樣后的原始值 */
temp = (float)adcx * (3.3 / 4096); /* 獲取計算后的帶小數(shù)的實際電壓值,比如3.1111 */
adcx = temp; /* 賦值整數(shù)部分給adcx變量,因為adcx為u16整形 */
lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE); /* 顯示電壓值的整數(shù)部分,3.1111的話,這里就是顯示3 */
temp -= adcx; /* 把已經(jīng)顯示的整數(shù)部分去掉,留下小數(shù)部分,比如3.1111-3=0.1111 */
temp *= 1000; /* 小數(shù)部分乘以1000,例如:0.1111就轉(zhuǎn)換為111.1,相當于保留三位小數(shù)。 */
lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE); /* 顯示小數(shù)部分(前面轉(zhuǎn)換為了整形顯示),這里顯示的就是111. */
g_adc_dma_sta = 0; /* 清除DMA采集完成狀態(tài)標志 */
adc_dma_enable(ADC_DMA_BUF_SIZE); /* 啟動下一次ADC DMA采集 */
}
LED0_TOGGLE();
delay_ms(100);
}
}
到這里我們的實驗代碼編寫就完成了。文章來源地址http://www.zghlxwxcb.cn/news/detail-595786.html
到了這里,關(guān)于STM32-單通道ADC采集(DMA讀?。嶒灥奈恼戮徒榻B完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!