環境
軟件
- uVision V4.02
- ISIS Professional 7.8
芯片
- AT89C51
- ADC0832
仿真圖
實現效果
顯示電壓值並用圖標顯示百分比
具體效果如下所示:
相關代碼及資源
https://github.com/duganlx/DSP
操作小記
芯片介紹
接口說明
CS
:片選使能,低電平芯片使能CH0
:模擬輸入通道0,或作爲IN+/-
使用CH1
:模擬輸入通道0,或作爲IN+/-
使用GND
:芯片參考0電位(地)DI
:數據信號輸入,選擇通道控制DO
:數據信號輸出,轉換數據輸出CLK
:芯片時鐘輸入VCC/REF
:電源輸入及參考電壓輸入(複用)
ADC0832 爲 8 位分辨率 A/D 轉換芯片,其最高分辨可達256 級,可以適應一般的模擬量轉換要求。其內部電源輸入與參考電壓的複用,使得芯片的模擬電壓輸入在 0~5V 之間。芯片轉換時間僅爲 32μS,具有雙數據輸出可作爲數據校驗,以減少數據誤差,轉換速度快且穩定性能強。獨立的芯片使能輸入,使多器件掛接和處理器控制變的更加方便。通過** DI 數據輸入端**,可以輕易的實現通道功能的選擇
單片機控制原理
正常情況下,ADC0832與單片機的接口應爲4條數據線,分別是CS
、CLK
、DO
、DI
。但由於DO
端與DI
端在通信時並未同時有效並與單片機的接口是雙向的,所以電路設計時可以將DO
和DI
並聯在一個數據線上使用。
當ADC0832 未工作時,其CS
輸入端應爲高電平,此時芯片禁用,CLK
和DO/DI
的電平任意
當進行A/D轉換時,須先將CS
使能端置於低電平並且保持低電平直到轉換完全結束。此時芯片開始轉換工作,同時由處理器向芯片時鐘輸入端CLK
輸入時鐘脈衝,DO/DI
端則使用DI
端輸入通道功能選擇的數據信號。
- 在第1個時鐘脈衝的下沉之前
DI
端必須是高電平,表示啓動信號 - 在第2、3個脈衝下沉之前
DI
端應輸入2位數據用於選擇通道功能,具體如下圖所示:
如上圖所示:
- 當兩位數據爲
10
時,只對CH0
進行單通道轉換- 當兩位數據爲
11
時,只對CH1
進行單通道轉換- 當兩位數據爲
00
時,將CH0
作爲正輸入端IN+
,將CH1
作爲負輸入端IN-
- 當兩位數據爲
01
時,將CH0
作爲負輸入端IN-
,將CH1
作爲正輸入端IN+
- 到第3個脈衝的下沉之後
DI
端的輸入電平就失去輸入作用,此時DO/DI
端則開始利用數據輸出DO
進行轉換數據的讀取 - 從第4個脈衝下沉開始由
DO
端輸出轉換數據最高位DATA7
,隨後每一個脈衝下沉DO
端輸出下一位數據 - 直到第11個脈衝時發出最低位數據
DATA0
,一個字節的數據輸出完成 - 也從第11個脈衝開始輸出下一個相反字節的數據,即從第11個脈衝的下沉輸出
DATA0
- 隨後輸出8位數據,直到第19個脈衝時數據輸出完成,也標誌一次A/D轉換的結束
CS
置高電平禁用芯片,直接將轉換後的數據進行處理就可以了
時序圖
代碼編寫
引用庫文件
#include<reg51.h>
#include<string.h>
#include<intrins.h>
宏定義
#define Uint unsigned int
#define Uchar unsigned char
全局變量定義
//對應仿真圖中引腳的對接
sbit CS=P1^0; // chip select
sbit CLK=P1^1; // clock
sbit DIO=P1^2; // data input and output
sbit RS=P2^0; // data register status register
sbit RW=P2^1; // read/write
sbit EN=P2^2; // enable
Uchar disp_buff1[]="VOLTAGE: 0.00V";
Uchar disp_buff2[16];
延時函數
/*
* 延時2微秒
*
* @return
*/
void delay2us()
{
_nop_();
_nop_();
}
AD轉換
/*
* AD轉換
*
* @return 輸出數據
*/
Uchar get_AD_Res()
{
Uchar i, data1=0, data2=0;
CS=0;
//第一個週期:轉換開始
CLK=0;DIO=1;delay2us();
CLK=1;delay2us();
//第二個週期:選擇單通道還是雙通道 DIO=0雙通道差分 或 DIO=1單通道
CLK=0;DIO=1;delay2us();
CLK=1;delay2us();
//第三個週期:DIO選擇CH1-->如果DIO=0 選擇CH0
CLK=0;DIO=0;delay2us();
CLK=1;delay2us();
//等待
CLK=0;DIO=1;delay2us();
//先進來的爲最高位,後進來爲最低位
for(i=0; i<8; i++)
{
CLK=1;delay2us();
CLK=0;delay2us();
/*
* 0000_0000|0000_000想=0000_000想
* 0000_00想0|0000_000翔=0000_00想翔
* 0000_0想翔0|0000_000子=0000_0想翔子
*/
data1=(data1<<1)|(Uchar)DIO;
}
//先進來的爲最低位,後進來爲最高位
for(i=0; i<8; i++)
{
/*
* 0000_0000|0000_000子=0000_000子
* 0000_000子|0000_00翔0=0000_00翔子
* 0000_00翔子|0000_0想00=0000_0想翔子
*/
data2= data2|(Uchar)DIO<<i;
CLK=1;delay2us();
CLK=0;delay2us();
}
//禁止片選
CS=1;
return (data1==data2)?data1:0;
}
lcd相關函數
lcd具體用法及函數說明可以參看:1602字符液晶顯示
/*
* 延時
*
* @param x 時間(不精確)
* @return
*/
void delay_ms(Uint x)
{
Uchar t;
while(x--)
for(t=0; t<120; t++);
}
/*
* 檢測BF(busy flag)位狀態
*
* @return
*/
bit test_BF()
{
Uchar LCD_status;
P0=0xFF;
EN=0;RS=0;RW=1;
EN=1;delay2us();LCD_status=P0;
EN=0;
return (LCD_status&0x80)?1:0;
}
/*
* 寫命令
*
* @param cmd 八位命令
* @return
*/
void write_CMD(Uchar cmd)
{
while(test_BF());
EN=0; RS=0; RW=0;
P0=cmd;
EN=1; _nop_(); EN=0;
}
/*
* 寫數據(一位一位的寫)
*
* @param data8 八位數據
* @return
*/
void write_Data(Uchar data8)
{
while(test_BF());
EN=0; RS=1; RW=0;
P0=data8;
EN=1; _nop_(); EN=0;
}
/*
* 初始化
*
* @return
*/
void init()
{
write_CMD(0x38);delay_ms(1);
write_CMD(0x01);delay_ms(1);
write_CMD(0x06);delay_ms(1);
write_CMD(0x0C);delay_ms(1);
}
/*
* 寫字符串
*
* @param r row
* @param c column
* @param str 字符串
* @return
*/
void write_Str(int r, int c, char *str)
{
int i=0;
unsigned char Addressx[] = {0x80, 0xC0};
unsigned char StartAdd = (Addressx[r] | c);//按位或
write_CMD(StartAdd);
for(i = 0; i < 16; i++){
if(str[i]==0) break;
write_Data(str[i]);
}
// 如果不夠16位,用空格填充
for(;i < 16; i++){
write_Data(' ');
}
}
主函數
void main()
{
Uchar i=0,AD=0;
Uint d=0;
init();
while(1)
{
AD=get_AD_Res();
d=(AD*500.0/256.0); //d需Uint
disp_buff1[10]=d/100+'0';
disp_buff1[12]=(d/10)%10+'0';
disp_buff1[13]=d%10+'0';
write_Str(0,0,disp_buff1);
//填充實心和空心的框框
i=(Uint)AD*16/256;
memset(disp_buff2,'\xFF',i);
memset(disp_buff2+i,'\xDB',16-i);
write_Str(1,0,disp_buff2);
}
}