/********************************************************************** * Copyright: (C)2021 LingYun IoT System Studio * Author: GuoWenxue QQ: 281143292 * Description: BearKE1 NB-IoT Board GPIO simulate I2C bus source code * * Notice: Must implement delay_us() by timer in tim.h first. * * ChangeLog: * Version Date Author Description * V1.0.0 2021.08.10 GuoWenxue Release initial version ***********************************************************************/ #include #include "stm32l4xx_hal.h" #include "tim.h" /* delay_us() implement */ #include "gpio.h" #include "gpio_i2c.h" /* comment follow macro will disable I2C bus clock stretching support * reference: http://www.i2c-bus.org/clock-stretching/ */ #define I2C_CLK_STRETCH_TIMEOUT 50 /* uncomment follow macro will enable debug print */ //#define CONFIG_GPIO_I2C_DEBUG #ifdef CONFIG_GPIO_I2C_DEBUG #define i2c_print(format,args...) printf(format, ##args) #else #define i2c_print(format,args...) do{} while(0) #endif /* GPIO Simulate I2C Bus pins */ typedef struct i2c_gpio_s { GPIO_TypeDef *group; uint16_t scl; /* SCL */ uint16_t sda; /* SDA */ } i2c_gpio_t; static i2c_gpio_t i2c_pins = { GPIOB, GPIO_PIN_13/*SCL*/, GPIO_PIN_14/*SDA*/ }; #define SDA_IN() do{ GPIO_InitTypeDef GPIO_InitStruct = {0}; \ GPIO_InitStruct.Pin = i2c_pins.sda; \ GPIO_InitStruct.Mode = GPIO_MODE_INPUT; \ GPIO_InitStruct.Pull = GPIO_PULLUP; \ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \ HAL_GPIO_Init(i2c_pins.group, &GPIO_InitStruct); \ }while(0) #define SDA_OUT() do{ GPIO_InitTypeDef GPIO_InitStruct = {0}; \ GPIO_InitStruct.Pin = i2c_pins.sda; \ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; \ GPIO_InitStruct.Pull = GPIO_PULLUP; \ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \ HAL_GPIO_Init(i2c_pins.group, &GPIO_InitStruct); \ }while(0) #define SCL_OUT() do{ GPIO_InitTypeDef GPIO_InitStruct = {0}; \ GPIO_InitStruct.Pin = i2c_pins.scl; \ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; \ GPIO_InitStruct.Pull = GPIO_PULLUP; \ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \ HAL_GPIO_Init(i2c_pins.group, &GPIO_InitStruct); \ }while(0) #define SCL_H() HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.scl, GPIO_PIN_SET) #define SCL_L() HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.scl, GPIO_PIN_RESET) #define SDA_H() HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.sda, GPIO_PIN_SET) #define SDA_L() HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.sda, GPIO_PIN_RESET) #define READ_SDA() HAL_GPIO_ReadPin(i2c_pins.group, i2c_pins.sda) #define READ_SCL() HAL_GPIO_ReadPin(i2c_pins.group, i2c_pins.scl) static inline uint8_t I2c_WaitWhileClockStretching(uint16_t timeout) { while( timeout-- > 0 ) { if( READ_SCL() ) break; delay_us(1); } return timeout ? NO_ERROR : BUS_ERROR; } /* StartCondition(S) */ uint8_t I2c_StartCondition() { uint8_t rv = NO_ERROR; SDA_OUT(); SCL_OUT(); /* StartCondition(S): A high to low transition on the SDA line while SCL is high. _______ SCL: |___ _____ SDA: |_____ */ SDA_H(); delay_us(1); SCL_H(); delay_us(1); #ifdef I2C_CLK_STRETCH_TIMEOUT rv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT); if( rv ) { i2c_print("ERROR: %s() I2C bus busy\n", __func__); return rv; } #endif SDA_L(); delay_us(2); SCL_L(); delay_us(2); return rv; } /* StopCondition(P) */ uint8_t I2c_StopCondition(void) { uint8_t rv = NO_ERROR; SDA_OUT(); /* StopCondition(P): A low to high transition on the SDA line while SCL is high. _____ SCL: ___| _____ SDA: _____| */ SCL_L(); SDA_L(); delay_us(2); SCL_H(); delay_us(2); #ifdef I2C_CLK_STRETCH_TIMEOUT rv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT); if( rv ) { i2c_print("ERROR: %s() I2C bus busy\n", __func__); } #endif SDA_H(); delay_us(2); return rv; } uint8_t I2c_WriteByte(uint8_t byte) { uint8_t rv = NO_ERROR; uint8_t mask; /* Data line changes must happened when SCL is low */ SDA_OUT(); SCL_L(); /* 1Byte=8bit, MSB send: bit[7]-->bit[0] */ for(mask=0x80; mask>0; mask>>=1) { if((mask & byte) == 0) SDA_L(); else SDA_H(); delay_us(1); // data set-up time (t_SU;DAT) SCL_H(); delay_us(5); // SCL high time (t_HIGH) #ifdef I2C_CLK_STRETCH_TIMEOUT rv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT); if( rv ) { i2c_print("ERROR: %s() I2C bus busy\n", __func__); goto OUT; } #endif SCL_L(); delay_us(1); // data hold time(t_HD;DAT) } /* clk #9 wait ACK/NAK from slave */ SDA_IN(); SCL_H(); // clk #9 for ack delay_us(1); // data set-up time (t_SU;DAT) #ifdef I2C_CLK_STRETCH_TIMEOUT rv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT); if( rv ) { i2c_print("ERROR: %s() I2C bus busy\n", __func__); goto OUT; } #endif /* High level means NAK */ if( READ_SDA() ) rv = ACK_ERROR; OUT: SCL_L(); delay_us(20); return rv; } uint8_t I2c_ReadByte(uint8_t *byte, uint8_t ack) { uint8_t rv = NO_ERROR; uint8_t mask; *byte = 0x00; SDA_IN(); /* 1Byte=8bit, MSB send: bit[7]-->bit[0] */ for(mask = 0x80; mask > 0; mask >>= 1) { SCL_H(); // start clock on SCL-line delay_us(1); // clock set-up time (t_SU;CLK) #ifdef I2C_CLK_STRETCH_TIMEOUT rv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT); if( rv ) { i2c_print("ERROR: %s() I2C bus busy\n", __func__); goto OUT; } #endif if(READ_SDA()) *byte |= mask; // read bit SCL_L(); delay_us(1); // data hold time(t_HD;DAT) } /* clk #9 send ACK/NAK to slave */ if(ack == ACK) { SDA_OUT(); SDA_L(); // send Acknowledge if necessary } else if( ack == NAK ) { SDA_OUT(); SDA_H(); // send NotAcknowledge if necessary } delay_us(1); // data set-up time (t_SU;DAT) SCL_H(); // clk #9 for ack delay_us(2); // SCL high time (t_HIGH) #ifdef I2C_CLK_STRETCH_TIMEOUT rv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT); if( rv ) { i2c_print("ERROR: %s() I2C bus busy\n", __func__); } #endif OUT: SCL_L(); delay_us(2); // wait to see byte package on scope return rv; } uint8_t I2c_SendAddress(uint8_t addr) { return I2c_WriteByte(addr); } int I2C_Master_Receive(uint8_t addr, uint8_t *buf, int len) { int i; int rv = NO_ERROR; uint8_t byte; I2c_StartCondition(); rv = I2c_SendAddress(addr); if( rv ) { i2c_print("Send I2C read address failure, rv=%d\n", rv); goto OUT; } #ifdef I2C_CLK_STRETCH_TIMEOUT /* wait while clock streching */ rv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT); if( rv ) { i2c_print("ERROR: %s() I2C wait clock stretching failure, rv=%d\n", __func__, rv); return rv; } #endif for (i=0; i