實現(xiàn)目的:利用ADC采集光敏傳感器/煙霧傳感器的值,并利用串口打印
實驗平臺:正點原子精英版
一、簡介
1.DMA的介紹
參考:STM32 hal庫使用筆記(四)DMA—內(nèi)存到內(nèi)存/內(nèi)存到外設(shè)_亂碼小伙的博客-CSDN博客
2.ADC簡介
? ? ?ADC(Analog-Digital Converter)模擬-數(shù)字轉(zhuǎn)換器 ADC可以將引腳上連續(xù)變化的模擬電壓轉(zhuǎn)換為內(nèi)存中存儲的數(shù)字變量,建立模擬電路到數(shù)字電路的橋梁;
? ? 12位逐次逼近型ADC,1us轉(zhuǎn)換時間;
? ? 輸入電壓范圍:0~3.3V,轉(zhuǎn)換結(jié)果范圍:0~4095;
? ? 18個輸入通道,可測量16個外部和2個內(nèi)部信號源;
? ? 規(guī)則組和注入組兩個轉(zhuǎn)換單元,可利用模擬看門狗自動監(jiān)測輸入電壓范圍。
3.一些概念
? ? ADC數(shù)據(jù)寄存器是32位,但是只用到16位,所以一般采用右對齊方式,方便計算。
? ? 如果多通道轉(zhuǎn)換ADC,次數(shù)頻繁,間隔時間短(循環(huán)模式),會導(dǎo)致數(shù)據(jù)寄存器的數(shù)值被覆蓋,所以要利用DMA轉(zhuǎn)運(yùn)。
? ? 注入組限制較多,一般采用規(guī)則組進(jìn)行轉(zhuǎn)換。
? ? TCONV = 采樣時間 + 12.5個ADC周期,采樣時間=n個ADC周期,ADC采用頻率最大是14MHZ,由于ADC掛載總線的頻率是72MHZ所以進(jìn)行6分頻,采用周期是12MHZ。
? ? ADC有一個內(nèi)置自校準(zhǔn)模式。校準(zhǔn)可大幅減小因內(nèi)部電容器組的變化而造成的準(zhǔn)精度誤差。校準(zhǔn)期間,在每個電容器上都會計算出一個誤差修正碼(數(shù)字值),這個碼用于消除在隨后的轉(zhuǎn)換中每個電容器上產(chǎn)生的誤差。HAL_ADCEx_Calibration_Start(),進(jìn)行校準(zhǔn)。
二、HAL庫配置
1.時鐘樹的設(shè)置
按照下圖配置即可:
?2.ADC的配置
關(guān)于串口的配置參考:STM32 hal庫使用筆記(二)中斷—串口中斷_亂碼小伙的博客-CSDN博客
? ? 本實驗不使用串口中斷,中斷部分的配置不用操作
2.1 單通道(代碼對應(yīng)3.1)
1)關(guān)閉掃描模式,由于只有一個通道;
2)關(guān)閉連續(xù)轉(zhuǎn)換模式,每次需要ADC轉(zhuǎn)換時打開ADC轉(zhuǎn)換即可;
3)采用規(guī)則組;
4)軟件觸發(fā)模式;
5)28.5個ADC周期,所以整個采采樣周期是30個ADC周期。
配置完成后生成代碼即可。
2.2 DMA雙通道(代碼對應(yīng)3.2)
1)開啟雙通道
2)掃描模式打開
3)轉(zhuǎn)換組設(shè)置為2
4)打開通道1
5)周期28.5個
6)打開通道2
7)周期28.5個
注意每個ADC的每個通道只能對應(yīng)一個GPIO。
ADC數(shù)據(jù)是16位的,所以采用半字字寬。
配置完成后,生成代碼即可。
三、代碼編寫
3.1?
以下代碼在adc.c中編寫
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)//配置ADC時鐘,通道、序列
{
Error_Handler();
}
HAL_ADCEx_Calibration_Start(&hadc1);//用戶添加,ADC校準(zhǔn),據(jù)了解最新版HAL庫已經(jīng)刪除
}
用戶編碼區(qū):
uint32_t adc_get_result(void)
{
HAL_ADC_Start(&hadc1);//單次轉(zhuǎn)換模式,每次轉(zhuǎn)換完成后ADC轉(zhuǎn)換會自動停止
HAL_ADC_PollForConversion(&hadc1, 10);//ADC轉(zhuǎn)換,轉(zhuǎn)換參數(shù)ms
return (uint16_t)HAL_ADC_GetValue(&hadc1);
}
以下在main.c
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_ADC1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(2000);
unsigned char arrary[4];
arrary[0] = ((adc_get_result()/1000))+0x30;
arrary[1] = (((adc_get_result()%100)/10))+0x30;
arrary[2] = ((adc_get_result()%10)/10)+0x30;
arrary[3] = ((adc_get_result()%1)/10)+0x30;
//unsigned char MyArray[1]={adc_get_result()};
//HAL_UART_Transmit(&huart1, MyArray,1, 10000);
HAL_UART_Transmit(&huart1, arrary,4,1000);
}
/* USER CODE END 3 */
}
實驗現(xiàn)象:用手蓋住和放開有明顯變化
3.2
添加接收數(shù)據(jù)數(shù)組:uint16_t g_adc_dma_buf[2];
以下代碼均在adc.c中編寫:
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)(g_adc_dma_buf) ,0);//觸發(fā)ADC轉(zhuǎn)換,DMA傳輸數(shù)據(jù)
}
?HAL_ADC_Start_DMA(&hadc1,(uint32_t *)(g_adc_dma_buf) ,0);這個可以省略,因為DMA不是循環(huán)模式,ADC也不是連續(xù)模式,所以每次采集ADC都需要重新開啟。
用戶編碼區(qū):
void adc_dma_enable(uint32_t cndtr)
{
__HAL_ADC_DISABLE(&hadc1);
__HAL_DMA_DISABLE(&hdma_adc1);
while (__HAL_DMA_GET_FLAG(&hdma_adc1, DMA_FLAG_TC1))
{
__HAL_DMA_CLEAR_FLAG(&hdma_adc1, DMA_FLAG_TC1); //清除標(biāo)志位
}//實測,刪去也能正常使用,因為測量間隔時間長,DMA肯定關(guān)閉了
DMA1_Channel1->CNDTR = cndtr;
__HAL_DMA_ENABLE(&hdma_adc1);
__HAL_ADC_ENABLE(&hadc1);
HAL_ADC_Start(&hadc1); //開啟ADC轉(zhuǎn)換,必須定時開啟,因為ADC不是連續(xù)掃描模式
}
以下均在main.c
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 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(2000);
adc_dma_enable(2);//必須添加
unsigned char arrary[4];
unsigned char arrary2[4];
arrary[0] = ((g_adc_dma_buf[0]/1000))+0x30;
arrary[1] = (((g_adc_dma_buf[0]%100)/10))+0x30;
arrary[2] = ((g_adc_dma_buf[0]%10)/10)+0x30;
arrary[3] = ((g_adc_dma_buf[0]%1)/10)+0x30;
printf("ADC1:");
HAL_UART_Transmit(&huart1, arrary,4,1000);
printf("\r\n");
arrary2[0] = ((g_adc_dma_buf[1]/1000))+0x30;
arrary2[1] = (((g_adc_dma_buf[1]%100)/10))+0x30;
arrary2[2] = ((g_adc_dma_buf[1]%10)/10)+0x30;
arrary2[3] = ((g_adc_dma_buf[1]%1)/10)+0x30;
printf("ADC2:");
HAL_UART_Transmit(&huart1, arrary2,4,1000);
printf("\r\n");
}
/* USER CODE END 3 */
}
實驗現(xiàn)象:沒有打火機(jī),煙霧傳感器值一直為0,ADC通道1連接光敏,有明顯變化(連接到通道2上也有明顯變化)。實測成功。
文章來源:http://www.zghlxwxcb.cn/news/detail-773197.html
?歡迎大家交流和指正?。。?span toymoban-style="hidden">文章來源地址http://www.zghlxwxcb.cn/news/detail-773197.html
到了這里,關(guān)于STM32 hal庫使用筆記(五)ADC—單通道/雙通道DMA傳輸?shù)奈恼戮徒榻B完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!