本帖最后由 michaelchain 于 2022-1-10 01:10 編輯
STC8G和STC8H的ADC部分在寄存器設置上基本上一致, 但是不同型號對應的通道編號, 通道數量和精度有區別
通道數量和精度
對應STC8G/STC8H的各個系列的通道數量和精度如下. [td]產品線 | ADC 分辨率 | ADC 通道數 | STC8H1K08 系列 | 10 位 | 9 通道 | STC8H1K28 系列 | 10 位 | 12 通道 | STC8H3K64S4 系列 | 12 位 | 12 通道 | STC8H3K64S2 系列 | 12 位 | 12 通道 | STC8H8K64U 系列 | 12 位 | 15 通道 | STC8H2K64T 系列 | 12 位 | 15 通道 | STC8H4K64TLR 系列 | 12 位 | 15 通道 | STC8H4K64TLCD 系列 | 12 位 | 15 通道 | STC8H4K64LCD 系列 | 12 位 | 15 通道 |
通道的選擇使用寄存器ADC_CONTR的低4位, 對應STC8G/STC8H的各個系列, 這個寄存器的數值對應的通道如下 [td] | STC8H1K28 | STC8H1K08 | STC8H3K64S4
STC8H3K64S2 | STC8H8K64U
STC8H2K64T
STC8H4K64TLR | STC8H4K64TLCD
STC8H4K64LCD | STC8G1K08A | STC8G1K08
STC8G1K08T | STC8G2K64S4
STC8G2K64S2 | 0000 | P1.0/ADC0 | P1.0/ADC0 | P1.0/ADC0 | P1.0/ADC0 | P1.0/ADC0 | P3.0/ADC0 | P1.0/ADC0 | P1.0/ADC0 | 0001 | P1.1/ADC1 | P1.1/ADC1 | P1.1/ADC1 | P1.1/ADC1 | P1.1/ADC1 | P3.1/ADC1 | P1.1/ADC1 | P1.1/ADC1 | 0010 | P1.2/ADC2 | N/A | P1.2/ADC2 | P5.4/ADC2 | P5.4/ADC2 | P3.2/ADC2 | P1.2/ADC2 | P1.2/ADC2 | 0011 | P1.3/ADC3 | N/A | N/A | P1.3/ADC3 | P1.3/ADC3 | P3.3/ADC3 | P1.3/ADC3 | P1.3/ADC3 | 0100 | P1.4/ADC4 | N/A | N/A | P1.4/ADC4 | P1.4/ADC4 | P5.4/ADC4 | P1.4/ADC4 | P1.4/ADC4 | 0101 | P1.5/ADC5 | N/A | N/A | P1.5/ADC5 | P1.5/ADC5 | P5.5/ADC5 | P1.5/ADC5 | P1.5/ADC5 | 0110 | P1.6/ADC6 | N/A | P1.6/ADC6 | P1.6/ADC6 | P6.2/ADC6 | N/A | P1.6/ADC6 | P1.6/ADC6 | 0111 | P1.7/ADC7 | N/A | P1.7/ADC7 | P1.7/ADC7 | P6.3/ADC7 | N/A | P1.7/ADC7 | P1.7/ADC7 | 1000 | P0.0/ADC8 | P3.0/ADC8 | P0.0/ADC8 | P0.0/ADC8 | P0.0/ADC8 | N/A | P3.0/ADC8 | P0.0/ADC8 | 1001 | P0.1/ADC9 | P3.1/ADC9 | P0.1/ADC9 | P0.1/ADC9 | P0.1/ADC9 | N/A | P3.1/ADC9 | P0.1/ADC9 | 1010 | P0.2/ADC10 | P3.2/ADC10 | P0.2/ADC10 | P0.2/ADC10 | P0.2/ADC10 | N/A | P3.2/ADC10 | P0.2/ADC10 | 1011 | P0.3/ADC11 | P3.3/ADC11 | P0.3/ADC11 | P0.3/ADC11 | P0.3/ADC11 | N/A | P3.3/ADC11 | P0.3/ADC11 | 1100 | N/A | P3.4/ADC12 | P0.4/ADC12 | P0.4/ADC12 | P0.4/ADC12 | N/A | P3.4/ADC12 | P0.4/ADC12 | 1101 | N/A | P3.5/ADC13 | P0.5/ADC13 | P0.5/ADC13 | P0.5/ADC13 | N/A | P3.5/ADC13 | P0.5/ADC13 | 1110 | N/A | P3.6/ADC14 | P0.6/ADC14 | P0.6/ADC14 | P0.6/ADC14 | N/A | P3.6/ADC14 | P0.6/ADC14 | 1111 | 1.19Vref | 1.19Vref | 1.19Vref | 1.19Vref | 1.19Vref | 1.19Vref | 1.19Vref | 1.19Vref |
轉換結果的對齊格式
ADC采樣的精度實際上是不能設置的, 采樣都是用的當前型號的最大精度, 結果存儲在[ADC_RES, ADC_RESL]這兩個寄存器. 為方便不同場合使用不同精度的結果, 可以將結果設置為左對齊或右對齊. - 當設置為左對齊時, 可以只取ADC_RES的值(8位), 忽略最后兩位.
- 當設置位右對齊時, 根據實際的精度, 可以取ADC_RES的低4位(12位精度)或低2位(10位精度), 加上ADC_RESL得到最終結果.
轉換的時間消耗
一個完整的 ADC 轉換時間為 = Tsetup + Tduty + Thold + Tconvert - Tsetup: 轉換的通道切換時間, 可以設置為1個或2個ADC時鐘周期
- Tduty: 轉換的采樣時間, 默認是最低的11個ADC時鐘, 最高為32個ADC時鐘周期
- Thold: 通道選擇的保持時間, 可以選擇1, 2, 3, 4個ADC時鐘周期
- Tconvert: 轉換時間是固定的, 10bit精度是10個ADC時鐘, 12bit精度是12個ADC時鐘
以上的時間單位都是ADC時鐘周期, 每個ADC時鐘周期占用系統時鐘(SYSCLK)的數量是可以設置的, 使用ADCCFG寄存器的低三位, 可以設置為最低2個系統時鐘周期到最高32個系統時鐘周期 對于轉換的最高頻率, DS上寫了全局限制
- 10 位 ADC 的速度不能高于 500KHz
- 12 位 ADC 的速度不能高于 800KHz
- 轉換的采樣時間不能小于 10,建議設置為 15
硬件連線
STC8G/STC8H的ADC硬件連線有兩種: 帶AVcc,AGrnd和不帶AVcc,AGrnd
帶 AVcc,AGrnd
高端型號STC8H3K64S2系列, 例如會帶這兩個pin腳, 分別對應的是轉換目標的電壓參考值和對地參考值. 對于普通使用, 這兩個可以直接接到VCC和GND, 連線為 - AGrnd -> GND
- AVcc -> VCC
- AVref -> VCC
- Vcc -> VCC
- Gnd -> GND
- ADC1 -> 采樣點
復制代碼
不帶 AVcc,AGrnd
低端型號以及STC8G系列不帶這兩個pin, 只需要接AVref, 采樣點與MCU共地連接, 連線為 - AVref -> VCC
- Vcc -> VCC
- Gnd -> GND
- ADC1 -> Test voltage
復制代碼
演示用例說明
關于如何運行演示用例, 可以參考前面介紹的Keil C51和VSCode PlatformIO的配置說明
使用ADC1進行8位ADC轉換, 主動查詢(polling)方式
下面的例子, 使用主動查詢的方式每隔0.1秒對P1.1口進行ADC轉換, 精度8位, 將結果輸出至串口 main.c代碼 - #include "fw_hal.h"
- void main(void)
- {
- uint8_t res;
- // 調整系統頻率, 如果使用STC-ISP設定頻率, 需要將這行注釋掉
- SYS_SetClock();
- // 用于結果輸出
- UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
- // 將 ADC1(GPIO P1.1) 設為高阻輸入
- GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
- // 使用通道: ADC1
- ADC_SetChannel(0x01);
- // 設置ADC時鐘 = SYSCLK / 2 / (1+1) = SYSCLK / 4
- ADC_SetClockPrescaler(0x01);
- // 設置結果左對齊, 只需要取值 ADC_RES
- ADC_SetResultAlignmentLeft();
- // 開啟ADC電源
- ADC_SetPowerState(HAL_State_ON);
- while(1)
- {
- // 開始轉換
- ADC_Start();
- // 等待兩個系統時鐘
- NOP();
- NOP();
- // 檢查轉換結果標志位是否置位
- while (!ADC_SamplingFinished());
- // 清除結果標志位
- ADC_ClearInterrupt();
- // 讀取結果
- res = ADC_RES;
- // 通過串口1輸出
- UART1_TxString("Result: ");
- UART1_TxHex(res);
- UART1_TxString("\r\n");
- // 等待100ms后再次進行轉換
- SYS_Delay(100);
- }
- }
復制代碼
使用ADC1進行10位/12位ADC轉換, 中斷(interrupt)方式
下面的例子, 使用中斷的方式對P1.1口進行ADC連續轉換, 精度10位(或12位, MCU型號不同精度不同), 每隔0.1秒將結果輸出至串口 - #include "fw_hal.h"
- // 16位變量用于記錄轉換結果
- uint16_t res;
- // 處理中斷的方法, 使用宏定義保證Keil C51和SDCC的兼容性
- INTERRUPT(ADC_Routine, EXTI_VectADC)
- {
- // 先清除中斷位
- ADC_ClearInterrupt();
- // 結果低8位
- res = ADC_RESL;
- // 結果高8位
- res |= (ADC_RES & 0x0F) << 8;
- // 再次啟動, 使得ADC連續轉換,
- ADC_Start();
- }
- void main(void)
- {
- // 設置系統頻率
- SYS_SetClock();
- // 結果輸出
- UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
- // 設置P11高阻輸入模式
- GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
- // 使用通道: ADC1
- ADC_SetChannel(0x01);
- // ADC時鐘 = SYSCLK / 2 / (1+15) = SYSCLK / 32
- ADC_SetClockPrescaler(0x0F);
- // 右對齊, 方便轉換為雙字節的結果
- ADC_SetResultAlignmentRight();
- // 開啟全局中斷和ADC中斷
- EXTI_Global_SetIntState(HAL_State_ON);
- EXTI_ADC_SetIntState(HAL_State_ON);
- // 開啟ADC電源
- ADC_SetPowerState(HAL_State_ON);
- // 開始ADC轉換
- ADC_Start();
- while(1)
- {
- // 轉換結果輸出
- UART1_TxString("Result: ");
- UART1_TxHex(res >> 8);
- UART1_TxHex(res & 0xFF);
- UART1_TxString("\r\n");
- SYS_Delay(100);
- }
- }
復制代碼
使用ADC1, ADC2雙通道進行轉換, 中斷(interrupt)方式
下面介紹一個更實用的例子, 中斷形式進行多通道ADC轉換, 可以用于無線小車遙控, 雙聲道音頻采樣等 - #include "fw_hal.h"
- // 用于記錄當前采樣的通道編號
- uint8_t pos;
- // 記錄各通道的采樣結果
- uint16_t res[2];
- // 中斷處理方法
- INTERRUPT(ADC_Routine, EXTI_VectADC)
- {
- ADC_ClearInterrupt();
- // 記錄采樣結果
- res[pos] = ADC_RESL;
- res[pos] |= (ADC_RES & 0x0F) << 8;
-
- // 切換到下一個通道
- pos = (pos+1) & 0x1;
- if (pos == 0)
- {
- /**
- * 在采樣頻率較高時, 加上這兩句能提高精度. 其機制是切換到開漏模式清除采樣口上的殘留電壓
- GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_InOut_OD);
- GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
- */
- ADC_SetChannel(0x01);
- }
- else
- {
- /**
- * Uncomment these lines in high speed ADC
- GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_InOut_OD);
- GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_Input_HIP);
- */
- ADC_SetChannel(0x02);
- }
- ADC_Start();
- }
- // 下面的代碼和前面的基本上是一樣的, 就不詳細注釋了
- void main(void)
- {
- SYS_SetClock();
- // For debug print
- UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
- // Channel: ADC1
- ADC_SetChannel(0x01);
- // ADC Clock = SYSCLK / 2 / (1+15) = SYSCLK / 32
- ADC_SetClockPrescaler(0x0F);
- // Right alignment, high 2-bit in ADC_RES, low 8-bit in ADC_RESL
- ADC_SetResultAlignmentRight();
- // Enable interrupts
- EXTI_Global_SetIntState(HAL_State_ON);
- EXTI_ADC_SetIntState(HAL_State_ON);
- // Turn on ADC power
- ADC_SetPowerState(HAL_State_ON);
- // Set ADC1(P1.1), ADC2(P1.2) HIP
- GPIO_P1_SetMode(GPIO_Pin_1|GPIO_Pin_2, GPIO_Mode_Input_HIP);
- // Start ADC
- ADC_Start();
- while(1)
- {
- UART1_TxString("Result: ");
- UART1_TxHex(res[0] >> 8);
- UART1_TxHex(res[0] & 0xFF);
- UART1_TxChar(' ');
- UART1_TxHex(res[1] >> 8);
- UART1_TxHex(res[1] & 0xFF);
- UART1_TxString("\r\n");
- SYS_Delay(100);
- }
- }
復制代碼
結束
以上就是STC8H使用FwLib_STC8封裝庫進行ADC轉換的演示用例說明. 在實際使用中, 主動查詢(polling)方式下的延時時間精度不高,
如果對采樣的時間間隔精度有要求, 建議使用中斷的形式.
|