硬件IIC的7位从机地址查询方式读写参考代码
本文中使用582测试,在整合先前博客中的代码的基础上,加上读写超时,加上返回值,加上16位从机寄存器地址的判断,希望读写各用一个函数就能解决硬件IIC的使用问题。
#include "CH58x_common.h" #define TIME_OUT 10000 //用于IIC读写超时,等待n次 #define DELAY_IIC DelayUs(5) //用于IIC读写超时,设置一次等待的时长 #define HOST_NO_ADDR 0x66 //随便设置,对主机不影响 #define EEPROM_ADDR 0xA0 //EEPROM设备地址 uint8_t IIC_read_nBytes(uint8_t addr, uint16_t mem, uint8_t mem_16, uint8_t *p_des, uint8_t len); uint8_t IIC_write_nBytes(uint8_t addr, uint16_t mem, uint8_t mem_16, uint8_t *p_src, uint8_t len); uint8_t wData[32] = {8}; //存放即将写的数据 uint8_t rData[32] = {0}; //存放读取出的数据 uint8_t mem_writing[5] = {0x10, 0x20, 0x30, 0x40, 0x50}; //存放8位的目标内存/寄存器 uint16_t mem_writing2[2] = {0x1020, 0x2022}; //存放16位的目标内存/寄存器 const uint8_t data[5][20] = { //随便写一些数值,每行第一个字节表示之后的字节长度 {8,1,2,3,4,5,6,7,8}, {5,10,48,48,77,3}, {7,168,42,44,145,1,38,44}, {6,4,44,145,1,16,44}, {6,48,47,46,45,44,43}, }; int main() { SetSysClock(CLK_SOURCE_PLL_80MHz); GPIOA_SetBits(GPIO_Pin_9); //串口1GPIO初始化,TXD在配置推挽输出前先置位 GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); UART1_DefInit(); //串口初始化 GPIOB_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13, GPIO_ModeIN_PU); I2C_Init(I2C_Mode_I2C, 400000, I2C_DutyCycle_16_9, I2C_Ack_Enable, I2C_AckAddr_7bit, HOST_NO_ADDR); DelayMs(300); //上电延时 PRINT("---------Init_OK---------\n"); while(1) { uint8_t trans_num = 0; for(uint8_t k=0; k<2; k++) //16位目标内存/寄存器读写 { trans_num = data[k][0] + 1; memcpy(wData, data[k], trans_num); PRINT("Writing to 0x%02x:", mem_writing2[k]); for(uint8_t i=0; i<trans_num; i++) { PRINT("%x ", wData[i]); } PRINT("\n"); IIC_write_nBytes(EEPROM_ADDR, mem_writing2[k], 1, wData, trans_num); DelayMs(100); IIC_read_nBytes(EEPROM_ADDR, mem_writing2[k], 1, rData, trans_num); PRINT("Read form 0x%02x:", mem_writing2[k]); for(uint8_t i=0; i<trans_num; i++) { PRINT("%x ", rData[i]); rData[i] = 0; } PRINT("\n"); DelayMs(100); } DelayMs(500); for(uint8_t k=0; k<5; k++) //8位目标内存/寄存器读写 { trans_num = data[k][0] + 1; memcpy(wData, data[k], trans_num); PRINT("Writing to 0x%02x:", mem_writing[k]); for(uint8_t i=0; i<trans_num; i++) { PRINT("%x ", wData[i]); } PRINT("\n"); IIC_write_nBytes(EEPROM_ADDR, mem_writing[k], 0, wData, trans_num); DelayMs(100); IIC_read_nBytes(EEPROM_ADDR, mem_writing[k], 0, rData, trans_num); PRINT("Read form 0x%02x:", mem_writing[k]); for(uint8_t i=0; i<trans_num; i++) { PRINT("%x ", rData[i]); rData[i] = 0; } PRINT("\n"); DelayMs(100); } DelayMs(1000); } } uint8_t IIC_timeout(uint16_t *p_times) //判断读写等待过程中是否超时 { (*p_times)++; DELAY_IIC; if(*p_times > TIME_OUT) { return 1; } return 0; } /*************************************** * 从从机的某寄存器起始,连续读取n个字节的数据 * 参数:addr 从机地址 * mem 内存/寄存器地址 * men_16 是否为16位内存/寄存器地址 * p_des 目的地址指针 * len 读取长度 * 返回值:0:正常 * 1~7:卡在第n步 * 0xFF:卡在连续读的过程中 */ uint8_t IIC_read_nBytes(uint8_t addr, uint16_t mem, uint8_t mem_16, uint8_t *p_des, uint8_t len) { uint16_t i_timeout = 0; //主机通知从机要读取它的哪块内存 while(I2C_GetFlagStatus(I2C_FLAG_BUSY)) //IIC主机判忙 { if(IIC_timeout(&i_timeout)) return 1; else i_timeout = 0; } I2C_GenerateSTART(ENABLE); //起始信号 while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)) //判断BUSY, MSL and SB flags { if(IIC_timeout(&i_timeout)) return 2; else i_timeout = 0; } I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送器件地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) //判断BUSY, MSL, ADDR, TXE and TRA flags { if(IIC_timeout(&i_timeout)) return 3; else i_timeout = 0; } if(mem_16) { I2C_SendData((uint8_t)(mem>>8)); //发送内存地址的高8位 while(!I2C_GetFlagStatus(I2C_FLAG_TXE)) //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据 { if(IIC_timeout(&i_timeout)) return 4; else i_timeout = 0; } } I2C_SendData((uint8_t)mem); //发送内存地址的低8位 while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) //判断TRA, BUSY, MSL, TXE and BTF flags { if(IIC_timeout(&i_timeout)) return 5; else i_timeout = 0; } //直接产生一个重起始信号即可开始读的过程 I2C_GenerateSTART(ENABLE); //重起始信号 while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)) //判断BUSY, MSL and SB flags { if(IIC_timeout(&i_timeout)) return 6; else i_timeout = 0; } I2C_Send7bitAddress(addr, I2C_Direction_Receiver); //发送地址+最低位1表示为“读” while(!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) //判断BUSY, MSL and ADDR flags { if(IIC_timeout(&i_timeout)) return 7; else i_timeout = 0; } I2C_GenerateSTOP(DISABLE); //关闭停止信号使能(某些情况下可能会在此清除PE,这一行可以去掉) for(uint8_t i=0; i<len; i++) { if(i == len-1) I2C_AcknowledgeConfig(DISABLE); //清除ACK位(某些情况下可能会在此清除PE,这一行可以去掉,但是逻辑分析仪抓时序,会多接收一个字节数据) //主设备为了能在收到最后一个字节后产生一个NACK信号,必须在读取倒数第二个字节之后(倒数第二个RxNE 事件之后)清除ACK位(ACK=0) while(!I2C_GetFlagStatus(I2C_FLAG_RXNE)) //获取RxEN的状态,等待收到数据 { if(IIC_timeout(&i_timeout)) return 0xff; else i_timeout = 0; } *(p_des+i) = I2C_ReceiveData(); //获得从机的寄存器中的数据 } I2C_GenerateSTOP(ENABLE); //使能停止信号 I2C_AcknowledgeConfig(ENABLE); //传输完毕,再次打开ACK使能 return 0; } /*************************************** * 向从机某寄内存地址起始,连续写入n个字节的数据 * 参数:addr 从机地址 * mem 寄存器地址 * men_16 是否为16位内存/寄存器地址 * p_src 源地址指针 * len 读取长度 * 返回值:0:正常 * 1~5:卡在第n步 * 0xFF:卡在连续写的过程中 */ uint8_t IIC_write_nBytes(uint8_t addr, uint16_t mem, uint8_t mem_16, uint8_t *p_src, uint8_t len) { uint16_t i_timeout = 0; //主机通知从机要写它的哪块内存 while(I2C_GetFlagStatus(I2C_FLAG_BUSY)) //IIC主机判忙 { if(IIC_timeout(&i_timeout)) return 1; else i_timeout = 0; } I2C_GenerateSTART(ENABLE); //起始信号 while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)) //判断BUSY, MSL and SB flags { if(IIC_timeout(&i_timeout)) return 2; else i_timeout = 0; } I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) //判断BUSY, MSL, ADDR, TXE and TRA flags { if(IIC_timeout(&i_timeout)) return 3; else i_timeout = 0; } if(mem_16) { I2C_SendData((uint8_t)(mem>>8)); //发送内存地址的高8位 while(!I2C_GetFlagStatus(I2C_FLAG_TXE)) //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据 { if(IIC_timeout(&i_timeout)) return 4; else i_timeout = 0; } } I2C_SendData((uint8_t)mem); //发送内存地址的低8位 //ACK之后直接写入数据 for(uint8_t i=0; i<len; i++) { while(!I2C_GetFlagStatus(I2C_FLAG_TXE)) //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据 { if(IIC_timeout(&i_timeout)) return 0xff; else i_timeout = 0; } I2C_SendData(*(p_src+i)); //发送数据 } while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) //判断TRA, BUSY, MSL, TXE and BTF flags { if(IIC_timeout(&i_timeout)) return 5; else i_timeout = 0; } I2C_GenerateSTOP(ENABLE); //停止信号 return 0; }