這是我參與8月更文挑戰的第13天,活動詳情查看:8月更文挑戰markdown
當STM8單片機使用ADC功能讀取多個通道的值時,可使用單次模式,採樣完一個通道以後,從新初始化另外一個通道,而後採樣,採樣完成後繼續從新初始化切換下一個通道。可是這樣採樣起來太麻煩。STM8單片機提供了一個掃描模式,能夠依次按照順序採樣多個通道的值,多個通道所有采樣完成後,會置位標誌位,這樣就能夠一次性將多個通道的值所有讀出來。app
其中官方文檔介紹以下:函數
從文檔中能夠看出,採樣都是從0通道開始的,好比想採樣3個通道值,那麼採樣的通道號就爲0---3。若是想採樣三、4通道,那麼也得從0通道開始,也就是說要從0通道開始掃描到4通道,就算0、一、2通道不用,它也會掃描,就這一點很差。post
單次採樣的時候,ADC_CSR寄存器中的通道號指的是要採樣的通道號,要採樣那個通道就設置爲幾,而在掃描模式下,這個通道號指的是要掃描的最大通道號,掃描都是從0通道開始。這裏設置時要注意。ui
單次掃描模式的配置其實也很簡單,只是比單次模式多了一個開啓掃描的設置。下面直接經過寄存器來講明單次掃描模式的設置方法。url
首先看ADC_CSR寄存器,這個寄存器裏面只須要設置一個通道轉換位,這個通道指的是掃描的最大通道。在單次模式下這個通道指的是要讀取的通道值,而在掃描模式下,這個通道指的是,最大掃描通道數。掃描都是從0通道開始。EOC位是轉換結束標誌位,這個在初始化的時候不用設置,只是在讀取數據的時候通道判斷這個位來讀取。spa
接下來看ADC_CR1寄存器,這個寄存器要設置的只有一個,就是ADON位,用來控制ADC的轉換開關,爲1時啓動轉換功能,爲0時關閉轉換功能。預分頻位和單次轉換模式默認值都爲0,恰好符合咱們的需求,也就是默認2分頻,單次轉換模式。3d
接下來看ADC_CR2寄存器,這個寄存器必須設置的位其實只有一個,就是要要使用掃描模式必須設置SCAN位爲1,其餘位使用默認值就行。若是數據對齊方式使用左對齊的話,就不用設置。這裏使用的是數據右對齊,因此須要將ALIGN位也設置爲1.其餘位默認爲0.code
最後就是這個ADC_TDR寄存器,用來禁止施密特觸發器,主要是用來下降單片機功耗。固然這個寄存器不用設置也能夠。若是要設置的話,使用了哪幾個ADC通道,就將對應的通道位置1就行。orm
下面就能夠開始編寫代碼了:
#include "adc.h"
#include "main.h"
#include "led.h"
_Bool ADC_flag = 0; //ADC轉換成功標誌
u16 ADC_DB[10] = {0};
u16 adc_data[5] = {0};
//AD通道引腳初始化
void ADC_GPIO_Init( void ) {
PD_DDR &= ~( 1 << 2 ); //PD2 設置爲輸入
PD_CR1 &= ~( 1 << 2 ); //PD2 設置爲懸空輸入
PD_DDR &= ~( 1 << 3 ); //PD3 設置爲輸入
PD_CR1 &= ~( 1 << 3 ); //PD3 設置爲懸空輸入
PC_DDR &= ~( 1 << 4 ); //PC4 設置爲輸入
PC_CR1 &= ~( 1 << 4 ); //PC4設置爲懸空輸入
}
//設置爲 單次掃描模式
//ch 爲ADC通道 連續轉換AIN0---AINch 通道的數據
void ADC_CH_Init( u8 ch ) {
char l = 0;
ADC_GPIO_Init();
ADC_CR1 &= ~( 7 << 4 ); //預分頻 2
ADC_CR2 &= ~( 1 << 6 ); //不使用外部觸發
//禁止 AIN2 AIN4 的施密特觸發器,下降 IO 靜態功耗 PD5,PD6 上的通道若是施密特方式禁用會致使串口沒法收發數據!
ADC_TDRL |= ( 1 << 2 );
ADC_TDRL |= ( 1 << 3 );
ADC_TDRL |= ( 1 << 4 );
ADC_CR1 &= ~( 1 << 1 ); //單次轉換
ADC_CSR |= 0x04; //配置通道號最大的那個
ADC_CR2 |= ( 1 << 3 ); //右對齊
ADC_CR1 |= ( 1 << 0 ); //開啓 ADC
ADC_CR2 |= ( 1 << 1 ); // SCAN = 1 開啓掃描模式
//當首次置位ADON位時,ADC從低功耗模式喚醒。爲了啓動轉換必須第二次使用寫指令來置位ADC_CR1寄存器的ADON位。
for( l = 0; l < 10; l++ ); //延時,保證ADC模塊的上電完成 至少7us
ADC_CR1 |= ( 1 << 0 ); //再次將CR1寄存器的最低位置1 使能ADC 並開始轉換
}
u16 ain2_val = 0,ain3_val = 0,ain4_val = 0;
//讀取採樣電壓值
u16 ReadVol_CHx( void ) {
u16 voltage = 0;
u16 temph = 0;
u8 templ = 0;
while( 1 )
{
LED = !LED; //程序運行一圈耗時 15us
while( ( ADC_CSR & 0x80 ) == 0 ); //等待轉換結束 等待時間爲 4us
ADC_CSR &= ~( 1 << 7 ); // 轉換結束標誌位清零 EOC
//讀取 AIN2 的值
templ = ADC_DB2RL;
temph = ADC_DB2RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );
ain2_val = temph;
//讀取 AIN3 的值
templ = ADC_DB3RL;
temph = ADC_DB3RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );
ain3_val = temph;
//讀取 AIN4 的值
templ = ADC_DB4RL;
temph = ADC_DB4RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );
ain4_val = temph;
ADC_CR1 |= 0x01; //開啓一次轉換
}
return voltage;
}
複製代碼
初始化流程就按照上面分析的流程進行,在啓動ADC轉換的時候,須要開啓兩次。
系統啓動後ADC默認是在低功耗模式下,第一次設置ADON位是將ADC從低功耗模式喚醒,而後須要延時1個轉換週期,等待系統轉換穩定。因此在初始化的時候須要設置兩次ADON爲1。因爲這裏使用的是單次觸發模式,因此每次轉換完成以後,ADC默認就關閉了,若是要繼續轉換,就須要手動將ADON位設置爲1.
也可使用中斷的方式來進行讀取數據。
#include "adc.h"
#include "main.h"
#include "led.h"
_Bool ADC_flag = 0; //ADC轉換成功標誌
u16 ADC_DB[10] = {0};
u16 adc_data[5] = {0};
//AD通道引腳初始化
void ADC_GPIO_Init( void ) {
PD_DDR &= ~( 1 << 2 ); //PD2 設置爲輸入
PD_CR1 &= ~( 1 << 2 ); //PD2 設置爲懸空輸入
PD_DDR &= ~( 1 << 3 ); //PD3 設置爲輸入
PD_CR1 &= ~( 1 << 3 ); //PD3 設置爲懸空輸入
PC_DDR &= ~( 1 << 4 ); //PC4 設置爲輸入
PC_CR1 &= ~( 1 << 4 ); //PC4設置爲懸空輸入
}
//設置爲 單次掃描模式
//ch 爲ADC通道 連續轉換AIN0---AINch 通道的數據
void ADC_CH_Init( u8 ch ) {
char l = 0;
ADC_GPIO_Init();
ADC_CR1 &= ~( 7 << 4 ); //預分頻 2
ADC_CR2 &= ~( 1 << 6 ); //不使用外部觸發
//禁止 AIN2 AIN4 的施密特觸發器,下降 IO 靜態功耗 PD5,PD6 上的通道若是施密特方式禁用會致使串口沒法收發數據!
ADC_TDRL |= ( 1 << 2 );
ADC_TDRL |= ( 1 << 4 );
ADC_CR1 &= ~( 1 << 1 ); //單次轉換
ADC_CSR |= 0x04; //配置通道號最大的那個
ADC_CR2 |= ( 1 << 3 ); //右對齊
ADC_CR1 |= ( 1 << 0 ); //開啓 ADC
ADC_CR2 |= ( 1 << 1 ); // SCAN = 1 開啓掃描模式
ADC_CSR |= ( 1 << 5 ); //EOCIE 使能轉換結束中斷
//當首次置位ADON位時,ADC從低功耗模式喚醒。爲了啓動轉換必須第二次使用寫指令來置位ADC_CR1寄存器的ADON位。
for( l = 0; l < 10; l++ ); //延時,保證ADC模塊的上電完成 至少7us
ADC_CR1 |= ( 1 << 0 ); //再次將CR1寄存器的最低位置1 使能ADC 並開始轉換
}
u16 ain2_val = 0, ain3_val = 0, ain4_val = 0;
u16 temph = 0;
u8 templ = 0;
//讀取採樣電壓值
u16 ReadVol_CHx( void ) {
u16 voltage = 0;
if( ADC_flag == 1 )
{
ADC_flag = 0;
//單通道掃描模式,轉換結果存儲在 ADC_DBxR 寄存器中
//讀取 AIN2 的值
templ = ADC_DB2RL;
temph = ADC_DB2RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );
ain2_val = temph;
//讀取 AIN3 的值
templ = ADC_DB3RL;
temph = ADC_DB3RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );
ain3_val = temph;
//讀取 AIN4 的值
templ = ADC_DB4RL;
temph = ADC_DB4RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );
ain4_val = temph;
ADC_CR1 |= 0x01; //開啓一次轉換
}
return voltage;
}
//AD中斷服務函數 中斷號22
#pragma vector = 24 // IAR中的中斷號,要在STVD中的中斷號上加2
__interrupt void ADC_Handle( void ) {
ADC_CSR &= ~( 1 << 7 ); // 轉換結束標誌位清零 EOC
ADC_flag = 1; // ADC中斷標誌 置1
}
複製代碼
中斷的開啓就是將ADC_CSR寄存器的EOCIE位設置爲1,而後當全部的通道轉換完成以後,就會產生一次中斷,在中斷中讀取數據。
這裏要注意的地方是在掃描模式時,採樣的數據結果不是存放在ADC_DR寄存器中,而是存放在ADC_DBxR寄存器中,通道幾就存放在對應的ADC_DBxR寄存器中,這個寄存器分爲高位和低位兩個。好比通道2的數據,就存放在ADC_DB2RL和ADC_DB2RH寄存器中,在讀取數據的時候也要注意,若是數據是左對齊必須先讀高8位,再讀低位。若是數據是右對齊必須先讀低8位,在讀高8位。
數據對齊在官方手冊中有詳細的說明,在使用的時候要注意這一點。