---
# System prepended metadata

title: STM的ADC
tags: [STM32]

---

[TOC]

# STM ADC 簡介
- 見野火- HAL庫開發指南30章
- rm0090 13章
- [其他網站1](https://zhuanlan.zhihu.com/p/599754496)
- [其他網站2](https://blog.csdn.net/qq_36347513/article/details/112850329)
- 以下只提及一般使用的部分，其他更多功能見手冊
- STM32F407有3個ADC電路轉換功能，每個ADC有多個輸入通道可選擇，不同的ADC可以獨立運作或互相協同工作，解析度等ADC基本參數亦可自由設定


# ADC的組成

## 電壓
- ADC 輸入範圍為 V~REF-~ ≤ V~IN~ ≤ V~REF+~，查閱 rm0090 的 Table 65.與 UM1472的電路圖可知
    - <font color="red">V~REF-~ = V~SSA~ = 0 (GND)</font>
    - <font color="red">V~REF+~ = VDD = 3V</font>
    - <font color="red">GPIO腳位使用ADC模式時絕對不能輸入超過此電壓的訊號否則可能燒毀晶片</font>
        - 大部分(請查手冊)GPIO腳位作為數位輸入時確實可以容忍到5V，但那是因為該模式下有二極體保護

## 通道 (Channel)
- ![image](https://hackmd.io/_uploads/S1u1qsPp0.png =80%x)
- 每個ADC有10種以上的外部通道，而且可以注意到腳位基本上是共用的，使用多個ADC時要特別注意
- 針對每個ADCx來說，每次轉換一定只針對一個通道，所以如果要使用多個通道，就會有通道轉換的優先順序區別，見轉換順序
- 有兩種通道
    1. **Regular Channel** : 
        - 由 ADC regular sequence register 控管，此類通道轉換時依照設定的優先順序一個一個進行轉換
    2. **Injected Channel** : 
        - 由 ADC injected sequence register 控管，類似中斷的概念，只有Regular Channel啟用時才有injected conversion。
        - 當Regular Channel正在轉換時，Injected Channel的觸發源被觸發(軟體內或外部源)，那麼Regular Channel的轉換會被reset，轉而先執行Injected Channel的轉換

## 轉換順序
- 不論是Regular Channel或是Injected Channel被觸發要轉換時都可能要決定輪到哪個Channel做轉換
- 主要分類見[其他網站1](https://zhuanlan.zhihu.com/p/599754496)
    - ![image](https://hackmd.io/_uploads/rknAenv6A.png)
    - 如果使用單次轉換，就是得等到下個觸發發生(軟體手動或是外部)，才會做下一輪轉換。連續轉換則一開始啟動後就一直轉換
    - 掃描就是每輪轉換時，全部有啟用的通道都轉換。不掃描就是只轉換一個，下一輪就依照順序換下一個
    - 無特別要求時，<font color="red">多通道使用連續掃描轉換，單通道使用連續轉換(掃不掃描沒差)</font>

## 時間
-  **時鐘源**
    - ADC clock 由 PCLK2決定，可再經過分頻
    - ADC clock決定ADC轉換的基礎週期
- **採樣時間**
    - ADC的採樣時間可以被設定(n個週期)，如果很在意ADC總轉換時間，要考慮ADC本身工作花的週期 : 
<p class="text-center">
        總轉換週期數 = 採樣週期數+12
</p>

## 其他
- ADC轉換完成後會有End of Conversion(EOC) 旗標立起，該旗標可以用來觸發中斷
- EOC旗標也可產生DMA request，DMA主要用於數據傳輸，可減少CPU負擔
- STM的ADC有<font color="red">Overrun Detection</font>，用於偵測資料的丟失，如果有資料丟失， ADC_SR的OVR bit會立起，需要手動清除才會繼續AD轉換，使用cubeMX設置時要注意。詳見rm0090 13.8節

# ADC模式
- 使用單個ADC時只有independent mode可以使用
- 使用多個ADC時，如果選用independent，則表示每個ADC之間獨立運作，使用多個協作可見rm0090 13.9章

# CubeMX
- Mode 勾選需要的Channel
- Parameter配置(單個ADC、單個Channel、連續自動轉換)
    - ![image](https://hackmd.io/_uploads/SyquHtppR.png)
    - ADC Mode : independent表示每個ADC之間獨立運作
    - 時鐘可分頻、解析度可以選擇，解析度後的週期數表示要花的時間
    - 數據可選擇對齊方式，right alignment 可直接食用，較方便
    - 依據需要選擇是否要Scan和Continous
    - 是需要可產生DMA request
    - EOC flag立起來的時機點可選擇，注意這會影響Overrun Detection的啟用，polling 方式見 rm009013.8.3
    - 選擇Regular通道數，下面Rank可以選擇每個通道的順序
    - External Trigger可以選擇是軟體觸發或是利用timer 觸發
    - 剩下的要用自己查
- 使用timer作為觸發源
    - [timer trigger ADC1](https://community.st.com/t5/stm32-mcus/using-timers-to-trigger-adc-conversions-periodically/ta-p/49889)
    - [timer trigger ADC2](https://www.wpgdadatong.com/blog/detail/41128)
    - 使用timer作為觸發源可以固定頻率的觸發ADC轉換，記得timer那邊也要開啟並做設定，trigger output 通常選update event(見基礎 timer 筆記)

- ADC轉換完成後啟用中斷，方便程式使用
    - ![image](https://hackmd.io/_uploads/SyoUGaDTC.png)


# 程式編寫
- 在 stm32f4xx_hal_adc.c 中的" Execution of ADC conversions "有講述ADC轉換使用方法，基本上分成輪詢和中斷兩種，因應控制需求，使用中斷方法
    - ![image](https://hackmd.io/_uploads/B18V4TvTA.png)
- 在user begin 2 區段加入以下，以啟用ADC與其中斷
    ``` c++=
    // adc_interrupt enable
    HAL_ADC_Start_IT(&hadc1); 
    ```
- 依據設定，ADC轉換會再觸發後進行轉換，上面做的是連續轉換，所以會不斷自動觸發
- 轉換完成後會觸發中斷，可以像timer中斷一樣調用get函式獲取ADC值
    ``` c++=
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
	{
	  if (HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
	  {
	    adc_val = HAL_ADC_GetValue(&hadc1);
	  }
	}
    ```    