一、原理图
二、软件模拟IIC
IIC协议是一种同步、串行、半双工的通信协议,使用两根线:SDA(数据线)和SCL(时钟线)。主设备控制时钟线,负责发起和终止传输。从设备通过地址识别。
初始化GPIO的话,需要将SDA和SCL设置为输出模式,并且初始状态是高电平,因为IIC总线在空闲时这两根线都是高电平。可能需要配置上拉电阻,确保总线空闲时能拉高。也需要配置GPIO的模式为开漏输出,这样才方便实现线与逻辑。这点可能需要特别注意,否则可能无法正确读取数据。
1、开始和终止信号
IIC的起始信号是在SCL高电平期间,SDA从高变低。所以模拟的时候,首先确保SCL为高,然后拉低SDA,接着再拉低SCL,准备后续的数据传输。需要严格按照时序来,可能需要插入一些延时,保证信号稳定。
void I2CStart(void) { sda = 1; scl = 1; //拉高释放总线 I2C_Delay(DELAY_TIME); //延时 sda = 0; I2C_Delay(DELAY_TIME); scl = 0; }
停止信号是在SCL高电平期间,SDA从低变高。所以主设备需要先拉低SCL,然后拉低SDA,再拉高SCL,最后在SCL高的时候拉高SDA。
void I2CStop(void) { sda = 0; scl = 1; I2C_Delay(DELAY_TIME); sda = 1; I2C_Delay(DELAY_TIME); }
2、数据发送
IIC的数据是在SCL高电平时稳定的,数据的变化只能在SCL低电平时进行。每个字节传输8位,高位在前。主设备在SCL低电平时改变SDA,然后拉高SCL,从设备在SCL高电平时读取SDA。之后需要等待一个时钟周期,可能还需要检测从设备的应答信号。应答信号是在每个字节后,发送方释放SDA(变为输入模式),接收方在第九个时钟周期拉低SDA表示应答。这里模拟的时候,主设备发送完8位后,需要切换SDA为输入,等待从设备的应答,检测SDA是否为低,然后再切回输出模式继续发送。
void I2CSendByte(unsigned char byt) { unsigned char i; for(i=0; i<8; i++){ //由高位到地位依次发送 scl = 0; //拉低传输数据 I2C_Delay(DELAY_TIME); if(byt & 0x80){ sda = 1; } else{ sda = 0; } I2C_Delay(DELAY_TIME); scl = 1; //拉高稳定 byt <<= 1; //下一位数据 I2C_Delay(DELAY_TIME); } scl = 0; }
3、应答信号
应答:数据发送信号(低电平),表示成功接收到数据,且可以继续接收。
非应答:数据接收方发送信号(高电平),表示由于某些原因不再接收数据unsigned char I2CWaitAck(void) { unsigned char ackbit; scl = 1; I2C_Delay(DELAY_TIME); ackbit = sda; //若为1时表示接收完成 scl = 0; I2C_Delay(DELAY_TIME); return ackbit; }
void I2CSendAck(unsigned char ackbit) //主动发送应答 { scl = 0; sda = ackbit; I2C_Delay(DELAY_TIME); scl = 1; I2C_Delay(DELAY_TIME); scl = 0; sda = 1; I2C_Delay(DELAY_TIME); }
4、接收数据信号
数据的读取是在SCL高电平期间(稳定),此时数据不在变化。读取完成后需主动发送应答信号来结束读取。
unsigned char I2CReceiveByte(void) { unsigned char da; unsigned char i; for(i=0;i<8;i++){ //8位数据由高位开始 scl = 1; I2C_Delay(DELAY_TIME); da <<= 1; if(sda) da |= 0x01; scl = 0; I2C_Delay(DELAY_TIME); } return da; }
三、PCF8591(A/D、D/A)
1、器件地址:
高4位固定,后面3位对应器件引脚连接地址,蓝桥杯板子均接地(置0),最后一位控制读写操作(1为读,0为写)。
2、控制字节
第7位:固定值0;
第6位:0—A/D转换,1—D/A转换;
第5、4位:00—四路单端输入,01—三路差分输入,10—两路单端,一路差分,11—两路差分输入;
第3位:固定值0;
第2位:0—禁止自动增量,1—允许自动增量;
第1、0位:00—选择AIN0,01—选择AIN1,10—选择AIN2,11—选择AIN3.
3、数据收发
unsigned char PCF8591_ADC(unsigned char channel) { unsigned char dat; I2CStart(); I2CSendByte(0x90); //发送写地址 I2CWaitAck(); if(channel == 1) //选择通道 I2CSendByte(0x01); else if(channel == 3) I2CSendByte(0x03); I2CWaitAck(); I2CStart(); I2CSendByte(0x91); //发送读 I2CWaitAck(); dat = I2CReceiveByte(); //接收数据 I2CWaitAck(); I2CSendAck(1); //发送非应答信号 I2CStop(); return dat; }
void PCF8591_DAC(unsigned char dat) { I2CStart(); I2CSendByte(0x90); //发送写 I2CWaitAck(); I2CSendByte(0x40); //打开DAC通道 I2CWaitAck(); I2CSendByte(dat); //发送数据 I2CWaitAck(); I2CSendAck(1); //此处可删除 I2CStop(); }
四、AT24C04
1、器件地址
注意:蓝桥杯板子位24C02。
2、向EEPROM写入数据
该芯片使用9位地址进行数据寻址。
void AT24C04_write_page(unsigned char add,unsigned char *dat) { I2CStart(); I2CSendByte(0xa0); I2CWaitAck(); I2CSendByte(add); I2CWaitAck(); I2CSendByte(dat[0]); I2CWaitAck(); //I2CSendAck(1); I2CStop(); }
3、从指定地址读取数据
void AT24C04_read_page(unsigned char add,unsigned char *dat) { I2CStart(); I2CSendByte(0xa0); //写操作 I2CWaitAck(); I2CSendByte(add); //指定地址 I2CWaitAck(); I2CStart(); I2CSendByte(0xa1); //读 I2CWaitAck(); dat[0] = I2CReceiveByte(); I2CSendAck(1); I2CStop(); }
tips:在数据读取时注意定时器中断,可能干扰IIC时序,最好在读取时关闭中断,结束后打开中断。