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