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

准备两个wait函数

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

复制代码
#define	TIMEOUT	50000
static 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;
// start
i2c_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 ********************/
// stop
i2c_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 **************/
//ack
i2c_ack_config(i2cx, I2C_ACK_ENABLE);
// 接收一个数据后,自动发送ACK
i2c_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是否占用,没有占用才可以使用。 |
| START | | 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 i2cx	I2C0

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 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 *************/
	//ack
	i2c_ack_config(i2cx, I2C_ACK_ENABLE);
	// 接收一个数据后,自动发送ACK
	i2c_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;
}
相关推荐
XINVRY-FPGA3 小时前
XCZU4EV-1FBVB900E Xilinx FPGA AMD Zynq UltraScale+ MPSoC EV(Embedded Vision)
arm开发·嵌入式硬件·计算机视觉·fpga开发·硬件架构·硬件工程·fpga
猫猫的小茶馆3 小时前
【STM32】FreeRTOS 任务的删除(三)
java·linux·stm32·单片机·嵌入式硬件·mcu·51单片机
学不动CV了3 小时前
单片机ADC采集机理层面详细分析(二)
c语言·arm开发·stm32·单片机·嵌入式硬件·开源·51单片机
学不动CV了3 小时前
51核和ARM核单片机OTA实战解析(二)
c语言·arm开发·stm32·单片机·嵌入式硬件·51单片机
Yuroo zhou4 小时前
IMU的精度对无人机姿态控制意味着什么?
单片机·嵌入式硬件·算法·无人机·嵌入式实时数据库
knight_20249 小时前
嵌入式学习日志————对射式红外传感器计次
stm32·单片机·嵌入式硬件·学习
深圳安凯星单片机开发方案公司9 小时前
用单片机怎么控制转速
单片机·51单片机
忆和熙9 小时前
【模电笔记】—— 波形发生电路(波形振荡器)
嵌入式硬件·模电笔记·波形发生电路
文火冰糖的硅基工坊9 小时前
[硬件电路-97]:模拟器件 - 如何通过外部的闭环负反馈,让运算放大器从“暴脾气”、“愣头青”、情绪容易失控者变成“沉着”、“冷静”的精密调控者的?
嵌入式硬件·架构·电路·跨学科融合