只要单片机的端口和你的一样只要复制下来用keil编译就可实验。
一路差分输入用数码管显示 p0段p1位没用锁存器。不一样话显示部分可自己编译 ,其他无需改动。
#include <reg52.h>。
#include <intrins.h> 。
#define AddWr 0x90 //写数据地址 。
#define AddRd 0x91 //读数据地址。
#define _Nop() _nop_() //定义空指令。
。
bit ack; //应答标志位。
#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换。
#define CtrlPort P1。
sbit SDA=P2^5;
sbit SCL=P2^4;
unsigned char code DuanMa[10]={0xc0,0xf9,0xa4,0xb0, 0x99,0x92,0x82,0xf8,0x80,0x90};// 显示段码值0~9。
unsigned char code WeiMa[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//分别对应相应的数码管点亮,即位码。
unsigned char TempData[8]; //存储显示值的全局变量。
extern bit ack;。
unsigned char ReadADC(unsigned char Chl);。
bit WriteDAC(unsigned char dat);。
/*------------------------------------------------。
主程序。
------------------------------------------------*/。
void DelayUs2x(unsigned char t)。
{
while(--t);
/*------------------------------------------------。
mS延时函数,含有输入参数 unsigned char t,无返回值。
unsigned char 是定义无符号字符变量,其值的范围是。
0~255 这里使用晶振12M,精确延时请使用汇编。
------------------------------------------------*/。
void DelayMs(unsigned char t)。
while(t--)
{
//大致延时1mS
DelayUs2x(245);。
DelayUs2x(245);。
}
void Display(unsigned char FirstBit,unsigned char Num)。
static unsigned char i=0;。
DataPort=0; //清空数据,防止有交替重影。
CtrlPort=WeiMa[i+FirstBit]; //取位码 。
DataPort=TempData[i]; //取显示数据,段码。
i++;
if(i==Num)。
i=0;
/*------------------------------------------------。
定时器初始化子程序。
------------------------------------------------*/。
void Init_Timer0(void)。
TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响 。
//TH0=0x00; //给定初值。
//TL0=0x00;
EA=1; //总中断打开。
ET0=1; //定时器中断打开。
TR0=1; //定时器开关打开。
/*------------------------------------------------。
定时器中断子程序。
------------------------------------------------*/。
void Timer0_isr(void) interrupt 1 。
TH0=(65536-2000)/256; //重新赋值 2ms。
TL0=(65536-2000)%256;。
Display(0,3);
void Start_I2c()。
SDA=1; //发送起始条件的数据信号。
_Nop();
SCL=1;
_Nop(); //起始条件建立时间大于4.7us,延时。
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0; //发送起始信号。
_Nop(); //起始条件锁定时间大于4μ。
_Nop();
_Nop();
_Nop();
_Nop(); 。
SCL=0; //钳住I2C总线,准备发送或接收数据。
_Nop();
_Nop();
/*------------------------------------------------。
结束总线。
------------------------------------------------*/。
void Stop_I2c()。
SDA=0; //发送结束条件的数据信号。
_Nop(); //发送结束条件的时钟信号。
SCL=1; //结束条件建立时间大于4μ。
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1; //发送I2C总线结束信号。
_Nop();
_Nop();
_Nop();
_Nop();
/*----------------------------------------------------------------。
字节数据传送函数 。
函数原型: void SendByte(unsigned char c);。
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对。
此状态位进行操作.(不应答或非应答都使ack=0 假) 。
发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
------------------------------------------------------------------*/。
void SendByte(unsigned char c)。
unsigned char BitCnt;。
for(BitCnt=0;BitCnt<8;BitCnt++) //要传送的数据长度为8位。
{
if((c<<BitCnt)&0x80)SDA=1; //判断发送位。
else SDA=0; 。
_Nop();
SCL=1; //置时钟线为高,通知被控器开始接收数据位。
_Nop();
_Nop(); //保证时钟高电平周期大于4μ。
_Nop();
_Nop();
_Nop(); 。
SCL=0;
}
_Nop();
_Nop();
SDA=1; //8位发送完后释放数据线,准备接收应答位。
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
if(SDA==1)ack=0; 。
else ack=1; //判断是否接收到应答信号。
SCL=0;
_Nop();
_Nop();
/*----------------------------------------------------------------。
字节数据传送函数 。
函数原型: unsigned char RcvByte();。
功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
发完后请用应答函数。
------------------------------------------------------------------*/ 。
unsigned char RcvByte()。
unsigned char retc;。
unsigned char BitCnt;。
retc=0;
SDA=1; //置数据线为输入方式。
for(BitCnt=0;BitCnt<8;BitCnt++)。
{
_Nop(); 。
SCL=0; //置时钟线为低,准备接收数据位。
_Nop();。
_Nop(); //时钟低电平周期大于4.7us。
_Nop();。
_Nop();。
_Nop();。
SCL=1; //置时钟线为高使数据线上数据有效。
_Nop();。
_Nop();。
retc=retc<<1;。
if(SDA==1)retc=retc+1; //读数据位,接收的数据位放入retc中。
_Nop();。
_Nop(); 。
}
SCL=0;
_Nop();
_Nop();
return(retc);。
/*----------------------------------------------------------------。
应答子函数。
原型: void Ack_I2c(void);。
----------------------------------------------------------------*/。
/*void Ack_I2c(void)。
SDA=0;
_Nop();
_Nop();
_Nop(); 。
SCL=1;
_Nop();
_Nop(); //时钟低电平周期大于4μ。
_Nop();
_Nop();
_Nop();
SCL=0; //清时钟线,钳住I2C总线以便继续接收。
_Nop();
_Nop();
}*/
/*----------------------------------------------------------------。
非应答子函数。
原型: void NoAck_I2c(void);。
----------------------------------------------------------------*/。
void NoAck_I2c(void)。
SDA=1;
_Nop();
_Nop();
_Nop(); 。
SCL=1;
_Nop();
_Nop(); //时钟低电平周期大于4μ。
_Nop();
_Nop();
_Nop();
SCL=0; //清时钟线,钳住I2C总线以便继续接收。
_Nop();
_Nop();
main()
unsigned char num=0;。
Init_Timer0();。
while (1) //主循环。
{
num=ReadADC(0);。
TempData[2]=DuanMa[num/100]; 。
TempData[1]=DuanMa[(num%100)/10];。
TempData[0]=DuanMa[(num%100)%10];。
//主循环中添加其他需要一直工作的程序。
DelayMs(100);
}
/*------------------------------------------------。
读AD转值程序。
输入参数 Chl 表示需要转换的通道,范围从0-3。
返回值范围0-255
------------------------------------------------*/。
unsigned char ReadADC(unsigned char Chl)。
{
unsigned char Val;。
Start_I2c(); //启动总线。
SendByte(AddWr); //发送器件地址。
if(ack==0)return(0);。
SendByte(0x62|Chl); //发送器件子地址 0x62控制字节可自行更改,可参考资料的图五 部分更改《8591中文资料》
if(ack==0)return(0);。
Start_I2c();。
SendByte(AddWr+1);。
if(ack==0)return(0);。
Val=RcvByte();。
NoAck_I2c(); //发送非应位。
Stop_I2c(); //结束总线。
return(Val);
}
这是吴鉴鹰单片机开发板配套的例程。
/*-----------------------------------------------。
名称:IIC协议 PCF8591 AD/DA转换。
内容:使用4路AD中的4路检测外部模拟量输入 使用液晶显示。
------------------------------------------------*/。
#include <reg52.h>。
#include "i2c.h"。
#include "delay.h"。
#include "1602.h"。
#include <stdio.h>。
#define AddWr 0x90 //写数据地址。
#define AddRd 0x91 //读数据地址。
extern bit ack;。
unsigned char ReadADC(unsigned char Chl);。
bit WriteDAC(unsigned char dat);。
/*------------------------------------------------。
主程序
------------------------------------------------*/。
main()
unsigned char num=0,i;。
unsigned char temp[7];//定义显示区域临时存储数组。
float Voltage; //定义浮点变量。
LCD_Init(); //初始化液晶。
DelayMs(20); //延时有助于稳定。
LCD_Clear(); //清屏。
while (1) //主循环。
{
for(i=0;i<5;i++) //连续读5次,取最后一次,以便读取稳定值。
num=ReadADC(0); //读取第1路电压值,范围是0-255。
Voltage=(float)num*5/256; //根据参考电源VREF算出时间电压,float是强制转换符号,用于将结果转换成浮点型。
sprintf(temp,"V0 %3.2f ",Voltage);//格式输出电压值,%3.2f 表示浮点输出,共3位数,小数点后2位。
LCD_Write_String(0,0,temp);。
for(i=0;i<5;i++)。
num=ReadADC(1);。
Voltage=(float)num*5/256;。
sprintf(temp,"V1 %3.2f ",Voltage);。
LCD_Write_String(8,0,temp);。
for(i=0;i<5;i++)。
num=ReadADC(2);。
Voltage=(float)num*5/256;。
sprintf(temp,"V2 %3.2f ",Voltage);。
LCD_Write_String(0,1,temp);。
for(i=0;i<5;i++)。
num=ReadADC(3);。
Voltage=(float)num*5/256;。
sprintf(temp,"V3 %3.2f ",Voltage);。
LCD_Write_String(8,1,temp);。
//主循环中添加其他需要一直工作的程序。
DelayMs(200);
/*------------------------------------------------。
读AD转值程序
输入参数 Chl 表示需要转换的通道,范围从0-3。
返回值范围0-255
操作分四步:
(1)、发送地址字节,选择该器件。
(2)、发送控制字节,选择相应通道。
(3)、重新发送地址字节,选择该器件的读写。
(4)、接收目标通道的数据。
------------------------------------------------*/。
unsigned char ReadADC(unsigned char Chl)。
unsigned char Val;。
Start_I2c(); //启动总线。
SendByte(AddWr); //发送器件地址。
if(ack==0)return(0);。
SendByte(0x40|Chl); //发送器件子地址。
if(ack==0)return(0);。
Start_I2c();
SendByte(AddWr+1); //1001 0001 是读命令。
if(ack==0)return(0);。
Val=RcvByte();
NoAck_I2c(); //发送非应位。
Stop_I2c(); //结束总线。
return(Val);
提供一个思路,8591转换得到一个数据,这个数据是0~255之间,可以将该数据换算成0~100之间的PWM值,再用定时器产生一个相应的脉冲序列就可以控制LED亮度了,定时时间可以定在50~100us这样PWM周期就在5~10毫秒。
也可能是你启动通道一时,它刚刚结束了通道四的转换。
把启动顺序改为:2341,即可。
建一个读取ad模数转换值函数,有返回值的,然后直接在main函数中调用就行了。
原文地址:http://www.qianchusai.com/pcf8591%E8%BF%94%E5%9B%9E255.html