> 文档中心 > PCF8951 A/D转换模块

PCF8951 A/D转换模块

PCF8591 的通信接口是 IIC协议,编程需要对 PCF8591 进行初始化。

PCF8951接线原理图:

  1.  AIN0 ~ AIN3 : 模拟信号的4个输入端口
  2. A0 ~ A2 : 芯片地址低三位。
  3. VDD、GND : 电源、地。(电源电压2.5~6V)
  4. SDA、SCL : IIC总线数据、时钟线。
  5. OSC  : 外部时钟输入端,内部时钟输出端。
  6. EXT : 内部、外部时钟选择线,使用内部时钟时EXT接地。
  7. AGND : 模拟信号地。
  8. VREF : 基准电源端。(内部转换使用)
  9. AOUT : D/A(数字转模拟信号)转换的模拟输出端。

PCF8951地址字节:

  1.  高4位 固定是 1001;
  2. 低三位 是 A2,A1,A0,在电路中我们直接接地;R/W作为读写功能。

PCF8951控制字节:

 控制字节用于控制 PCF8591 的功能。控制寄存器的所有位在上电复位时被复位为 0 ;

其中

D3 位和 D7 位是固定的 0,是为芯片以后扩展功能用的。

D1、D0 :这两位是4路A/D通道编号,也就是 AIN0 ~ AIN3 。00 通道0,01 通道1,10 通道2,11 通道3。

D2:是自动增量标志位。设置为1时,模块在采集完通道0数据后继续采集通道1、通道2、通道3。设置单路采集时需要置0。

D5、D4:模拟量输入选择:00为四路单端输入、01为三路差分输入、10为单端与差分配合输入、11为两路差分输入。参考下图。

D6 :D/A是数字转模拟信号输出使能控制位(有效位为1)。我们用的一般是A/D转换,默认为0。

代码部分:

          /***********************************主程序代码************************************/

/*****************************main.c 文件程序源代码******************************/#include #include "LCD1602.h"#include "IIC.h"bit flag300ms = 1; //300ms 定时标志unsigned char T0RH = 0;  //T0 重载值的高字节unsigned char T0RL = 0;  //T0 重载值的低字节void ConfigTimer0(unsigned int ms);unsigned char GetADCValue(unsigned char chn);void ValueToString(unsigned char *str, unsigned char val);void main(){    unsigned char val;    unsigned char str[10];EA = 1; //开总中断    ConfigTimer0(10);   //配置 T0 定时 10ms    LCD_Init(); //初始化液晶    LCD_ShowString(1, 2, "AIN0 AIN1 AIN3"); //显示通道指示 while (1){ if (flag300ms){     flag300ms = 0;//显示通道 0 的电压     val = GetADCValue(0); //获取 ADC 通道 0 的转换值     ValueToString(str, val); //转为字符串格式的电压值     LCD_ShowString(2, 2, str); //显示到LCD1602上     //显示通道 1 的电压     val = GetADCValue(1);     ValueToString(str, val);     LCD_ShowString(2, 7, str);     //显示通道 3 的电压     val = GetADCValue(3);     ValueToString(str, val);     LCD_ShowString(2, 12, str); }    }}/* 读取当前的 ADC 转换值,chn-ADC 通道号 0~3 */unsigned char GetADCValue(unsigned char chn){    unsigned char val;    I2CStart();    if (!I2CWrite(0x48<<1))//寻址 PCF8591,如未应答,则停止操作并返回 0{  I2CStop(); return 0;    }    I2CWrite(0x40|chn); //写入控制字节,选择转换通道    I2CStart();    I2CWrite((0x48<>8); //定时器重载值拆分为高低字节    T0RL = (unsigned char)tmp;    TMOD &= 0xF0; //清零 T0 的控制位    TMOD |= 0x01; //配置 T0 为模式 1    TH0 = T0RH; //加载 T0 重载值    TL0 = T0RL;    ET0 = 1; //使能 T0 中断    TR0 = 1; //启动 T0}/* T0 中断服务函数,执行 300ms 定时 */void InterruptTimer0() interrupt 1{    static unsigned char tmr300ms = 0;    TH0 = T0RH; //重新加载重载值    TL0 = T0RL;    tmr300ms++;    if (tmr300ms >= 30)    //定时 300ms{  tmr300ms = 0; flag300ms = 1;    }}

        

          /***************************LCD1602.c 文件程序源代码*****************************/

/*****************************LCD1602显示代码************************/#include //注意:LCD1602只有2行16列 不能超出范围//LCD_Init();      LCD初始化//LCD_ShowChar(1,1,'A');  1行1列显示一个字符//LCD_ShowString(1,3,"hello");   1行3列显示字符串//LCD_ShowNum(1,9,123,3); 1行9列显示十进制数字 3位 显示数字超出位数最高位消失,多于则最高位补0    //LCD_ShowSignedNum(1,13,-66,2); 1行13列显示有符号十进制数字 2位 位数不包含符号 //LCD_ShowHexNum(2,1,0xA8,2);    显示十六进制数字 A8//LCD_ShowBinNum(2,4,0xAA,8);    显示二进制数字  10101010//引脚配置: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;i0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}}/**  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字  * @param  Line 起始行位置,范围:1~2  * @param  Column 起始列位置,范围:1~16  * @param  Number 要显示的数字,范围:-32768~32767  * @param  Length 要显示数字的长度,范围:1~5  * @retval 无  */void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length){unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}}/**  * @brief  在LCD1602指定位置开始以十六进制显示所给数字  * @param  Line 起始行位置,范围:1~2  * @param  Column 起始列位置,范围:1~16  * @param  Number 要显示的数字,范围:0~0xFFFF  * @param  Length 要显示数字的长度,范围:1~4  * @retval 无  */void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}}

     /**********************************IIC.c 文件程序源代码***************************************/

/******************************I2C.c 文件程序源代码******************************/#include #include #define I2CDelay() {_nop_();_nop_();_nop_();_nop_();}sbit I2C_SCL = P3^7;sbit I2C_SDA = P3^6;/* 产生总线起始信号 */void I2CStart(){    I2C_SDA = 1; //首先确保 SDA、SCL 都是高电平    I2C_SCL = 1;    I2CDelay();    I2C_SDA = 0; //先拉低 SDA    I2CDelay();    I2C_SCL = 0; //再拉低 SCL}/* 产生总线停止信号 */void I2CStop(){    I2C_SCL = 0; //首先确保 SDA、SCL 都是低电平    I2C_SDA = 0;    I2CDelay();    I2C_SCL = 1; //先拉高 SCL    I2CDelay();    I2C_SDA = 1; //再拉高 SDA    I2CDelay();}/* I2C 总线写操作,dat-待写入字节,返回值-从机应答位的值 */bit I2CWrite(unsigned char dat){    bit ack;    //用于暂存应答位的值    unsigned char mask;  //用于探测字节内某一位值的掩码变量for (mask=0x80; mask!=0; mask>>=1)//从高位到低位依次进行{  if ((mask&dat) == 0)//该位的值输出到 SDA 上     I2C_SDA = 0;else     I2C_SDA = 1; I2CDelay(); I2C_SCL = 1; //拉高 SCL I2CDelay(); I2C_SCL = 0; //再拉低 SCL,完成一个位周期    }I2C_SDA = 1; //8 位数据发送完后,主机释放 SDA,以检测从机应答    I2CDelay();    I2C_SCL = 1; //拉高 SCL    ack = I2C_SDA; //读取此时的 SDA 值,即为从机的应答值    I2CDelay();    I2C_SCL = 0; //再拉低 SCL 完成应答位,并保持住总线    //应答值取反以符合通常的逻辑:    //0=不存在或忙或写入失败,1=存在且空闲或写入成功    return (~ack);}/* I2C 总线读操作,并发送非应答信号,返回值-读到的字节 */unsigned char I2CReadNAK(){    unsigned char mask;    unsigned char dat;I2C_SDA = 1; //首先确保主机释放 SDA    for (mask=0x80; mask!=0; mask>>=1)//从高位到低位依次进行{  I2CDelay(); I2C_SCL = 1; //拉高 SCL if(I2C_SDA == 0)//读取 SDA 的值     dat &= ~mask; //为 0 时,dat 中对应位清零else     dat |= mask; //为 1 时,dat 中对应位置 1 I2CDelay(); I2C_SCL = 0; //再拉低 SCL,以使从机发送出下一位    }    I2C_SDA = 1;//8 位数据发送完后,拉高 SDA,发送非应答信号    I2CDelay();    I2C_SCL = 1;     //拉高 SCL    I2CDelay();    I2C_SCL = 0; //再拉低 SCL 完成非应答位,并保持住总线    return dat;}/* I2C 总线读操作,并发送应答信号,返回值-读到的字节 */unsigned char I2CReadACK(){    unsigned char mask;    unsigned char dat;I2C_SDA = 1; //首先确保主机释放 SDA    for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行{  I2CDelay(); I2C_SCL = 1; //拉高 SCL if(I2C_SDA == 0)//读取 SDA 的值     dat &= ~mask; //为 0 时,dat 中对应位清零else     dat |= mask; //为 1 时,dat 中对应位置 1 I2CDelay(); I2C_SCL = 0; //再拉低 SCL,以使从机发送出下一位    }    I2C_SDA = 0; //8 位数据发送完后,拉低 SDA,发送应答信号    I2CDelay();    I2C_SCL = 1; //拉高 SCL    I2CDelay();    I2C_SCL = 0; //再拉低 SCL 完成应答位,并保持住总线    return dat;}