/********************************************************************** * Copyright: (C)2023 LingYun IoT System Studio * Author: GuoWenxue QQ: 281143292 * Description: ISKBoard GPIO simulate I2C Bus Hardware Abstract Layer driver * * ChangeLog: * Version Date Author Description * V1.0.0 2023.04.3 GuoWenxue Release initial version ***********************************************************************/ #include "i2c_bitbang.h" i2c_bus_t i2c_bus[I2CBUSMAX] = { /* Bus ChipAddr SCL_Pin SDA_Pin */ {I2CBUS0, 0x00, {GPIOB, GPIO_PIN_6 }, {GPIOB, GPIO_PIN_7 } }, /* SHT20, RTC, OLED */ {I2CBUS1, 0x00, {GPIOB, GPIO_PIN_10}, {GPIOB, GPIO_PIN_11} }, /* MikroBUS I2C interface */ }; enum { LOW, HIGH, }; /*+--------------------------+ *| Low level GPIO API | *+--------------------------+*/ static inline void SDA_IN(uint8_t bus) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = i2c_bus[bus].sda.pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(i2c_bus[bus].sda.group, &GPIO_InitStruct); } static inline void SDA_OUT(uint8_t bus) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = i2c_bus[bus].sda.pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(i2c_bus[bus].sda.group, &GPIO_InitStruct); } static inline void SCL_OUT(uint8_t bus) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = i2c_bus[bus].scl.pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(i2c_bus[bus].scl.group, &GPIO_InitStruct); } static inline void SCL_SET(uint8_t bus, int level) { HAL_GPIO_WritePin(i2c_bus[bus].scl.group, i2c_bus[bus].scl.pin, level?GPIO_PIN_SET:GPIO_PIN_RESET); } static inline void SDA_SET(uint8_t bus, int level) { HAL_GPIO_WritePin(i2c_bus[bus].sda.group, i2c_bus[bus].sda.pin, level?GPIO_PIN_SET:GPIO_PIN_RESET); } static inline GPIO_PinState SCL_GET(uint8_t bus) { return HAL_GPIO_ReadPin(i2c_bus[bus].scl.group, i2c_bus[bus].scl.pin); } static inline GPIO_PinState SDA_GET(uint8_t bus) { return HAL_GPIO_ReadPin(i2c_bus[bus].sda.group, i2c_bus[bus].sda.pin); } /*+--------------------------+ *| I2C Clock Sequence API | *+--------------------------+*/ /* */ #if 1 #include "miscdev.h" /* delay_us() implement by TIM6 */ #else static inline void delay_us(uint32_t us) { uint32_t i, j; for(j=0; j<450; j++)// 450 ~500 for(i = 0; i < us; i++) { __NOP(); } } #endif void I2C_StartCondition(uint8_t bus) { SDA_OUT(bus); SCL_OUT(bus); /* Start Condition Clock Sequence: ______ SCL: |___ _____ SDA: |_____ */ SDA_SET(bus, HIGH); SCL_SET(bus, HIGH); delay_us(1); SDA_SET(bus, LOW); delay_us(1); // hold time start condition (t_HD;STA) SCL_SET(bus, LOW); delay_us(1); } void I2C_StopCondition(uint8_t bus) { SDA_OUT(bus); /* SCL Stop Condition Clock Sequence: _______ SCL: ___| _____ SDA: _____| */ SCL_SET(bus, LOW); SDA_SET(bus, LOW); delay_us(1); SCL_SET(bus, HIGH); SDA_SET(bus, HIGH); delay_us(1); } void I2C_Ack(uint8_t bus, uint8_t ack) { if(ACK_NONE == ack) return ; SCL_SET(bus, LOW); SDA_OUT(bus); if( ACK==ack ) SDA_SET(bus, LOW); else if( NAK==ack ) SDA_SET(bus, HIGH); delay_us(1); SCL_SET(bus, HIGH); delay_us(1); SCL_SET(bus, LOW); } uint8_t I2C_WriteByte(uint8_t bus, uint8_t txByte) { uint8_t error = NO_ERROR; uint8_t mask; SDA_OUT(bus); SCL_SET(bus, LOW); /* start send 8 bits data */ for(mask=0x80; mask>0; mask>>=1) { if((mask & txByte) == 0) SDA_SET(bus, LOW); else SDA_SET(bus, HIGH); delay_us(1); // data set-up time (t_SU;DAT) SCL_SET(bus, HIGH); delay_us(1); // SCL high time (t_HIGH) SCL_SET(bus, LOW); delay_us(1); // data hold time(t_HD;DAT) } /* Wait for ACK/NAK */ SDA_IN(bus); SCL_SET(bus, HIGH); // clk #9 for ack delay_us(1); // data set-up time (t_SU;DAT) /* High level means NAK */ if( SDA_GET(bus) ) error = ACK_ERROR; SCL_SET(bus, LOW); delay_us(1); return error; } static inline uint8_t I2C_WaitWhileClockStreching(uint8_t bus, uint8_t timeout) { uint8_t error = NO_ERROR; while( SCL_GET(bus)== 0) { if(timeout-- == 0) return TIMEOUT_ERROR; delay_us(10); } return error; } uint8_t I2C_ReadByte(uint8_t bus, uint8_t *rxByte, uint8_t ack, uint32_t timeout) { //============================================================================== uint8_t error = NO_ERROR; uint8_t mask; *rxByte = 0x00; SDA_IN(bus); /* start read 8 bits data */ for(mask = 0x80; mask > 0; mask >>= 1) { SCL_SET(bus, HIGH); // start clock on SCL-line delay_us(1); // clock set-up time (t_SU;CLK) // wait while clock streching error = I2C_WaitWhileClockStreching(bus, timeout); delay_us(1); // SCL high time (t_HIGH) if( SDA_GET(bus) ) *rxByte |= mask; // read bit SCL_SET(bus, LOW); delay_us(1); // data hold time(t_HD;DAT) } I2C_Ack(bus, ack); return error; } uint8_t I2C_SendAddress(uint8_t bus, uint8_t dir) { uint8_t addr; uint8_t rv; if( I2C_WR == dir ) { addr = W_ADDR(i2c_bus[bus].addr); } else { addr = R_ADDR(i2c_bus[bus].addr); } rv=I2C_WriteByte(bus, addr); return rv; } int i2c_init(uint8_t bus, uint8_t addr) { if( bus >= I2CBUSMAX ) { return -1; } #if 0 /* I2C bus used already */ if( 0x00 != i2c_bus[bus].addr ) { return -2; } #endif /* Set I2C slave address */ i2c_bus[bus].addr = addr; return 0; } int i2c_term(uint8_t bus) { if( bus >= I2CBUSMAX ) { return -1; } /* Set I2C slave address */ i2c_bus[bus].addr = 0x0; return 0; } int i2c_read(uint8_t bus, uint8_t *buf, int size) { int i; int rv = NO_ERROR; uint8_t byte; I2C_StartCondition(bus); if( NO_ERROR != (rv=I2C_SendAddress(bus, I2C_RD) ) ) { goto OUT; } for (i=0; i