- static uint8_t recv() {
- uint8_t i, data;
- SDA_IN();
- data = 0;
- for(i = 0; i < 8; i++) {
- SCL(0);
- delay_1us(5);
- SCL(1);
- delay_1us(5);
-
- data <<= 1;
-
- data |= SDA_STATE();
-
- delay_1us(5);
- }
- SCL(0);
- return data;
- }
发送响应
发送ACK
- static void send_ack() {
- SDA_OUT();
- SCL(0);
- SDA(0);
- delay_1us(5);
-
- SDA(0);
-
- SCL(1);
- delay_1us(5);
- SCL(0);
- SDA(1);
- }
发送NACK
- static void send_nack() {
- SDA_OUT();
- SCL(0);
- SDA(0);
- delay_1us(5);
-
- SDA(1);
-
- SCL(1);
- delay_1us(5);
- SCL(0);
- SDA(1);
- }
软件I2C开发流程
GD32软件I2C初始化
- void SoftI2C_init() {
- // 时钟配置
- rcu_periph_clock_enable(SCL_RCU);
- // 设置输出模式
- gpio_mode_set(SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SCL_PIN);
- gpio_output_options_set(SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, SCL_PIN);
-
- // 时钟配置
- rcu_periph_clock_enable(SDA_RCU);
- // 设置输出模式
- gpio_mode_set(SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SDA_PIN);
- gpio_output_options_set(SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, SDA_PIN);
- }
GD32软件I2C引脚功能- #define SCL_RCU RCU_GPIOB
- #define SCL_PORT GPIOB
- #define SCL_PIN GPIO_PIN_6
- #define SCL_AF GPIO_AF_4
-
- #define SDA_RCU RCU_GPIOB
- #define SDA_PORT GPIOB
- #define SDA_PIN GPIO_PIN_7
- #define SDA_AF GPIO_AF_4
-
- /************** io ***************/
- #define SCL(BIT) gpio_bit_write(SCL_PORT, SCL_PIN, BIT?SET:RESET)
- #define SDA(BIT) gpio_bit_write(SDA_PORT, SDA_PIN, BIT?SET:RESET)
- #define SDA_STATE() gpio_input_bit_get(SDA_PORT, SDA_PIN)
-
- #define SDA_IN() gpio_mode_set(SDA_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SDA_PIN)
- #define SDA_OUT() gpio_mode_set(SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SDA_PIN)
- IO引脚定义
- 引脚输出模式高低电平输出:SCL高和低,SDA高和低
- SDA模式配置:SDA输出模式,SDA输入模式
- SDA输入模式状态读取。
写操作
代码:
- uint8_t SoftI2C_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
- start();
-
- send(addr << 1); //发送设备写地址
- if(wait_ack()) return 1; //等待响应
-
- send(reg); //发送寄存器地址
- if(wait_ack()) return 2; //等待响应
-
- do {
- send(*data++);
- if(wait_ack()) return 3;
- } while(--len);
-
- stop();
- return 0;
- }
读操作
代码:
- uint8_t SoftI2C_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
- start();
-
- send(addr << 1); //发送设备写地址
- if(wait_ack()) return 1; //等待响应
-
- send(reg); //发送寄存器地址
- if(wait_ack()) return 2; //等待响应
-
- start();
- send((addr << 1) | 0x01); //发送设备读地址
- if(wait_ack()) return 3; //等待响应
-
- do {
- *data = recv();
- data++;
- if(len != 1) send_ack(); // 发送 NACK
- } while(--len);
- send_nack(); // 发送 NACK
- stop();
-
- return 0;
- }
逻辑分析仪解析:
说明:读取时,会有两个开始标记。从设备会获取控制权,获取数据并发送给主设备。
硬件I2C-GD32F4系列初始化操作- uint32_t i2cx_scl_port_rcu = RCU_GPIOB;
- uint32_t i2cx_scl_port = GPIOB;
- uint32_t i2cx_scl_pin = GPIO_PIN_6;
- uint32_t i2cx_scl_af = GPIO_AF_4;
-
- uint32_t i2cx_sda_port_rcu = RCU_GPIOB;
- uint32_t i2cx_sda_port = GPIOB;
- uint32_t i2cx_sda_pin = GPIO_PIN_7;
- uint32_t i2cx_sda_af = GPIO_AF_4;
-
- uint32_t i2cx = I2C0;
- uint32_t i2cx_rcu = RCU_I2C0;
- uint32_t i2cx_speed = 400000;
- /****************** GPIO config **********************/
- // 时钟配置
- rcu_periph_clock_enable(i2cx_scl_port_rcu);
- // 设置复用功能
- gpio_af_set(i2cx_scl_port, i2cx_scl_af, i2cx_scl_pin);
- // 设置输出模式
- gpio_mode_set(i2cx_scl_port, GPIO_MODE_AF, GPIO_PUPD_NONE, i2cx_scl_pin);
- gpio_output_options_set(i2cx_scl_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2cx_scl_pin);
-
- // 时钟配置
- rcu_periph_clock_enable(i2cx_sda_port_rcu);
- // 设置复用功能
- gpio_af_set(i2cx_sda_port, i2cx_sda_af, i2cx_sda_pin);
- // 设置输出模式
- gpio_mode_set(i2cx_sda_port, GPIO_MODE_AF, GPIO_PUPD_NONE, i2cx_sda_pin);
- gpio_output_options_set(i2cx_sda_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2cx_sda_pin);
-
- /****************** I2C config **********************/
- i2c_deinit(i2cx);
- // 时钟配置
- rcu_periph_clock_enable(i2cx_rcu);
- // I2C速率配置
- i2c_clock_config(i2cx, i2cx_speed, I2C_DTCY_2);
-
- // 使能i2c
- i2c_mode_addr_config(i2cx, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x00);
- i2c_enable(i2cx);
-
- // i2c ack enable
- i2c_ack_config(i2cx, I2C_ACK_ENABLE);
- 哪个I2C
- SCL是哪个引脚
- SDA是哪个引脚
- 速度是多快
写操作流程开始- /************* start ***********************/
- // 等待I2C闲置
- if(I2C_waitn(i2cx, I2C_FLAG_I2CBSY)) return 1;
-
- // start
- i2c_start_on_bus(i2cx);
-
- // 等待I2C主设备成功发送起始信号
- if(I2C_wait(i2cx, I2C_FLAG_SBSEND)) return 2;
发送设备地址- /************* device address **************/
- // 发送设备地址
- i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);
- // 等待地址发送完成
- if(I2C_wait(i2cx, I2C_FLAG_ADDSEND)) return 3;
- i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);
发送寄存器地址 - /************ register address ************/
- // 寄存器地址
- // 等待发送数据缓冲区为空
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 4;
-
- // 发送数据
- i2c_data_transmit(i2cx, reg);
-
- // 等待数据发送完成
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 5;
数据发送- /***************** data ******************/
- // 发送数据
- uint32_t i;
- for(i = 0; i < data_len; i++) {
- uint32_t d = data[i];
-
- // 等待发送数据缓冲区为空
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 6;
-
- // 发送数据
- i2c_data_transmit(i2cx, d);
-
- // 等待数据发送完成
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 7;
- }
停止- /***************** stop ********************/
- // stop
- i2c_stop_on_bus(i2cx);
- if(I2C_waitn(i2cx, I2C_CTL0(I2C0)&I2C_CTL0_STOP)) return 8;
读操作流程开始- /************* start ***********************/
- // 等待I2C空闲
- if(I2C_waitn(i2cx, I2C_FLAG_I2CBSY)) return 1;
-
- // 发送启动信号
- i2c_start_on_bus(i2cx);
- if(I2C_wait(i2cx, I2C_FLAG_SBSEND)) return 2;
发送设备地址(写)- /************* device address **************/
- // 发送从设备地址
- i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);
-
- if(I2C_wait(i2cx, I2C_FLAG_ADDSEND)) return 3;
- i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);
发送寄存器地址- /********** register address **************/
- // 等待发送缓冲区
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 4;
-
- // 发送寄存器地址
- i2c_data_transmit(i2cx, reg);
-
- // 等待发送数据完成
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 5;
-
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 6;
开始- /************* start ***********************/
- // 发送再启动信号
- i2c_start_on_bus(i2cx);
-
- if(I2C_wait(i2cx, I2C_FLAG_SBSEND)) return 7;
发送设备地址(读)- /************* device address **************/
- // 发送从设备地址
- i2c_master_addressing(i2cx, address, I2C_RECEIVER);
- if(I2C_wait(i2cx, I2C_FLAG_ADDSEND)) return 8;
- i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);
-
- //ack
- i2c_ack_config(i2cx, I2C_ACK_ENABLE);
- i2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);
- if(I2C_wait(i2cx, (I2C_CTL0(i2cx) & I2C_CTL0_ACKEN))) return 23;
数据读取- /************* data **************/
- //ack
- i2c_ack_config(i2cx, I2C_ACK_ENABLE);
- i2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);
- if(I2C_wait(i2cx, (I2C_CTL0(i2cx) & I2C_CTL0_ACKEN))) return 11;
-
- // 读取数据
- uint8_t i;
- for (i = 0; i < len; i++) {
- if(i != len - 1) {
- // 等待接收缓冲区
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 9;
- }
-
- // 等待ACK数据发送完成
- // 等待接收缓冲区
- if(I2C_wait(i2cx, I2C_FLAG_RBNE)) return 10;
- data[i] = i2c_data_receive(i2cx);
-
- if (i == len - 1) {
- // 在读取最后一个字节之前,禁用ACK,并发送停止信号
- // 配置自动NACK
- i2c_ack_config(i2cx, I2C_ACK_DISABLE);
- }
- }
结束- /***************** stop ********************/
- i2c_stop_on_bus(i2cx);
- if(I2C_waitn(i2cx, I2C_CTL0(I2C0) & I2C_CTL0_STOP)) return 11;
完整代码I2C0.H
- #ifndef __I2C0_H__
- #define __I2C0_H__
-
- #include "systick.h"
- #include "gd32f4xx.h"
-
- void I2C0_init();
-
- uint8_t I2C0_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);
-
- uint8_t I2C0_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);
-
- uint8_t I2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len);
-
- void I2C0_deinit();
-
- #endif
I2C.C
- #include "I2C0.h"
-
- void I2C0_init() {
- uint32_t i2cx_scl_port_rcu = RCU_GPIOB;
- uint32_t i2cx_scl_port = GPIOB;
- uint32_t i2cx_scl_pin = GPIO_PIN_6;
- uint32_t i2cx_scl_af = GPIO_AF_4;
-
- uint32_t i2cx_sda_port_rcu = RCU_GPIOB;
- uint32_t i2cx_sda_port = GPIOB;
- uint32_t i2cx_sda_pin = GPIO_PIN_7;
- uint32_t i2cx_sda_af = GPIO_AF_4;
-
- uint32_t i2cx = I2C0;
- uint32_t i2cx_rcu = RCU_I2C0;
- uint32_t i2cx_speed = 400000;
- /****************** GPIO config **********************/
- // 时钟配置
- rcu_periph_clock_enable(i2cx_scl_port_rcu);
- // 设置复用功能
- gpio_af_set(i2cx_scl_port, i2cx_scl_af, i2cx_scl_pin);
- // 设置输出模式
- gpio_mode_set(i2cx_scl_port, GPIO_MODE_AF, GPIO_PUPD_NONE, i2cx_scl_pin);
- gpio_output_options_set(i2cx_scl_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2cx_scl_pin);
-
- // 时钟配置
- rcu_periph_clock_enable(i2cx_sda_port_rcu);
- // 设置复用功能
- gpio_af_set(i2cx_sda_port, i2cx_sda_af, i2cx_sda_pin);
- // 设置输出模式
- gpio_mode_set(i2cx_sda_port, GPIO_MODE_AF, GPIO_PUPD_NONE, i2cx_sda_pin);
- gpio_output_options_set(i2cx_sda_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2cx_sda_pin);
-
- /****************** I2C config **********************/
- i2c_deinit(i2cx);
- // 时钟配置
- rcu_periph_clock_enable(i2cx_rcu);
- // I2C速率配置
- i2c_clock_config(i2cx, i2cx_speed, I2C_DTCY_2);
-
- // 使能i2c
- i2c_mode_addr_config(i2cx, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x00);
- i2c_enable(i2cx);
-
- // i2c ack enable
- i2c_ack_config(i2cx, I2C_ACK_ENABLE);
- //i2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);
-
- }
-
- static uint8_t I2C_wait(uint32_t i2cx, uint32_t flag) {
- uint16_t TIMEOUT = 50000;
- uint16_t cnt = 0;
-
- while(!i2c_flag_get(i2cx, flag)) {
- cnt++;
- if(cnt > TIMEOUT) return 1;
- }
- return 0;
- }
-
- static uint8_t I2C_waitn(uint32_t i2cx, uint32_t flag) {
- uint16_t TIMEOUT = 50000;
- uint16_t cnt = 0;
-
- while(i2c_flag_get(i2cx, flag)) {
- cnt++;
- if(cnt > TIMEOUT) return 1;
- }
- return 0;
- }
-
-
- uint8_t I2C0_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t data_len) {
- uint32_t i2cx = I2C0;
- uint8_t address = addr << 1;
-
- /************* start ***********************/
- // 等待I2C闲置
- if(I2C_waitn(i2cx, I2C_FLAG_I2CBSY)) return 1;
-
- // start
- i2c_start_on_bus(i2cx);
-
- // 等待I2C主设备成功发送起始信号
- if(I2C_wait(i2cx, I2C_FLAG_SBSEND)) return 2;
-
- /************* device address **************/
- // 发送设备地址
- i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);
- // 等待地址发送完成
- if(I2C_wait(i2cx, I2C_FLAG_ADDSEND)) return 3;
- i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);
-
- /************ register address ************/
- // 寄存器地址
- // 等待发送数据缓冲区为空
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 4;
-
- // 发送数据
- i2c_data_transmit(i2cx, reg);
-
- // 等待数据发送完成
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 5;
-
- /***************** data ******************/
- // 发送数据
- uint32_t i;
- for(i = 0; i < data_len; i++) {
- uint32_t d = data[i];
-
- // 等待发送数据缓冲区为空
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 6;
-
- // 发送数据
- i2c_data_transmit(i2cx, d);
-
- // 等待数据发送完成
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 7;
- }
-
- /***************** stop ********************/
- // stop
- i2c_stop_on_bus(i2cx);
- if(I2C_waitn(i2cx, I2C_CTL0(I2C0)&I2C_CTL0_STOP)) return 8;
-
- return 0;
- }
-
- uint8_t I2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len) {
- uint32_t i2cx = I2C0;
- uint8_t address = addr << 1;
-
- /************* start ***********************/
- // 等待I2C闲置
- if(I2C_waitn(i2cx, I2C_FLAG_I2CBSY)) return 1;
-
- // start
- i2c_start_on_bus(i2cx);
-
- // 等待I2C主设备成功发送起始信号
- if(I2C_wait(i2cx, I2C_FLAG_SBSEND)) return 2;
-
- /************* device address **************/
- // 发送设备地址
- i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);
- // 等待地址发送完成
- if(I2C_wait(i2cx, I2C_FLAG_ADDSEND)) return 3;
- i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);
-
- /************ register address ************/
- // 寄存器地址
- // 等待发送数据缓冲区为空
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 4;
-
- // 发送数据
- i2c_data_transmit(i2cx, reg);
-
- // 等待数据发送完成
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 5;
-
- /***************** data ******************/
- // 发送数据
- do {
- // 等待发送数据缓冲区为空
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 6;
-
- // 发送数据
- i2c_data_transmit(i2cx, *data);
- data += offset;
-
- // 等待数据发送完成
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 7;
- } while(--len);
-
- /***************** stop ********************/
- // stop
- i2c_stop_on_bus(i2cx);
- if(I2C_waitn(i2cx, I2C_CTL0(I2C0)&I2C_CTL0_STOP)) return 8;
-
- return 0;
- }
-
-
- void I2C0_deinit() {
-
- }
-
-
- uint8_t I2C0_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
- uint32_t i2cx = I2C0;
- uint8_t address = addr << 1;
-
- /************* start ***********************/
- // 等待I2C空闲
- if(I2C_waitn(i2cx, I2C_FLAG_I2CBSY)) return 1;
-
- // 发送启动信号
- i2c_start_on_bus(i2cx);
- if(I2C_wait(i2cx, I2C_FLAG_SBSEND)) return 2;
-
- /************* device address **************/
- // 发送从设备地址
- i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);
-
- // //ack
- // i2c_ack_config(i2cx, I2C_ACK_ENABLE);
- // i2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);
- // if(I2C_wait(i2cx, (I2C_CTL0(i2cx) & I2C_CTL0_ACKEN))) return 11;
- // // i2c_ack_config(i2cx, I2C_ACK_DISABLE);
-
- if(I2C_wait(i2cx, I2C_FLAG_ADDSEND)) return 3;
- i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);
-
- /********** register address **************/
- // 等待发送缓冲区
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 4;
-
- // 发送寄存器地址
- i2c_data_transmit(i2cx, reg);
-
- // 等待发送数据完成
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 5;
- if(I2C_wait(i2cx, I2C_FLAG_TBE)) return 6;
-
- /************* start ***********************/
- // 发送再启动信号
- i2c_start_on_bus(i2cx);
-
-
- if(I2C_wait(i2cx, I2C_FLAG_SBSEND)) return 7;
-
- /************* device address **************/
- // 发送从设备地址
- i2c_master_addressing(i2cx, address, I2C_RECEIVER);
- if(I2C_wait(i2cx, I2C_FLAG_ADDSEND)) return 8;
- i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);
-
-
- /************* data **************/
- //ack
- i2c_ack_config(i2cx, I2C_ACK_ENABLE);
- i2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);
- if(I2C_wait(i2cx, (I2C_CTL0(i2cx) & I2C_CTL0_ACKEN))) return 23;
-
- // 读取数据
- uint8_t i;
- for (i = 0; i < len; i++) {
- if(i != len - 1) {
- // 等待ACK发送完成
- if(I2C_wait(i2cx, I2C_FLAG_BTC)) return 9;
- }
-
- // 等待ACK数据发送完成
- // 等待接收缓冲区
- if(I2C_wait(i2cx, I2C_FLAG_RBNE)) return 10;
- data[i] = i2c_data_receive(i2cx);
-
- if (i == len - 1) {
- // 在读取最后一个字节之前,禁用ACK,并发送停止信号
- // 配置自动NACK
- //i2c_ackpos_config(i2cx, I2C_ACKPOS_NEXT);
- //if(I2C_wait(i2cx, (I2C_CTL0(i2cx) & I2C_CTL0_ACKEN))) return 9;
- i2c_ack_config(i2cx, I2C_ACK_DISABLE);
- }
- }
-
- /***************** stop ********************/
- i2c_stop_on_bus(i2cx);
- if(I2C_waitn(i2cx, I2C_CTL0(I2C0) & I2C_CTL0_STOP)) return 11;
- return 0;
- }