GD32F407之硬件IIC(主机模式)

2024-02-08 15:40

本文主要是介绍GD32F407之硬件IIC(主机模式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

IIC总线应用真的太广泛了,介绍什么的就不说了。

GD32F407真的太像STM32F407了,之前用STM32的时候网上都说硬件IIC不稳定容易死机,不过没有真正在项目中使用过,主要官方给的源码都是while结构的,如何敢用,简直了。

最近项目用到了GD32F407的三组IIC,而且还有一组作为从机模式,如果用模拟GPIO方式来做从机之前真没有玩过,网上看资料不多(主要看有没现成拿来用,哈哈),那就都用硬件方式,咨询过GD的工程师完全可以用硬件来做项目的。但是非常遗憾的事GD的源码也是while结构的,单独运行起来杠杠的,但是能直接放到项目嘛,当然不行,那就直接修改了。下面记录修改过程,及遇到问题

一、下面是硬件IIC作为主机模式发送接受软件流程图

Datasheet上的主机发送 接收模式的软件流程图,中文版太给力了,这是第一次认真去分析每一步如何发送数据该如何设置标志和清除标志

主机发送模式下的软件流程

主机接受模式,A和B都差不多,

非常详细介绍了每一步应该如何判断哪个标志位清除哪个标志位,我们照着来就好了。

二、首先看看官方硬件接收源码:

/*!\brief      write more than one byte to the EEPROM with a single write cycle\param[in]  p_buffer: pointer to the buffer containing the data to be written to the EEPROM\param[in]  write_address: EEPROM's internal address to write to\param[in]  number_of_byte: number of bytes to write to the EEPROM\param[out] none\retval     none
*/
void eeprom_page_write(uint8_t* p_buffer, uint8_t write_address, uint8_t number_of_byte)
{/* wait until I2C bus is idle */while(i2c_flag_get(I2C0, I2C_I2CBSY));/* send a start condition to I2C bus */i2c_start_on_bus(I2C0);/* wait until SBSEND bit is set */while(!i2c_flag_get(I2C0, I2C_SBSEND));/* send slave address to I2C bus */i2c_master_addressing(I2C0, eeprom_address, I2C_TRANSMITTER);/* wait until ADDSEND bit is set */while(!i2c_flag_get(I2C0, I2C_ADDSEND));/* clear the ADDSEND bit */i2c_flag_clear(I2C0,I2C_STAT0_ADDSEND);/* wait until the transmit data buffer is empty */while( SET != i2c_flag_get(I2C0, I2C_TBE));/* send the EEPROM's internal address to write to : only one byte address */i2c_transmit_data(I2C0, write_address);/* wait until BTC bit is set */while(!i2c_flag_get(I2C0, I2C_BTC));/* while there is data to be written */while(number_of_byte--){  i2c_transmit_data(I2C0, *p_buffer);/* point to the next byte to be written */p_buffer++; /* wait until BTC bit is set */while(!i2c_flag_get(I2C0, I2C_BTC));}/* send a stop condition to I2C bus */i2c_stop_on_bus(I2C0);/* wait until the stop condition is finished */while(I2C_CTL0(I2C0)&0x0200);
}

都是while的结构,如果有一个地方卡主那就死机了,所以下面将它修改成不用while模式。用硬件IIC最好用中断模式,即在硬件标志了就会产生中断这个时候可你以处理数据。

下面是一个主机读写开始的一个函数,不管读数据还是写数据都是主机发送起始信号,

void Mi2c_RW_Start(BYTE CountByte,uint32_t i2c_periph,BYTE Channel)
{//DEBUG(0x05);if((!i2c_flag_get(i2c_periph, I2C_I2CBSY)) && (Start_Flag[Channel] == 1)){i2c_interrupt_enable(i2c_periph, I2C_CTL1_ERRIE | I2C_CTL1_BUFIE | I2C_CTL1_EVIE);I2C_nBytes[Channel] = CountByte;if(2 == I2C_nBytes[Channel]){/* send ACK for the next byte */i2c_ackpos_config(i2c_periph, I2C_ACKPOS_NEXT);}/* send a start condition to I2C bus */i2c_start_on_bus(i2c_periph);Start_Flag[Channel] = 0;Start_Flag2[Channel] =0;} 
}

 下面是一个读写数据选择,这个函数应该放在中断处理函数里面

void Mi2c_RW_Data(BYTE Channel)
{//DEBUG(0x16);if(1 == (M_tran_buffer[Channel][0]>>7)){//receiverMi2c_read_data(Channel);}else{//transmitter//DEBUG(0x37);Mi2c_write_Data(Channel);}
}

中断处理函数

void I2C2_EV_IRQHandler(void)
{Mi2c_RW_Data(2);
}

中断错误函数,非常重要

void I2C2_ER_IRQHandler(void)
{    if(i2c_flag_get(I2C2, I2C_AERR)){/* no acknowledge received */i2c_flag_clear(I2C2, I2C_AERR);//ErrorFlag += 1;//ErrorDebug(2,ErrorFlag);          OEM_Cheack_Error(2);}   if(i2c_flag_get(I2C2, I2C_SMBALT)){/* SMBus alert */i2c_flag_clear(I2C2, I2C_SMBALT);}   if(i2c_flag_get(I2C2, I2C_SMBTO)){/* bus timeout in SMBus mode */i2c_flag_clear(I2C2, I2C_SMBTO);}  if(i2c_flag_get(I2C2, I2C_OUERR)){/* over-run or under-run when SCL stretch is disabled */i2c_flag_clear(I2C2, I2C_OUERR);}    if(i2c_flag_get(I2C2, I2C_LOSTARB)){/* arbitration lost */i2c_flag_clear(I2C2, I2C_LOSTARB);} if(i2c_flag_get(I2C2, I2C_BERR)){ /* bus error */i2c_flag_clear(I2C2, I2C_BERR);}   if(i2c_flag_get(I2C2, I2C_PECERR)){/* CRC value doesn't match */i2c_flag_clear(I2C2, I2C_PECERR);}/* disable the error interrupt */i2c_interrupt_disable(I2C2,I2C_CTL1_ERRIE | I2C_CTL1_BUFIE | I2C_CTL1_EVIE);
}

测试过程中发现会出现(I2C_AERR)NO ACK的报错,导致卡机,增加一个 NO ACk的处理函数,做一下清标志位

void OEM_Cheack_Error(BYTE Channel)
{//检测从机没有ACK回应后处理OEM_TRAN_Timer[Channel] = 0;i2c_stop_on_bus( Channel_buffer[Channel][0]);/* enable acknowledge */i2c_ack_config( Channel_buffer[Channel][0], I2C_ACK_ENABLE);i2c_ackpos_config( Channel_buffer[Channel][0], I2C_ACKPOS_CURRENT);Start_Flag[Channel] = 1;Start_Flag2[Channel] = 0
}

我们是将读写函数整合到一起了,所以上面的函数都是为读写函数做准备的。

写数据函数

void Mi2c_write_Data(BYTE Channel)
{//DEBUG(0X17);//第一部分发送从机地址和寄存器地址if(Start_Flag2[Channel] == 0){if(i2c_flag_get( Channel_buffer[Channel][0], I2C_SBSEND)){/* send slave address to I2C bus */i2c_master_addressing( Channel_buffer[Channel][0], M_tran_buffer[Channel][1], I2C_TRANSMITTER);            }else if(i2c_flag_get( Channel_buffer[Channel][0], I2C_ADDSEND)){i2c_flag_clear( Channel_buffer[Channel][0],I2C_STAT0_ADDSEND);}else if(i2c_flag_get(  Channel_buffer[Channel][0] , I2C_TBE )){/* send the register address to write to : only one byte address */i2c_transmit_data( Channel_buffer[Channel][0],M_tran_buffer[Channel][2]);Start_Flag2[Channel] = 1;               Write_count[Channel] = I2C_nBytes[Channel];}}//写入数据if(Start_Flag2[Channel] == 1){//DEBUG(0X19);if(i2c_flag_get( Channel_buffer[Channel][0], I2C_BTC)){if(I2C_nBytes[Channel]>0){i2c_transmit_data( Channel_buffer[Channel][0], M_i2c_Txbuffer[Channel][Write_count[Channel] - I2C_nBytes[Channel]]);I2C_nBytes[Channel]--;  }else if(0 == I2C_nBytes[Channel]){ OEM_TRAN_Timer[Channel] = 0;i2c_stop_on_bus( Channel_buffer[Channel][0]);/* enable acknowledge */i2c_ack_config( Channel_buffer[Channel][0], I2C_ACK_ENABLE);i2c_ackpos_config( Channel_buffer[Channel][0], I2C_ACKPOS_CURRENT);Start_Flag[Channel] = 1;Start_Flag2[Channel] = 0;//DEBUG(0x85);i2c_interrupt_disable( Channel_buffer[Channel][0], I2C_CTL1_ERRIE | I2C_CTL1_BUFIE | I2C_CTL1_EVIE);}           }}
}

可以看到还是相当的复杂,主要是将读写做成了统一架构的模式,所以很多都是为了更好地封装加上的,下面介绍读数据

读数据比写数据要复杂一点,

官方源代码

/*!\brief      read data from the EEPROM\param[in]  p_buffer: pointer to the buffer that receives the data read from the EEPROM\param[in]  read_address: EEPROM's internal address to start reading from\param[in]  number_of_byte: number of bytes to reads from the EEPROM\param[out] none\retval     none
*/
void eeprom_buffer_read(uint8_t* p_buffer, uint8_t read_address, uint16_t number_of_byte)
{  /* wait until I2C bus is idle */while(i2c_flag_get(I2C0, I2C_I2CBSY));if(2 == number_of_byte){i2c_ackpos_config(I2C0,I2C_ACKPOS_NEXT);}/* send a start condition to I2C bus */i2c_start_on_bus(I2C0);/* wait until SBSEND bit is set */while(!i2c_flag_get(I2C0, I2C_SBSEND));/* send slave address to I2C bus */i2c_master_addressing(I2C0, eeprom_address, I2C_TRANSMITTER);/* wait until ADDSEND bit is set */while(!i2c_flag_get(I2C0, I2C_ADDSEND));/* clear the ADDSEND bit */i2c_flag_clear(I2C0,I2C_STAT0_ADDSEND);/* wait until the transmit data buffer is empty */while(SET != i2c_flag_get( I2C0 , I2C_TBE ));/* enable I2C0*/i2c_enable(I2C0);/* send the EEPROM's internal address to write to */i2c_transmit_data(I2C0, read_address);  /* wait until BTC bit is set */while(!i2c_flag_get(I2C0, I2C_BTC));/* send a start condition to I2C bus */i2c_start_on_bus(I2C0);/* wait until SBSEND bit is set */while(!i2c_flag_get(I2C0, I2C_SBSEND));/* send slave address to I2C bus */i2c_master_addressing(I2C0, eeprom_address, I2C_RECEIVER);if(number_of_byte < 3){/* disable acknowledge */i2c_ack_config(I2C0,I2C_ACK_DISABLE);}/* wait until ADDSEND bit is set */while(!i2c_flag_get(I2C0, I2C_ADDSEND));/* clear the ADDSEND bit */i2c_flag_clear(I2C0,I2C_STAT0_ADDSEND);if(1 == number_of_byte){/* send a stop condition to I2C bus */i2c_stop_on_bus(I2C0);}/* while there is data to be read */while(number_of_byte){if(3 == number_of_byte){/* wait until BTC bit is set */while(!i2c_flag_get(I2C0, I2C_BTC));/* disable acknowledge */i2c_ack_config(I2C0,I2C_ACK_DISABLE);}if(2 == number_of_byte){/* wait until BTC bit is set */while(!i2c_flag_get(I2C0, I2C_BTC));/* send a stop condition to I2C bus */i2c_stop_on_bus(I2C0);}/* wait until the RBNE bit is set and clear it */if(i2c_flag_get(I2C0, I2C_RBNE)){/* read a byte from the EEPROM */*p_buffer = i2c_receive_data(I2C0);/* point to the next location where the byte read will be saved */p_buffer++; /* decrement the read bytes counter */number_of_byte--;} }/* wait until the stop condition is finished */while(I2C_CTL0(I2C0)&0x0200);/* enable acknowledge */i2c_ack_config(I2C0,I2C_ACK_ENABLE);i2c_ackpos_config(I2C0,I2C_ACKPOS_CURRENT);
}

比写数据起码多了一倍啊

修改后代码

void Mi2c_read_data(BYTE Channel)
{//ErrorDebug(Channel,0x12);//第一部分发送从机地址和寄存器地址if(Start_Flag2[Channel] == 0){if(i2c_flag_get(Channel_buffer[Channel][0], I2C_SBSEND)){/* send slave address to I2C bus */i2c_master_addressing(Channel_buffer[Channel][0], M_tran_buffer[Channel][1], I2C_TRANSMITTER);}else if(i2c_flag_get(Channel_buffer[Channel][0], I2C_ADDSEND)){/* clear the ADDSEND bit */i2c_flag_clear(Channel_buffer[Channel][0],I2C_STAT0_ADDSEND);}else if(i2c_flag_get( Channel_buffer[Channel][0] , I2C_TBE )){i2c_enable(Channel_buffer[Channel][0]);/* send the register address to write to : only one byte address */i2c_transmit_data(Channel_buffer[Channel][0], M_tran_buffer[Channel][2]);Start_Flag2[Channel] = 1;}}//需要转换模式,所以重新启动IIC到接收模式if(Start_Flag2[Channel] == 1){if(i2c_flag_get(Channel_buffer[Channel][0], I2C_BTC)){//DEBUG(0x74);i2c_start_on_bus(Channel_buffer[Channel][0]);Start_Flag2[Channel] = 2;}}//第二部分接收数据if(Start_Flag2[Channel] == 2){          if(i2c_flag_get(Channel_buffer[Channel][0], I2C_SBSEND)){/* the master sends slave address */i2c_master_addressing(Channel_buffer[Channel][0], M_tran_buffer[Channel][1], I2C_RECEIVER);}else if(i2c_flag_get(Channel_buffer[Channel][0], I2C_ADDSEND)){if(I2C_nBytes[Channel] < 3){/* clear the ACKEN before the ADDSEND is cleared */i2c_ack_config(Channel_buffer[Channel][0], I2C_ACK_DISABLE);/* clear the ADDSEND bit */i2c_flag_clear(Channel_buffer[Channel][0],I2C_STAT0_ADDSEND);}else{/* clear the ADDSEND bit */i2c_flag_clear(Channel_buffer[Channel][0],I2C_STAT0_ADDSEND);}if(1 == I2C_nBytes[Channel]){i2c_stop_on_bus(Channel_buffer[Channel][0]);}   Read_count[Channel] = I2C_nBytes[Channel];               }else if(i2c_flag_get(Channel_buffer[Channel][0], I2C_RBNE)){//DEBUG(0x83);if(I2C_nBytes[Channel]>0){// if(3 == I2C_nBytes[Channel]){//      i2c_ack_config(Channel_buffer[Channel][0],I2C_ACK_DISABLE);// }if(I2C_nBytes[Channel] == 2){   i2c_ack_config(Channel_buffer[Channel][0],I2C_ACK_DISABLE);i2c_stop_on_bus(Channel_buffer[Channel][0]);}/* read a data byte from I2C_DATA*/M_i2c_Rxbuffer[Channel][Read_count[Channel]-I2C_nBytes[Channel]] = i2c_receive_data(Channel_buffer[Channel][0]);I2C_nBytes[Channel]--;if(0 == I2C_nBytes[Channel]){   OEM_TRAN_Timer[Channel] = 0;i2c_stop_on_bus(Channel_buffer[Channel][0]);i2c_ack_config(Channel_buffer[Channel][0], I2C_ACK_ENABLE);i2c_ackpos_config(Channel_buffer[Channel][0], I2C_ACKPOS_CURRENT);Start_Flag[Channel] = 1;Start_Flag2[Channel] = 0;//DEBUG(0x85);i2c_interrupt_disable(Channel_buffer[Channel][0], I2C_CTL1_ERRIE | I2C_CTL1_BUFIE | I2C_CTL1_EVIE);}}}}
}

读数据分两步,

第一步先作为写(发送)数据模式。给从机写入从机地址和寄存器地址

i2c_master_addressing(Channel_buffer[Channel][0], M_tran_buffer[Channel][1], I2C_TRANSMITTER);

第二步,第一部完后重新启动IIC为接收数据模式kn

i2c_master_addressing(Channel_buffer[Channel][0], M_tran_buffer[Channel][1], I2C_RECEIVER);

一个写WORD数据的应用函数

void SMB_wrWORD(BYTE Channel, BYTE SMB_SLAVE, BYTE SMB_COMMAND, WORD wData,BYTE RETRY_CNT)
{/* SMB_SLAVE: Slave address.RETRY_CNT: Number of times to attempt an SMBus transfer.Must be >= 1 and <= 3.  The RETRY_CNT is notthe number of retries but is the number ofSMBus transfer attempts.FLAG == TRUE  -> SMBus write word is ok.== FALSE -> SMBus write word failed. */Channel_buffer[Channel][0] = Channel_Select(Channel);    M_tran_buffer[Channel][0]    = SMB_WRITE_WORD;   /* Protocol byte. */M_tran_buffer[Channel][1]    = SMB_SLAVE & ~0x01;  /* Address and read/write = 0 for write. */M_tran_buffer[Channel][2]    = SMB_COMMAND;      /* SMBus command. */M_i2c_Txbuffer[Channel][0]   = (BYTE) (wData & 0xFF);M_i2c_Txbuffer[Channel][1]   = (BYTE) ((wData >> 8) & 0xFF);//   /* Number of attempts for SMBus transfer. *///   attempts[Channel] = RETRY_CNT;I2C_SADDR0(Channel_buffer[Channel][0]) |= M_tran_buffer[Channel][1];/* Set the timer to start the SMBus transfer. */OEM_TRAN_Timer[Channel] = START;Mi2c_RW_Start((M_tran_buffer[Channel][0]&0x03),Channel_buffer[Channel][0],Channel);
}

一个读WORD数据的应用函数

void SMB_rdWORD(BYTE Channel, BYTE SMB_SLAVE, BYTE SMB_COMMAND, BYTE RETRY_CNT)
{//DEBUG(0x15);/* SMB_SLAVE: Slave address.RETRY_CNT: Number of times to attempt an SMBus transfer.Must be >= 1 and <= 3.  The RETRY_CNT is notthe number of retries but is the number ofSMBus transfer attempts. */Channel_buffer[Channel][0] = Channel_Select(Channel);    M_tran_buffer[Channel][0]    = SMB_READ_WORD; /* Protocol byte. */M_tran_buffer[Channel][1]    = SMB_SLAVE;   /* Address and read/write = 0 for write. */M_tran_buffer[Channel][2]    = SMB_COMMAND; /* SMBus command. *///   /* Number of attempts for SMBus transfer. *///   attempts[Channel] = RETRY_CNT;I2C_SADDR0(Channel_buffer[Channel][0]) |= M_tran_buffer[Channel][1];/* Set the timer to start the SMBus transfer. */OEM_TRAN_Timer[Channel] = START;Mi2c_RW_Start((M_tran_buffer[Channel][0]&0x03),Channel_buffer[Channel][0],Channel);
}

这两个读写WORD函数放到轮询函数里面即可

文章主要点是将while去除的读写数据的两个函数,其它都是根据自己的项目来搭建框架的封装函数

GD32F407资料下载--http://www.gd32mcu.com/cn/download

eive

这篇关于GD32F407之硬件IIC(主机模式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/691485

相关文章

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

C#实现获取电脑中的端口号和硬件信息

《C#实现获取电脑中的端口号和硬件信息》这篇文章主要为大家详细介绍了C#实现获取电脑中的端口号和硬件信息的相关方法,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 我们经常在使用一个串口软件的时候,发现软件中的端口号并不是普通的COM1,而是带有硬件信息的。那么如果我们使用C#编写软件时候,如

如何安装HWE内核? Ubuntu安装hwe内核解决硬件太新的问题

《如何安装HWE内核?Ubuntu安装hwe内核解决硬件太新的问题》今天的主角就是hwe内核(hardwareenablementkernel),一般安装的Ubuntu都是初始内核,不能很好地支... 对于追求系统稳定性,又想充分利用最新硬件特性的 Ubuntu 用户来说,HWEXBQgUbdlna(Har

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)