今日尝试配置软件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/2EDIS;}//产生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 lowDEVICE_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 highDEVICE_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);//时钟输出0return 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();//发送nACKelseAT24_IIC_Ack(); //发送ACKreturn 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 输出1else { GpioDataRegs.GPACLEAR.bit.GPIO16 = 1; } // GPIO16 输出0 }static inline void AT24_setIIC_SCL(Uint16 value) {if (value) { GpioDataRegs.GPASET.bit.GPIO26 = 1; } // GPIO26 输出1else { 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.GPIO16void 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