ISKBoard example project
Wenxue
2025-09-02 6e5f041f9aa1c91150988b47ea7de67b9e34b7db
GPIO模拟I2C实现SHT20
2 files added
4 files modified
2 files deleted
687 ■■■■■ changed files
Core/Inc/i2c.h 52 ●●●●● patch | view | raw | blame | history
Core/Inc/stm32l4xx_hal_conf.h 2 ●●● patch | view | raw | blame | history
Core/Src/board/i2c_bitbang.c 341 ●●●●● patch | view | raw | blame | history
Core/Src/board/i2c_bitbang.h 106 ●●●●● patch | view | raw | blame | history
Core/Src/board/sht20.c 13 ●●●●● patch | view | raw | blame | history
Core/Src/i2c.c 140 ●●●●● patch | view | raw | blame | history
Core/Src/main.c 2 ●●●●● patch | view | raw | blame | history
ISKBoard.ioc 31 ●●●●● patch | view | raw | blame | history
Core/Inc/i2c.h
File was deleted
Core/Inc/stm32l4xx_hal_conf.h
@@ -40,7 +40,7 @@
/*#define HAL_CRYP_MODULE_ENABLED   */
/*#define HAL_CAN_MODULE_ENABLED   */
/*#define HAL_COMP_MODULE_ENABLED   */
#define HAL_I2C_MODULE_ENABLED
/*#define HAL_I2C_MODULE_ENABLED   */
/*#define HAL_CRC_MODULE_ENABLED   */
/*#define HAL_CRYP_MODULE_ENABLED   */
/*#define HAL_DAC_MODULE_ENABLED   */
Core/Src/board/i2c_bitbang.c
New file
@@ -0,0 +1,341 @@
/**********************************************************************
 *   Copyright: (C)2024 LingYun IoT System Studio
 *      Author: GuoWenxue<guowenxue@gmail.com>
 *
 * Description: The purpose of this code is to provide a simple C library,
 *              which providing software bit-bang of the I2C protocol on
 *              any GPIO pins for ISKBoard.
 *
 *   ChangeLog:
 *        Version    Date       Author            Description
 *        V1.0.0  2024.08.29    GuoWenxue      Release initial version
 *
 ***********************************************************************/
#include "i2c_bitbang.h"
#include "miscdev.h"  /* udelay() implement by TIM6 */
/*
 *+---------------------------------+
 *|  I2C GPIO port Level functions  |
 *+---------------------------------+
 */
#define mdelay(x)       HAL_Delay(x)
enum
{
    LOW,
    HIGH,
};
i2c_bus_t i2c_bus =
{
 /* Addr        SCL_Pin               SDA_Pin      */
    0x00,  {GPIOB, GPIO_PIN_6 }, {GPIOB, GPIO_PIN_7 },
};
static inline void SDA_IN(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = i2c_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.sda.group, &GPIO_InitStruct);
}
static inline void SDA_OUT(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = i2c_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.sda.group, &GPIO_InitStruct);
}
static inline void SCL_OUT(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = i2c_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.scl.group, &GPIO_InitStruct);
}
static inline void SCL_SET(int level)
{
    HAL_GPIO_WritePin(i2c_bus.scl.group, i2c_bus.scl.pin, level?GPIO_PIN_SET:GPIO_PIN_RESET);
}
static inline void SDA_SET(int level)
{
    HAL_GPIO_WritePin(i2c_bus.sda.group, i2c_bus.sda.pin, level?GPIO_PIN_SET:GPIO_PIN_RESET);
}
static inline GPIO_PinState SCL_GET(void)
{
    return HAL_GPIO_ReadPin(i2c_bus.scl.group, i2c_bus.scl.pin);
}
static inline GPIO_PinState SDA_GET(void)
{
    return HAL_GPIO_ReadPin(i2c_bus.sda.group, i2c_bus.sda.pin);
}
/*
 *+--------------------------------+
 *|  I2C Low Level API functions   |
 *+--------------------------------+
 */
void I2C_StartCondition(void)
{
    SDA_OUT();
    SCL_OUT();
    /* Start Condition Clock Sequence:
              ______
     SCL:           |___
            _____
     SDA:        |_____
    */
    SDA_SET(HIGH);
    SCL_SET( HIGH);
    udelay(1);
    SDA_SET(LOW);
    udelay(1);  // hold time start condition (t_HD;STA)
    SCL_SET(LOW);
    udelay(1);
}
void I2C_StopCondition(void)
{
    SDA_OUT();
    /* SCL Stop Condition Clock Sequence:
                _______
      SCL:   ___|
                   _____
      SDA:   _____|
    */
    SCL_SET(LOW);
    SDA_SET(LOW);
    udelay(1);
    SCL_SET(HIGH);
    SDA_SET(HIGH);
    udelay(1);
}
int I2C_SendAddress(uint8_t dir)
{
    uint8_t       addr;
    uint8_t       rv;
    if( I2C_WR == dir )
    {
        addr = W_ADDR(i2c_bus.addr);
    }
    else
    {
        addr = R_ADDR(i2c_bus.addr);
    }
    rv=I2C_WriteByte(addr);
    return rv;
}
void I2C_Ack(uint8_t ack)
{
    if(ACK_NONE == ack)
        return ;
    SCL_SET(LOW);
    SDA_OUT();
    if( ACK==ack )
        SDA_SET(LOW);
    else if( NAK==ack )
        SDA_SET(HIGH);
    udelay(1);
    SCL_SET( HIGH);
    udelay(1);
    SCL_SET(LOW);
}
int I2C_WriteByte(uint8_t txByte)
{
    int     error = 0;
    uint8_t mask;
    SDA_OUT();
    SCL_SET(LOW);
    /* start send 8 bits data, MSB */
    for(mask=0x80; mask>0; mask>>=1)
    {
        if((mask & txByte) == 0)
            SDA_SET(LOW);
        else
            SDA_SET(HIGH);
        udelay(1);  // data set-up time (t_SU;DAT)
        SCL_SET(HIGH);
        udelay(1);  // SCL high time (t_HIGH)
        SCL_SET(LOW);
        udelay(1);  // data hold time(t_HD;DAT)
    }
    /* Wait for ACK/NAK */
    SDA_IN();
    SCL_SET(HIGH); // clk #9 for ack
    udelay(1); // data set-up time (t_SU;DAT)
    /* High level means NAK */
    if( SDA_GET() )
        error = -ERROR_ACK;
    SCL_SET(LOW);
    udelay(1);
    return error;
}
static inline int I2C_WaitWhileClockStreching(uint8_t timeout)
{
    while( SCL_GET()== 0)
    {
        if(timeout-- == 0)
            return -ERROR_TIMEOUT;
        udelay(10);
    }
    return ERROR_NONE;
}
int I2C_ReadByte(uint8_t *rxByte, uint8_t ack, uint32_t timeout)
{
    uint8_t error = 0;
    uint8_t mask;
    *rxByte = 0x00;
    SDA_IN();
    /* start read 8 bits data, MSB */
    for(mask = 0x80; mask > 0; mask >>= 1)
    {
        SCL_SET(HIGH);  // start clock on SCL-line
        udelay(1);         // clock set-up time (t_SU;CLK)
        // wait while clock streching
        error = I2C_WaitWhileClockStreching(timeout);
        udelay(1); // SCL high time (t_HIGH)
        if( SDA_GET() )
            *rxByte |= mask; // read bit
        SCL_SET(LOW);
        udelay(1); // data hold time(t_HD;DAT)
    }
    I2C_Ack(ack);
    return error;
}
/*
 *+--------------------------------+
 *|  I2C High Level API functions  |
 *+--------------------------------+
 */
int i2c_lock(uint8_t addr)
{
    /* I2C bus already be used */
    while( i2c_bus.addr && i2c_bus.addr!=addr )
    {
        mdelay(10);
    }
    i2c_bus.addr = addr;
    return 0;
}
void i2c_free(void)
{
    i2c_bus.addr = 0x0;
}
int i2c_write(uint8_t *data, int bytes)
{
    int       i;
    int       rv = 0;
    if( !data )
    {
        return -ERROR_PARM;
    }
    /* send start condition  */
    I2C_StartCondition();
    /* send device address for write */
    if( (rv=I2C_SendAddress(I2C_WR)) < 0 )
        goto OUT;
    /* send data to slave device */
    for (i=0; i<bytes; i++)
    {
        if( (rv=I2C_WriteByte(data[i])) < 0 )
        {
            break;
        }
    }
OUT:
    /* send stop condition */
    I2C_StopCondition();
    return rv;
}
int i2c_read(uint8_t *buf, int size)
{
    int       i;
    int       rv = ERROR_NONE;
    uint8_t   byte;
    /* send start condition  */
    I2C_StartCondition();
    /* send device address for read */
    if( (rv=I2C_SendAddress(I2C_RD)) < 0 )
        goto OUT;
    /* read data from slave device */
    for (i=0; i<size; i++)
    {
        if( (rv=I2C_ReadByte(&byte, ACK, I2C_CLK_STRETCH_TIMEOUT)) < 0 )
            goto OUT;
        buf[i] = byte;
    }
OUT:
    /* send stop condition  */
    I2C_StopCondition();
    return rv;
}
Core/Src/board/i2c_bitbang.h
New file
@@ -0,0 +1,106 @@
/**********************************************************************
 *   Copyright: (C)2024 LingYun IoT System Studio
 *      Author: GuoWenxue<guowenxue@gmail.com>
 *
 * Description: The purpose of this code is to provide a simple C library,
 *              which providing software bit-bang of the I2C protocol on
 *              any GPIO pins for ISKBoard.
 *
 *   ChangeLog:
 *        Version    Date       Author            Description
 *        V1.0.0  2024.08.29    GuoWenxue      Release initial version
 *
 ***********************************************************************/
#ifndef __I2C_BITBANG_H_
#define __I2C_BITBANG_H_
#include "stm32l4xx_hal.h"
#define I2C_CLK_STRETCH_TIMEOUT   6000
/*
 *+---------------------------------+
 *|  I2C GPIO port Level functions  |
 *+---------------------------------+
 */
/* GPIO port for I2C typedef */
typedef struct i2c_gpio_s
{
    GPIO_TypeDef        *group; /* GPIO group */
    uint16_t             pin;   /* GPIO pin   */
} i2c_gpio_t;
/* I2C bus typedef */
typedef struct i2c_bus_s
{
    uint8_t             addr; /* 7-bits slave address */
    i2c_gpio_t          scl;  /* SCL GPIO pin */
    i2c_gpio_t          sda;  /* SDA GPIO pin */
} i2c_bus_t;
/*
 *+--------------------------------+
 *|  I2C High Level API functions  |
 *+--------------------------------+
 */
/* I2C bus error number */
enum
{
  ERROR_NONE     = 0x00, // no error
  ERROR_PARM     = 0x01, // parameter out of range error
  ERROR_ACK      = 0x02, // no acknowledgment error
  ERROR_TIMEOUT  = 0x03, // timeout error
  ERROR_CHECKSUM = 0x04, // checksum mismatch error
};
/* Set I2C slave device 7-bits address in the bus to lock it */
extern int i2c_lock(uint8_t addr);
/* Clear I2C slave device 7-bits address in the bus to free it */
extern void i2c_free(void);
/* write data by to slave device */
extern int i2c_write(uint8_t *data, int bytes);
/* read data from slave device */
extern int i2c_read(uint8_t *buf, int size);
/*
 *+--------------------------------+
 *|  I2C Low Level API functions   |
 *+--------------------------------+
 */
/* Send I2C start condition */
extern void I2C_StartCondition(void);
/* Send I2C stop condition */
extern void I2C_StopCondition(void);
/* Send I2C slave device 8-bits address */
#define W_ADDR(x)     ((x)<<1 )    /* I2C address bit[0]=0 means write */
#define R_ADDR(x)     ((x)<<1 | 1) /* I2C address bit[0]=1 means read  */
enum
{
    I2C_WR,  /* Direction for write */
    I2C_RD,  /* Direction for read  */
};
extern int I2C_SendAddress(uint8_t dir);
/* Write a byte data to I2C slave device */
extern int I2C_WriteByte(uint8_t txByte);
/* Read a byte data from I2C slave device */
enum
{
    ACK_NONE, /* ACK None */
    ACK,      /* ACK */
    NAK,      /* NAK */
};
extern int I2C_ReadByte(uint8_t *rxByte, uint8_t ack, uint32_t timeout);
#endif /* __I2C_BITBANG_H_ */
Core/Src/board/sht20.c
@@ -11,7 +11,7 @@
 ***********************************************************************/
#include <stdio.h>
#include <string.h>
#include "i2c.h"
#include "i2c_bitbang.h"
#include "sht20.h"
#define CONFIG_DEBUG_SHT2X /* Enable SHT20 debug */
@@ -61,6 +61,8 @@
    float           temp, rh;
    int             rv;
    i2c_lock(SHT2X_CHIPADDR);
    if( sht2x_softreset() < 0 )
    {
        sht2x_print("SHT20 soft reset failed.\r\n");
@@ -94,6 +96,7 @@
        *humdity=rh;
OUT:
    i2c_free();
    return rv;
}
@@ -104,8 +107,8 @@
    sht2x_print("Start soft reset sht2x\r\n");
    rv=HAL_I2C_Master_Transmit(&hi2c1, SHT2X_CHIPADDR<<1, &command, 1, 0xFFFFFFFF);
    if( rv != HAL_OK )
    rv=i2c_write(&command, 1);
    if( rv )
    {
        sht2x_print("SHT2X send soft reset command 0x%0x failure: rv=0x%02x\r\n", command, rv);
        return -rv;
@@ -161,7 +164,7 @@
        return -2;
    }
    if( HAL_I2C_Master_Transmit(&hi2c1, SHT2X_CHIPADDR<<1, &command, 1, 0xFFFFFFFF) != HAL_OK )
    if( i2c_write(&command, 1) < 0 )
    {
        sht2x_print("SHT2X send measure command 0x%0x failure\r\n", command);
        return -3;
@@ -176,7 +179,7 @@
    {
        memset(buf, 0, 3);
        if( HAL_OK == HAL_I2C_Master_Receive(&hi2c1, SHT2X_CHIPADDR<<1|1, buf, 3, 0xFFFFFFFF) )
        if( !i2c_read(buf, 3) )
        {
            break;
        }
Core/Src/i2c.c
File was deleted
Core/Src/main.c
@@ -19,7 +19,6 @@
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
@@ -96,7 +95,6 @@
  MX_ADC1_Init();
  MX_TIM6_Init();
  MX_TIM1_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
  beep_start(2, 300);
  /* USER CODE END 2 */
ISKBoard.ioc
@@ -18,20 +18,17 @@
CAD.provider=
File.Version=6
GPIO.groupedBy=Group By Peripherals
I2C1.IPParameters=Timing
I2C1.Timing=0x10D19CE4
KeepUserPlacement=false
Mcu.CPN=STM32L431RCT6
Mcu.Family=STM32L4
Mcu.IP0=ADC1
Mcu.IP1=I2C1
Mcu.IP2=NVIC
Mcu.IP3=RCC
Mcu.IP4=SYS
Mcu.IP5=TIM1
Mcu.IP6=TIM6
Mcu.IP7=USART1
Mcu.IPNb=8
Mcu.IP1=NVIC
Mcu.IP2=RCC
Mcu.IP3=SYS
Mcu.IP4=TIM1
Mcu.IP5=TIM6
Mcu.IP6=USART1
Mcu.IPNb=7
Mcu.Name=STM32L431R(B-C)Tx
Mcu.Package=LQFP64
Mcu.Pin0=PH0-OSC_IN (PH0)
@@ -40,10 +37,8 @@
Mcu.Pin11=PA10
Mcu.Pin12=PA11
Mcu.Pin13=PD2
Mcu.Pin14=PB6
Mcu.Pin15=PB7
Mcu.Pin16=VP_SYS_VS_Systick
Mcu.Pin17=VP_TIM6_VS_ClockSourceINT
Mcu.Pin14=VP_SYS_VS_Systick
Mcu.Pin15=VP_TIM6_VS_ClockSourceINT
Mcu.Pin2=PB0
Mcu.Pin3=PB1
Mcu.Pin4=PB2
@@ -52,7 +47,7 @@
Mcu.Pin7=PB14
Mcu.Pin8=PC6
Mcu.Pin9=PC9
Mcu.PinsNb=18
Mcu.PinsNb=16
Mcu.ThirdPartyNb=0
Mcu.UserConstants=
Mcu.UserName=STM32L431RCTx
@@ -104,10 +99,6 @@
PB2.Locked=true
PB2.PinState=GPIO_PIN_SET
PB2.Signal=GPIO_Output
PB6.Mode=I2C
PB6.Signal=I2C1_SCL
PB7.Mode=I2C
PB7.Signal=I2C1_SDA
PC6.GPIOParameters=PinState
PC6.Locked=true
PC6.PinState=GPIO_PIN_SET
@@ -154,7 +145,7 @@
ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=true
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_USART1_UART_Init-USART1-false-HAL-true,4-MX_ADC1_Init-ADC1-false-HAL-true,5-MX_TIM6_Init-TIM6-false-HAL-true,6-MX_TIM1_Init-TIM1-false-HAL-true,7-MX_I2C1_Init-I2C1-false-HAL-true
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_USART1_UART_Init-USART1-false-HAL-true,4-MX_ADC1_Init-ADC1-false-HAL-true,5-MX_TIM6_Init-TIM6-false-HAL-true,6-MX_TIM1_Init-TIM1-false-HAL-true
RCC.ADCFreq_Value=12000000
RCC.AHBFreq_Value=80000000
RCC.APB1Freq_Value=80000000