最近因为要用rx8025,所以仔细看了一下i2c的协议文件,之前用过i2c的器件,不过是直接用的网上的例程,没仔细看协议的具体实现。仔细看了之后把延时时间给优化了一下。
开始和停止都很简单,主要是中间的传输
中间传输出现的 (重新)开始信号 和开始传输的 开始信号 都是一样的,只要在SCL时钟线高电平期间,SDA数据线来一个下降沿就行。
i2c传输的要点就是: 传输一个字节 后面必然紧跟一个 响应 信号,这个响应信号可能来自主机,或者是从机,具体是谁,就要看传输方向。
传输方向分两种情况:
1.主机->从机,主机对从机发一个字节之后,主机要读取从机的响应信号(主机读SDA线)
A) 主机读SDA为高电平,说明从机无应答,主机在此之后可以选择发送 停止信号
或者 (重新)开始信号。(重新)开始信号之后主机可以选择重新发送刚才的字节。
B) 主机读SDA为低电平,说明从机有应答,主机可以选择 直接发送下一字节 或者 停止信号
或者 (重新)开始信号 。(重新)开始信号之后主机也可以继续发送下一字节。
2.从机->主机, 主机读取从机一个字节之后,主机要向从机发送一个响应信号(主机写SDA线)
A) 主机写SDA为高电平,从机收到主机的无应答信号之后,从机停止传输,
等待主机的停止信号。
B) 主机写SDA为低电平,从机收到主机的应答信号之后,从机继续输出下一字节。
多说无益,直接上示例。
其实下面的代码实用价值并不是很大,仅为最简演示用,因为没有对总线的各种状态做判断,出现意外情况很容易死机,卡总线。
关于对总线的判断(程序中没有的部分):开始传输前主机要读一次总线确保都是高电平,传输过程中还要根据响应信号决定重发、等待、停止。停止之后还要读总线确保总线被释放,即数据线与时钟线都是高电平。
头文件
#ifndef _I2C_H_ #define _I2C_H_ #ifdef __cplusplus extern "C" { #endif #include "stm32f10x_gpio.h" /******************************************************************************* * 注意: RX8025的Tbuf为1300ns 但在通信中发生计时数据的进位时 * 需要61000ns(Tsp)的总线空闲时间更新寄存器 ******************************************************************************/ /****** I2C时序 参数说明 标准模式 高速模式 RX8025 ****/ #define Thd_sta 46 // (重复)起始条件的保持时间 4000ns 600ns 600ns #define Tlow 95 // SCL时钟的低电平周期 4700ns 1300ns 1300ns #define Thigh 46 // SCL时钟的高电平周期 4000ns 600ns 600ns #define Tsu_sta 46 // 重复起始条件的建立时间 4700ns 600ns 600ns #define Thd_dat 0 // SDA数据保持时间 0 0 0 #define Tsu_dat 15 // SDA数据建立时间 250ns 100ns 200ns #define Tsu_sto 46 // 停止条件的建立时间 4000ns 600ns 600ns #define Tbuf 4500// 停止和启动之间的总线空闲 1300ns 1300ns 61000ns(Tsp) #define ACK 0 // 应答 (SDA低电平) #define NACK 1 // 无应答 (SDA高电平) /******************** SCL PA0 ************* SDA PA1 ***************************/ #define I2C_SCL_W(X) X?(GPIOA->BSRR = 0x00000001):(GPIOA->BSRR = 0x00010000); #define I2C_SDA_W(X) X?(GPIOA->BSRR = 0x00000002):(GPIOA->BSRR = 0x00020000); #define I2C_SCL_R() ((GPIOA->IDR & 0x00000001)>>0) // PA0 #define I2C_SDA_R() ((GPIOA->IDR & 0x00000002)>>1) // PA1 void I2C_Delay(uint32_t t); void I2C_Start(void); void I2C_Stop(void); uint8_t I2C_SendByte(uint8_t dat); uint8_t I2C_ReadByte(uint8_t ack); #ifdef __cplusplus } #endif #endif
函数文件
#include "i2c.h" /** * @brief delay func. * @param delay time = t * 14ns * @retval none */ void I2C_Delay(uint32_t t) { SysTick->LOAD = tick; SysTick->CTRL = 0x00000005; while(!(SysTick->CTRL & 0x00010000)); SysTick->CTRL = 0x00000000; } /** * @brief i2c start func. * @param none * @retval none */ void I2C_Start(void) { I2C_SDA_W(1); I2C_Delay(Tsu_dat); // Tsu;dat S > 250ns, F > 100ns I2C_SCL_W(1); I2C_Delay(Thigh); // Thigh S > 4.0us, F > 0.6us I2C_SCL_W(1); I2C_Delay(Tsu_sta); // Tsu;sta S > 4.7us, F > 0.6us I2C_SDA_W(0); I2C_Delay(Thd_sta); // Thd;sta S > 4.0us, F > 0.6us I2C_SCL_W(0); I2C_Delay(Tlow); // Tlow S > 4.7us, F > 1.3us } /** * @brief i2c stop func. * @param none * @retval none */ void I2C_Stop(void) { I2C_SCL_W(0); I2C_Delay(Tlow); // Tlow S > 4.7us, F > 1.3us I2C_SDA_W(0); I2C_Delay(Tsu_dat); // Tsu;dat S > 250ns, F > 100ns I2C_SCL_W(1); I2C_Delay(Tsu_sto); // Tsu;sto S > 4.0us, F > 0.6us I2C_SDA_W(1); I2C_Delay(Tbuf); // Tbuf(Tsp)S > 4.7us, F > 1.3us, for RX8025 > 61us } /** * @brief i2c send data func. * @param 1 byte data * @retval slave response signal: ACK(0) or NACK(1) */ uint8_t I2C_SendByte(uint8_t dat) { uint8_t i; for(i = 0; i < 8; i ++) { if((dat >> 7) & 1) // MSB first { I2C_SDA_W(1); } else { I2C_SDA_W(0); } dat <<= 1; I2C_Delay(Tsu_dat); // Tsu;dat S > 250ns, F > 100ns, for rx8025 > 200ns I2C_SCL_W(1); I2C_Delay(Thigh); // Thigh S > 4.0us, F > 0.6us I2C_SCL_W(0); I2C_Delay(Tlow); // Tlow S > 4.7us, F > 1.3us } I2C_SDA_W(1); I2C_Delay(Tsu_dat); // Tsu;dat S > 250ns, F > 100ns, for rx8025 > 200ns I2C_SCL_W(1); I2C_Delay(Thigh); // Thigh S > 4.0us, F > 0.6us i = 0; if( I2C_SDA_R() ) { i = 1; } I2C_SCL_W(0); I2C_Delay(Tlow); // Tlow S > 4.7us, F > 1.3us return i; // ACK == 0, NACK == 1 } /** * @brief i2c read data func. * @param master response signal: ACK(0) or NACK(1) * @retval 1 byte data */ uint8_t I2C_ReadByte(uint8_t ack) { uint8_t a; uint8_t dat = 0; I2C_SDA_W(1); // master release sda and go high I2C_Delay(Tsu_dat); // Tsu;dat S > 250ns, F > 100ns, for rx8025 > 200ns for(a = 0; a < 8; a ++) { I2C_SCL_W(1); I2C_Delay(Thigh); dat <<= 1; if( I2C_SDA_R() ) dat |= 1; I2C_SCL_W(0); I2C_Delay(Tlow); } if(ack) // master response signal { I2C_SDA_W(1); // master nack } else { I2C_SDA_W(0); // master ack } I2C_Delay(Tsu_dat); // Tsu;dat S > 250ns, F > 100ns, for rx8025 > 200ns I2C_SCL_W(1); I2C_Delay(Thigh); // Thigh S > 4.0us, F > 0.6us I2C_SCL_W(0); I2C_Delay(Tlow); // Tlow S > 4.7us, F > 1.3us return dat; }