STM32和GD32可移植软件模拟I2C驱动实现,支持时钟延展和400KHz快速模式
STM平台及GD平台 – 软件模拟I2C驱动实现
一、需知
二、背景
三、代码实现
3.1 延时函数
#if defined(SOFT_I2C_GD32F3_USED)
#define SOFT_I2C_DELAY_CYCLE (10U) ///< 延时周期数
#elif defined(SOFT_I2C_STM32F1_USED)
#define SOFT_I2C_DELAY_CYCLE (1U) ///< 延时周期数
#else
#define SOFT_I2C_DELAY_CYCLE (1U) ///< 延时周期数
#endif
/**
* @brief 内部调用 微秒延时函数
*/
static void soft_i2c_delay_us(void) {
uint32_t i, j = 0;
for (i = 0; i < 2; i++) {
for (j = 0; j < SOFT_I2C_DELAY_CYCLE; j++) {
__asm("NOP");
}
}
}
/**
* @brief 内部调用 800纳秒延时函数
*/
static void soft_i2c_delay_800ns(void) {
uint32_t i = 0;
#if defined(SOFT_I2C_GD32F3_USED)
uint32_t j = 0;
for (i = 0; i < 2; i++) {
for (j = 0; j < (SOFT_I2C_DELAY_CYCLE * 4) / 5; j++) {
__asm("NOP");
}
}
#elif defined(SOFT_I2C_STM32F1_USED)
for (i = 0; i < 2; i++) {
__asm("NOP");
}
#else
return;
#endif
}
3.2 时钟延展
/**
* @brief 软件模拟 I2C 等待 SCL 释放
*
* @param [in] p_i2c I2C结构体指针
* @param [in] waitCnt 等待次数, 必须在前文声明
*
* @details 用于开启时钟延展功能, 等待从机释放时钟线
*/
#define SOFT_I2C_WAIT_SCL_RELEASE(p_i2c, waitCnt) \
while (waitCnt > 0 && \
soft_i2c_read_gpio(&p_i2c->scl) == SOFT_I2C_LEVEL_LOW) { \
waitCnt--; \
}
/**
* @brief 内部调用 I2C停止信号
*
* @param [in] p_i2c I2C结构体指针
*
* @return soft_i2c_err_t
* @retval 0 成功, 其他值 失败 @ref SOFT_I2C_ERR_CODE
* @details 特殊说明:
* @par eg:
* @code
*
* @endcode
*/
static soft_i2c_err_t soft_i2c_stop(P_SOFT_I2C_T p_i2c) {
soft_i2c_write_gpio(&p_i2c->scl, SOFT_I2C_LEVEL_LOW);
soft_i2c_write_gpio(&p_i2c->sda, SOFT_I2C_LEVEL_LOW);
soft_i2c_delay_us();
soft_i2c_write_gpio(&p_i2c->scl, SOFT_I2C_LEVEL_HIGH);
#if defined(__SOFT_I2C_CLOCK_STRECH_EN__)
uint32_t waitCnt = 0xFFF; ///< 等待计数
SOFT_I2C_WAIT_SCL_RELEASE(p_i2c, waitCnt); // 等待SCL释放
if (waitCnt == 0) {
return SOFT_I2C_ERR_TIMEOUT;
}
#endif
soft_i2c_delay_us();
soft_i2c_write_gpio(&p_i2c->sda, SOFT_I2C_LEVEL_HIGH);
return SOFT_I2C_ERR_OK;
}
3.3 枚举及结构体定义
/**
* @brief 软件模拟 I2C GPIO时钟枚举
*/
typedef enum _SOFT_I2C_GPIO_CLK_E {
SOFT_I2C_GPIOA_CLK = 0x00000004U, ///< GPIOA_CLK
SOFT_I2C_GPIOB_CLK = 0x00000008U, ///< GPIOB_CLK
SOFT_I2C_GPIOC_CLK = 0x00000010U, ///< GPIOC_CLK
SOFT_I2C_GPIOD_CLK = 0x00000020U, ///< GPIOD_CLK
SOFT_I2C_GPIOE_CLK = 0x00000040U, ///< GPIOE_CLK
SOFT_I2C_GPIOF_CLK = 0x00000080U, ///< GPIOF_CLK
SOFT_I2C_GPIOG_CLK = 0x00000100U, ///< GPIOG_CLK
} SOFT_I2C_GPIO_CLK_E;
/**
* @brief 软件模拟 I2C 监控信息枚举
*/
typedef enum _SOFT_I2C_STA_E {
SOFT_I2C_IDLE = 0, ///< 空闲
SOFT_I2C_BUSY = 1, ///< 忙
SOFT_I2C_WRITE_START = 2, ///< 写开始
SOFT_I2C_WRITE_END = 3, ///< 写结束
SOFT_I2C_READ_START = 4, ///< 读开始
SOFT_I2C_READ_END = 5, ///< 读结束
} SOFT_I2C_STA_E;
/**
* @brief 软件模拟 I2C GPIO通用结构体
*/
typedef struct _SOFT_I2C_GPIO_COMM_T {
uint32_t gpioPort; ///< GPIO端口, GD: GPIOx, STM32: GPIOx_BASE
uint32_t gpioPin; ///< GPIO引脚, GD: GPIO_PIN_x, STM32: GPIO_Pin_x
SOFT_I2C_GPIO_CLK_E gpioClk; ///< GPIO时钟 @ref SOFT_I2C_GPIO_CLK_E
} SOFT_I2C_GPIO_COMM_T, *P_SOFT_I2C_GPIO_COMM_T;
/**
* @brief 软件模拟 I2C 结构体
*/
typedef struct _SOFT_I2C_T {
bool isValid; ///< 芯片是否有效
bool isInit; ///< 是否初始化
SOFT_I2C_GPIO_COMM_T scl; ///< 时钟线
SOFT_I2C_GPIO_COMM_T sda; ///< 数据线
SOFT_I2C_STA_E i2cSta; ///< I2C监控状态
} SOFT_I2C_T, *P_SOFT_I2C_T;
3.4 对外接口
#if defined(SOFT_I2C_GD32F3_USED) || defined(SOFT_I2C_STM32F1_USED)
/**
* @brief 软件模拟 I2C 外部声明
*
* @param [in] name I2C结构体名称
*/
#define SOFT_I2C_EXT(name) \
extern void *const name
#else
#define SOFT_I2C_EXT(name)
#endif
#if defined(SOFT_I2C_GD32F3_USED)
/**
* @brief 软件模拟 I2C 定义结构体
*
* @param [in] name I2C结构体名称
* @param [in] scl_port 时钟线端口, A ~ G
* @param [in] scl_pin 时钟线引脚, 0 ~ 15
* @param [in] sda_port 数据线端口, A ~ G
* @param [in] sda_pin 数据线引脚, 0 ~ 15
*/
#define SOFT_I2C_DEF(name, scl_port, scl_pin, sda_port, sda_pin) \
SOFT_I2C_T soft_i2c_##name = { \
false, false, \
{GPIO##scl_port, GPIO_PIN_##scl_pin, SOFT_I2C_GPIO##scl_port##_CLK}, \
{GPIO##sda_port, GPIO_PIN_##sda_pin, SOFT_I2C_GPIO##sda_port##_CLK}, \
SOFT_I2C_IDLE \
}; \
void *const name = &soft_i2c_##name \
#elif defined(SOFT_I2C_STM32F1_USED)
/**
* @brief 软件模拟 I2C 定义结构体
*
* @param [in] name I2C结构体名称
* @param [in] scl_port 时钟线端口, A ~ G
* @param [in] scl_pin 时钟线引脚, 0 ~ 15
* @param [in] sda_port 数据线端口, A ~ G
* @param [in] sda_pin 数据线引脚, 0 ~ 15
*/
#define SOFT_I2C_DEF(name, scl_port, scl_pin, sda_port, sda_pin) \
SOFT_I2C_T soft_i2c_##name = { \
false, false, \
{GPIO##scl_port##_BASE, GPIO_Pin_##scl_pin, SOFT_I2C_GPIO##scl_port##_CLK}, \
{GPIO##sda_port##_BASE, GPIO_Pin_##sda_pin, SOFT_I2C_GPIO##sda_port##_CLK}, \
SOFT_I2C_IDLE \
}; \
void *const name = &soft_i2c_##name \
#else
#define SOFT_I2C_DEF(name, scl_port, scl_pin, sda_port, sda_pin)
#endif
/**
* @brief 软件模拟 I2C 初始化
*
* @param [in] p_i2c I2C结构体指针
*
* @return uint32_t
* @retval 0 成功, 其他值 失败 @ref SOFT_I2C_ERR_CODE
* @details 特殊说明:
* @par eg:
* @code
*
* @endcode
*/
extern soft_i2c_err_t soft_i2c_init(P_SOFT_I2C_T p_i2c);
/**
* @brief 软件模拟 I2C 写数据
*
* @param [in] p_i2c I2C结构体指针
* @param [in] slaveAddr 从机地址
* @param [in] regAddr 寄存器地址
* @param [in] regAddrLen 寄存器地址长度 @ref SOFT_I2C_REG_ADDR_LEN_1 , @ref SOFT_I2C_REG_ADDR_LEN_2
* @param [in] p_data 数据指针
* @param [in] dataLen 数据长度(字节)
*
* @return soft_i2c_err_t
* @retval 0 成功, 其他值 失败 @ref SOFT_I2C_ERR_CODE
* @details 特殊说明:
* @par eg:
* @code
*
* @endcode
*/
extern soft_i2c_err_t soft_i2c_write(P_SOFT_I2C_T p_i2c, uint32_t slaveAddr, uint32_t regAddr, uint32_t regAddrLen, uint8_t *p_data, uint32_t dataLen);
/**
* @brief 软件模拟 I2C 读数据
*
* @param [in] p_i2c I2C结构体指针
* @param [in] slaveAddr 从机地址
* @param [in] regAddr 寄存器地址
* @param [in] regAddrLen 寄存器地址长度 @ref SOFT_I2C_REG_ADDR_LEN_1 , @ref SOFT_I2C_REG_ADDR_LEN_2
* @param [in] p_data 数据指针
* @param [in] dataLen 数据长度(字节)
*
* @return soft_i2c_err_t
* @retval 0 成功, 其他值 失败 @ref SOFT_I2C_ERR_CODE
* @details 特殊说明:
* @par eg:
* @code
*
* @endcode
*/
extern soft_i2c_err_t soft_i2c_read(P_SOFT_I2C_T p_i2c, uint32_t slaveAddr, uint32_t regAddr, uint32_t regAddrLen, uint8_t *p_data, uint32_t dataLen);
四、使用示例
参照5.2节获取已移植I2C驱动的示例工程
使能时钟延展功能需在 bsp_soft_i2c.h 文件内取消注释__SOFT_I2C_CLOCK_STRECH_EN__
或者在工程的全局宏中添加__SOFT_I2C_CLOCK_STRECH_EN__
4.1 GD32F303RET6核心板
4.1.1 移植



4.1.2 使用
#include "gd32f30x.h"
#include "gd32f303e_eval.h"
#include "systick.h"
// 引入软件模拟I2C头文件
#include "soft_i2c_dev.h"
#include <string.h>
#define SLAVE_ADDR 0xA0 ///< 从机地址
#define REG_ADDR 0x00 ///< 寄存器地址
uint8_t w_buf[8] = {0xAA,0xA5,0x5A,0xFF,0xFA,0xAF,0xDD,0xEE};
uint8_t r_buf[8] = {0};
uint8_t flag = 1;
soft_i2c_err_t ret = SOFT_I2C_ERR_OK;
bool equal = false;
int main(void)
{
gd_eval_led_init(LED);
systick_config();
// 初始化结构体对象
ret = soft_i2c_init(I2C_DEV);
while(1){
/* turn on LED1 */
gd_eval_led_on(LED);
/* insert 200 ms delay */
delay_1ms(200);
memset(r_buf, 0, 8);
if (flag) {
ret = soft_i2c_write(I2C_DEV, SLAVE_ADDR, REG_ADDR, 1, w_buf, 8);
// if (ret != SOFT_I2C_ERR_OK) {
// while(1);
// }
equal = false;
} else {
ret = soft_i2c_read(I2C_DEV, SLAVE_ADDR, REG_ADDR, 1, r_buf, 8);
// if (ret != SOFT_I2C_ERR_OK) {
// while(1);
// }
equal = (memcmp(r_buf, w_buf, 8) == 0) ? true : false;
}
flag = !flag;
/* turn off LEDs */
gd_eval_led_off(LED);
/* insert 200 ms delay */
delay_1ms(200);
}
}
4.1.3 资源占用

4.1.4 通信波形


4.2 STM32F103C8T6核心板
4.2.1 使用
#include "stm32f10x.h"
#include "stm32_eval.h"
#include "soft_i2c_dev.h"
#include <stdio.h>
#include <string.h>
#define SLAVE_ADDR 0xA0 ///< 从机地址
#define REG_ADDR 0x00 ///< 寄存器地址
uint8_t w_buf[8] = {0xAA,0xA5,0x5A,0xFF,0xFA,0xAF,0xDD,0xEE};
uint8_t r_buf[8] = {0};
uint8_t flag = 1;
bool equal = false;
soft_i2c_err_t ret = SOFT_I2C_ERR_OK;
int main(void)
{
STM_EVAL_LEDInit(LED1); // PC13
STM_EVAL_LEDOn(LED1);
ret = soft_i2c_init(I2C_DEV);
while (1)
{
STM_EVAL_LEDOn(LED1);
block_delay();
{
memset(r_buf, 0, 8);
if (flag) {
ret = soft_i2c_write(I2C_DEV, SLAVE_ADDR, REG_ADDR, 1, w_buf, 8);
// if (ret != SOFT_I2C_ERR_OK) {
// while(1);
// }
equal = false;
} else {
ret = soft_i2c_read(I2C_DEV, SLAVE_ADDR, REG_ADDR, 1, r_buf, 8);
// if (ret != SOFT_I2C_ERR_OK) {
// while(1);
// }
equal = (memcmp(r_buf, w_buf, 8) == 0) ? true : false;
}
flag = !flag;
}
STM_EVAL_LEDOff(LED1);
block_delay();
}
}
4.2.2 资源占用

4.2.3 通信波形


4.3 时钟延展波形
五、驱动获取方式
Soft_I2C_Driver 主文件夹
|
├─ software_i2c 子文件夹
| |
│ ├─ dsview 波形文件夹
│ │
│ ├─ lib 底层库
| | |
│ │ ├─ CMSIS CMSIS-Cortex-M4
│ │ │
│ │ ├─ GD32F30x_standard_peripheral GD32F30X标准库
│ │ │
│ │ └─ STM32F10x_StdPeriph_Lib_V3.6.0 STM32F10X标准库
│ │
│ ├─ bsp_soft_i2c.c 软件模拟I2C源文件
| |
│ ├─ bsp_soft_i2c.h 软件模拟I2C头文件
| |
│ ├─ bsp_soft_i2c_private.h 软件模拟I2C私有头文件
| |
│ └─ readme.txt readme文件
│
└─ test_project 示例工程
│
├─ GD32F303_PRJ GD32F303示例工程
│
└─ STM32F1_PRJ STM32F103示例工程

5.1 百度网盘
5.2 GitHub(推荐)
