C8051F320內部有一個10位逐次逼近型ADC,可以工作在單端方式或者差分方式。
一、簡要原理
單片機內集成了2個多路選擇器,分別作為ADC的正輸入信號和負輸入。
正輸入端由寄存器AMX0P控制輸入信號,可以是P1~P3、溫度傳感器、VDD之一;
負輸入端由寄存器AMX0N控制輸入信號,可以是P1~P3、VREF、GND之一。
單負輸入端選擇GND時,采用單端方式;其他情況則采用差分方式,即用正端相對于負端的電壓進行轉換。
*采用并行口作為輸入信號時,必須將對應輸入引腳設為模擬輸入,并且對應的SKIP要設置為1,即跳過
二、寄存器
1、轉換結果保存在兩個8位寄存器ADC0H和ADC0L中,由于轉換結果是10位,可以自由選擇在寄存器中采用左對齊或者右對齊(下詳)
單端方式下,轉換結果直接保存為10位的無符號數
差分方式下,結果保存為10位有符號整數(原說明:2的補碼。未深究)
2、溫度傳感器的輸出電壓由下面公式決定:
V = 2.86(T)+ 776 (單位mv) 從圖表看,最高只能在1000mv左右,也就是100°時僅1V上下
3、AD啟動方式
有六種啟動方式,包括四個定時器溢出啟動、特定位置1啟動和P0.6上升沿啟動。(下詳)
采用中斷時,中斷號interrupt 10
4、跟蹤方式 對跟蹤不是很理解!
5、寄存器AMX0P,正輸入通道選擇寄存器
00H~10H,對應P1.0~P3.0 0x1E對應溫度傳感器 0x1F對應VDD
寄存器XMXON,負輸入通道選擇寄存器
00H~10H,對應P1.0~P3.0 0x1E對應VREF 0x1F對應GND,此時為單端方式
6、寄存器ADC0CF,配置寄存器,控制轉換時鐘,和數據保存方向
D7~D3 時鐘控制位,大意就是分頻數,系統時鐘與AD時鐘的比值減1
D2,為0時數據右對齊,為1時左對齊
7、寄存器ADC0CN,控制寄存器。
D7,AD使能,0時禁止轉換
D6,跟蹤方式,不懂
D5,中斷標志位,要手動清0
D4,讀取時為忙標志位,寫入時可為啟動標志位,但不知道要不要清0
D3,窗口比較中斷標志,不是很清楚
D2~D0 轉換方式選擇,且受到D6影響。具體未深究。
8、寄存器REF0CN,電壓基準控制器
與AD的關系不完全明朗
D3 決定了電壓基準 D2 使能溫度傳感器
下面是完整例程,但不包含12864的C文件。
完整例程下載地址:http://m.raoushi.com/f/c8051sad.rar
#include "c8051f3xx.h"
#include "12864.h"
完整例程下載地址:http://m.raoushi.com/f/c8051sad.rar
#include "c8051f3xx.h"
#include "12864.h"
#define uchar unsigned char
#define uint unsigned int
#define uint unsigned int
sfr16 TMR2RL = 0xca; // Timer2 reload value定時器2重載值
sfr16 TMR2 = 0xcc; // Timer2 counter定時器2計數器
sfr16 TMR2 = 0xcc; // Timer2 counter定時器2計數器
//這兩行相當好用,直接把T2的四個8位寄存器重新定義成2個16位寄存器!!!!!!
uchar adnum3,adnum2,adnum1; //打算用來顯示的數百位、十位和個位,在這里沒有進行運算,只是直接的AD結果
sbit led0=P0^5; //連了個發光二極管觀察有沒有死機。。
uchar code table[]="0123456789"; //顯示數據用
uchar code hang1[17]="1234567"; //以下四行為12864初始顯示的內容,不重要
uchar code hang2[17]="123456789 ";
uchar code hang3[17]="2011-7-10 星期日";
uchar code hang4[17]=" 00:00:00 ";
void Timer2_ISR (void) interrupt 5 // T2只是用來溢出的,沒程序,清標志位而已
{
TF2H = 0;
}
void Adc_ConvComplete_ISR (void) interrupt 10 //AD中斷程序,除了清標志位,只是把數據送到12864第三行
{
AD0INT = 0;
lcd_pos(3,0);
adnum3 = ADC0H/100;
adnum2 = (ADC0H%100)/10;
adnum1 = (ADC0H%100)%10;
disp_only(tableaa[adnum3]);
disp_only(tableaa[adnum2]);
disp_only(tableaa[adnum1]);
}
void Port_Init (void) //端口初始化,哪個位要輸入,就要設為模擬,并跳過
{
P1MDIN = 0x7F;
P0MDIN = 0xff;
P2MDIN = 0xff;
P3MDIN = 0x00;
P0MDOUT |= 0xfF;
P1MDOUT |= 0x0F;
P2MDOUT |= 0x0C;
P1SKIP = 0x80;
P0SKIP = 0x00;
P2SKIP = 0x00;
XBR0 = 0x00;
XBR1 = 0x40;
}
void Timer_Init (void)
{
TMR2CN = 0x00;
CKCON &= ~0xF0;
TMR2RL = 0;
TMR2 = 0xffff;
ET2 = 1;
TR2 = 1;
}
void ADC0_Init (void)
{
REF0CN = 0x0E; // VDD作為基準電壓,啟用內部溫度傳感器
AMX0P = 0x10; // 10是P3.0,試過07(1.7)和1E(溫度),都沒問題
ADC0CF = 0xFC; // 11111,32分頻? (*表示分頻數-1=31) D2為1,左對齊
AMX0N = 0x1F; // 單端方式
ADC0CN = 0xC2; // T2溢出作為啟動信號
EIE1 |= 0x08; // 開中斷
}
void System_Init (void)
{
PCA0MD &= ~0x40;
OSCICN |= 0x03;
Port_Init ();
Timer_Init ();
ADC0_Init ();
}
void DelayMS(uint x)
{
uchar i;
while(x--)
{
for(i=120;i>0;i--);
}
}
void main(void)
{
System_Init ();
lcd_init();
clr_screen();
DelayMS(100);
lcd_pos(0,0);
disp_chinese(hang1);
lcd_pos(1,0);
disp_chinese(hang2);
lcd_pos(2,0);
disp_chinese(hang3);
lcd_pos(3,0);
disp_chinese(hang4);
EA = 1;
while (1)
{
led0=~led0;
DelayMS(5500);
}
}
測試結果:
1、用開發板上的電位器,可以讓高位結果在0~255之間變化
2、用溫度傳感器,室溫下顯示高位為65,*近筆記本風扇數秒后變成66,說明溫度有變化,因為是高位,不明顯,也沒計算。
3、用全新的南孚電池一節,顯示穩定的121。
____________________________________________________________________________
一、修改程序,將10位數字量轉換成0~1023顯示在屏幕上
*用變位器,可以實現1~1023的變化,1和0之間無法穩定,直接接地也無法顯示0
*用一節全新電池,顯示485,VDD為3V,偏差不大。
二、再修改程序,將正輸入設為溫度傳感器
*顯示263,稍微加熱后變成265,仍然覺得不夠明顯
*263對應電壓約770mv,根據公式換算溫度是負數。。。。
在筆記本散熱口放了一會升到268,還是低!
