|
更加實用的ADC轉(zhuǎn)換程序
來接著繼續(xù)分享單片機的一些學(xué)習(xí)經(jīng)歷,上一次,我們寫了一個測試電壓的程序,但是這樣的程序是不實用的,所以我們這一節(jié),來介紹個比較實用的程序,還是測電池電壓,然后我們解釋一下這個代碼,這一節(jié)狠狠狠重要。。。
先把代碼獻(xiàn)上,然后我們再來分析- /*******************************************************************************
- * 文件名: ADC轉(zhuǎn)換器使用
- * 描 述: 電池電壓
- * 功 能:中斷方式
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- #include "stc15w.h"//頭文件
- #include "intrins.h"
- /*******************************************************************************
- * 文件名: 重定義
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- typedef unsigned char uint8;
- typedef unsigned int uint16;
- typedef unsigned long uint32;
- #define ADC_POWER 0x80 //ADC電源控制位
- #define ADC_FLAG 0x10 //ADC完成標(biāo)志
- #define ADC_START 0x08 //ADC起始控制位
- #define ADC_SPEEDLL 0x00 //540個時鐘
- //#define ADC_SPEEDL 0x20 //360個時鐘
- //#define ADC_SPEEDH 0x40 //180個時鐘
- //#define ADC_SPEEDHH 0x60 //90個時鐘
- /*******************************************************************************
- * 文件名:全局變量定義區(qū)域
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2015.03.03)
- *******************************************************************************/
- uint8 val,ch;
- uint16 temp;
- bit flag_ad2 = 0;//電壓采集完成標(biāo)志
- bit flag_coll1 = 0;//數(shù)據(jù)采集間隔
- uint16 Adresult_val = 0;//采集的AD數(shù)值xx
- uint8 ad_count = 0; //采集AD的次數(shù)計數(shù)器
- /*******************************************************************************
- * 文件名:共陽數(shù)碼管真值表
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2015.03.03)
- *******************************************************************************/
- code uint8 LedChar[] = {
- 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
- 0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xc1
- };
- /*******************************************************************************
- * 文件名:單獨位定義
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- sbit LED0 = P1^0;//第1組LED
- sbit LED1 = P1^1;//第2組LED
- sbit LED2 = P1^2;//第3組LED
- sbit LED3 = P1^3;//第4組LED
- sbit LED4 = P1^4;//第5組LED
- sbit LED5 = P3^2;//第6組LED
- sbit LED6 = P0^0;//第7組LED
- sbit LED7 = P0^1;//第8組LED
- sbit LEDS1 = P3^3;//數(shù)碼管1
- sbit LEDS2 = P3^4;//數(shù)碼管2
- sbit LEDS3 = P3^6;//數(shù)碼管3
- sbit LEDS4 = P3^7;//數(shù)碼管4
- /*******************************************************************************
- * 文件名:函數(shù)前置聲明
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void Mcu_Port_Init();
- void LedScan();
- void Time0_Init();//定時器0
- void InitADC(void);
- uint16 VolTage_Monitor(uint8 times); //AD轉(zhuǎn)換與查表處理程序
- /*******************************************************************************
- * 文件名
- * 描 述: 主函數(shù)
- * 功 能:入口
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void main(void)
- {
- Mcu_Port_Init();//IO上電初始化
- Time0_Init();
- InitADC();
- while(1)
- {
- VolTage_Monitor(16);//采集16次數(shù)據(jù)
- }
- }
- /*******************************************************************************
- * 文件名:void LedScan()
- * 描 述: LED刷新
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void LedScan()
- {
- static uint8 i = 0;
- P2 = 0Xff;
- switch(i)
- {
- case 0: LEDS4 = 0;LEDS1 = 1;P2 = 0x7f & LedChar[16];i++;break;
- case 1: LEDS1 = 0;LEDS2 = 1;P2 = LedChar[val / 10 % 10];i++;break;
- case 2: LEDS2 = 0;LEDS3 = 1;P2 = LedChar[val % 10];i++;break;
- case 3: LEDS3 = 0;LEDS4 = 1;P2 = LedChar[17];i = 0;break;
- default:break;
- }
- }
- /*******************************************************************************
- * 文件名:void InitADC(void)
- * 描 述: //初始化 AD 轉(zhuǎn)換
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2015.03.03)
- *******************************************************************************/
- void InitADC(void)
- {
- P1ASF = 0xE0; //設(shè)置 P1 口為模擬口
- ADC_RES = 0; //清除結(jié)果寄存器
- CLK_DIV |= 0x20; //ADRJ 為 1,ADC_RES 存放高兩位結(jié)果,ADC_RESL 存放低 8 位結(jié)果
- //ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START;
- ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START; //ADC上電并延時
- }
- /*******************************************************************************
- * 文件名:void adc_isr() interrupt 5 using 1
- * 描 述: 中斷服務(wù)程序
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2015.03.03)
- *******************************************************************************/
- void adc_isr() interrupt 5 using 1
- {
- EADC = 1;//開ADC中斷
- ADC_CONTR &= !ADC_FLAG; //清除ADC中斷標(biāo)志
- temp = ADC_RES;
- temp <<= 8;
- temp |= ADC_RESL;
- flag_ad2 = 1; //電壓采集完成標(biāo)志
- ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | 5;
- EADC = 0;//關(guān)閉中斷
- }
- /*******************************************************************************
- * 文件名:VolTage_Monitor(void);
- * 描 述: 電壓結(jié)果計算
- * 功 能:模編程塊化
- * 作 者:大核桃
- * 版本號:1.0.1(2015.03.03)
- *******************************************************************************/
- uint16 VolTage_Monitor(uint8 times) //AD轉(zhuǎn)換與查表處理程序
- {
- if(flag_coll1) //每次采集AD的時間間隔標(biāo)志位
- {
- if(ad_count < times)//連續(xù)采集16次后再把求總數(shù)據(jù)的平均值
- {
- if(flag_ad2 == 1) //完成一次AD采樣
- {
- flag_ad2 = 0; //清除完成一次采樣的標(biāo)志位
- Adresult_val = Adresult_val + temp;
- ad_count++;
- ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | 5;
- }
- } //右移動一位數(shù)據(jù)就相當(dāng)于整除以2
- else //已經(jīng)采集完16次數(shù)據(jù),這個時候把總累加數(shù)據(jù)除以16就可以求得平均值了
- {
- Adresult_val >>= 4;
- val = ((Adresult_val) * 2 * (3.3 / 1023) * 10);//放大10
- Adresult_val = 0; //AD暫存清零
- temp = 0; //把采集AD的結(jié)果清零
- ad_count = 0; //把采集次數(shù)重新清零
- }
- }
- return val;//返回采集的電壓數(shù)值
- }
- /*******************************************************************************
- * 文件名:void Time0_Init()
- * 描 述: 定時器0初始化
- * 功 能:1毫秒@11.0592MHz
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void Time0_Init(void)
- {
- AUXR |= 0x80; //定時器時鐘1T模式
- TMOD &= 0xF0; //設(shè)置定時器模式
- TL0 = 0xCD; //設(shè)置定時初值
- TH0 = 0xD4; //設(shè)置定時初值
- ET0 = 1;
- TR0 = 1; //定時器0開始計時
- EA = 1;
- }
- /*******************************************************************************
- * 文件名:
- * 描 述: 中斷函數(shù)
- * 功 能:1毫秒@11.0592MHz
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void ET0_IRQHandler() interrupt 1
- {
- static uint8 tmrcoll1 = 0;//數(shù)據(jù)采集間隔
- EADC = 0; //在定時中斷中禁止AD中斷
- TL0 = 0xCD; //設(shè)置定時初值
- TH0 = 0xD4; //設(shè)置定時初值
- tmrcoll1++;//數(shù)據(jù)采集時間累加
- if(tmrcoll1 >= 2) // 2 = 2ms //1
- {
- tmrcoll1 = 0;
- flag_coll1 = 1;//數(shù)據(jù)采集間隔標(biāo)志位 2ms讀取一次數(shù)據(jù)
- }
- LedScan();
- EADC = 1; //在定時中斷中打開AD中斷
-
- }
- /*******************************************************************************
- * 文件名:void Mcu_Port_Init()
- * 描 述: io初始化
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void Mcu_Port_Init()
- {
- IE = 0xa8;//允許AD轉(zhuǎn)換
- //將P0口低二位配置為推挽輸出
- //234567位配置位高阻輸入
- P0M1 = 0xFC;//1111 1100
- P0M0 = 0X03;//0000 0011
- //P0 = 0X01;//第6個
- //P0 = 0X02;//第7個
- //高3位配置高阻輸入,用作模擬口
- //其他配置推挽輸出,驅(qū)動LED
- P1M1 = 0xE0;//1110 0000
- P1M0 = 0X1F;//0001 1111
- //P2口配置準(zhǔn)雙向口
- P2M1 = 0X00;
- P2M0 = 0X00;
- P2 = 0Xff; //上電為1111 1111
- // //P54,P55口為推挽輸出
- P5M1 = 0X00;
- P5M0 = 0X00;
- P5 = 0xFF;
- //P37,P36,3.2,P3.3 P3.4口為推挽輸出
- P3M1 = 0X00;
- P3M0 = 0XFC;
- P3 = 0X23; //0010 0111//第5個LED端口
-
- LED0 = 0;//第1組LED,如果使能請置為1
- LED1 = 0;
- LED2 = 0;
- LED3 = 0;
- LED4 = 0;
- LED5 = 0;
- LED6 = 0;
- LED7 = 0;
- }
復(fù)制代碼
先來介紹一些基本的理論知識,不然的話,可能有些東西無法搞懂。
關(guān)于ADC的參考電壓
因為我們的電子時鐘是鋰電池供電的,電壓是3.7V的,我們這里用了一個3.3V的穩(wěn)壓芯片662K,輸出3.3V直接作為單片機的電源,也作為ADC的參考電壓,這里我們簡化了設(shè)計,沒有用外部的參考電壓源,對于一個簡單的來說,這樣也是可以的。
關(guān)于ADC的位數(shù)和分辨率
在這里,我們選擇ADC工作在10位方式,10位的ADC,是從0-1023,那么分辨率也就是3.3/1023 = 0.0032258064516129V,大概一個分辨率3mv左右。
關(guān)于轉(zhuǎn)換時間和轉(zhuǎn)換速率
轉(zhuǎn)換時間和轉(zhuǎn)換速率是倒數(shù)的關(guān)系,所謂的轉(zhuǎn)換時間,指的是ADC從開始啟動,到ADC轉(zhuǎn)換完成出結(jié)果,這個時間該怎么去計算呢?我們在程序中選擇了時鐘頻率是11.0592MHZ,那么我們ADC的時鐘頻率也就是11.0592MHZ了,在程序中,我們選擇了540個時鐘周期完成一個ADC轉(zhuǎn)換,轉(zhuǎn)換速率也就是20KHZ左右,轉(zhuǎn)換時間大約是48US左右
關(guān)于采樣頻率和采樣周期
采樣頻率和采樣周期也是互為倒數(shù)的關(guān)系,這個和上面的轉(zhuǎn)換時間,轉(zhuǎn)換速率非常容易讓人搞迷糊,關(guān)于采樣頻率,有一個采樣定理,叫奈奎斯特采樣定律,這個定律說的是,采樣頻率不能低于輸入ADC的信號的最高頻率的2倍,舉個例子,比如上面這樣的情況,我們選擇540個時鐘周期完成一次ADC轉(zhuǎn)換,那么轉(zhuǎn)換速率是20KHZ,那么也就是說,如果我們要保證信號采集的是完整的波形,那么這個輸入的信號不能超過10KHZ,你想想看,如果輸入的信號大于10KHZ,而你轉(zhuǎn)換速率是20KHZ,如果采樣頻率小于20KHZ,那么可能你還沒有完成一個完整的ADC轉(zhuǎn)換過程,或者采集的波形不是完整的,那么這樣的ADC的結(jié)果跟實際值比較會存在嚴(yán)重的失真,這樣是不被允許的。
好了,經(jīng)過以上知識的鋪墊,再來看程序代碼就應(yīng)該比較容易懂了,在程序中,我們選擇了在ADC進(jìn)中斷前打開EADC,處理完數(shù)據(jù)后,要關(guān)閉EADC這個ADC轉(zhuǎn)換中斷使能標(biāo)志位,防止其他中斷或者任務(wù)打斷ADC的采集。我們在任務(wù)中選擇的采樣頻率是500HZ,也就是2MS啟動ADC采集依次數(shù)據(jù),連續(xù)采集16次,因為我們所采集的電壓信號的變化頻率沒有那么快速,所以,我們這里是可以這樣用的,當(dāng)啟動了一次AD轉(zhuǎn)換之后,進(jìn)行計數(shù),如果小于16次,那么一直在IF里面執(zhí)行,當(dāng)系統(tǒng)檢測到完成一個AD轉(zhuǎn)換,將AD轉(zhuǎn)換完成標(biāo)志位清零,將讀取的相關(guān)通道的ADC數(shù)值累加,繼續(xù)采集,如果采集完成了,那么進(jìn)行取平均運算,然后將結(jié)果計算出來,賦值給相關(guān)的變量就可以了,然后對相關(guān)的變量或者緩沖區(qū)清零,這樣,顯示在數(shù)碼管上的電壓結(jié)果是比較穩(wěn)定的。和前面那個例子不一樣,我們這個例子用的是STC15W單片機定時器0的1T模式,也就是說比原來快了12倍,這個移植的時候一定要注意。
好了,今天就到這里吧,源碼奉獻(xiàn)上。
007 實用的AD轉(zhuǎn)換器的使用.rar
(50.8 KB, 下載次數(shù): 259)
2018-10-13 09:22 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|
評分
-
查看全部評分
|