今日尝试配置软件I2C通信,我的目标通信芯片是AT24C64,相较于AT24C02这样的8位寻址,它是16位寻址的,所以有些不同
文章提供测试代码讲解、完整工程下载、测试效果图
目录
软件I2C引脚初始化:
C内联函数改变SCL与SDA的输出:
#include "AT24C64.h"
主函数调用:
测试效果截图:
遇到的问题:
完整测试工程下载:
软件I2C引脚初始化:
I2C SCL = GPIO26
I2C SDA = GPIO16
C内联函数改变SCL与SDA的输出:
除了能够联系起SDA与SCL对应引脚寄存器输出0与1
还能改变SDA引脚寄存器的输入输出模式
#include "AT24C64.h"
移植的正点原子的代码,稍作修改
当时有个冗余等待ACK,影响了我实际的读取,因此被我删去
/* * AT24C64.c * * Created on: 2025年3月14日 * Author: 30313 */ #include "AT24C64.h" //初始化 AT24C64 void Init_AT24_I2C_software(void) { //设置GPIO16和GPIO26为GPIO模式 EALLOW; GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 0;// 将A17/GPIO16设置为GPIO模式 GpioCtrlRegs.GPAMUX2.bit.GPIO26 = 0; // 将B17/GPIO26设置为GPIO模式 //禁用模拟 //GpioCtrlRegs.GPAAMSEL.bit.GPIO26 = 0; // 禁用GPIO26的模拟功能 GpioCtrlRegs.GPAAMSEL.bit.GPIO16 = 0; // 禁用GPIO16的模拟功能 //设置GPIO10和GPIO11为输出 GpioCtrlRegs.GPADIR.bit.GPIO16 = 1; // 设置GPIO16为输出 GpioCtrlRegs.GPADIR.bit.GPIO26 = 1; // 设置GPIO26为输出 //设置GPIO10和GPIO11为标准输入/输出模式 GpioCtrlRegs.GPAPUD.bit.GPIO16 = 1; // 启用GPIO16的上拉电阻 GpioCtrlRegs.GPAPUD.bit.GPIO26 = 1; // 启用GPIO26的上拉电阻 //设置GPIO10和GPIO11为同步模式(可选) GpioCtrlRegs.GPAQSEL2.bit.GPIO16 = 0; // 设置GPIO16为同步模式 GpioCtrlRegs.GPAQSEL2.bit.GPIO26 = 0; // 设置GPIO26为同步模式 //GpioCtrlRegs.GPACTRL.bit.QUALPRD2=0x01 // 设置GPIO16~23 的 采样周期为 PLLSYSCLK/2 EDIS; } //产生IIC起始信号 void AT24_IIC_Start(void) { AT24_SDA_OUT(); //sda线输出 AT24_IIC_SDA(1); AT24_IIC_SCL(1); DEVICE_DELAY_US(4); AT24_IIC_SDA(0);//START:when CLK is high,DATA change form high to low DEVICE_DELAY_US(4); AT24_IIC_SCL(0);//钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号 void AT24_IIC_Stop(void) { AT24_SDA_OUT();//sda线输出 AT24_IIC_SCL(0); AT24_IIC_SDA(0);//STOP:when CLK is high DATA change form low to high DEVICE_DELAY_US(4); AT24_IIC_SCL(1); AT24_IIC_SDA(1);//发送I2C总线结束信号 DEVICE_DELAY_US(4); } //等待应答信号到来 //返回值:1,接收应答失败 // 0,接收应答成功 INT8U AT24_IIC_WaitAck(void) { INT8U ucErrTime=0; AT24_SDA_IN(); //SDA设置为输入 AT24_IIC_SDA(1);DEVICE_DELAY_US(1); AT24_IIC_SCL(1);DEVICE_DELAY_US(1); while(AT24_READ_SDA) { ucErrTime++; if(ucErrTime>250) { AT24_IIC_Stop(); return 1; } } AT24_IIC_SCL(0);//时钟输出0 return 0; } //产生ACK应答 void AT24_IIC_Ack(void) { AT24_IIC_SCL(0); AT24_SDA_OUT(); AT24_IIC_SDA(0); DEVICE_DELAY_US(2); AT24_IIC_SCL(1); DEVICE_DELAY_US(2); AT24_IIC_SCL(0); } //不产生ACK应答 void AT24_IIC_NAck(void) { AT24_IIC_SCL(0); AT24_SDA_OUT(); AT24_IIC_SDA(1); DEVICE_DELAY_US(2); AT24_IIC_SCL(1); DEVICE_DELAY_US(2); AT24_IIC_SCL(0); } //读1个字节,ack=1时,发送ACK,ack=0,发送nACK INT8U AT24_IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; AT24_SDA_IN();//SDA设置为输入 for(i=0;i<8;i++ ) { AT24_IIC_SCL(0); DEVICE_DELAY_US(2); AT24_IIC_SCL(1); receive<<=1; if(AT24_READ_SDA == 1)receive++; DEVICE_DELAY_US(1); } if (!ack) AT24_IIC_NAck();//发送nACK else AT24_IIC_Ack(); //发送ACK return receive; } void AT24_Write_IIC_Byte(unsigned char IIC_Byte) { unsigned char i; unsigned char da; da=IIC_Byte; AT24_SDA_OUT(); AT24_setIIC_SCL(0); //DEVICE_DELAY_US(2); for(i=0;i<8;i++) { if((da & 0x80)>>7) { AT24_IIC_SDA(1); //DEVICE_DELAY_US(2); } else { AT24_IIC_SDA(0); //DEVICE_DELAY_US(2); } da <<= 1; DEVICE_DELAY_US(2); AT24_setIIC_SCL(1); DEVICE_DELAY_US(2); AT24_setIIC_SCL(0); DEVICE_DELAY_US(2); } } /******************************************************************************* * 函数名:x24Cxx_WriteByte * 功 能:写一个字节 * 参 数:u16Addr要写入的地址 u8Data要写入的数据 * 返回值:无 * 说 明:无 *******************************************************************************/ void AT24CXX_WriteOneByte(uint16_t u16Addr, uint8_t u8Data) { //x24Cxx_WriteEnable();//使能写入 AT24_IIC_Start();//起始信号 if(EE_TYPE>AT24C16) { AT24_Write_IIC_Byte(DEV_ADDR); AT24_IIC_WaitAck();//等待应答 AT24_Write_IIC_Byte(u16Addr>>8);//发送高地址 }else { AT24_Write_IIC_Byte(DEV_ADDR+((u16Addr/256)<<1)); //发送器件地址0XA0,写数据 } AT24_IIC_WaitAck();//等待应答 AT24_Write_IIC_Byte(u16Addr % 256); //发送低地址 AT24_IIC_WaitAck();//等待应答 AT24_Write_IIC_Byte(u8Data); //发送字节 AT24_IIC_WaitAck();//等待应答 AT24_IIC_Stop();//停止信号 //x24Cxx_WriteDisble();//禁止写入 } /******************************************************************************* * 函数名:x24Cxx_ReadByte * 功 能:读一个字节 * 参 数:u16Addr要读取的地址 * 返回值:u8Data读出的数据 * 说 明:无 *******************************************************************************/ uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr) { uint8_t u8Data = 0; AT24_IIC_Start();//起始信号 if(EE_TYPE>AT24C16) { AT24_Write_IIC_Byte(DEV_ADDR); AT24_IIC_WaitAck();//等待应答 AT24_Write_IIC_Byte(ReadAddr>>8); //发送高地址 //AT24_IIC_WaitAck();//等待应答 } else AT24_Write_IIC_Byte(DEV_ADDR+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据 AT24_IIC_WaitAck();//等待应答 AT24_Write_IIC_Byte(ReadAddr%256); //发送低地址 AT24_IIC_WaitAck();//等待应答 AT24_IIC_Start();//起始信号 AT24_Write_IIC_Byte(0xa1);//器件寻址+读 AT24_IIC_WaitAck();//等待应答 u8Data = AT24_IIC_Read_Byte(1); AT24_IIC_Stop();//停止信号 return u8Data; } 检查AT24CXX是否正常 这里用了24XX的最后一个地址(255)来存储标志字. 如果用其他24C系列,这个地址要修改 返回1:检测失败 返回0:检测成功 //INT8U AT24CXX_Check(void) //{ // INT8U temp; // temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX // if(temp==0X55)return 0; // else//排除第一次初始化的情况 // { // AT24CXX_WriteOneByte(255,0X55); // temp=AT24CXX_ReadOneByte(255); // if(temp==0X55)return 0; // } // return 1; //}
#ifndef USER_AT24C64_H_ #define USER_AT24C64_H_ #include "f28x_project.h" #include "driverlib.h" #include "device.h" #include "Typedef.h" #define READ_CMD 1 #define WRITE_CMD 0 #define DEV_ADDR 0xA0 //设备硬件地址 #define AT24C01 127 #define AT24C02 255 #define AT24C04 511 #define AT24C08 1023 #define AT24C16 2047 #define AT24C32 4095 #define AT24C64 8191 #define AT24C128 16383 #define AT24C256 32767 #define EE_TYPE AT24C64 #define u8 unsigned char #define u32 unsigned int #define u16 unsigned short //宏定义 设定寄存器来改变SDA线的输入输出状态: #define AT24_SDA_IN() {EALLOW; GpioCtrlRegs.GPADIR.bit.GPIO16 = 0; EDIS;} //SDA输入模式 #define AT24_SDA_OUT() {EALLOW; GpioCtrlRegs.GPADIR.bit.GPIO16 = 1; EDIS;} //SDA输出模式 static inline void AT24_setIIC_SDA(Uint16 value) { if (value) { GpioDataRegs.GPASET.bit.GPIO16 = 1; }// GPIO16 输出1 else { GpioDataRegs.GPACLEAR.bit.GPIO16 = 1; } // GPIO16 输出0 } static inline void AT24_setIIC_SCL(Uint16 value) { if (value) { GpioDataRegs.GPASET.bit.GPIO26 = 1; } // GPIO26 输出1 else { GpioDataRegs.GPACLEAR.bit.GPIO26 = 1; } // GPIO26 输出0 } //IO操作函数 #define AT24_IIC_SCL AT24_setIIC_SCL #define AT24_IIC_SDA AT24_setIIC_SDA #define AT24_READ_SDA GpioDataRegs.GPADAT.bit.GPIO16 void Init_AT24_I2C_software(void); void AT24_IIC_NAck(void); void AT24_IIC_Ack(void); INT8U AT24_IIC_WaitAck(void); void AT24_IIC_Stop(void); void AT24_IIC_Start(void); INT8U AT24_IIC_Read_Byte(unsigned char ack); void AT24_Write_IIC_Byte(unsigned char IIC_Byte); void AT24CXX_WriteOneByte(uint16_t u16Addr, uint8_t u8Data); //指定地址写入一个字节 uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr); //u8 AT24CXX_Check(void); //检查器件 #endif /* USER_AT24C64_H_ */
主函数调用:
每隔 300ms 读写一次EEPROM 0x0002地址的数据,每次数据数值加一,断电会从断电前的数值大小继续往下计数
测试效果截图:
这次运行时正常的,而且我还开起了一个CPUTimer0,100us中断,中断服务函数有80us的阻塞,这样都没影响软件I2C的de正常读写:
TMS320F28P550SJ9软件I2C_驱动AT24Cx
遇到的问题:
当时直接移植的代码,它里面有俩次等待ACK,第一次逻辑分析仪得出正常响应了,第二次是冗余的,会造成器件通信失败,直接进入停止信号:
将额外的这句等待ack删去就好了
还有就是I2C在写器件数据之后,需要等至少10ms再去读,或者写,否则器件也会歇菜不干活:
完整测试工程下载:
这个文件的功能描述有点不对,以这里的描述为准
https://download.csdn.net/download/qq_64257614/90486368?spm=1001.2014.3001.5503