From 53de825c4c4d8325c02341177f62ab84897eb5ee Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Sat, 25 Jul 2026 22:09:43 +0800
Subject: [PATCH] update vscode task.json and add stm32flash tools

---
 ex2.i2c-bitbang/Board/i2c_bitbang.c |  734 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 734 insertions(+), 0 deletions(-)

diff --git a/ex2.i2c-bitbang/Board/i2c_bitbang.c b/ex2.i2c-bitbang/Board/i2c_bitbang.c
new file mode 100644
index 0000000..18adc9c
--- /dev/null
+++ b/ex2.i2c-bitbang/Board/i2c_bitbang.c
@@ -0,0 +1,734 @@
+/**********************************************************************
+ *   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 <stdbool.h>
+#include <stdio.h>
+#include "miscdev.h"  /* udelay() */
+#include "i2c_bitbang.h"
+
+/* I2C1 bus instance for SHT20, RTC, OLED on board */
+i2c_bus_t i2c1_bus =
+{
+    .lock = ATOMIC_VAR_INIT(false),
+    .addr = 0x00,
+    .scl  = {GPIOB, GPIO_PIN_6},
+    .sda  = {GPIOB, GPIO_PIN_7},
+};
+
+/* I2C2 bus instance on MikroBUS */
+i2c_bus_t i2c2_bus =
+{
+    .lock = ATOMIC_VAR_INIT(false),
+    .addr = 0x00,
+    .scl  = {GPIOB, GPIO_PIN_10},
+    .sda  = {GPIOB, GPIO_PIN_11},
+};
+
+/*
+ *+---------------------------------+
+ *|  I2C GPIO port Level functions  |
+ *+---------------------------------+
+ */
+
+/**
+ * @brief  Initialize I2C GPIO pins
+ * @param  bus: pointer to I2C bus structure
+ * @retval None
+ */
+void i2c_gpio_init(i2c_bus_t *bus)
+{
+    GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+    /* Enable GPIO clocks */
+    gpio_clk_enable(bus->scl.group);
+    gpio_clk_enable(bus->sda.group);
+
+    /* Configure SCL pin */
+    GPIO_InitStruct.Pin = bus->scl.pin;
+    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;  /* Open-drain output */
+    GPIO_InitStruct.Pull = GPIO_PULLUP;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+    HAL_GPIO_Init(bus->scl.group, &GPIO_InitStruct);
+
+    /* Configure SDA pin */
+    GPIO_InitStruct.Pin = bus->sda.pin;
+    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;  /* Open-drain output */
+    GPIO_InitStruct.Pull = GPIO_PULLUP;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+    HAL_GPIO_Init(bus->sda.group, &GPIO_InitStruct);
+
+    /* Set both pins to HIGH (idle state) */
+    HAL_GPIO_WritePin(bus->scl.group, bus->scl.pin, GPIO_PIN_SET);
+    HAL_GPIO_WritePin(bus->sda.group, bus->sda.pin, GPIO_PIN_SET);
+}
+
+/**
+ * @brief  Set SCL pin level
+ * @param  bus: pointer to I2C bus structure
+ * @param  level: LOW or HIGH
+ * @retval None
+ */
+static inline void i2c_scl_set(i2c_bus_t *bus, uint8_t level)
+{
+    HAL_GPIO_WritePin(bus->scl.group, bus->scl.pin, level?GPIO_PIN_SET:GPIO_PIN_RESET);
+}
+
+/**
+ * @brief  Set SDA pin level
+ * @param  bus: pointer to I2C bus structure
+ * @param  level: LOW or HIGH
+ * @retval None
+ */
+static inline void i2c_sda_set(i2c_bus_t *bus, uint8_t level)
+{
+    HAL_GPIO_WritePin(bus->sda.group, bus->sda.pin, level?GPIO_PIN_SET:GPIO_PIN_RESET);
+}
+
+/**
+ * @brief  Read SDA pin level
+ * @param  bus: pointer to I2C bus structure
+ * @retval pin level (0 or 1)
+ */
+static uint8_t i2c_sda_read(i2c_bus_t *bus)
+{
+    return (HAL_GPIO_ReadPin(bus->sda.group, bus->sda.pin) == GPIO_PIN_SET) ? 1 : 0;
+}
+
+
+/*
+ *+---------------------------------+
+ *|    I2C bus control function     |
+ *+---------------------------------+
+ */
+
+/**
+ * @brief  工业级模拟 I2C 总线初始化 (带硬件死锁九脉冲自愈)
+ * @param  bus: pointer to I2C bus structure
+ * @retval None
+ */
+void i2c_init(i2c_bus_t *bus)
+{
+    if (!bus)
+    {
+        return;
+    }
+
+    /* 1. 初始化 I2C GPIO 口 */
+    i2c_gpio_init(bus);
+
+    /* 2. 连续发送 9 个 SCL 时钟脉冲 */
+    i2c_sda_set(bus, HIGH);
+    for (int i = 0; i < 9; i++)
+    {
+        i2c_scl_set(bus, LOW);
+        udelay(I2C_DELAY_US);
+        i2c_scl_set(bus, HIGH);
+        udelay(I2C_DELAY_US);
+    }
+
+    /* 3. 补发一个结束信号,强迫所有从机回到空闲态 */
+    i2c_scl_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+    i2c_sda_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+    i2c_scl_set(bus, HIGH);
+    udelay(I2C_DELAY_US);
+    i2c_sda_set(bus, HIGH);
+    udelay(I2C_DELAY_US);
+}
+
+/**
+ * @brief  Deinitialize I2C GPIO pins
+ * @param  bus: pointer to I2C bus structure
+ * @retval None
+ */
+void i2c_deinit(i2c_bus_t *bus)
+{
+    HAL_GPIO_DeInit(bus->scl.group, bus->scl.pin);
+    HAL_GPIO_DeInit(bus->sda.group, bus->sda.pin);
+    atomic_store(&bus->lock, false);
+}
+
+/**
+ * @brief  绑定设备及地址到 I2C 总线上
+ */
+int i2c_bind_dev(i2c_dev_t *dev, i2c_bus_t *bus, uint8_t dev_addr)
+{
+    if (!dev || !bus)
+    {
+        return -1;
+    }
+
+    dev->bus = bus;
+    dev->dev_addr = dev_addr;
+
+    return 0;
+}
+
+/**
+ * @brief  将I2C总线加锁让设备独占总线
+ */
+int i2c_lock_dev(i2c_dev_t *dev)
+{
+    if( !dev || !dev->bus )
+    {
+        return -1;
+    }
+
+    /* 安全上锁,若总线被占用则在此阻塞等待 */
+    if( I2C_OK != i2c_lock(dev->bus) )
+    {
+        return -2;
+    }
+
+    return 0;
+}
+
+/**
+ * @brief  释放设备的 I2C 总线锁
+ */
+void i2c_free_dev(i2c_dev_t *dev)
+{
+    if (!dev || !dev->bus)
+    {
+        return;
+    }
+
+    /* 1. 释放总线锁,让其他外设可以竞争总线 */
+    i2c_unlock(dev->bus);
+}
+
+/**
+ * @brief  Lock I2C bus for exclusive access (bare metal mutex)
+ * @param  bus: pointer to I2C bus structure
+ * @retval I2C_OK if success, I2C_BUSY if bus is locked
+ */
+i2c_status_t i2c_lock(i2c_bus_t *bus)
+{
+    uint32_t timeout = 10000;
+
+    /* atomic_compare_exchange_weak 会原子性地检查 bus->lock 是否为 false。
+     * 如果是 false,就把它设置为 true,并返回 true;
+     * 如果已经是 true,则返回 false,进入循环等待。
+     */
+    while (timeout > 0)
+    {
+        _Bool expected = false;
+        if (atomic_compare_exchange_weak(&bus->lock, &expected, true))
+        {
+            return I2C_OK;
+        }
+        udelay(10);
+        timeout--;
+    }
+
+    return I2C_BUSY;
+}
+
+/**
+ * @brief  Unlock I2C bus
+ * @param  bus: pointer to I2C bus structure
+ * @retval None
+ */
+void i2c_unlock(i2c_bus_t *bus)
+{
+    atomic_store(&bus->lock, false);
+}
+
+/**
+ * @brief  Scan I2C bus for connected devices
+ * @param  bus: pointer to I2C bus structure
+ * @retval none
+ */
+void i2c_scan(i2c_bus_t *bus)
+{
+    uint8_t addr;
+    int found = 0;
+
+    if (i2c_lock(bus) != I2C_OK) {
+        printf("I2C bus lock failed!\r\n");
+        return;
+    }
+
+    printf("Scanning I2C bus...\r\n");
+
+    /*10-bit 地址前缀保留区(0x70 ~ 0x77)*/
+    for (addr = 0x03; addr < 0x70; addr++) {
+        i2c_start(bus);
+        i2c_write_byte(bus, (addr << 1) | 0x01);  // read mode
+
+        if (i2c_wait_ack(bus) == I2C_ACK) {
+            printf("Found device at 0x%02X\r\n", addr);
+            found = 1;
+        }
+
+        i2c_stop(bus);
+        udelay(100);
+    }
+
+    i2c_unlock(bus);
+
+    if (!found) {
+        printf("No I2C devices found.\r\n");
+    }
+}
+
+
+/*
+ *+---------------------------------+
+ *|  I2C timing sequence function   |
+ *+---------------------------------+
+ */
+
+/**
+ * @brief  Generate I2C start condition
+ * @param  bus: pointer to I2C bus structure
+ * @retval None
+ */
+void i2c_start(i2c_bus_t *bus)
+{
+    /* Start Condition Timing Sequence:
+              ______
+     SCL:           |___
+            _____
+     SDA:        |_____
+    */
+
+    /* 无论前序是空闲还是刚结束 ACK,先确保 SCL 为低时 SDA 释放为高 */
+    i2c_sda_set(bus, HIGH);
+    i2c_scl_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+
+    /* 此时拉高 SCL,SDA 必然处于稳定的高电平 */
+    i2c_scl_set(bus, HIGH);
+    i2c_sda_set(bus, HIGH);
+    udelay(I2C_DELAY_US);
+
+    /* 在 SCL 为高期间拉低 SDA,制造标准的 START / RESTART 沿 */
+    i2c_sda_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+
+    i2c_scl_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+}
+
+/**
+ * @brief  Generate I2C stop condition
+ * @param  bus: pointer to I2C bus structure
+ * @retval None
+ */
+void i2c_stop(i2c_bus_t *bus)
+{
+    /* SCL Stop Condition Timing Sequence:
+                _______
+      SCL:   ___|
+                   _____
+      SDA:   _____|
+    */
+
+    i2c_scl_set(bus, LOW);
+    i2c_sda_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+
+    i2c_scl_set(bus, HIGH);
+    udelay(I2C_DELAY_US);
+
+    i2c_sda_set(bus, HIGH);
+    udelay(I2C_DELAY_US);
+}
+
+/**
+ * @brief  Wait for ACK from slave
+ * @param  bus: pointer to I2C bus structure
+ * @retval I2C_ACK or I2C_NACK
+ */
+i2c_ack_t i2c_wait_ack(i2c_bus_t *bus)
+{
+    uint16_t timeout = 0;
+
+    i2c_sda_set(bus, HIGH); /* 释放SDA线允许从机控制 */
+    udelay(I2C_DELAY_US);
+
+    i2c_scl_set(bus, HIGH);
+    udelay(I2C_DELAY_US); /* 确保SCL高电平波形稳定 */
+
+    while (i2c_sda_read(bus))
+    {
+        timeout++;
+        if (timeout > I2C_TIMEOUT_COUNT)
+        {
+            break; /* 超时退出,返回NACK */
+        }
+    }
+
+    i2c_scl_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+
+    return (timeout > I2C_TIMEOUT_COUNT) ? I2C_NACK : I2C_ACK;
+}
+
+/**
+ * @brief  Send ACK to slave
+ * @param  bus: pointer to I2C bus structure
+ * @retval None
+ */
+void i2c_send_ack(i2c_bus_t *bus)
+{
+    i2c_scl_set(bus, LOW);
+    i2c_sda_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+
+    i2c_scl_set(bus, HIGH);
+    udelay(I2C_DELAY_US);
+
+    i2c_scl_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+}
+
+/**
+ * @brief  Send NACK to slave
+ * @param  bus: pointer to I2C bus structure
+ * @retval None
+ */
+void i2c_send_nack(i2c_bus_t *bus)
+{
+    i2c_scl_set(bus, LOW);
+    i2c_sda_set(bus, HIGH);
+    udelay(I2C_DELAY_US);
+
+    i2c_scl_set(bus, HIGH);
+    udelay(I2C_DELAY_US);
+
+    i2c_scl_set(bus, LOW);
+    udelay(I2C_DELAY_US);
+}
+
+/**
+ * @brief  Write one byte to I2C bus
+ * @param  bus: pointer to I2C bus structure
+ * @param  byte: byte to write
+ * @retval None
+ */
+void i2c_write_byte(i2c_bus_t *bus, uint8_t byte)
+{
+    uint8_t i;
+
+    i2c_scl_set(bus, LOW);
+
+    for (i = 0; i < 8; i++)
+    {
+        i2c_sda_set(bus, (byte & 0x80) ? HIGH : LOW);
+        udelay(I2C_DELAY_US); /* 数据建立时间 Setup Time */
+
+        i2c_scl_set(bus, HIGH);
+        udelay(I2C_DELAY_US); /* 保持高电平采样 */
+
+        i2c_scl_set(bus, LOW);
+        udelay(I2C_DELAY_US); /* 确保时钟低电平持续时间 */
+
+        byte <<= 1;
+    }
+}
+
+/**
+ * @brief  Read one byte from I2C bus
+ * @param  bus: pointer to I2C bus structure
+ * @retval received byte
+ */
+uint8_t i2c_read_byte(i2c_bus_t *bus)
+{
+    uint8_t i;
+    uint8_t byte = 0;
+
+    i2c_sda_set(bus, HIGH);
+    udelay(I2C_DELAY_US);
+
+    for (i = 0; i < 8; i++)
+    {
+        byte <<= 1;
+
+        i2c_scl_set(bus, HIGH);
+        udelay(I2C_DELAY_US); /* 延时确保 SCL 电平平稳后采样 */
+
+        if (i2c_sda_read(bus))
+        {
+            byte |= 0x01;
+        }
+
+        i2c_scl_set(bus, LOW);
+        udelay(I2C_DELAY_US); /* 确保时钟低电平持续时间 */
+    }
+
+    return byte;
+}
+
+
+/*
+ *+-----------------------------------+
+ *|      I2C read/write function      |
+ *+-----------------------------------+
+ */
+
+/**
+ * @brief  Write data to I2C slave device
+ * @param  bus: pointer to I2C bus structure
+ * @param  addr: 7-bit slave address
+ * @param  reg: register address
+ * @param  data: pointer to data buffer
+ * @param  len: data length
+ * @retval I2C status
+ */
+i2c_status_t i2c_write(i2c_bus_t *bus, uint8_t addr, uint8_t reg, uint8_t *data, uint16_t len)
+{
+    uint16_t i;
+    int rv = I2C_OK;
+
+    i2c_start(bus);
+
+    /* Send slave address + write bit */
+    i2c_write_byte(bus, (addr << 1) | 0x00);
+    if (i2c_wait_ack(bus) != I2C_ACK)
+    {
+        rv = I2C_ERROR;
+        goto cleanup;
+    }
+
+    /* Send register address */
+    i2c_write_byte(bus, reg);
+    if (i2c_wait_ack(bus) != I2C_ACK)
+    {
+        rv = I2C_ERROR;
+        goto cleanup;
+    }
+
+    /* Send data */
+    for (i = 0; i < len; i++)
+    {
+        i2c_write_byte(bus, data[i]);
+        if (i2c_wait_ack(bus) != I2C_ACK)
+        {
+            rv = I2C_ERROR;
+            goto cleanup;
+        }
+    }
+
+cleanup:
+    i2c_stop(bus);
+    return rv;
+}
+
+/**
+ * @brief  Read data from I2C slave device
+ * @param  bus: pointer to I2C bus structure
+ * @param  addr: 7-bit slave address
+ * @param  reg: register address
+ * @param  data: pointer to data buffer
+ * @param  len: data length
+ * @retval I2C status
+ */
+i2c_status_t i2c_read(i2c_bus_t *bus, uint8_t addr, uint8_t reg, uint8_t *data, uint16_t len)
+{
+    uint16_t i;
+    int rv = I2C_OK;
+
+    /* Write register address */
+    i2c_start(bus);
+    i2c_write_byte(bus, (addr << 1) | 0x00);
+    if (i2c_wait_ack(bus) != I2C_ACK)
+    {
+        rv = I2C_ERROR;
+        goto cleanup;
+    }
+
+    i2c_write_byte(bus, reg);
+    if (i2c_wait_ack(bus) != I2C_ACK)
+    {
+        rv = I2C_ERROR;
+        goto cleanup;
+    }
+
+    /* Restart and read data */
+    i2c_start(bus);
+    i2c_write_byte(bus, (addr << 1) | 0x01);
+    if (i2c_wait_ack(bus) != I2C_ACK)
+    {
+        rv = I2C_ERROR;
+        goto cleanup;
+    }
+
+    for (i = 0; i < len; i++)
+    {
+        data[i] = i2c_read_byte(bus);
+
+        if (i < len - 1)
+        {
+            i2c_send_ack(bus);
+        }
+        else
+        {
+            i2c_send_nack(bus);
+        }
+    }
+
+cleanup:
+    i2c_stop(bus);
+    return rv;
+}
+
+/**
+ * @brief  Write data to I2C slave device without register address
+ * @param  bus: pointer to I2C bus structure
+ * @param  addr: 7-bit slave address
+ * @param  data: pointer to data buffer
+ * @param  len: data length
+ * @retval I2C status
+ */
+i2c_status_t i2c_write_no_reg(i2c_bus_t *bus, uint8_t addr, uint8_t *data, uint16_t len)
+{
+    uint16_t i;
+    int rv = I2C_OK;
+
+    i2c_start(bus);
+
+    /* Send slave address + write bit */
+    i2c_write_byte(bus, (addr << 1) | 0x00);
+    if (i2c_wait_ack(bus) != I2C_ACK)
+    {
+        rv = I2C_ERROR;
+        goto cleanup;
+    }
+
+    /* Send data */
+    for (i = 0; i < len; i++)
+    {
+        i2c_write_byte(bus, data[i]);
+        if (i2c_wait_ack(bus) != I2C_ACK)
+        {
+            rv = I2C_ERROR;
+            goto cleanup;
+        }
+    }
+
+cleanup:
+    i2c_stop(bus);
+    return rv;
+}
+
+/**
+ * @brief  Read data from I2C slave device without register address
+ * @param  bus: pointer to I2C bus structure
+ * @param  addr: 7-bit slave address
+ * @param  data: pointer to data buffer
+ * @param  len: data length
+ * @retval I2C status
+ */
+i2c_status_t i2c_read_no_reg(i2c_bus_t *bus, uint8_t addr, uint8_t *data, uint16_t len)
+{
+    uint16_t i;
+    int rv = I2C_OK;
+
+    i2c_start(bus);
+
+    /* Send slave address + read bit */
+    i2c_write_byte(bus, (addr << 1) | 0x01);
+    if (i2c_wait_ack(bus) != I2C_ACK)
+    {
+        rv = I2C_ERROR;
+        goto cleanup;
+    }
+
+    /* Read data */
+    for (i = 0; i < len; i++)
+    {
+        data[i] = i2c_read_byte(bus);
+
+        if (i < len - 1)
+        {
+            i2c_send_ack(bus);
+        }
+        else
+        {
+            i2c_send_nack(bus);
+        }
+    }
+
+cleanup:
+    i2c_stop(bus);
+    return rv;
+}
+
+
+/*
+ *+-----------------------------------+
+ *| Linux I2C API–compatible function |
+ *+-----------------------------------+
+ */
+
+ /* Functions compatible with the Linux I2C adapter API */
+i2c_status_t i2c_transfer(i2c_bus_t *bus, i2c_msg_t *msgs, int num)
+{
+    i2c_status_t status = I2C_OK;
+    int i, j;
+
+    for (i = 0; i < num; i++)
+    {
+        i2c_msg_t *msg = &msgs[i];
+
+        /* 1. 每条消息都以 Start (或 Repeated Start) 开始 */
+        i2c_start(bus);
+
+        /* 2. 发送从机地址 + 读写位 */
+        uint8_t dir = (msg->flags & I2C_M_RD) ? 0x01 : 0x00;
+        i2c_write_byte(bus, (msg->addr << 1) | dir);
+        if (i2c_wait_ack(bus) != I2C_ACK)
+        {
+            status = I2C_ERROR;
+            goto cleanup;
+        }
+
+        /* 3. 根据读写标志传输主体数据 */
+        if (msg->flags & I2C_M_RD)
+        {
+            /* 读模式 */
+            for (j = 0; j < msg->len; j++)
+            {
+                msg->buf[j] = i2c_read_byte(bus);
+                /* 只要不是当前消息的最后一个字节,就回复 ACK */
+                if (j < msg->len - 1)
+                    i2c_send_ack(bus);
+                else
+                    i2c_send_nack(bus);
+            }
+        }
+        else
+        {
+            /* 写模式 */
+            for (j = 0; j < msg->len; j++)
+            {
+                i2c_write_byte(bus, msg->buf[j]);
+                if (i2c_wait_ack(bus) != I2C_ACK)
+                {
+                    status = I2C_ERROR;
+                    goto cleanup;
+                }
+            }
+        }
+    }
+
+cleanup:
+    /* 4. 只有当所有消息都传输完毕,或者中途出错时,才发送 Stop 释放总线 */
+    i2c_stop(bus);
+
+    return status;
+}

--
Gitblit v1.10.0