目錄
AD/DA介紹
AD(Analog to Digital):模擬-數字轉換,將模擬信號轉換為計算機可操作的數字信號
DA(Digital to Analog):數字-模擬轉換,將計算機輸出的數字信號轉換為模擬信號
AD/DA轉換打開了計算機與模擬信號的大門,極大的提高了計算機系統的應用范圍,也為模擬信號數字化處理提供了可能
硬件電路模型
AD轉換通常有多個輸入通道,用多路選擇開關連接至AD轉換器,以實現AD多路復用的目的,提高硬件利用率
AD/DA與單片機數據傳送可使用並口(一次性輸入8位,速度快、原理簡單),也可使用串口(一位一位的輸入,接線少、使用方便)
可將AD/DA模塊直接集成在單片機內,這樣直接寫入/讀出寄存器就可進行AD/DA轉換,單片機的IO口可直接復用為AD/DA的通道
硬件電路圖
典型的AD轉換器芯片是ADC0808/AD0809但是這塊板子為了更好的拓展性接的是XPT2046,好像是用來驅動電阻觸摸顯示屏的,但是在這里用來進行AD轉換也行。
同樣的DA轉換常用的是DAC0832這塊芯片,但是這里顯然也不是,但是結構圖很清楚的發現他是一個PWM型的DA轉換器
這里看不懂也沒有關系,我們后面說這么配置
運算放大器
運算放大器(簡稱“運放”)是具有很高放大倍數的放大電路單元。內部集成了差分放大器、電壓放大器、功率放大器三級放大電路,是一個性能完備、功能強大的通用放大電路單元,由於其應用十分廣泛,現已作為基本的電路元件出現在電路圖中
運算放大器可構成的電路有:電壓比較器、反相放大器、同相放大器、電壓跟隨器、加法器、積分器、微分器等
運算放大器電路的分析方法:虛短、虛斷(負反饋條件下)
運放電路
AD原理
DA原理
其實這個DA轉換和我們之前講到過的PWM是有幾分相似的:實事上只要我們加上低通濾波器和電壓跟隨器,來增強穩定與驅動。PWM也可以實現DA轉換
AD/DA性能指標
分辨率:指AD/DA數字量的精細程度,通常用位數表示。例如,對於5V電源系統來說,8位的AD可將5V等分為256份,即數字量變化最小一個單位時,模擬量變化5V/256=0.01953125V,所以,8位AD的電壓分辨率為0.01953125V,AD/DA的位數越高,分辨率就越高
轉換速度:表示AD/DA的最大采樣/建立頻率,通常用轉換頻率或者轉換時間來表示,對於采樣/輸出高速信號,應注意AD/DA的轉換速度
XPT2046的使用介紹與配置
在配置的時候主要對這些端口進行配置:
sbit XPY2046_CS=P3^5;//開始中斷
sbit XPY2046_DCLK=P3^6;//相當於時鍾
sbit XPY2046_DIN=P3^4;//輸入
sbit XPY2046_DOUT=P3^7;//輸出
Command位介紹:
A2,A1,A0根據上面的XPT2046的手冊來按需求是測XP,YP還是別的位連接的器件(在這里的結構圖中,AIN0連接的是AD1502即變阻器,AIN1連接的是NTC1熱敏電阻,AIN2連接的是GR1光敏電阻)
第七位S置1,表示開始。
第三位MODE置1,選擇8位為轉換分辨率。
第二位SER/DFR置1,表示單端輸入方式。
第一位PD1置0,用外部VCC提供的電壓。
第0位PD0置0,不開啟低功耗模式。
XPT2046的時序
代碼解釋:
XPT2046代碼:
#include <REGX52.H>
#include <INTRINS.H>
//引腳定義
sbit XPY2046_CS=P3^5;//開始中斷
sbit XPY2046_DCLK=P3^6;//相當於時鍾
sbit XPY2046_DIN=P3^4;//輸入
sbit XPY2046_DOUT=P3^7;//輸出
#define XPT2046_VBAT 0xAC
#define XPT2046_AUX 0xEC
#define XPT2046_XP 0x9C //或者0xBC
#define XPT2046_YP 0xDC
/**
* @brief ZPT2046讀取AD值
* @param Command 命令字,范圍:頭文件內定義的宏,結尾的數字表示轉換的位數
* @retval AD轉換后的數字量,范圍:8位為0~255,12位為0~4095
*/
unsigned int XPT2046_ReadAD(unsigned char Command)
{
unsigned char i;
unsigned int Data=0;
XPY2046_DCLK=0;
XPY2046_CS=0;
for(i=0;i<8;i++)//依次將8位發出
{
XPY2046_DIN=Command&(0x80>>i);
XPY2046_DCLK=1;
XPY2046_DCLK=0;
}
for(i=0;i<16;i++)
//時序要讀16位,有效位只有前面的12位,因為之前MODE選擇的是讀8位,所以最后8位置0
//時序要讀16位,有效位只有前面的12位,如果MODE選擇的是讀16位,那么最后4位置0,那么要將第36行改寫為Data>>4
{
XPY2046_DCLK=1;
XPY2046_DCLK=0;
if(XPY2046_DOUT){Data|=(0x8000>>i);}
}
XPY2046_CS=1;
return Data>>8;//因為之前MODE選擇的是讀8位
}
主函數:
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "XPT2046.h"
unsigned int ADValue;
void main(void)
{
LCD_Init();
LCD_ShowString(1,1,"ADJ NTC GR");
while(1)
{
ADValue=XPT2046_ReadAD(XPT2046_XP); //讀取AIN0,可調電阻
LCD_ShowNum(2,1,ADValue,3); //顯示AIN0
ADValue=XPT2046_ReadAD(XPT2046_YP); //讀取AIN1,熱敏電阻
LCD_ShowNum(2,6,ADValue,3); //顯示AIN1
ADValue=XPT2046_ReadAD(XPT2046_VBAT); //讀取AIN2,光敏電阻
LCD_ShowNum(2,11,ADValue,3); //顯示AIN2
Delay(100);
}
}
LCD1602代碼:
#include <REGX52.H>
//引腳配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函數定義:
/**
* @brief LCD1602延時函數,12MHz調用可延時1ms
* @param 無
* @retval 無
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602寫命令
* @param Command 要寫入的命令
* @retval 無
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602寫數據
* @param Data 要寫入的數據
* @retval 無
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602設置光標位置
* @param Line 行位置,范圍:1~2
* @param Column 列位置,范圍:1~16
* @retval 無
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函數
* @param 無
* @retval 無
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位數據接口,兩行顯示,5*7點陣
LCD_WriteCommand(0x0c);//顯示開,光標關,閃爍關
LCD_WriteCommand(0x06);//數據讀寫操作后,光標自動加一,畫面不動
LCD_WriteCommand(0x01);//光標復位,清屏
}
/**
* @brief 在LCD1602指定位置上顯示一個字符
* @param Line 行位置,范圍:1~2
* @param Column 列位置,范圍:1~16
* @param Char 要顯示的字符
* @retval 無
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置開始顯示所給字符串
* @param Line 起始行位置,范圍:1~2
* @param Column 起始列位置,范圍:1~16
* @param String 要顯示的字符串
* @retval 無
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置開始顯示所給數字
* @param Line 起始行位置,范圍:1~2
* @param Column 起始列位置,范圍:1~16
* @param Number 要顯示的數字,范圍:0~65535
* @param Length 要顯示數字的長度,范圍:1~5
* @retval 無
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
Delay函數
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
DA轉換的代碼其實就是和之前電機控制調速是一樣的,利用的是PWM來模擬脈沖來實現。這里用與呼吸燈相同的代碼后位聲明時這樣,sbit DA=P2^1;替換之前的端口即可