一、前言
由于单片机资源不足,第一次使用IO扩展器,顺便记录下来使用心得,网上查询资料很少,使用的人不多,基本都得照着手册去手搓,搞底层难啊.
需要扩展的IO需求不是很复杂,也不是用来在驱动总线信号,就是扩展为IO,有输入和输出控制即可,之前总用移位寄存器74HC595去扩展IO输出控制,但是需要输入的时候,还是得用专用的IO扩展芯片了。
目录
二、环境
stm32驱动
windows
三、正文
1.PCA9555PW
此款芯片具有16路IO扩展,分为2组8位IO通道,每一个通道都可以单独设置为输入或者是输出模式,功能很强大。
下面是官方手册介绍:
PCA9555是一款24引脚CMOS器件,为12c总线/SMBus应用提供16位通用并行输入/输出(GPIO)扩展,旨在增强NXP半导体家族的i2c总线I/O扩展器。这些改进包括更高的驱动能力、5 V VO容限、更低的电源电流、单独的l/O配置和更小的封装。当ACPI电源开关、传感器、按钮、led、风扇等需要额外的I/O时,I/O扩展器提供了一个简单的解决方案。PCA9555由两个8位配置(输入或输出选择)组成;输入,输出和极性反转(活动高或活动低操作)寄存器。系统主机可以通过写入I/O配置位来启用I/O作为输入或输出。每个输入或输出的数据保存在相应的输入或输出寄存器中。读寄存器的极性可以用极性反转寄存器反转。所有的寄存器都可以被系统主机读取。虽然引脚对引脚和l2c总线地址与PCF8575兼容,但由于增强,需要对软件进行更改,并在应用笔记AN469中进行了讨论。当任何输入状态与其相应的输入端口寄存器状态不同时,PCA9555开漏中断输出被激活,并用于向系统主机指示输入状态已经改变。上电复位将寄存器设置为其默认值并初始化设备状态机。三个硬件引脚(AO, A1, A2)改变固定的i2c总线地址,允许多达8个设备共享相同的12c总线/SMBus。PCA9555的固定i2c总线地址与PCA9554相同,允许多达8个这些设备以任何组合共享相同的12c总线/SMBus。
架构图如下:

芯片原理图管脚如下:


但是常用的很好买到的还是PCA9555PW
下面是我测试画的测试板原理图:


控制引脚SDA和SCLK给3.3V上拉,INT中断引脚虽然也加了上拉,但是没有连接到STM32上,没使用中断触发,使用轮询读取得方式,如果需要中断触发自行扩展,电源5V驱动,这样在IO输出或者输入浮空的情况下,都是有5V电平。
如果电源给3.3V,那么输出的IO也是3.3V电平。
这里我使用的是2个芯片在一条IIC总线上,通过设置芯片不同的地址,总共就是可以驱动32路IO了

此芯片一条总线最大支持8个芯片,最多控制IO数量也就是16*8=128个,还是很香的。
芯片驱动首先要配置IO模式,是输出还是输入,默认全部IO是输入模式,需要配置寄存器06去修改对应IO的模式,在输出模式下也可以读取IO状态,输出模式也可以强行将高低电平反向提供,在输入寄存器读取数据仍然会改变,但是这里建议尽量不要这样操作,不正规操作芯片容易对芯片的寿命大打折扣,还是输出就是输出使用,输入就是输入使用。
在读写数据时,他的寄存器读出数据时反向的,如1~8的IO数据是8~1,9~16的数据是16~9

不过对于我们搞程序的来说这都是小事情了
寄存器如下:
#define PCA9555_SLA (0x40 >> 1) // 定义PAC9555的器件地址
#define PCA9555_REG_IN0 0x00 // 定义输入寄存器0地址
#define PCA9555_REG_IN1 0x01 // 定义输入寄存器1地址
#define PCA9555_REG_OUT0 0x02 // 定义输出寄存器0地址
#define PCA9555_REG_OUT1 0x03 // 定义输出寄存器1地址
#define PCA9555_REG_POL0 0x04 // 定义极性反转寄存器0地址
#define PCA9555_REG_POL1 0x05 // 定义极性反转寄存器1地址
#define PCA9555_REG_CFG0 0x06 // 定义方向配置寄存器0地址
#define PCA9555_REG_CFG1 0x07 // 定义方向配置寄存器1地址
#define PCA9555_DEVICE_ADDR 0x40 // 定义PCA9555地址
核心代码如下:
//PCA9555驱动
//写pca9555第一层,最底层驱动
void IIC_Write_Pca9555(unsigned char addr,unsigned char reg_addr,unsigned char low_byte,unsigned char high_byte)
{
IIC_Start();
IIC_Write_Byte(addr & 0xfe);
IIC_Ack();
IIC_Write_Byte(reg_addr);
IIC_Ack();
IIC_Write_Byte(low_byte);
IIC_Ack();
IIC_Write_Byte(high_byte);
IIC_Ack();
IIC_Stop();
}
//读pca9555第一层,最底层驱动
void IIC_Read_Pca9555(unsigned char addr,unsigned char reg_addr,unsigned char* pBuffer,unsigned int num)
{
IIC_Start();
IIC_Write_Byte(addr & 0xfe);
IIC_Ack();
IIC_Write_Byte(reg_addr);
IIC_Ack();
IIC_Start();
IIC_Write_Byte(addr | 0x01);
IIC_Ack();
while (num){
*pBuffer = IIC_Read_Byte();
if (num == 1){
IIC_Send_NoAck();
}
else{
IIC_Send_Ack();
}
pBuffer++;
num--;
}
IIC_Stop();
}
/*********************************************************
//pca9555初始化
配置输入/输出模式,PCA9555_REG_CFG0和PCA9555_REG_CFG1配置谁都可以,采用最后配置的模式
16个通道每个都可以配置输入/输出(这里我默认整个芯片使用一种模式,需要不同IO不同模式修改配置数据0xXX即可)
输入模式下不可设置输出,输出模式下可以监测输入(建议输出模式下不要强行输入)
*********************************************************/
void PCA9555_Init(void)
{
IIC_PORT_INIT();//IIC初始化
IIC_Write_Pca9555(PCA9555_ADDR1,PCA9555_REG_CFG0,0x00,0x00);//配置模式:输出
IIC_Write_Pca9555(PCA9555_ADDR2,PCA9555_REG_CFG0,0x00,0x00);//配置模式:输出
PCA9555_Set_All(0);
// IIC_Write_Pca9555(PCA9555_ADDR1,PCA9555_REG_CFG0,0xff,0xff);//配置模式:输入
// IIC_Write_Pca9555(PCA9555_ADDR2,PCA9555_REG_CFG0,0xff,0xff);//配置模式:输入
}
/*********************************************************
//写pca9555第二层驱动,可辅助逻辑不破坏最底层驱动
addr 芯片地址,0x40表示第一个芯片地址
data_L 写入芯片的前八位数据
data_H 写入芯片的前后位数据
*********************************************************/
void PCA9555_writedata(unsigned char addr,unsigned char data_L,unsigned char data_H)
{
IIC_Write_Pca9555(addr,PCA9555_REG_OUT0,data_L,data_H);// PCA9555_REG_OUT0 输出寄存器0地址
}
/*********************************************************
//读pca9555第二层驱动,可辅助逻辑不破坏最底层驱动
addr 芯片地址,0x40表示第一个芯片地址
pBuffer 读出芯片的数据
*********************************************************/
void PCA9555_readdata(unsigned char addr,unsigned char* pBuffer)
{
IIC_Read_Pca9555(addr,PCA9555_REG_IN0,pBuffer,2);//2为读出芯片数据组数,9555为2组
}
/*********************************************************
//设置某一通道IO高低输出
num 控制的IO序号
value 控制的IO输出状态 0:低电平 1:高电平
*********************************************************/
void PCA9555_Set_One_Value(unsigned char num,unsigned char value)
{
if(num >0 && num <17){//第一个芯片IO1-16控制
if(num <9){
if(value)pca9555writedata[0] |= 0x01<<(num - 1);//赋值位高
else pca9555writedata[0] &= ~(0x01<<(num - 1));//赋值位低
}
else{
if(value)pca9555writedata[1] |= 0x01<<(num - 9);//赋值位高
else pca9555writedata[1] &= ~(0x01<<(num - 9));//赋值位低
}
PCA9555_writedata(PCA9555_ADDR1,pca9555writedata[0],pca9555writedata[1]);
}
else if(num >16 && num <33){//第二个芯片IO1-16控制,使用更多IO以此类推,最多支持0x000~0x111 共8个芯片串联,共128个IO,如果不够可以使用PCA9505,40个IO,最多串联320个IO
if(num <25){
if(value)pca9555writedata[2] |= 0x01<<(num - 17);//赋值位高
else pca9555writedata[2] &= ~(0x01<<(num - 17));//赋值位低
}
else{
if(value)pca9555writedata[3] |= 0x01<<(num - 25);//赋值位高
else pca9555writedata[3] &= ~(0x01<<(num - 25));//赋值位低
}
PCA9555_writedata(PCA9555_ADDR2,pca9555writedata[2],pca9555writedata[3]);
}
else{//超出芯片控制
//error
}
}
/*********************************************************
//设置所有输出
value 控制的IO输出状态 0:全部输出低 1:全部输出高
*********************************************************/
void PCA9555_Set_All(unsigned char value)
{
int i;
for(i=0;i<4;i++){
if(value==0)
pca9555writedata[i]=0;
else
pca9555writedata[i]=0xff;
}
PCA9555_writedata(PCA9555_ADDR1,pca9555writedata[0],pca9555writedata[1]);
PCA9555_writedata(PCA9555_ADDR2,pca9555writedata[2],pca9555writedata[3]);
}
/*********************************************************
//读取所有输入
pca9555readdata1 芯片1数据,分2组
pca9555readdata2 芯片2数据,分2组
*********************************************************/
void PCA9555_Read_All(void)
{
PCA9555_readdata(PCA9555_ADDR1,pca9555readdata1);
PCA9555_readdata(PCA9555_ADDR2,pca9555readdata2);
}
如果需要测试程序源代码和手册以及测试板图纸的可以在这里下载,搬砖也是不容易的~你懂得^_^
测试结果:
void PCA9555_Init(void)
{
IIC_PORT_INIT();//IIC初始化
IIC_Write_Pca9555(PCA9555_ADDR1,PCA9555_REG_CFG0,0x00,0x00);//配置模式:输出
IIC_Write_Pca9555(PCA9555_ADDR2,PCA9555_REG_CFG0,0x00,0x00);//配置模式:输出
PCA9555_Set_All(0);
// IIC_Write_Pca9555(PCA9555_ADDR1,PCA9555_REG_CFG0,0xff,0xff);//配置模式:输入
// IIC_Write_Pca9555(PCA9555_ADDR2,PCA9555_REG_CFG0,0xff,0xff);//配置模式:输入
PCA9555_Set_One_Value(1,1);
PCA9555_Set_One_Value(8,1);
PCA9555_Set_One_Value(15,1);
PCA9555_Set_One_Value(30,1);
PCA9555_Set_All(0);
PCA9555_Set_One_Value(5,1);
PCA9555_Set_One_Value(16,1);
PCA9555_Set_One_Value(17,1);
PCA9555_Set_One_Value(24,1);
PCA9555_Set_One_Value(31,1);
}
通过以上代码,在初始化之后分别设置了1、8、15、30通道高电平,随后马上复位电平归零,又设置了5、16、17、24、31输出高电平,使用万用表测试所有通道得出结果与预设值相同,这些通道全部均为高电平,且电平与芯片供电电源电平一致(手册说电源要在2.3~5.5V之间,我测试使用3.3V和5V均正常)
此时通过定时器读取寄存器函数,读出输出通道数据如下:

第一个芯片读取寄存器8~1数据为0x10, 即00010000
第一个芯片读取寄存器16~9数据为0x80, 即10000000
第二个芯片读取寄存器24~17数据为0x81,即10000001
第二个芯片读取寄存器32~25数据为0x40,即01000000
解读后输出高得分别是5、16、17、24、31,与预设值相同。
测试输入时默认全为0xff,即全部有内部上拉,给某个IO为低电平时对应数值会根据逻辑改变。
测试到此基本就算是结束了,如有问题欢迎评论和留言,大家共同探讨,共同解决。
2.PCA9505DGG
这一款芯片驱动基本上与PCA9555相同,但是又有不同之处,控制上引脚多了OE输出使能控制引脚,多了RESET芯片复位引脚,寄存器上多了MSK中断配置寄存器,IO数量上扩展成了5组,共40个IO,地址还是A2 A1 A0三个,总线最多支持挂载8个芯片共320路IO控制,非常nice了
手册介绍如下:
PCA9505/PCA9506提供40位并行输入/输出(I/O)端口扩展,用于组织在5个8 I/O银行的2c总线应用程序。在5 V供电电压下,输出能够输出10 mA和15 mA,总封装负载为600 mA,可以直接驱动40个led。40个I/O端口中的任何一个都可以配置为输入或输出。输出端口是图腾柱,它们的逻辑状态在确认时发生变化(银行更改)。PCA9505与PCA9506相同,除了它在所有I/ o上包括100个kΩ内部上拉电阻。PCA9506不包括I/ o上的内部上拉,以减少作为输出或输入由推挽驱动器驱动时的功耗。该设备可以配置为使每个输入端口被屏蔽,以防止在其状态改变时产生中断,并使I/O数据逻辑状态在系统主机读取时被反转。漏极开路中断(INT)输出引脚允许监控输入引脚,并且每次在一个或几个输入端口发生变化时都断言(除非被屏蔽)。输出使能(OE)引脚3-状态任何I/O选择作为输出,可以用作闪烁或调暗led (PWM频率> 80hz和改变占空比)的输入信号。内部Power-On Reset (POR)或hardware Reset (Reset)引脚将40个I/ o初始化为输入。三个地址选择引脚配置8个从地址之一。PCA9506提供56引脚TSSOP和HVQFN封装,而PCA9505仅提供TSSOP封装。它们都指定在-40°C到+85°C的工业温度范围内。
架构图如下:

芯片引脚图如下:


我测试的原理图:


其中设备地址还是同PCA9555一样控制

控制引脚上多出的RESET功能是为芯片复位,这个基本接个上拉就行,就是整个设备断电才会复位,如果需要单片机复位同事复位芯片,那么就要驱动这个引脚在初始化前给个低电平保持一段时间在给高电平。PS:我的使用场合与PCA9555相同,都是设备一起上电直接配置好模式,无需芯片单独反复复位操作,且PCA9555也无复位控制,所以PCA9505也不用控制复位,大家按需配置。
控制引脚多出的OE是比较重要的,在输出模式下要将OE引脚拉低,再能真正的使能控制输出,否则引脚在3态状态保护,下面是我配置输出并将OE引脚在定时器500ms高低电平转换测试结果。可以看见输出模式下读取的数据一直是使能和失能切换的(至于失能下为何数据全是0xff看后文)

手册OE引脚说明:活动LOW输出使能引脚允许同时启用或禁用所有I/ o。当低电平应用到OE引脚时,配置为输出的所有I/ o都被启用,并且在各自的OP寄存器中编程的逻辑值应用到引脚。当一个高电平被应用到OE引脚时,所有配置为输出的I/ o都是3状态的。对于需要LED闪烁亮度控制的应用,该引脚可以通过在OE引脚上施加高频PWM信号来控制亮度。led可以使用输出端口寄存器闪烁,也可以使用OE引脚上的PWM信号调暗,从而通过调整占空比来控制亮度。
中断引脚就没什么好说的了,还是当电平改变时就会触发中断,这个芯片的升级之处就是这个中断触发不触发是可以配置的,是每一个IO都可以配置哦。
下面说明一下寄存器,寄存器总共分为如下5种:
IP:输入端口寄存器(5个寄存器)
OP:输出端口寄存器(5个寄存器)
Pl:极性反转寄存器(5个寄存器)
IOC: I/O配置寄存器(5个寄存器)
MSK:屏蔽中断寄存器(5个寄存器)
IP手册原文介绍:这些寄存器是只读的。它们反映端口引脚的传入逻辑电平,而不管该引脚是由l/O配置寄存器定义为输入还是输出。如果Pl寄存器中对应的Px[y]位被设置为逻辑0,或者如果PI寄存器中对应的Px[y]位被设置为逻辑1,则反向输入逻辑电平。对这些寄存器的写入没有效果。
OP手册原文介绍:这些寄存器反映由I/O配置寄存器定义为输出的引脚的输出逻辑电平。这些寄存器中的位值对定义为输入的引脚没有影响。反过来,从这些寄存器读取反映控制输出选择的触发器中的值,而不是实际的引脚值。Ox[y]=0: IOx_y=0,如果IOx_y定义为输出(Cx[y]在IOC寄存器=0)。Ox[y] = 1: IOx_y= 1,如果IOx_y定义为输出(Cx[y]在IOC寄存器= 0)。式中“x”为银行编号(0 ~ 4);“y”为位(0 ~ 7)。
PI手册原文介绍:这些寄存器允许反转相应输入端口寄存器的极性。Px[y]= 0:保留相应的输入端口寄存器数据极性。Px[y]= 1:对应的输入端口寄存器数据极性倒置。式中“x”为银行编号(0 ~ 4);“Y”为比特数(0 ~ 7)。
IOC手册原文介绍:这些寄存器配置I/O引脚的方向。Cx[y] = 0:对应端口引脚为输出。Cx[y] = 1:对应的端口引脚为输入。式中“x”为银行编号(0 ~ 4);“y”为位(0 ~ 7)。
MSK手册原文介绍:由于配置为输入的I/O引脚发生变化,这些寄存器屏蔽了中断。“x”为银行号(0 ~ 4);“y”为位数(0 ~ 7)。Mx[y] = 0:如果IOx_y被定义为输入,则I/O端的电平改变将产生中断(IOC 寄存器中的 Cx[y] = 1)。Mx[y] = 1:如果IOx_y定义为输入(IOC register = 1中的Cx[y]),则输入端口的电平变化不会产生中断。
寄存器总共如下:
#define PCA9505_IP 0x00
#define PCA9505_IP0 0x00
#define PCA9505_IP1 0x01
#define PCA9505_IP2 0x02
#define PCA9505_IP3 0x03
#define PCA9505_IP4 0x04
#define PCA9505_OP 0x08
#define PCA9505_OP0 0x08
#define PCA9505_OP1 0x09
#define PCA9505_OP2 0x0A
#define PCA9505_OP3 0x0B
#define PCA9505_OP4 0x0C
#define PCA9505_PI 0x10
#define PCA9505_PI0 0x10
#define PCA9505_PI1 0x11
#define PCA9505_PI2 0x12
#define PCA9505_PI3 0x13
#define PCA9505_PI4 0x14
#define PCA9505_IOC 0x18
#define PCA9505_IOC0 0x18
#define PCA9505_IOC1 0x19
#define PCA9505_IOC2 0x1A
#define PCA9505_IOC3 0x1B
#define PCA9505_IOC4 0x1C
#define PCA9505_MSK 0x20
#define PCA9505_MSK0 0x20
#define PCA9505_MSK1 0x21
#define PCA9505_MSK2 0x22
#define PCA9505_MSK3 0x23
#define PCA9505_MSK4 0x24
核心代码如下:
//PCA9505驱动
//写pca9505第一层,最底层驱动
void IIC_Write_Pca9505(unsigned char addr,unsigned char reg_addr,unsigned char byte)
{
IIC_Start1();
IIC_Write_Byte1(addr & 0xfe);
IIC_Ack1();
IIC_Write_Byte1(reg_addr);
IIC_Ack1();
IIC_Write_Byte1(byte);
IIC_Ack1();
IIC_Stop1();
}
//读pca9505第一层,最底层驱动
void IIC_Read_Pca9505(unsigned char addr,unsigned char reg_addr,unsigned char* pBuffer)
{
IIC_Start1();
IIC_Write_Byte1(addr & 0xfe);
IIC_Ack1();
IIC_Write_Byte1(reg_addr);
IIC_Ack1();
IIC_Start1();
IIC_Write_Byte1(addr | 0x01);
IIC_Ack1();
*pBuffer = IIC_Read_Byte1();
IIC_Send_NoAck1();
//IIC_Send_Ack1();
//pBuffer++;
IIC_Stop1();
}
/*********************************************************
//pca9505初始化
配置输入/输出模式,PCA9505_REG_CFG0和PCA9505_REG_CFG1配置谁都可以,采用最后配置的模式
16个通道每个都可以配置输入/输出(这里我默认整个芯片使用一种模式,需要不同IO不同模式修改配置数据0xXX即可)
输入模式下不可设置输出,输出模式下可以监测输入(建议输出模式下不要强行输入)
*********************************************************/
void PCA9505_Init(void)
{
IIC_PORT_INIT1();//IIC初始化
//输出配置
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC0,0x00);//配置模式:输出
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC1,0x00);//配置模式:输出
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC2,0x00);//配置模式:输出
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC3,0x00);//配置模式:输出
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC4,0x00);//配置模式:输出
// OE9505_LOW;//OE使能输出模式
// delay_ms(10);
// PCA9505_Set_All(0); //配置所有输出0
//输入配置
//输入模式必须配置
IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC0,0xff);//配置模式:输入
IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC1,0xff);//配置模式:输入
IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC2,0xff);//配置模式:输入
IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC3,0xff);//配置模式:输入
IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_IOC4,0xff);//配置模式:输入
//输入模式选择性配置
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_PI0,0xff);//配置模式:输入极性反转,还是默认上拉,但是读取数据数值反转,ff读为00,给低电平0变1,不配置默认0不反转极性
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_PI1,0xff);//配置模式:输入极性反转,还是默认上拉,但是读取数据数值反转,ff读为00,给低电平0变1,不配置默认0不反转极性
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_PI2,0xff);//配置模式:输入极性反转,还是默认上拉,但是读取数据数值反转,ff读为00,给低电平0变1,不配置默认0不反转极性
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_PI3,0xff);//配置模式:输入极性反转,还是默认上拉,但是读取数据数值反转,ff读为00,给低电平0变1,不配置默认0不反转极性
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_PI4,0xff);//配置模式:输入极性反转,还是默认上拉,但是读取数据数值反转,ff读为00,给低电平0变1,不配置默认0不反转极性
//输入模式选择性配置
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_MSK0,0x00);//配置模式:输入模式电平改变产生中断,不配置默认1不产生中断
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_MSK1,0x00);//配置模式:输入模式电平改变产生中断,不配置默认1不产生中断
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_MSK2,0x00);//配置模式:输入模式电平改变产生中断,不配置默认1不产生中断
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_MSK3,0x00);//配置模式:输入模式电平改变产生中断,不配置默认1不产生中断
// IIC_Write_Pca9505(PCA9505_ADDR1,PCA9505_MSK4,0x00);//配置模式:输入模式电平改变产生中断,不配置默认1不产生中断
}
/*********************************************************
//写pca9505第二层驱动,可辅助逻辑不破坏最底层驱动
addr 芯片地址,0x40表示第一个芯片地址
data_x 写入芯片的数据
*********************************************************/
void PCA9505_writedata(unsigned char addr,unsigned char data_1,unsigned char data_2,unsigned char data_3,unsigned char data_4,unsigned char data_5)
{
IIC_Write_Pca9505(addr,PCA9505_OP0,data_1);// PCA9505_OP0 输出寄存器0地址
IIC_Write_Pca9505(addr,PCA9505_OP1,data_2);// PCA9505_OP1 输出寄存器1地址
IIC_Write_Pca9505(addr,PCA9505_OP2,data_3);// PCA9505_OP2 输出寄存器2地址
IIC_Write_Pca9505(addr,PCA9505_OP3,data_4);// PCA9505_OP3 输出寄存器3地址
IIC_Write_Pca9505(addr,PCA9505_OP4,data_5);// PCA9505_OP4 输出寄存器4地址
}
/*********************************************************
//读pca9505第二层驱动,可辅助逻辑不破坏最底层驱动
addr 芯片地址,0x40表示第一个芯片地址
pBuffer 读出芯片的数据
*********************************************************/
void PCA9505_readdata(unsigned char addr,unsigned char* pBuffer1,unsigned char* pBuffer2,unsigned char* pBuffer3,unsigned char* pBuffer4,unsigned char* pBuffer5)
{
IIC_Read_Pca9505(addr,PCA9505_IP0,pBuffer1);//PCA9505_IP0 读出芯片寄存器0地址数据组数
IIC_Read_Pca9505(addr,PCA9505_IP1,pBuffer2);//PCA9505_IP1 读出芯片寄存器1地址数据组数
IIC_Read_Pca9505(addr,PCA9505_IP2,pBuffer3);//PCA9505_IP2 读出芯片寄存器2地址数据组数
IIC_Read_Pca9505(addr,PCA9505_IP3,pBuffer4);//PCA9505_IP3 读出芯片寄存器3地址数据组数
IIC_Read_Pca9505(addr,PCA9505_IP4,pBuffer5);//PCA9505_IP4 读出芯片寄存器4地址数据组数
}
/*********************************************************
//设置某一通道IO高低输出
num 控制的IO序号
value 控制的IO输出状态 0:低电平 1:高电平
*********************************************************/
void PCA9505_Set_One_Value(unsigned char num,unsigned char value)
{
if(num >0 && num <41){//第一个芯片IO1-16控制
if(num <9){
if(value)pca9505writedata[0] |= 0x01<<(num - 1);//赋值位高
else pca9505writedata[0] &= ~(0x01<<(num - 1));//赋值位低
}
else if(num <17){
if(value)pca9505writedata[1] |= 0x01<<(num - 9);//赋值位高
else pca9505writedata[1] &= ~(0x01<<(num - 9));//赋值位低
}
else if(num <25){
if(value)pca9505writedata[2] |= 0x01<<(num - 17);//赋值位高
else pca9505writedata[2] &= ~(0x01<<(num - 17));//赋值位低
}
else if(num <33){
if(value)pca9505writedata[3] |= 0x01<<(num - 25);//赋值位高
else pca9505writedata[3] &= ~(0x01<<(num - 25));//赋值位低
}
else{
if(value)pca9505writedata[4] |= 0x01<<(num - 33);//赋值位高
else pca9505writedata[4] &= ~(0x01<<(num - 33));//赋值位低
}
PCA9505_writedata(PCA9505_ADDR1,pca9505writedata[0],pca9505writedata[1],pca9505writedata[2],pca9505writedata[3],pca9505writedata[4]);
}
else{//超出芯片控制
//error
}
}
/*********************************************************
//设置所有输出
value 控制的IO输出状态 0:全部输出低 1:全部输出高
*********************************************************/
void PCA9505_Set_All(unsigned char value)
{
int i;
for(i=0;i<5;i++){
if(value==0)
pca9505writedata[i]=0;
else
pca9505writedata[i]=0xff;
}
PCA9505_writedata(PCA9505_ADDR1,pca9505writedata[0],pca9505writedata[1],pca9505writedata[2],pca9505writedata[3],pca9505writedata[4]);
}
/*********************************************************
//读取所有输入
pca9505readdata1 芯片1数据,分2组
pca9505readdata2 芯片2数据,分2组
*********************************************************/
void PCA9505_Read_All(void)
{
PCA9505_readdata(PCA9505_ADDR1,&pca9505readdata[0],&pca9505readdata[1],&pca9505readdata[2],&pca9505readdata[3],&pca9505readdata[4]);
}
如果需要测试程序源代码和手册以及测试板图纸的可以在这里下载PCA9505驱动含PCA9555驱动,搬砖也是不容易的~你懂得^_^
测试结果:
PCA9505_Init();
// //测试PCA9505 20230915
PCA9505_Set_One_Value(3,1);
PCA9505_Set_One_Value(11,1);
PCA9505_Set_One_Value(20,1);
PCA9505_Set_One_Value(30,1);
PCA9505_Set_One_Value(35,1);
通过以上代码,在初始化之后分别设置了3、11、20、30、35通道高电平,使用万用表测试所有通道得出结果与预设值相同,这些通道全部均为高电平,且电平与芯片供电电源电平一致(手册说电源要在2.3~5.5V之间,我测试使用3.3V和5V均正常)
测试输入时默认全为0xff,即全部有内部上拉,给某个IO为低电平时对应数值会根据逻辑改变。
可以看见上面实物图种PCA9505的40个IO,我给IO25低电平,测试结果如下:

IO25属于第四组第一个IO,从第四组读取IO32~IO25 即 1111 1110 ,数据为0xFE
这里对于IO默认高电平的情况进行说明,目前配置输入模式时IO在浮空情况下默认有上拉,在实际测试输入时会根据实际高低电平进行状态读取,这里我尝试使用PI寄存器对输入极性反转设置,但是经过尝试发现极性反转只是输入数据的变化,电平还是上拉该点评,而且极性反转高电平显示数据0,低电平显示数据1,也不太适应,暂不需求,所以PI寄存器按需配置
测试到此基本就算是结束了,如有问题欢迎评论和留言,大家共同探讨,共同解决。
四、结语
成长就是不断的积累知识