什么是ADC
資料獲取見文末
你以為的ADC

哈哈,開個玩笑~~~
說起來ADC,先來聊聊模擬信號與數字信號,簡單來說就是
模擬信號與數字信號簡介
-
模擬信號
模擬電壓信號在時間上和幅值上均是連續的信號叫做模擬信號,此類信號的特點是,在一定動態范圍內幅值可取任意值,
-
數字信號
與模擬信號相對應,時間和幅值均離散( 不連續 ) 的信號叫做數字信號,數字信號的特點是幅值只可以取有限個值,
下文引自:
https://baijiahao.baidu.com/s?id=1701463655357562703&wfr=spider&for=pc

通過觀察聲音的波形我們就會發現:
模擬信號在一段連續的時間范圍內可以在任意時間點呈現任意數值,但是這種數值是隨著時間連續變化,

數字信號的取值是離散的二進制數,它和具有連續波形的模擬信號所展現出來的東西千差萬別,

模擬信號與數字信號如何相互轉換
模擬信號一般通過脈碼調制PCM的方法量化調制成數字信號,這個動作叫做模數轉換,模擬信號會經過采樣、量化、編碼等一系列的動作最終轉化成能被存盤介質存盤的一串0和1組成的數字信號,
也即是我們本次要介紹的ADC(analogue-to-digital conversion),模數轉換,

數字信號也可以還原成模擬信號,這種轉化器件簡稱DAC,基本由即權電阻網路、運算放大器、基準電源和模擬開關組成,

STM32 ADC介紹
上面說了一大堆,還是屬于比較基礎的介紹,有興趣的小伙伴可以多多了解下AD轉換器構成,實作原理,通信原理相信都學過,PCM編碼都忘了吧,哈哈,好巧,我也忘了...
STM32 擁有 1~3 個 ADC(STM32F101/102 系列只有 1 個 ADC),這些 ADC 可以獨立使用, 也可以使用雙重模式(提高采樣率),

STM32 的 ADC 是 12 位逐次逼近型的模擬數字轉換器, 它有 18 個通道,可測量 16 個外部和 2 個內部信號源,各通道的 A/D 轉換可以單次、連續、掃 描或間斷模式執行,ADC 的結果可以左對齊或右對齊方式存盤在 16 位資料暫存器中,
STM32 的 ADC 最大的轉換速率為 1Mhz,也就是轉換時間為 1us(在 ADCCLK=14M,采樣周期為 1.5 個 ADC 時鐘下得到),不要讓 ADC 的時鐘超過 14M,否則將導致結果準確度下降
-
轉換時間
采樣周期最小是 1.5 個,即如果我們要達到最快的采樣,那么應該設定采樣周期為 1.5 個周期,這里說的周期就是1/ADC_CLK
ADC 的總轉換時間跟 ADC 的輸入時鐘和采樣時間有關,其公式如下:
Tconv = 采樣時間 + 12.5 個周期
其中 Tconv 為 ADC 總轉換時間,當 ADC_CLK=14Mhz 的時候,并設定 1.5 個周期的采樣時間,則 Tcovn=1.5+12.5=14 個周期=1us,通常經過 ADC 預分頻器能分頻到最大的時鐘只能是 12M,采樣周期設定為 1.5 個周期,算出最短的轉換時間為 1.17us
外部的 16 個通道在轉換的時候可分為 2 組通道:規則通道組和注入通道組,其中規則通道組最多有 16 路,注入通道組最多有 4 路
-
規則通道組:
從名字來理解,規則通道就是一種規規矩矩的通道,類似于正常執行的程式,通常我們使用的都是這個通道
-
注入通道組:
從名字來理解,注入即為插入,是一種不安分的通道,類似于中斷,當程式正常往下執行時,中斷可以打斷程式的執行,同樣如果在規則通道轉換程序中,有注入通道插隊,那么就要先轉換完注入通道,等注入通道轉換完成后,再回到規則通道的轉換流程
-
DMA 請求
規則和注入通道轉換結束后,除了產生中斷外,還可以產生 DMA 請求,把轉換好的資料直接存盤在記憶體里面,
要注意的是只有 ADC1 和 ADC3 可以產生DMA 請求,一般我們在使用 ADC 的時候都會開啟 DMA 傳輸
-
轉換方式
單次轉換: 顧名思義,ADC 執行一次轉換,想要在轉換需要再次開啟
連續轉換:
ADC 結束一個轉換后立即啟動一個新的轉換,需要注意的是:此模式無法連續轉換注入通道,連續模式下唯一的例外情況是,注入通道配置為在規則通道之后自動轉換
STM ADC引腳映射
有些沒有的管腳就不用關心了,比如F1的沒有PF6-10引腳


DMA通道映射

cubemx 配置
時鐘之類的配置,勞煩各位小伙伴補補課,翻翻小飛哥前面的文章哈,直接進入正題
單通道DMA轉換
時鐘配置為分頻之后為12MHZ

選擇 ADC1->IN8->PB0

需要關注的幾個點,掃描模式,這個在單通道時是無法使能的,只有多通道才可以開啟,連續轉換模式,根據自己實際需求決定是連續轉換還是單次轉換,觸發方式,觸發方式是非常多的,可以軟體觸發,PWM觸發,定時器觸發,也是根據自己的需要選擇即可

中斷,需要就開啟,不需要就不用開啟,直白~

DMA配置,DMA的中斷是默認開啟的,并且無法配置關閉


配置很簡單,你學廢了嗎...
代碼實作
ADC配置的代碼

關于DMA的配置

extern ADC_HandleTypeDef hadc1;
extern DMA_HandleTypeDef hdma_adc1;
uint16_t adc_buffer[50] = {0};
static void prvPrintTask( void *pvParameters )
{
float adc_value = 0;
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_buffer,50);
int iIndexToString;
/* Two instances of this task are created. The task parameter is used to pass
an index into an array of strings into the task. Cast this to the required type. */
iIndexToString = ( int ) pvParameters;
for( ;; )
{
for(int index =0;index < 50;index++)
{
adc_value += adc_buffer[index];
}
adc_value/=50;
adc_value = adc_value *3.3/(1<<12);
debug_printf("\nadc_value = %.2f\n",adc_value);
vTaskDelay( ( rand() & 0x1FF ) );
}
}
配置為連續模式,DMA為回圈模式,資料在buffer中不斷回圈更新

配置為不連續模式,只轉換一次

我是直接接到3.3V測驗的,精度還可以

接GND,是有一些非零值的,所以必要的濾波還是要做的,這里我是用了最簡單的均值濾波處理

多通道DMA轉換
配置和單通道有些不同,掃描模式就可以打開了,通道數可以選擇,我們選擇4即可,下面的順序就是我們要轉換的順序

/* ADC1 init function */
void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 4;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_9;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_10;
sConfig.Rank = ADC_REGULAR_RANK_3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_11;
sConfig.Rank = ADC_REGULAR_RANK_4;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
主函式中撰寫如下代碼:
為了更直觀的觀察效果,定義二維陣列,每個存盤4個值,可以看到,4個通道是依次轉換的,各取10個值,求平均值
extern ADC_HandleTypeDef hadc1;
extern DMA_HandleTypeDef hdma_adc1;
uint16_t adc_buffer[10][4] = {0,0};
static void prvPrintTask( void *pvParameters )
{
float adc_value[4] = {0};
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_buffer,40);
int iIndexToString;
/* Two instances of this task are created. The task parameter is used to pass
an index into an array of strings into the task. Cast this to the required type. */
iIndexToString = ( int ) pvParameters;
for( ;; )
{
for(int i=0;i<4;i++)
{
for(int j=0;j<10;j++)
{
adc_value[i]+=adc_buffer[j][i];
}
adc_value[i]=(float)adc_value[i]/(10*4096)*3.3;//求平均值并轉換成電壓值
//ADC_Value[i]=(float)sum/10;
printf("ADC_Value[%d] = %.2f\n",i,adc_value[i]);
}
vTaskDelay( ( rand() & 0x1FF ) );
}
}

經驗交流
關注公眾號,后臺回復“ADC”,即可獲取本文原始碼~
歡迎添加小飛哥好友,進群一起交流有趣的話題,探討有趣的知識

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/341970.html
標籤:其他
