> 技术文档 > ARM-I2C硬实现

ARM-I2C硬实现

硬件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 enablei2c_ack_config(i2cx, I2C_ACK_ENABLE);
  • 哪个I2C
  • SCL是哪个引脚
  • SDA是哪个引脚
  • 速度是多快

准备两个wait函数

等待指定外设的flag状态变化的函数

#defineTIMEOUT50000static uint8_t I2C_wait(uint32_t flag) { 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 flag) { uint16_t cnt = 0; while(i2c_flag_get(i2cx, flag)) { cnt++; if(cnt > TIMEOUT) return 1; }return 0;}

===写操作流程===

开始

/************* start ***********************/// 等待I2C闲置if(I2C_waitn(I2C_FLAG_I2CBSY)) return 1;// starti2c_start_on_bus(i2cx);// 等待I2C主设备成功发送起始信号if(I2C_wait(I2C_FLAG_SBSEND)) return 2;

发送设备地址

注意⚠️,这里是设备的写地址write_addr

/************* device address **************/// 发送设备地址i2c_master_addressing(i2cx, write_addr, I2C_TRANSMITTER);// 等待地址发送完成if(I2C_wait(I2C_FLAG_ADDSEND)) return 3;i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);

发送寄存器地址

/************ register address ************/// 寄存器地址// 等待发送数据缓冲区为空if(I2C_wait(I2C_FLAG_TBE)) return 4;// 发送数据i2c_data_transmit(i2cx, reg);// 等待数据发送完成if(I2C_wait(I2C_FLAG_BTC)) return 5;

数据发送

/***************** data ******************/// 发送数据uint32_t i;for(i = 0; i < len; i++) { uint32_t d = data[i]; // 等待发送数据缓冲区为空 if(I2C_wait(I2C_FLAG_TBE)) return 6; // 发送数据 i2c_data_transmit(i2cx, d); // 等待数据发送完成 if(I2C_wait(I2C_FLAG_BTC)) return 7;}

停止

/***************** stop ********************/// stopi2c_stop_on_bus(i2cx);if(I2C_waitn(I2C_CTL0(i2cx)&I2C_CTL0_STOP)) return 8;

===读操作流程===

开始

/************* start ***********************/// 等待I2C空闲if(I2C_waitn(I2C_FLAG_I2CBSY)) return 1;// 发送启动信号i2c_start_on_bus(i2cx);// 等待I2C主设备成功发送起始信号if(I2C_wait(I2C_FLAG_SBSEND)) return 2;

发送设备地址(写)

/************* device address **************/// 发送从设备地址i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);if(I2C_wait(I2C_FLAG_ADDSEND)) return 3;i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);

发送寄存器地址

/********** register address **************/// 等待发送缓冲区if(I2C_wait(I2C_FLAG_TBE)) return 4;// 发送寄存器地址i2c_data_transmit(i2cx, reg);// 等待发送数据完成if(I2C_wait(I2C_FLAG_BTC)) return 5;

开始

/************* start ***********************/// 发送再启动信号i2c_start_on_bus(i2cx);if(I2C_wait(I2C_FLAG_SBSEND)) return 7;

发送设备地址(读)

/************* device address **************/// 发送从设备地址i2c_master_addressing(i2cx, address, I2C_RECEIVER);if(I2C_wait(I2C_FLAG_ADDSEND)) return 8;i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);

数据读取

/************* data **************///acki2c_ack_config(i2cx, I2C_ACK_ENABLE);// 接收一个数据后,自动发送ACKi2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);// 确认ACK已启用if(I2C_wait(I2C_CTL0(i2cx) & I2C_CTL0_ACKEN)) return 11;// 读取数据uint32_t i;for (i = 0; i < len; i++) { if (i == len - 1) { // 在读取最后一个字节之前,禁用ACK,配置为自动NACK i2c_ack_config(i2cx, I2C_ACK_DISABLE); } // 等待接收缓冲区不为空 if(I2C_wait(I2C_FLAG_RBNE)) return 10; data[i] = i2c_data_receive(i2cx);}

停止

/***************** stop ********************/i2c_stop_on_bus(i2cx);if(I2C_waitn(I2C_CTL0(i2cx)&I2C_CTL0_STOP)) return 12;

GD32F4寄存器

流程

功能

标记

描述

START

I2C_FLAG_I2CBSY

busy标记。I2C是否占用,没有占用才可以使用。

I2C_FLAG_SBSEND

起始信号发送状态标记。START成功或失败。

数据

设备地址

I2C_FLAG_ADDSEND

地址发送状态标记。成功或失败。

发送

I2C_FLAG_TBE

发送数据寄存器是否为空的标记。为空才可以继续发送。

I2C_FLAG_BTC

发送数据寄存器中数据是否发送完成。

接收

I2C_FLAG_RBNE

接收缓冲区寄存器是否为空的标记。为空才可以继续接收。

STOP

I2C_CTL0_STOP

停止标记位。

完整代码

//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

 

//I2C0.c#include \"I2C0.h\"#define i2cxI2C0void 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 enablei2c_ack_config(i2cx, I2C_ACK_ENABLE); //i2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);}static uint8_t I2C_wait(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 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) { uint8_t address = addr << 1;/************* start ***********************/// 等待I2C闲置 if(I2C_waitn(I2C_FLAG_I2CBSY)) return 1; // start i2c_start_on_bus(i2cx); // 等待I2C主设备成功发送起始信号 if(I2C_wait(I2C_FLAG_SBSEND)) return 2;/************* device address **************/ // 发送设备地址 i2c_master_addressing(i2cx, address, I2C_TRANSMITTER); // 等待地址发送完成 if(I2C_wait(I2C_FLAG_ADDSEND)) return 3; i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);/************ register address ************/ // 寄存器地址 // 等待发送数据缓冲区为空 if(I2C_wait(I2C_FLAG_TBE)) return 4;// 发送数据 i2c_data_transmit(i2cx, reg); // 等待数据发送完成 if(I2C_wait(I2C_FLAG_BTC)) return 5;/***************** data ******************/ // 发送数据 uint32_t i; for(i = 0; i < data_len; i++) { uint32_t d = data[i]; // 等待发送数据缓冲区为空 if(I2C_wait(I2C_FLAG_TBE)) return 6; // 发送数据 i2c_data_transmit(i2cx, d); // 等待数据发送完成 if(I2C_wait(I2C_FLAG_BTC)) return 7; }/***************** stop ********************/ // stop i2c_stop_on_bus(i2cx); if(I2C_waitn(I2C_CTL0(i2cx)&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) { uint8_t address = addr << 1;/************* start ***********************/// 等待I2C闲置 if(I2C_waitn(I2C_FLAG_I2CBSY)) return 1; // start i2c_start_on_bus(i2cx); // 等待I2C主设备成功发送起始信号 if(I2C_wait(I2C_FLAG_SBSEND)) return 2;/************* device address **************/ // 发送设备地址 i2c_master_addressing(i2cx, address, I2C_TRANSMITTER); // 等待地址发送完成 if(I2C_wait(I2C_FLAG_ADDSEND)) return 3; i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);/************ register address ************/ // 寄存器地址 // 等待发送数据缓冲区为空 if(I2C_wait(I2C_FLAG_TBE)) return 4;// 发送数据 i2c_data_transmit(i2cx, reg); // 等待数据发送完成 if(I2C_wait(I2C_FLAG_BTC)) return 5;/***************** data ******************/ // 发送数据do { // 等待发送数据缓冲区为空 if(I2C_wait(I2C_FLAG_TBE)) return 6; // 发送数据 i2c_data_transmit(i2cx, *data);data += offset; // 等待数据发送完成 if(I2C_wait(I2C_FLAG_BTC)) return 7; } while(--len);/***************** stop ********************/ // stop i2c_stop_on_bus(i2cx); if(I2C_waitn(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(I2C_FLAG_I2CBSY)) return 1; // 发送启动信号 i2c_start_on_bus(i2cx);if(I2C_wait(I2C_FLAG_SBSEND)) return 2;/************* device address **************/ // 发送从设备写地址 i2c_master_addressing(i2cx, address, I2C_TRANSMITTER); if(I2C_wait(I2C_FLAG_ADDSEND)) return 3; i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);/********** register address **************/// 等待发送缓冲区 if(I2C_wait(I2C_FLAG_TBE)) return 4; // 发送寄存器地址 i2c_data_transmit(i2cx, reg);// 等待发送数据完成 if(I2C_wait(I2C_FLAG_BTC)) return 5;/************* start ***********************/ // 发送再启动信号 i2c_start_on_bus(i2cx);if(I2C_wait(I2C_FLAG_SBSEND)) return 7;/************* device address **************/ // 发送从设备读地址 i2c_master_addressing(i2cx, address + 1, I2C_RECEIVER); if(I2C_wait(I2C_FLAG_ADDSEND)) return 8; i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND); /**************** 接收数据data *************///acki2c_ack_config(i2cx, I2C_ACK_ENABLE);// 接收一个数据后,自动发送ACKi2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);// 确认ACK已启用if(I2C_wait(I2C_CTL0(i2cx) & I2C_CTL0_ACKEN)) return 11;// 读取数据uint32_t i;for (i = 0; i < len; i++) { if (i == len - 1) { // 在读取最后一个字节之前,禁用ACK,配置为自动NACK i2c_ack_config(i2cx, I2C_ACK_DISABLE); } // 等待接收缓冲区不为空 if(I2C_wait(I2C_FLAG_RBNE)) return 10; data[i] = i2c_data_receive(i2cx);}/***************************************//***************** stop ********************/i2c_stop_on_bus(i2cx);if(I2C_waitn(I2C_CTL0(i2cx)&I2C_CTL0_STOP)) return 12; return 0;}