一、简介
PCF8575是一个I/O扩展芯片,德州仪器的芯片。有3个硬件地址引脚寻址,也就是说这个芯片可以设置8个IIC器件地址;芯片工作电压2.5V~5.5V,具有16位准双向输入/输出(I/O)端口(P07-P00、P17-P10)。当我们在做嵌入式开发时,MCU接口不够用可以考虑使用这一款芯片扩展,详细查看相关芯片手册。
淘宝有这一款模块售卖,我使用的经验总结是:使用IO扩展模块当输出时,必须加上拉电阻。上拉电阻选择是1K-10K之间就行了。不然驱动的电压很低!
二、例程
备注:使用例程编译的时候会出现警告,已经检查过警告原因是没有调用函数,不影响功能运行。不用理会即可。
例程1:使用STC12使端口持续高低电平切换
//
//PCF8575控制电平和读取电平
//作者:小途
//时间:2022年2月17日
//
#include <STC12C5A60S2.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
//常用数据宏定义
#define MAIN_Fosc 11059200L //宏定义主时钟HZ
//#define MAIN_Fosc 12000000L
//当A0 A1 A2 为000时
#define PCF8575_READ_ADDRESS 0x41 //IIC总线从机8位读地址
#define PCF8575_WRITEADDRESS 0x40 //IIC总线从机8位写地址
/*I2C硬件接口定义*/
sbit SCL = P3^2; //I2C时钟总线
sbit SDA = P3^3; //I2C数据总线
/*I2C常用变量宏定义*/
#define I2cRead 1 //I2C读方向位
#define I2cWrite 0 //I2C写方向
/*全局变量定义*/
bit AckFlag; //应答标志位
uchar PCF8575_LVal,PCF8575_HVal;
//基于STC12单片机1ms延时函数
//函数说明:内部调用
static void Delay1ms()
{
# if MAIN_Fosc == 11059200L
//晶振11.0592MHz
unsigned char i, j;
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
#elif MAIN_Fosc == 12000000L
//晶振12.000000MHZ
unsigned char i, j;
_nop_();
_nop_();
i = 12;
j = 168;
do
{
while (--j);
} while (--i);
#endif
}
//基于STC12单片机1us延时函数
//函数说明:内部调用
static void Delaym1us()
{
# if MAIN_Fosc == 11059200L
//晶振11.0592MHz
_nop_();
#elif MAIN_Fosc == 12000000L
//晶振12.000000MHZ
_nop_();
_nop_();
#endif
}
//基于STC12单片机ms延时函数
//函数说明:外部调用
void Delay_ms(uint time)
{
int i;
for(i=0; i<time; i++)
{
Delay1ms();
}
}
//基于STC12单片机us延时函数
//函数说明:外部调用
void Delay_us(uint time)
{
int i;
for(i=0; i<time; i++)
{
Delaym1us();
}
}
//I2C延时5us函数
void I2cDelay_5us(void)
{
Delay_us(5);
}
//I2C总线起始信号
void I2cStart(void)
{
SCL = 1;
SDA = 1;
I2cDelay_5us();//状态保持5us
SDA = 0;
I2cDelay_5us();//状态保持5us
}
//I2C总线停止信号
void I2cStop(void)
{
SCL = 0;
SDA = 0;
SCL = 1;
I2cDelay_5us();//状态保持5us
SDA = 1;
I2cDelay_5us();//状态保持5us
}
/********************************
*函数名称:ReadACK(void)
*函数输入:无
*函数返回:1非应答,0应答
*函数说明:I2C总线读从机应答信号
********************************/
bit ReadACK(void)
{
uint i;
SCL = 0;//拉低时钟总线,允许从机控制SDA
SCL = 1;//拉高,读SDA
I2cDelay_5us();
while((SDA==1) && (i<250))//等待SDA应答,或者超过时间默认应答
i++;
SCL = 0;
I2cDelay_5us();
return(0);//返回0应答
}
/***************************************
*函数名称:SendACK(bit i)
*函数输入:1主机发送非应答,0发送应答
*函数返回:无
*函数说明:主机发送应答信号
***************************************/
void SendACK(bit i)
{
SCL = 0; //拉低时钟总线,允许主机控制SDA
if(i) //发送非应答
SDA = 1;
else
SDA = 0;
SCL = 1; //拉高总线,让从机读SDA
I2cDelay_5us();
SCL = 0; //拉低时钟总线,允许SDA释放
SDA = 1; //释放数据总线
}
/***************************************
*函数名称:I2cSendByte(uchar DAT)
*函数输入:DAT需要发送的数据
*函数返回:无
*函数说明:I2C发送一个字节数据
***************************************/
void I2cSendByte(uchar DAT)
{
uchar i;
for(i=0; i<8; i++) //分别写8次,每次写1位
{
SCL = 0; //拉低时钟总线,允许SDA变化
if(DAT & 0x80) //先写数据最高位
SDA = 1; //写1
else
SDA = 0; //写0
SCL = 1; //拉高时钟,让从机读SDA
DAT <<= 1; //为发送下一位左移1位
}
SCL = 0; //拉低时钟总线,允许SDA释放
SDA = 1; //释放数据总线
}
/*====================================
函数 :I2cReadByte()
参数 :无
返回值 :返回读出的一字节数据
描述 :I2C总线读一字节数据
====================================*/
uchar I2cReadByte(void)
{
uchar i, DAT;
for(i=0; i<8; i++)//分别读8次,每次读一位
{
DAT <<= 1; //数据左移1位,准备接收一位
SCL = 0; //拉低时钟总线,允许从机控制SDA变化
SCL = 1; //拉高时钟总线,读取SDA上的数据
if(SDA)
DAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
}
return(DAT); //返回读出的数据
}
/*=========================================
*函数名称:PCF8575Read(uchar dat)
*函数输入:要控制硬件的数据(16位)
*函数返回:无
*函数说明:控制PCF引脚
*=========================================*/
void PCF8575Read(uchar dat1, uchar dat2)
{
bit AckTemp=1;
I2cStart();//发送启动位
I2cSendByte(PCF8575_WRITEADDRESS);//发送地址
AckTemp=ReadACK();//接收应答
I2cSendByte(dat1);//控制P0.0-P0.7
AckTemp=ReadACK();//接收应答
I2cSendByte(dat2);//控制P1.0-P1.7
AckTemp=ReadACK();//接收应答
I2cStop();
}
void PCF8575_Read()
{
bit AckTemp = 1;
I2cStart();//发送启动位
I2cSendByte(PCF8575_READ_ADDRESS);//发送地址位
AckTemp=ReadACK();//接收应答
PCF8575_LVal = I2cReadByte();//接收1个字节(P0.0-P0.7)
SendACK(0);//发送应答位
PCF8575_HVal = I2cReadByte();//接收1个字节(P1.0-P1.7)
SendACK(1);//发送非应答位
I2cStop();
}
void main()
{
PCF8575Read(0xff,0xff);//上电复位将PCF8575的电平拉高
while(1)
{
//使控制的led闪烁
Delay_ms(1000);
PCF8575Read(0xff,0xff);
Delay_ms(1000);
PCF8575Read(0x00,0x00);
}
}
例程2:使用STC12读取PCF8575端口电平,通过串口发送当前电平状态
#include <STC12C5A60S2.h>
#include <intrins.h>
//数据类型重定义
#define uchar unsigned char
#define uint unsigned int
//常用数据宏定义
#define MAIN_Fosc 11059200L //宏定义主时钟HZ
//#define MAIN_Fosc 12000000L
//当A0 A1 A2 为000时
//IIC总线从机8位读地址
//IIC总线从机8位写地址
//#define PCF8575_READ_ADDRESS 0x41
//#define PCF8575_WRITEADDRESS 0x40
/*I2C常用变量宏定义*/
#define I2cRead 1 //I2C读方向位
#define I2cWrite 0 //I2C写方向
//IO管脚定义
sbit SDA = P0^7;//I2C时钟总线
sbit SCL = P0^6;//I2C数据总线
sbit INT0 = P3^2;//按键中断引脚
//全局变量定义
uchar PCF8575_LVal;
uchar PCF8575_HVal;
//基于STC12单片机1ms延时函数
//函数说明:内部调用
static void Delay1ms()
{
# if MAIN_Fosc == 11059200L
//晶振11.0592MHz
unsigned char i, j;
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
#elif MAIN_Fosc == 12000000L
//晶振12.000000MHZ
unsigned char i, j;
_nop_();
_nop_();
i = 12;
j = 168;
do
{
while (--j);
} while (--i);
#endif
}
//基于STC12单片机1us延时函数
//函数说明:内部调用
static void Delaym1us()
{
# if MAIN_Fosc == 11059200L
//晶振11.0592MHz
_nop_();
#elif MAIN_Fosc == 12000000L
//晶振12.000000MHZ
_nop_();
_nop_();
#endif
}
//基于STC12单片机ms延时函数
//函数说明:外部调用
void Delay_ms(uint time)
{
int i;
for(i=0; i<time; i++)
{
Delay1ms();
}
}
//基于STC12单片机us延时函数
//函数说明:外部调用
void Delay_us(uint time)
{
int i;
for(i=0; i<time; i++)
{
Delaym1us();
}
}
// 发送I2C启动位
void I2C_Start(void)
{
SDA=1;
SCL=1;
Delay_us(2);
SDA=0;
Delay_us(2);
SCL=0;
Delay_us(2);
}
// 发送I2C停止位
void I2C_Stop(void)
{
SDA=0;
SCL=1;
Delay_us(2);
SDA=1;
Delay_us(2);
SCL=0;
Delay_us(2);
}
// 发送BIT0
void I2C_Send_Bit_0(void)
{
SDA=0;
SCL=1;
Delay_us(2);
SCL=0;
Delay_us(2);
}
// 发送BIT1
void I2C_Send_Bit_1(void)
{
SDA=1;
SCL=1;
Delay_us(2);
SCL=0;
Delay_us(2);
}
// 接收应答信号
bit I2C_Check_Ack(void)
{
SDA=1;
SCL=1;
Delay_us(2);
F0=SDA;
Delay_us(2);
SCL=0;
Delay_us(2);
if(F0==1) return 1;
return 0;
}
// 发送应答信号
void I2C_Ack()
{
SDA=0;
SCL=1;
Delay_us(2);
SDA=0;
Delay_us(2);
SCL=0;
Delay_us(2);
}
void I2C_NoAck()
{
SDA=1;
SCL=1;
Delay_us(2);
SDA=1;
Delay_us(2);
SCL=0;
Delay_us(2);
}
// 写一个字节
void I2C_Write8Bit(unsigned char I2C_data)
{
unsigned char i;
for(i=0;i<8;i++)
{
if((I2C_data<<i)&0x80)
I2C_Send_Bit_1();
else
I2C_Send_Bit_0();
}
}
// 接收一个字节
unsigned char I2C_Read8Bit(void)
{
unsigned char I2C_data=0,i;
for(i=0;i<8;i++)
{
SDA=1;
SCL=1;
Delay_us(2);
F0=SDA;
Delay_us(2);
SCL=0;
if(F0==1)
{
I2C_data=I2C_data<<1;
I2C_data=I2C_data|0x01;
}
else
I2C_data=I2C_data<<1;
}
return I2C_data;
}
// 控制PCF8575引脚电平
void PCF8575_Wirte(unsigned char addr, unsigned int val)
{
bit AckTemp=1;
I2C_Start();
I2C_Write8Bit(0x40|(addr<<1));
I2C_Check_Ack();
I2C_Write8Bit(val);
I2C_Check_Ack();
I2C_Write8Bit(val>>8);
I2C_Check_Ack();
I2C_Stop();
}
// 读出PCF8575引脚电平
void PCF8575_Read(unsigned char addr)
{
bit AckTemp=1;
I2C_Start();
I2C_Write8Bit(0x41|(addr<<1));
AckTemp=I2C_Check_Ack();
PCF8575_LVal=I2C_Read8Bit();
I2C_Ack();
PCF8575_HVal=I2C_Read8Bit();
I2C_NoAck();
I2C_Stop();
}
//串口初始化,晶振11.0592,波特率9600
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x04; //独立波特率发生器时钟为Fosc,即1T
BRT = 0xDC; //设定独立波特率发生器重装值
AUXR |= 0x01; //串口1选择独立波特率发生器为波特率发生器
AUXR |= 0x10; //启动独立波特率发生器
EA = 1;//开总中断
ES = 1;//开串口中断
}
//向串口发送一个字符
void putchar(char ch)
{
SBUF = ch;
while(!TI);TI = 0;
}
void main()
{
UartInit();//串口初始化
PCF8575_Wirte(0,0xffff); // 让PCF8575的所有IO口输出高电平
while(1)
{
Delay_ms(100);
PCF8575_Read(0); // 读PCF8575引脚电平
putchar(PCF8575_LVal); // 把P0.0~P0.7的电平字节发送到串口
putchar(PCF8575_HVal); // 把P1.0~P1.7的电平字节发送到串口
}
}