GD32f303的硬件IIC.从机配置与接收流程

一、写在开头

        1、要注意硬件IIC配置相当坑,看起来没关系的两个函数,只要初始化配置化调用写反了就可能导致问题。比如:最终使能IIC的函数< i2c_enable(I2Cx) >与配置应答的函数< i2c_ack_config(I2Cx, I2C_ACK_ENABLE) >写反了就会导致配置失败。非常离谱。

        2、在写自己的中断函数时要注意好逻辑,从机发与从机收是两个套路。而且< i2c_interrupt_flag_clear() >函数不是万能的。要注意这个函数只能清除哪些标志位。

        2023.2.7新增---> 3、由于iic从移位寄存器传给发送寄存器时会触发把发送寄存器的值传给接收机的操作,因此特别注意要对这进行处理。

        2023.2.7新增---> 4、很多时候iic中断不能只思考单发或者单收的情况,有时候会同时进行读跟写,也就是读标志位跟写标志位会在一段时间内出现,在编程时不考虑这种情况就会出错。我现在继续回顾我的代码,当初认为完美的代码再看也就那样。

        2023.4.18新增--->5、iic主设备发送给从设备第一个字节前会先发一个0或上一次发送报文的尾字节的问题不会所有的设备都存在,在我目前接触的有的第一字节会出问题有的不会出问题,记得具体问题具体分析。最好能进debug或者买一个逻辑分析仪看看。

        2023.4.18新增--->6、如果频繁的发送报文,也就是频繁的钳位iic总线。这份代码会概率出问题。如果出问题了,在i2c0_rw_Data_slave()、i2c1_rw_Data_slave()的末尾的死等50次循环可以注释掉。这个循环是为了防止某些iic外设莫名奇妙的行为,但现在想想意义不大,反而更容易出问题了,去掉就行,保持中断的全速运行。

        5、GD32f303单片机的从机收发流程图如下:

                发:

                        收:<值得注意,stpdet标志位不可以使用i2c_interrupt_flag_clear()函数清除> 

二、我的硬件IIc.从机初始化流程<带应答>

        --下面时我最初的初始化代码,不要抄!!!有问题

void i2cSlaveInit(void)
{
    // enable clock 
    rcu_periph_clock_enable(RCU_AF);
    rcu_periph_clock_enable(RCU_GPIOB);//看自己用的iic配置哪个io端口
    rcu_periph_clock_enable(RCU_I2Cx);

    // PB6:I2Cx_SCL, PB7:I2Cx_SDA
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_7);

    i2c_deinit(I2Cx);
    // I2C clock configure : 100kHz, 
    i2c_clock_config(I2Cx, 100000, I2C_DTCY_2);
    // I2C address configure 
    i2c_mode_addr_config(I2Cx, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, I2Cx_SLAVE_ADDRESS8);//注意地址,8bit,虽然不知道为什么写7bit的地址需要填入8bit,但注意就好
    // 中断配置 
    nvic_irq_enable(I2Cx_EV_IRQn, 0, 1);
    nvic_irq_enable(I2Cx_ER_IRQn, 0, 2);
    i2c_interrupt_enable(I2Cx, I2C_INT_ERR);
	i2c_interrupt_enable(I2Cx, I2C_INT_BUF);
    i2c_interrupt_enable(I2Cx, I2C_INT_EV);
    // enable I2Cx 
    i2c_enable(I2Cx);
	// enable ack
    i2c_ack_config(I2Cx, I2C_ACK_ENABLE);
}

        --下面是我最新的初始化代码

/**
 * @fn         : 
 * @brief      : iic0从模式初始化
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint32_t} address : 经左移1位的7bit地址
 */
void i2c0SlaveInit(uint32_t address)
{
    /* enable af clock */
    rcu_periph_clock_enable(RCU_AF);
    /* enable GPIOB clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    /* enable I2C0 clock */
    rcu_periph_clock_enable(RCU_I2C0);

    /* connect PB6 to I2C0_SCL */
    /* connect PB7 to I2C0_SDA */
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_7);

    /* 取消初始化iic0 */
    i2c_deinit(I2C0);
    /* I2C clock configure : 100kHz, */
    i2c_clock_config(I2C0, 100000, I2C_DTCY_2);
    /* I2C address configure */
    i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, address);
    /* 中断配置 */
    nvic_irq_enable(I2C0_EV_IRQn, 3, 0);
    nvic_irq_enable(I2C0_ER_IRQn, 4, 0);
    /* enable the I2C0 interrupt */
    i2c_interrupt_enable(I2C0, I2C_INT_ERR);
    i2c_interrupt_enable(I2C0, I2C_INT_BUF);
    i2c_interrupt_enable(I2C0, I2C_INT_EV);
    /* enable I2C0 */
    i2c_enable(I2C0);
    /* enable acknowledge */
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);
}

/**
 * @fn         : 
 * @brief      : iic1从模式初始化
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint32_t} address : 经左移1位的7bit地址
 */
void i2c1SlaveInit(uint32_t address)
{
    /* enable af clock */
    rcu_periph_clock_enable(RCU_AF);
    /* enable GPIOB clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    /* enable I2C1 clock */
    rcu_periph_clock_enable(RCU_I2C1);

    /* connect PB6 to I2C1_SCL */
    /* connect PB7 to I2C1_SDA */
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_11);

    /* 取消初始化iic1 */
    i2c_deinit(I2C1);
    /* I2C clock configure : 100kHz, */
    i2c_clock_config(I2C1, 100000, I2C_DTCY_2);
    /* I2C address configure */
    i2c_mode_addr_config(I2C1, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, address);
    /* 中断配置 */
    nvic_irq_enable(I2C1_EV_IRQn, 3, 0);
    nvic_irq_enable(I2C1_ER_IRQn, 4, 0);
    /* enable the I2C0 interrupt */
    i2c_interrupt_enable(I2C1, I2C_INT_ERR);
    i2c_interrupt_enable(I2C1, I2C_INT_BUF);
    i2c_interrupt_enable(I2C1, I2C_INT_EV);
    /* enable I2C1 */
    i2c_enable(I2C1);
    /* enable acknowledge */
    i2c_ack_config(I2C1, I2C_ACK_ENABLE);
}

三、我的硬件IIC.从机流程代码<收+发>

--下面时我最初的中断收发代码,不要抄!!!有问题

#define i2c_periph I2C0

uint8_t buffer[20]; //接收的数据
extern uint8_t *i2c_data;//发送的数据,外部给出

//单次接收有长度限制 20byte,发送没有
void i2c_rw_Data_slave(uint8_t *const i2c_rx, uint8_t *const i2c_tx)
{
    static uint8_t ADDSEND_FLAG = 0, wRFlag = 2;                // wRFalg ==0 是发送模式,==1 是接收模式
    uint8_t *const rxbuffer = i2c_rx, *const txbuffer = i2c_tx; //分别保存接收、发送的数据
    static uint8_t index = 0;

    /* wait until ADDSEND bit is set */
    if (i2c_interrupt_flag_get(i2c_periph, I2C_INT_FLAG_ADDSEND))
    { //第一次addsend
        ADDSEND_FLAG++;
        i2c_interrupt_flag_clear(i2c_periph, I2C_INT_FLAG_ADDSEND);
    }
    else if (i2c_interrupt_flag_get(i2c_periph, I2C_INT_FLAG_BERR))
    {
        i2c_interrupt_flag_clear(i2c_periph, I2C_INT_FLAG_BERR);
    }

    if (i2c_interrupt_flag_get(i2c_periph, I2C_INT_FLAG_TBE)) //确定是发
    {
        wRFlag = 0;
    }
    if (i2c_interrupt_flag_get(i2c_periph, I2C_INT_FLAG_RBNE)) //确定是收
    {
        wRFlag = 1;
    }

    if (ADDSEND_FLAG != 0)
    {
        while (wRFlag != 2)
        {
            if (wRFlag == 0) //发送
            {
                if (i2c_interrupt_flag_get(i2c_periph, I2C_INT_FLAG_TBE))
                {
                    i2c_data_transmit(i2c_periph, txbuffer[index]);
                    index++;
                }
                if (i2c_interrupt_flag_get(i2c_periph, I2C_INT_FLAG_AERR))
                {
                    wRFlag = 2;
                    index = 0;
                    ADDSEND_FLAG = 0;
                    i2c_interrupt_flag_clear(i2c_periph, I2C_INT_FLAG_AERR);
                }
            }
            else if (wRFlag == 1) //接收
            {
                if (i2c_interrupt_flag_get(i2c_periph, I2C_INT_FLAG_RBNE))
                {
                    if (index < 20)
                    {
                        rxbuffer[index] = i2c_data_receive(i2c_periph);
                        index++;
                    }
                    else
                    {
                        i2c_data_receive(i2c_periph); //过长丢弃
                    }
                }
                if (i2c_interrupt_flag_get(i2c_periph, I2C_INT_FLAG_STPDET))
                {
                    wRFlag = 2;
                    index = 0;
                    ADDSEND_FLAG = 0;

                    I2C_STAT0(i2c_periph); // 清标志:I2C_INT_FLAG_STPDET
                    I2C_CTL0(i2c_periph) = I2C_CTL0(i2c_periph);
                }
            }
        }
    }
    i2c_interrupt_flag_clear(i2c_periph, I2C_INT_FLAG_AERR);
	
	I2C_STAT0(i2c_periph); // clear: I2C_INT_FLAG_STPDET
    I2C_CTL0(i2c_periph) = I2C_CTL0(i2c_periph);
}

void I2C0_EventIRQ_Handler()
{
    i2c_rw_Data_slave(buffer, i2c_data);
}

void I2C0_ErrorIRQ_Handler(void)
{
}

         --下面是我最新的中断代码

/**
 * @fn         : 
 * @brief      : 单次接收有长度限制 20byte <不限制长度的接收是不可取的>,发送没有
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint8_t} *i2c_send
 * @param {uint8_t} *i2c_receive
 */
void i2c0_rw_Data_slave(uint8_t *const i2c_send, uint8_t *const i2c_receive)
{
    static uint8_t ADDSEND_FLAG = 0, wRFlag = 2;                // wRFalg ==0 是发送模式,==1 是接收模式
    uint8_t *const rxbuffer = i2c_receive, *const txbuffer = i2c_send; //分别保存接收、发送的数据
    static uint8_t index_rx = 0, index_tx = 0;

    if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_ADDSEND)){ //检查addsend标志位
        ADDSEND_FLAG++;
        i2c_interrupt_flag_clear(I2C0, I2C_INT_FLAG_ADDSEND);//清理 ADDSEND bit 
    }

    if (ADDSEND_FLAG != 0){
		switch(wRFlag){
			case 0://发送
				if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_TBE)){
                    i2c_data_transmit(I2C0, txbuffer[index_tx]);
                    index_tx++;
                }
				if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_AERR)){
                    wRFlag = 2;
                    index_tx = 0;
                    ADDSEND_FLAG = 0;
                    i2c_interrupt_flag_clear(I2C0, I2C_INT_FLAG_AERR);
                }
				break;
			case 1://接收
				if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_RBNE)){
                    if (index_rx < 20){
                        rxbuffer[ (index_rx>0)?index_rx-1 : index_rx ] = i2c_data_receive(I2C0);//因为iic的特性,第一个收到的是移位寄存器的数据,是不需要的
                        index_rx++;
                    }else{
                        i2c_data_receive(I2C0); //过长丢弃
                    }
                }
                if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_STPDET)){
                    wRFlag = 2;
                    index_rx = 0;
                    ADDSEND_FLAG = 0;

                    I2C_STAT0(I2C0); // clear: I2C_INT_FLAG_STPDET
                    I2C_CTL0(I2C0) = I2C_CTL0(I2C0);
                }
				break;
			case 2:
				__nop();
				break;
			default:
				//严重的错误,不应该会执行到这里
				break;
		}
    }
	
	if(ADDSEND_FLAG == 1 && i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_RBNE)){//确定是收
		wRFlag = 1;
		for(int i=0; i<50; i++){//硬等一段时间
			__nop();
		}
	}else if (ADDSEND_FLAG == 2 && i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_TBE)){ //确定是发
        wRFlag = 0;
		for(int i=0; i<50; i++){//硬等一段时间
			__nop();
		}
	}
}

/**
 * @fn         : 
 * @brief      : 单次接收有长度限制 20byte <不限制长度的接收是不可取的>,发送没有
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint8_t} *i2c_send
 * @param {uint8_t} *i2c_receive
 */
void i2c1_rw_Data_slave(uint8_t *const i2c_send, uint8_t *const i2c_receive)
{
    static uint8_t ADDSEND_FLAG = 0, wRFlag = 2;                // wRFalg ==0 是发送模式,==1 是接收模式
    uint8_t *const rxbuffer = i2c_receive, *const txbuffer = i2c_send; //分别保存接收、发送的数据
    static uint8_t index_rx = 0, index_tx = 0;

    if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_ADDSEND)){ //检查addsend标志位
        ADDSEND_FLAG++;
        i2c_interrupt_flag_clear(I2C1, I2C_INT_FLAG_ADDSEND);//清理 ADDSEND bit 
    }

    if (ADDSEND_FLAG != 0){
		switch(wRFlag){
			case 0://发送
				if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_TBE)){
                    i2c_data_transmit(I2C1, txbuffer[index_tx]);
                    index_tx++;
                }
				if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_AERR)){
                    wRFlag = 2;
                    index_tx = 0;
                    ADDSEND_FLAG = 0;
                    i2c_interrupt_flag_clear(I2C1, I2C_INT_FLAG_AERR);
                }
				break;
			case 1://接收
				if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_RBNE)){
                    if (index_rx < 20){
                        rxbuffer[ (index_rx>0)?index_rx-1 : index_rx ] = i2c_data_receive(I2C1);//因为iic的特性,第一个收到的是移位寄存器的数据,是不需要的
                        index_rx++;
                    }else{
                        i2c_data_receive(I2C1); //过长丢弃
                    }
                }
                if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_STPDET)){
                    wRFlag = 2;
                    index_rx = 0;
                    ADDSEND_FLAG = 0;

                    I2C_STAT0(I2C1); // clear: I2C_INT_FLAG_STPDET
                    I2C_CTL0(I2C1) = I2C_CTL0(I2C1);
                }
				break;
			case 2:
				__nop();
				break;
			default:
				//严重的错误,不应该会执行到这里
				break;
		}
    }
	
	if(ADDSEND_FLAG == 1 && i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_RBNE)){//确定是收
		wRFlag = 1;
		for(int i=0; i<50; i++){//硬等一段时间
			__nop();
		}
	}else if (ADDSEND_FLAG == 2 && i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_TBE)){ //确定是发
        wRFlag = 0;
		for(int i=0; i<50; i++){//硬等一段时间
			__nop();
		}
	}
}

/**
 * @fn         : 
 * @brief      : iic0中断函数
 * @return      {*}
 * @attention  : 
 * @author     : 
 */
void I2C0_EventIRQ_Handler()
{
    i2c0_rw_Data_slave(i2c0_send, i2c0_receiver);
}

/**
 * @fn         :
 * @brief      :
 * @return      {*}
 * @attention  :
 * @author     : 
 */
void I2C0_ErrorIRQ_Handler(void)
{
    printf("function:%s, iic0 error\r\n", __func__);
}

/**
 * @fn         : 
 * @brief      : iic1中断函数
 * @return      {*}
 * @attention  : 
 * @author     : 
 */
void I2C1_EventIRQ_Handler()
{
    i2c1_rw_Data_slave(i2c1_send, i2c1_receiver);
}

/**
 * @fn         :
 * @brief      :
 * @return      {*}
 * @attention  :
 * @author     : 
 */
void I2C1_ErrorIRQ_Handler(void)
{
    printf("function:%s, iic1 error\r\n", __func__);
}

四、完整的文件

------ 头文件hal_iic.h <中断定义在gd32f30x_it.c文件里,把下面的中断函数写到中断函数体内即可>

#ifndef __HAL_IIC_H__
#define __HAL_IIC_H__

#include <stdint.h>
#include <stdbool.h>
#include "FreeRTOS.h"
#include "task.h"

#ifdef __cplusplus
extern "C" {
#endif

    typedef enum {
        open_iic0=0x01,
        open_iic1=0x02
    }iic_select;

    void iic_Init(iic_select select, ...);
	
	/* iic0的中断回调函数 */
	void I2C0_EventIRQ_Handler(void);
	/* iic1的中断回调函数 */
	void I2C1_EventIRQ_Handler(void);
    
    void I2C0_ErrorIRQ_Handler(void);
    void I2C1_ErrorIRQ_Handler(void);
#ifdef __cplusplus
}
#endif

#endif

------ 源文件hal_iicSlave.c

#include "hal_iic.h"
#include "log.h"
#include "stdarg.h"

uint8_t *i2c0_send;
uint8_t i2c0_receiver[20];
uint8_t *i2c1_send;
uint8_t i2c1_receiver[20];

/**
 * @fn         : 
 * @brief      : iic0注册主设备读取时被读取的数据
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint8_t} *array
 */
void i2c0SlaveRegister(uint8_t *array)
{
    i2c0_send = array;
}

/**
 * @fn         : 
 * @brief      : iic1注册主设备读取时被读取的数据
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint8_t} *array
 */
void i2c1SlaveRegister(uint8_t *array)
{
    i2c1_send = array;
}

/**
 * @fn         : 
 * @brief      : iic0从模式初始化
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint32_t} address : 经左移1位的7bit地址
 */
void i2c0SlaveInit(uint32_t address)
{
    /* enable af clock */
    rcu_periph_clock_enable(RCU_AF);
    /* enable GPIOB clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    /* enable I2C0 clock */
    rcu_periph_clock_enable(RCU_I2C0);

    /* connect PB6 to I2C0_SCL */
    /* connect PB7 to I2C0_SDA */
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_7);

    /* 取消初始化iic0 */
    i2c_deinit(I2C0);
    /* I2C clock configure : 100kHz, */
    i2c_clock_config(I2C0, 100000, I2C_DTCY_2);
    /* I2C address configure */
    i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, address);
    /* 中断配置 */
    nvic_irq_enable(I2C0_EV_IRQn, 3, 0);
    nvic_irq_enable(I2C0_ER_IRQn, 4, 0);
    /* enable the I2C0 interrupt */
    i2c_interrupt_enable(I2C0, I2C_INT_ERR);
    i2c_interrupt_enable(I2C0, I2C_INT_BUF);
    i2c_interrupt_enable(I2C0, I2C_INT_EV);
    /* enable I2C0 */
    i2c_enable(I2C0);
    /* enable acknowledge */
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);
}

/**
 * @fn         : 
 * @brief      : iic1从模式初始化
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint32_t} address : 经左移1位的7bit地址
 */
void i2c1SlaveInit(uint32_t address)
{
    /* enable af clock */
    rcu_periph_clock_enable(RCU_AF);
    /* enable GPIOB clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    /* enable I2C1 clock */
    rcu_periph_clock_enable(RCU_I2C1);

    /* connect PB6 to I2C1_SCL */
    /* connect PB7 to I2C1_SDA */
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_11);

    /* 取消初始化iic1 */
    i2c_deinit(I2C1);
    /* I2C clock configure : 100kHz, */
    i2c_clock_config(I2C1, 100000, I2C_DTCY_2);
    /* I2C address configure */
    i2c_mode_addr_config(I2C1, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, address);
    /* 中断配置 */
    nvic_irq_enable(I2C1_EV_IRQn, 3, 0);
    nvic_irq_enable(I2C1_ER_IRQn, 4, 0);
    /* enable the I2C0 interrupt */
    i2c_interrupt_enable(I2C1, I2C_INT_ERR);
    i2c_interrupt_enable(I2C1, I2C_INT_BUF);
    i2c_interrupt_enable(I2C1, I2C_INT_EV);
    /* enable I2C1 */
    i2c_enable(I2C1);
    /* enable acknowledge */
    i2c_ack_config(I2C1, I2C_ACK_ENABLE);
}

/**
 * @fn         : 
 * @brief      : 单次接收有长度限制 20byte <不限制长度的接收是不可取的>,发送没有
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint8_t} *i2c_send
 * @param {uint8_t} *i2c_receive
 */
void i2c0_rw_Data_slave(uint8_t *const i2c_send, uint8_t *const i2c_receive)
{
    static uint8_t ADDSEND_FLAG = 0, wRFlag = 2;                // wRFalg ==0 是发送模式,==1 是接收模式
    uint8_t *const rxbuffer = i2c_receive, *const txbuffer = i2c_send; //分别保存接收、发送的数据
    static uint8_t index_rx = 0, index_tx = 0;

    if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_ADDSEND)){ //检查addsend标志位
        ADDSEND_FLAG++;
        i2c_interrupt_flag_clear(I2C0, I2C_INT_FLAG_ADDSEND);//清理 ADDSEND bit 
    }

    if (ADDSEND_FLAG != 0){
		switch(wRFlag){
			case 0://发送
				if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_TBE)){
                    i2c_data_transmit(I2C0, txbuffer[index_tx]);
                    index_tx++;
                }
				if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_AERR)){
                    wRFlag = 2;
                    index_tx = 0;
                    ADDSEND_FLAG = 0;
                    i2c_interrupt_flag_clear(I2C0, I2C_INT_FLAG_AERR);
                }
				break;
			case 1://接收
				if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_RBNE)){
                    if (index_rx < 20){
                        rxbuffer[ (index_rx>0)?index_rx-1 : index_rx ] = i2c_data_receive(I2C0);//因为iic的特性,第一个收到的是移位寄存器的数据,是不需要的,但是在某些ic却没有这种情况,所以这里应该由自己需求决定要不要去掉第一个发送过来的字节
                        index_rx++;
                    }else{
                        i2c_data_receive(I2C0); //过长丢弃
                    }
                }
                if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_STPDET)){
                    wRFlag = 2;
                    index_rx = 0;
                    ADDSEND_FLAG = 0;

                    I2C_STAT0(I2C0); // clear: I2C_INT_FLAG_STPDET
                    I2C_CTL0(I2C0) = I2C_CTL0(I2C0);
                }
				break;
			case 2:
				__nop();
				break;
			default:
				//严重的错误,不应该会执行到这里
				break;
		}
    }
	
	if(ADDSEND_FLAG == 1 && i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_RBNE)){//确定是收
		wRFlag = 1;
		for(int i=0; i<50; i++){//硬等一段时间
			__nop();
		}
	}else if (ADDSEND_FLAG == 2 && i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_TBE)){ //确定是发
        wRFlag = 0;
		for(int i=0; i<50; i++){//硬等一段时间
			__nop();
		}
	}
}

/**
 * @fn         : 
 * @brief      : 单次接收有长度限制 20byte <不限制长度的接收是不可取的>,发送没有
 * @return      {*}
 * @attention  : 
 * @author     : 
 * @param {uint8_t} *i2c_send
 * @param {uint8_t} *i2c_receive
 */
void i2c1_rw_Data_slave(uint8_t *const i2c_send, uint8_t *const i2c_receive)
{
    static uint8_t ADDSEND_FLAG = 0, wRFlag = 2;                // wRFalg ==0 是发送模式,==1 是接收模式
    uint8_t *const rxbuffer = i2c_receive, *const txbuffer = i2c_send; //分别保存接收、发送的数据
    static uint8_t index_rx = 0, index_tx = 0;

    if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_ADDSEND)){ //检查addsend标志位
        ADDSEND_FLAG++;
        i2c_interrupt_flag_clear(I2C1, I2C_INT_FLAG_ADDSEND);//清理 ADDSEND bit 
    }

    if (ADDSEND_FLAG != 0){
		switch(wRFlag){
			case 0://发送
				if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_TBE)){
                    i2c_data_transmit(I2C1, txbuffer[index_tx]);
                    index_tx++;
                }
				if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_AERR)){
                    wRFlag = 2;
                    index_tx = 0;
                    ADDSEND_FLAG = 0;
                    i2c_interrupt_flag_clear(I2C1, I2C_INT_FLAG_AERR);
                }
				break;
			case 1://接收
				if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_RBNE)){
                    if (index_rx < 20){
                        rxbuffer[ (index_rx>0)?index_rx-1 : index_rx ] = i2c_data_receive(I2C1);//因为iic的特性,第一个收到的是移位寄存器的数据,是不需要的。但是在某些ic却没有这种情况,所以这里应该由自己需求决定要不要去掉第一个发送过来的字节
                        index_rx++;
                    }else{
                        i2c_data_receive(I2C1); //过长丢弃
                    }
                }
                if (i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_STPDET)){
                    wRFlag = 2;
                    index_rx = 0;
                    ADDSEND_FLAG = 0;

                    I2C_STAT0(I2C1); // clear: I2C_INT_FLAG_STPDET
                    I2C_CTL0(I2C1) = I2C_CTL0(I2C1);
                }
				break;
			case 2:
				__nop();
				break;
			default:
				//严重的错误,不应该会执行到这里
				break;
		}
    }
	
	if(ADDSEND_FLAG == 1 && i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_RBNE)){//确定是收
		wRFlag = 1;
		for(int i=0; i<50; i++){//硬等一段时间
			__nop();
		}
	}else if (ADDSEND_FLAG == 2 && i2c_interrupt_flag_get(I2C1, I2C_INT_FLAG_TBE)){ //确定是发
        wRFlag = 0;
		for(int i=0; i<50; i++){//硬等一段时间
			__nop();
		}
	}
}

/**
 * @fn         : 
 * @brief      : iic0中断函数
 * @return      {*}
 * @attention  : 
 * @author     : 
 */
void I2C0_EventIRQ_Handler()
{
    i2c0_rw_Data_slave(i2c0_send, i2c0_receiver);
}

/**
 * @fn         :
 * @brief      :
 * @return      {*}
 * @attention  :
 * @author     : 
 */
void I2C0_ErrorIRQ_Handler(void)
{
    printf("function:%s, iic0 error\r\n", __func__);//会重复进入这个中断
}

/**
 * @fn         : 
 * @brief      : iic1中断函数
 * @return      {*}
 * @attention  : 
 * @author     : 
 */
void I2C1_EventIRQ_Handler()
{
    i2c1_rw_Data_slave(i2c1_send, i2c1_receiver);
}

/**
 * @fn         :
 * @brief      :
 * @return      {*}
 * @attention  :
 * @author     : 
 */
void I2C1_ErrorIRQ_Handler(void)
{
    printf("function:%s, iic1 error\r\n", __func__);//会重复进入这个中断
}

/**
 * @fn         : 
 * @brief      : 选择要打开的iic外设,设置数据的输入输出,默认是100k的速度,地址可调        
 * @return      {*}
 * @attention  : 发送需要外部任务提供数组,接收需要外部任务提供指针
 * @author     : 
 * @param {iic_select} select
 *              #open_iic0 :打开iic0
 *              #open_iic1 : 打开iic1
 * @param {...} ...
 *              #参数1 :第一个iic的send的数组的指针 uint8_t*          
 *              #参数2 :指向第一个iic的receive的指针 uint8_t*        
 *              #参数3 :指向第一个iic的从地址 uint8_t 
 */
void iic_Init(iic_select select, ...)
{
    if((select & (open_iic0 | open_iic1)) != 0){
        va_list args; //定义一个可变参数列表  
        va_start(args,select);//初始化args指向强制参数arg的下一个参数;  

        if((select & open_iic0) == open_iic0){//打开iic0
            i2c0SlaveRegister((uint8_t *)va_arg(args,uint32_t));  //获取当前参数内容并将args指向下一个参数  
            *((uint8_t **)va_arg(args,uint32_t)) = i2c0_receiver;  //获取当前参数内容并将args指向下一个参数 
            i2c0SlaveInit(va_arg(args,uint32_t));  
        }
        
        if((select & open_iic1) == open_iic1){//打开iic1
            i2c1SlaveRegister((uint8_t *)va_arg(args,uint32_t));  //获取当前参数内容并将args指向下一个参数  
            *((uint8_t **)va_arg(args,uint32_t)) = i2c1_receiver;  //获取当前参数内容并将args指向下一个参数 
            i2c1SlaveInit(va_arg(args,uint32_t));
        }
        
        va_end(args);//释放args
    }else if(select != 0){
        logE("Attempt to open an iic that does not exist, input number is :%d, format is: ...iic3|iic2|iic1|iic0\r\n", select);
    }else{
        logW("call the function : iic_Init, but not select iic\r\n");
    }
}

        --- 使用示例<在代码的初始化函数里如下即可完成iic注册,自动收发到自己定义的数组里>

注:代码里的中断函数不是中断向量表定义的中断函数名,要触发中断还得自己添加中断函数

uint8_t iic0Send_Data[40]; 
uint8_t * iic0Receive_Data;
uint8_t iic1Send_Data[40]; 
uint8_t * iic1Receive_Data;

iic_Init(open_iic0, iic0Send_Data, &iic0Receive_Data, 0xA0);
iic_Init(open_iic1, iic1Send_Data, &iic1Receive_Data, 0xA2);

/* 下面是不推荐的写法 */
iic_Init(open_iic0|open_iic1, iic0Send_Data, &iic0Receive_Data, 0xA0, iic1Send_Data, &iic1Receive_Data, 0xA2);//必须先写iic0的实参后写iic1的实参

  • 23
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
对于 GD32F303 硬件 IIC,以下是一个参考例程: ```c #include "gd32f30x.h" #define IIC_SCL_PIN GPIO_PIN_6 #define IIC_SDA_PIN GPIO_PIN_7 #define IIC_GPIO_PORT GPIOB #define IIC_GPIO_CLK RCU_GPIOB #define IIC_SCL_HIGH() gpio_bit_set(IIC_GPIO_PORT, IIC_SCL_PIN) #define IIC_SCL_LOW() gpio_bit_reset(IIC_GPIO_PORT, IIC_SCL_PIN) #define IIC_SDA_HIGH() gpio_bit_set(IIC_GPIO_PORT, IIC_SDA_PIN) #define IIC_SDA_LOW() gpio_bit_reset(IIC_GPIO_PORT, IIC_SDA_PIN) #define IIC_SDA_READ() gpio_input_bit_get(IIC_GPIO_PORT, IIC_SDA_PIN) #define IIC_ACK 0 #define IIC_NACK 1 void iic_gpio_init(void) { rcu_periph_clock_enable(IIC_GPIO_CLK); gpio_mode_set(IIC_GPIO_PORT, GPIO_MODE_OUTPUT_OD, GPIO_PUPD_PULLUP, IIC_SCL_PIN); gpio_output_options_set(IIC_GPIO_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, IIC_SCL_PIN); gpio_mode_set(IIC_GPIO_PORT, GPIO_MODE_OUTPUT_OD, GPIO_PUPD_PULLUP, IIC_SDA_PIN); gpio_output_options_set(IIC_GPIO_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, IIC_SDA_PIN); IIC_SCL_HIGH(); IIC_SDA_HIGH(); } void iic_delay_us(uint32_t us) { uint32_t i, j; for (i = 0; i < us; i++) for (j = 0; j < 8; j++); } void iic_start(void) { IIC_SDA_HIGH(); IIC_SCL_HIGH(); iic_delay_us(4); IIC_SDA_LOW(); iic_delay_us(4); IIC_SCL_LOW(); } void iic_stop(void) { IIC_SDA_LOW(); IIC_SCL_HIGH(); iic_delay_us(4); IIC_SDA_HIGH(); iic_delay_us(4); } uint8_t iic_wait_ack(void) { uint8_t ack; IIC_SDA_HIGH(); iic_delay_us(1); IIC_SCL_HIGH(); iic_delay_us(1); ack = IIC_SDA_READ(); IIC_SCL_LOW(); return ack; } void iic_send_byte(uint8_t byte) { uint8_t i; for (i = 0; i < 8; i++) { if (byte & 0x80) IIC_SDA_HIGH(); else IIC_SDA_LOW(); byte <<= 1; iic_delay_us(1); IIC_SCL_HIGH(); iic_delay_us(1); IIC_SCL_LOW(); iic_delay_us(1); } } uint8_t iic_receive_byte(void) { uint8_t i, byte = 0; IIC_SDA_HIGH(); for (i = 0; i < 8; i++) { byte <<= 1; IIC_SCL_HIGH(); iic_delay_us(1); if (IIC_SDA_READ()) byte |= 0x01; IIC_SCL_LOW(); iic_delay_us(1); } return byte; } uint8_t iic_write_byte(uint8_t dev_addr, uint8_t reg_addr, uint8_t data) { iic_start(); iic_send_byte(dev_addr); if (iic_wait_ack() == IIC_NACK) { iic_stop(); return 1; } iic_send_byte(reg_addr); if (iic_wait_ack() == IIC_NACK) { iic_stop(); return 1; } iic_send_byte(data); if (iic_wait_ack() == IIC_NACK) { iic_stop(); return 1; } iic_stop(); return 0; } uint8_t iic_read_byte(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data) { iic_start(); iic_send_byte(dev_addr); if (iic_wait_ack() == IIC_NACK) { iic_stop(); return 1; } iic_send_byte(reg_addr); if (iic_wait_ack() == IIC_NACK) { iic_stop(); return 1; } iic_start(); iic_send_byte(dev_addr | 0x01); if (iic_wait_ack() == IIC_NACK) { iic_stop(); return 1; } *data = iic_receive_byte(); iic_stop(); return 0; } ``` 该例程中,包含了 IIC 总线初始化、延时、启动、停止、等待应答、发送字节和接收字节等基本操作函数。其中,启动和停止函数为 IIC 总线上的起始和停止信号,等待应答函数为等待从设备的应答信号,发送字节和接收字节函数为 IIC 总线上的数据通信操作。在发送和接收数据时,需要先发送从设备地址和寄存器地址,再发送或接收数据。需要注意的是,在发送或接收数据时,需要等待从设备的应答信号,以判断通信是否成功。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值