PCF8951 A/D转换模块
PCF8591 的通信接口是 IIC协议,编程需要对 PCF8591 进行初始化。
PCF8951接线原理图:
- AIN0 ~ AIN3 : 模拟信号的4个输入端口
- A0 ~ A2 : 芯片地址低三位。
- VDD、GND : 电源、地。(电源电压2.5~6V)
- SDA、SCL : IIC总线数据、时钟线。
- OSC : 外部时钟输入端,内部时钟输出端。
- EXT : 内部、外部时钟选择线,使用内部时钟时EXT接地。
- AGND : 模拟信号地。
- VREF : 基准电源端。(内部转换使用)
- AOUT : D/A(数字转模拟信号)转换的模拟输出端。
PCF8951地址字节:
- 高4位 固定是 1001;
- 低三位 是 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;}