PCF8591 A/D转换模块

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

PCF8591接线原理图:

  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(数字转模拟信号)转换的模拟输出端。

PCF8591地址字节:

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

PCF8591控制字节:

 控制字节用于控制 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 <reg52.h>
#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<<1)|0x01); 	//寻址 PCF8591,指定后续为读操作
    I2CReadACK(); 				//先空读一个字节,提供采样转换时间
    val = I2CReadNAK(); 		//读取刚刚转换完的值
    I2CStop();
    return val;
}

/* ADC 转换值转为实际电压值的字符串形式,str-字符串指针,val-AD 转换值 */
void ValueToString(unsigned char *str, unsigned char val)
{
    val = (val*50) / 255;	  //电压值=转换结果*5.0V/255,式中的 50 隐含了一位十进制小数
    str[0] = (val/10) + '0';  //整数位空字符
    str[1] = '.'; 			  //小数点
    str[2] = (val%10) + '0';  //小数位空字符
    str[3] = 'V'; 			  //电压单位
    str[4] = '\0'; 			  //结束符
}

/* 配置并启动 T0,ms-T0 定时时间 */
void ConfigTimer0(unsigned int ms)
{
    unsigned long tmp; 				//临时变量
    tmp = 11059200 / 12; 			//定时器计数频率
    tmp = (tmp * ms) / 1000; 		//计算所需的计数值
    tmp = 65536 - tmp; 				//计算定时器重载值
    tmp = tmp + 12; 				//补偿中断响应延时造成的误差
    T0RH = (unsigned char)(tmp>>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 <REGX52.H>

//注意: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;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');
	}
}

/**
  * @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(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(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(2,i-1)%2+'0');
	}
}

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

/******************************I2C.c 文件程序源代码******************************/
#include <reg52.h>
#include <intrins.h>
#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;
}

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值