1,AD轉換基本概念
51 單片機系統內部運算時用的全部是數字量,即0 和1,因此對單片機系統而言,無法直接操作模擬量,必須將模擬量轉換成數字量。所謂數字量,就是用一系列0 和1 組成的二進制代碼表示某個信號大小的量。用數字量表示同一個模擬量時,數字位數可以多也可以少,位數越多則表示的精度越高,位數越少表示的精度就越低。?
ADC(analog to digital converter)也稱為模數轉換器,是指一個將模擬信號轉變為數字信號。單片機在采集模擬信號時,通常都需要在前端加上A/D 芯片。?
?A(A,analog,模擬的,D,digital,數字的)現實世界是模擬的,連續分布的,無法被分成有限份;計算機世界是數字的,離散分布的,是可以被分成有限份的;AD轉換就是把一個物理量從模擬的轉換成數字的。
AD轉換中的主要概念:
(1)位數,AD轉換后轉出來的二進制數由幾位二進制來表示。位數越多,越細膩;
(2)量程,AD轉換器可以接受的模擬量的范圍;
(3)精度,簡單理解就是轉出來到底有多準;
(4)分辨率,AD轉換器轉出來的二進制數,每一格表示多少;
(5)轉換速率(轉換時間);?
例:輸入電壓范圍0-5V,AD轉換輸出位數是10,精度是0.01V,則:量程為0-5V,分辨率為:(5-0)/2exp(10)=0.00488V,譬如一次AD轉換后得到的數據是1010101010,則對應的電壓值為:3.328V,考慮精度后為3.33V?。
AD轉換在系統中存在的方式:
(1)CPU外部擴展專用AD芯片;
(2)CPU內部集成AD模塊(內部外設);?
電池單體的電壓采集芯片有一種叫AFE的(Anlog Front End),即是一種AD轉換模塊,采集單體的電壓轉換為數字量發給MCU。?
2,AD轉換原理圖和數據手冊?
?ET2046 AD轉換模塊通過AIN0/AIN1/AIN2分別連接滑線變阻器、熱敏電阻、光敏電阻。與單片機連接的接口為CS(使能接口,低有效)、CLK(時鐘信號)、DI(數據輸入,從單片機到AD轉換模塊)、DO(數據輸出、從AD轉換模塊到單片機)。
X+、Y+、VBAT?和AUX 模擬信號經過片內的控制寄存器選擇后進入ADC,ADC 可以配置為單端或差分模式。選擇VBAT和AUX 時應該配置為單端模式;作為觸摸屏應用時,應該配置為差分模式。
單片機在對AD轉換模塊進行控制時,控制字節由DIN 輸入的控制字命令格式如下所示:
Bit7為開始位,為1表示一個新的控制字節到來,為0則忽略PIN引腳上的數據;
A2-A0為通道選擇位,表示選擇哪個通道的輸入電壓進行AD轉換;
MODE為12/8位轉換分辨率選擇位,為1選擇8位轉換分辨率,為0選擇12位分辨率;
SER/DFR:單端輸入方式/差分輸入方式選擇,為1是單端輸入方式,為0是差分輸入方式;
PD1-PD0為低功耗模式選擇位,若為11,器件總處于供電狀態,若為00,器件在轉換之間處于低功耗模式。
單端模式時采集通道的選擇如下表所示(通過上述控制字節的A2-A1進行選擇):?
選擇X+通道、12位分辨率、單端模式、低功耗模式的控制字節命令:0b1001 0100 = 0x94。?
AD轉換模塊的時序圖如下所示:?
從時序圖上可見,轉換模塊進入工作狀態時,CS為低,DCLK為低,DIN和DOUT不用關注;首先通過DIN數據線從單片機發送控制字節到AD轉換模塊,在DCLK的上升沿AD轉換模塊讀入數據(從高到低進行讀入),當8位控制字節命令發送完成后,轉換模塊進入busy狀態,即轉換模塊開始進行AD轉換,此時間可從數據手冊獲取,一般程序中通過延遲一段時間來進行處理;然后單片機在DOUT數據線讀取轉換模塊發出的數據,每個DCLK的下降沿轉換模塊會將一位數據發送到DOUT數據線上,仍然是從高到低的順序。
可見AD轉換和單片機的通訊方式類似于SPI通訊。?
3,AD轉換代碼?
AD采樣轉換代碼包括單片機和AD轉換模塊寫入命令和讀取數據的底層時序代碼,通過串口顯示采樣數據代碼,以及main文件。
ET2046.c底層時序代碼:?
#include "ET2046.h"
#include <intrins.h>void delay10us(void) //誤差 0us
{unsigned char a,b;for(b=1;b>0;b--)for(a=2;a>0;a--);
}unsigned int read_AD_value(unsigned char cmd)
{unsigned char i = 0;unsigned int AD_Value = 0;CS = 0;SCLK = 0;for(i = 0;i < 8;i++){DIN = cmd >> 7;cmd <<= 1; //注意將一個數據移位后再賦給本身的運算符位 <<=SCLK = 1;_nop_();SCLK = 0;_nop_();}delay10us();SCLK = 1; //發送一個時鐘周期,清除BUSY_nop_();_nop_();SCLK = 0;_nop_();_nop_();for(i = 0;i<12;i++){AD_Value <<= 1;SCLK = 1;_nop_();SCLK = 0;_nop_();AD_Value |= DOUT;}CS = 1;return AD_Value;}
ET2046.h?
#ifndef __ET2046_H__
#define __ET2046_H__#include <reg51.h>sbit SCLK = P1^0;
sbit CS = P1^1;sbit DIN = P1^2;
sbit DOUT = P1^3;unsigned int read_AD_value(unsigned char cmd);#endif
串口調試代碼:?
#include "uart.h"// 串口設置為: 波特率9600、數據位8、停止位1、奇偶校驗無
// 使用的晶振是11.0592MHz的,注意12MHz和24MHz的不行
void uart_init(void)
{// 波特率9600SCON = 0x50; // 串口工作在模式1(8位串口)、允許接收PCON = 0x00; // 波特率不加倍// 通信波特率相關的設置TMOD = 0x20; // 設置T1為模式2TH1 = 253;TL1 = 253; // 8位自動重裝,意思就是TH1用完了之后下一個周期TL1會// 自動重裝到TH1去TR1 = 1; // 開啟T1讓它開始工作
// ES = 1;
// EA = 1;
}// 通過串口發送1個字節出去
void uart_send_byte(unsigned char c)
{// 第1步,發送一個字節SBUF = c;// 第2步,先確認串口發送部分沒有在忙while (!TI);// 第3步,軟件復位TI標志位TI = 0;
}void uart_send_adVal(unsigned int val)
{uart_send_byte(val>>8); //AD采樣的數據為12位的,首先左移8位串口輸出高4位uart_send_byte(val); //再輸出低8位
}
注意:因為AD采樣的數據是12位的數據,串口每次只能發送8位數據,需要分兩次將12位數據進行發送;?
#ifndef __UART_H__
#define __UART_H__#include <reg51.h>void uart_init(void);
void uart_send_byte(unsigned char c);
void uart_send_adVal(unsigned int val);#endif
main.c函數?
#include "ET2046.h"
#include "uart.h"#define AIN0 0x94 //滑動變阻器
#define AIN1 0xd4 //熱敏電阻
#define AIN2 0xa4 //光敏電阻void delay1s(void) //誤差 0us
{unsigned char a,b,c;for(c=167;c>0;c--)for(b=171;b>0;b--)for(a=16;a>0;a--);
}void main()
{unsigned int ad_val = 0;uart_init();//uart_send_adVal(0xabc); //測試代碼//uart_send_byte(0x0d);//uart_send_byte(0x0a);//while(1);while(1){ad_val= read_AD_value(AIN2);uart_send_adVal(ad_val);uart_send_byte(0); //發送數據0區分每次采樣數值delay1s(); //如何實現在串口調試助手中發送一次采樣數據后換行?}}
思考:上述代碼中,main函數是通過while()不斷采樣和發送AD轉換的數據,如何通過中斷來采樣和發送AD轉換數據??
4,AD轉換代碼-直接在串口顯示電壓值?
串口助手中看到的數據以16進制顯示或以對應字符形式來顯示,因此在顯示AD轉換的電壓時不直觀,為了直觀顯示采集到的電壓值,通過對采集到的電壓值根據ASCii表對每一個十進制數轉化為對應的數字符號,如下圖所示,入藥顯示電壓值中的數字5,只需要發送5+48的十進制數,在串口助手中就可以看到對應的符號5。
代碼如下:?
5,DA轉換?
待完善?