【技术分享】GD32硬件I2C调试中的问题与解决过程-续

2023-05-16

使用GD32303C-EVAL开发板和MPL3115A2模块测量气压或高度数据,两者间使用硬件I2C进行通讯。
上次调试发现官方例程(单一I2C读写功能)可以正常读写MPL芯片的寄存器,而我建立的由官方例程融合(多个功能模块组合)的工程无法正常读写,会造成程序卡死在一个地方。
上次定位到了I2C异常的原因是屏幕初始化函数exmc_lcd_init()导致的。
为了再次确认确实是这个函数导致的问题,再进行一次反向验证:在有问题的工程里,将I2C的操作地址修改为0xA0(EEPROM的操作地址),进行同样的验证。
验证结果一致,即注释掉屏幕初始化函数exmc_lcd_init()即可正常读写,加上这个函数就会出问题。
由此,已经从各方面印证了问题就是出在屏幕初始化函数exmc_lcd_init()这里。
那么这个函数里都有什么呢?
/*!



    \brief      lcd peripheral initialize



    \param[in]  none



    \param[out] none



    \retval     none



*/



void exmc_lcd_init(void)



{



    exmc_norsram_parameter_struct lcd_init_struct;



    exmc_norsram_timing_parameter_struct lcd_timing_init_struct;



 



    /* EXMC clock enable */



    rcu_periph_clock_enable(RCU_EXMC); // EXMC初始化语句1



 



    /* GPIO clock enable */



    rcu_periph_clock_enable(RCU_GPIOD);



    rcu_periph_clock_enable(RCU_GPIOE);



 



    /* configure EXMC_D[0~15]*/



    /* PD14(EXMC_D0), PD15(EXMC_D1),PD0(EXMC_D2), PD1(EXMC_D3), PD8(EXMC_D13), PD9(EXMC_D14), PD10(EXMC_D15) */



    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1| GPIO_PIN_8 | GPIO_PIN_9 |



                                                         GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15);



 



    /* PE7(EXMC_D4), PE8(EXMC_D5), PE9(EXMC_D6), PE10(EXMC_D7), PE11(EXMC_D8), PE12(EXMC_D9),



       PE13(EXMC_D10), PE14(EXMC_D11), PE15(EXMC_D12) */



    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 |



                                                         GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |



                                                         GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);



 



    /* configure PE2(EXMC_A23) */



    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);



    



    /* configure NOE and NWE */



    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4 | GPIO_PIN_5);



 



    /* configure EXMC NE0 */



    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);



 



    lcd_timing_init_struct.asyn_access_mode = EXMC_ACCESS_MODE_A;



    lcd_timing_init_struct.syn_data_latency = EXMC_DATALAT_2_CLK;



    lcd_timing_init_struct.syn_clk_division = EXMC_SYN_CLOCK_RATIO_DISABLE;



    lcd_timing_init_struct.bus_latency = 2;



    lcd_timing_init_struct.asyn_data_setuptime = 18;



    lcd_timing_init_struct.asyn_address_holdtime = 3;



    lcd_timing_init_struct.asyn_address_setuptime = 8;



 



    lcd_init_struct.norsram_region = EXMC_BANK0_NORSRAM_REGION0;



    lcd_init_struct.write_mode = EXMC_ASYN_WRITE;



    lcd_init_struct.extended_mode = DISABLE;



    lcd_init_struct.asyn_wait = DISABLE;



    lcd_init_struct.nwait_signal = DISABLE;



    lcd_init_struct.memory_write = ENABLE;



    lcd_init_struct.nwait_config = EXMC_NWAIT_CONFIG_BEFORE;



    lcd_init_struct.wrap_burst_mode = DISABLE;



    lcd_init_struct.nwait_polarity = EXMC_NWAIT_POLARITY_LOW;



    lcd_init_struct.burst_mode = DISABLE;



    lcd_init_struct.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;



    lcd_init_struct.memory_type = EXMC_MEMORY_TYPE_SRAM;



    lcd_init_struct.address_data_mux = DISABLE;



    lcd_init_struct.read_write_timing = &lcd_timing_init_struct;



    lcd_init_struct.write_timing = &lcd_timing_init_struct;



 



    exmc_norsram_init(&lcd_init_struct); // EXMC初始化语句2



 



    exmc_norsram_enable(EXMC_BANK0_NORSRAM_REGION0); // EXMC初始化语句3



}

可以看到,程序里有EXMC模块的初始化和屏幕驱动使用的GPIO端口的初始化。
EXMC是什么呢?这个是外部存储器控制器。GD32F30x用户手册上关于该模块的简介是这样的:
外部存储器控制器EXMC,用来访问各种片外存储器,通过配置寄存器,EXMC可以把AMBA协议转换为专用的片外存储器通信协议,包括SRAM,ROM,NOR Flash,NAND Flash,PC卡。用户还可以调整配置寄存器中的时间参数来提高通信效率。EXMC的访问空间被划分为许多个块(Bank),每个块支持特定的存储器类型,用户可以通过对Bank的控制寄存器配置来控制外部存储器。
这个模块应该是用来驱动TFT屏的驱动芯片的,通过EXMC模块将需要显示的数据(字符或图片、颜色、位置等信息)写入到TFT屏的驱动芯片里,类似操作外部存储器一样。

首先确认是哪一个初始化步骤影响到了I2C通讯。
先屏蔽掉EXMC相关的三个初始化语句:前面的EXMC时钟初始化语句和最后的两条EXMC初始化语句。
void exmc_lcd_init(void)



{



    exmc_norsram_parameter_struct lcd_init_struct;



    exmc_norsram_timing_parameter_struct lcd_timing_init_struct;



 



    /* EXMC clock enable */



//    rcu_periph_clock_enable(RCU_EXMC); // EXMC初始化语句1



 



/*    省略    */



 



//    exmc_norsram_init(&lcd_init_struct); // EXMC初始化语句2



 



//    exmc_norsram_enable(EXMC_BANK0_NORSRAM_REGION0); // EXMC初始化语句3



}
屏蔽掉这三个语句之后进行调试验证,确认I2C可以正常读写。
由此可以确认I2C读写故障是由EXMC模块的初始化导致,而不是exmc_lcd_init()函数里的GPIO初始化部分导致的。

下面再来一一验证这三句EXMC初始化代码具体是哪一句造成的异常结果。
先取消EXMC初始化语句1:即rcu_periph_clock_enable(RCU_EXMC);的注释,然后进行调试验证,发现问题复现了。
为了确认,再次注释语句1,然后进行调试验证,果然又恢复正常了。
反复两到三次,可确认该语句会导致I2C异常复现。

同样的方式对后面两个语句进行验证,发现并不会导致I2C异常复现。
于是将后面两个语句取消注释,再次对语句1进行验证,确认了只是该语句导致的I2C读写异常,其他语句均不会影响I2C读写操作。

这句代码是起到什么作用呢?这个代码是官方库文件,用来启动外设模块的时钟的。
/*!



    \brief      enable the peripherals clock



    \param[in]  periph: RCU peripherals, refer to rcu_periph_enum



                only one parameter can be selected which is shown as below:



      \arg        RCU_GPIOx (x=A,B,C,D,E,F,G): GPIO ports clock



      \arg        RCU_AF : alternate function clock



      \arg        RCU_CRC: CRC clock



      \arg        RCU_DMAx (x=0,1): DMA clock



      \arg        RCU_ENET: ENET clock(CL series available)



      \arg        RCU_ENETTX: ENETTX clock(CL series available)



      \arg        RCU_ENETRX: ENETRX clock(CL series available)



      \arg        RCU_USBD: USBD clock(HD,XD series available)



      \arg        RCU_USBFS: USBFS clock(CL series available)



      \arg        RCU_EXMC: EXMC clock



      \arg        RCU_TIMERx (x=0,1,2,3,4,5,6,7,8,9,10,11,12,13,TIMER8..13 are not available for HD series): TIMER clock



      \arg        RCU_WWDGT: WWDGT clock



      \arg        RCU_SPIx (x=0,1,2): SPI clock



      \arg        RCU_USARTx (x=0,1,2): USART clock



      \arg        RCU_UARTx (x=3,4): UART clock



      \arg        RCU_I2Cx (x=0,1): I2C clock



      \arg        RCU_CANx (x=0,1,CAN1 is only available for CL series): CAN clock



      \arg        RCU_PMU: PMU clock



      \arg        RCU_DAC: DAC clock



      \arg        RCU_RTC: RTC clock



      \arg        RCU_ADCx (x=0,1,2,ADC2 is not available for CL series): ADC clock



      \arg        RCU_SDIO: SDIO clock(not available for CL series)



      \arg        RCU_CTC: CTC clock



      \arg        RCU_BKPI: BKP interface clock



    \param[out] none



    \retval     none



*/



void rcu_periph_clock_enable(rcu_periph_enum periph)



{



    RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph));



}
通过传入的参数,可以知道这个语句是使能EXMC模块的时钟的。
再来进行深度验证一下。
exmc_lcd_init()函数里rcu_periph_clock_enable(RCU_EXMC);语句的位置更换为rcu_periph_clock_disable(RCU_EXMC);语句,发现I2C读写正常。
恢复exmc_lcd_init()函数里的内容(即正常运行EXMC时钟使能语句),同时在main()函数里exmc_lcd_init()后面增加rcu_periph_clock_disable(RCU_EXMC);语句,即按如下操作:
i2c_gpio_config(); /* configure GPIO */



    i2c_config(); /* configure I2C */



    i2c_eeprom_init(); /* initialize EEPROM  */



 



    exmc_lcd_init();    /* configure the EXMC access mode */



    delay_1ms(50);



    rcu_periph_clock_disable(RCU_EXMC);



    MPL_Init();
再次进行验证,发现I2C读写操作正常。
结论如下:无论之前是否操作过EXMC模块的时钟使能寄存器,只要最终是关闭状态,I2C读写就是正常的;而只要是打开状态,I2C读写流程就会异常。

EXMC模块的时钟为什么会影响到I2C的读写操作呢?估计要到用户手册上去查找问题,或者联系GD32的FAE了。
先来查看一下用户手册。
GD32F303的系统架构图如下:

可以发现EXMC模块使用的总线是AHB,和I2C模块(使用总线为APB1)并没有在一个区域,应该不是总线问题导致的互相影响。

再来看一下GD32F303的时钟单元。下图是GD32F303的时钟树结构:

可以看到,系统时钟CK_SYS先经过了AHB预分频器,然后给到EXMC模块的时钟CK_EXMC;同时AHB预分频器之后还给到APB1预分频器,然后才给到PCLK1,提供给APB1总线上的外设使用。

难道是EXMC模块的时钟使能操作影响到了AHB时钟的分频,导致I2C总线的传输速度收到影响,从而导致数据读写操作失败?

从理论上来讲,应该不会发生这种情况的,后端的时钟使用与否不会影响到前面分频器的工作。

继续详细查看用户手册ing。

忽然间,在芯片手册里,发现了GPIO引脚定义表格,发现PB7引脚的第二功能同时具有I2C0_SDA功能和EXMC_NADV功能!

难道是引脚冲突了?EXMC_NADV功能只能用PB7,而例程里的I2C0初始化部分,也确实用了PB7。

/*!



    \brief      configure the GPIO ports



    \param[in]  none



    \param[out] none



    \retval     none



*/



void i2c_gpio_config(void)



{



    /* 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_PIN_7);



}
验证一下看看。
 
这个怎么验证呢?通过remap功能,将I2C0引脚重定义到其他引脚上,再去验证两者的功能是否正常。
先看一下I2C0引脚可以重定义到什么引脚上。
可以看到,I2C0的两个引脚可以通过remap重定义到PB8和PB9上。
修改I2C引脚初始化函数(即i2c_gpio_config()函数),增加I2C0引脚remap,启动AF时钟,同时将引脚初始化改为PB8和PB9。
void i2c_gpio_config(void)



{



    /* enable GPIOB clock */



    rcu_periph_clock_enable(RCU_GPIOB);



    /* enable I2C0 clock */



    rcu_periph_clock_enable(RCU_I2C0);



    rcu_periph_clock_enable(RCU_AF);



 



    /* connect PB8 to I2C0_SCL */



    /* connect PB9 to I2C0_SDA */



    gpio_pin_remap_config(GPIO_I2C0_REMAP, ENABLE);



    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_8 | GPIO_PIN_9);



}
由于开发板上PB8和PB9没有使用,没加上拉电阻,所以需要焊两颗4.7K的电阻上拉到3.3V,同时将杜邦线转移到PB8和PB9上。

然后调试验证,I2C读写正常,屏幕显示正常。

 

至此,硬件I2C读写异常问题完美解决。

导致问题出现的原因是开发板在不同模块的例程上都用到了PB7,从而导致两个功能无法同时使用。

进行引脚功能remap即可解决该问题。
---------------------
作者:blust5
链接:https://bbs.21ic.com/icview-3300846-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【技术分享】GD32硬件I2C调试中的问题与解决过程-续 的相关文章

随机推荐

  • pwm超详细解读,大佬细说pwm的控制方式

    pwm xff0c 也就是常说的脉冲宽度调制脉冲宽度调制技术 目前 xff0c pwm技术的应用是非常多的 为了增进大家对pwm的认识 xff0c 本文将对pwm 的几种控制方法进行详细的阐述 xff0c 保证大家彻底理解 如果你对pwm
  • WiFi信号覆盖面积小?如何扩大Wifi信号覆盖范围?

    WiFi已是现代生活不可缺少的组成之一 xff0c 在WiFi的帮助下 xff0c 一个家庭网络或办公网络 xff0c 能够通过无线的方法连接诸多设备 为增进大家对WiFi的认识 xff0c 本文将对WiFi以及扩大WiFi信号覆盖范围的方
  • 数据存储方式有哪些?这3种数据存储方式了解吗?

    数据存储具有很强的现实意义 xff0c 只有采取合理的数据存储方式 xff0c 才能够有利于数据的管理 检索等 为增进大家对数据存储方式的认识 xff0c 本文将对三种数据存储方式予以介绍 如果你对数据存储方式具有兴趣 xff0c 不妨继续
  • 这里带你了解IR2104驱动电路

    Ir2014驱动电路及自举电容的应用 以前做智能车的时候用H桥驱动电路 xff0c 驱动芯片就是用的IR2104 xff0c MOS管用的IR7843 xff0c 想把以前的知识总结一下 1 为什么需要H桥电路 因为驱动电机正反转 xff0
  • 什么是神经网络模型,常见神经网络模型有哪些?

    神经网络模型是机器学习 深度学习的核心 xff0c 针对不同的问题 xff0c 我们需要搭建不同的神经网络模型 为增进大家对神经网络模型的认识 xff0c 本文将对常见的神经网络模型予以介绍 如果你对神经网络模型具有兴趣 xff0c 不妨继
  • STM32L431使用TencentOS tiny操作系统连接腾讯物联网开发平台

    去年腾讯为了宣传自己针对物联网的开源操作系统TencentOS tiny xff0c 举办了一场试用活动 xff0c 当时幸运获得一块 板子实物就这样 xff0c 没啥好介绍的 xff0c 都是基础的外设 xff0c 如今闲来无事 xff0
  • [总线技术]汽车ECU网络传输-诊断协议测试-基于Canoe软件CAPL脚本编写

    一 xff0e 汽车电子电控结构 先介绍一下汽车电子电控的结构 xff0c 汽车并不像电脑一样 xff0c 只有一个CPU xff0c 而是分为多个 CPU 独立工作 xff0c 就是由很多很多个控制器组成的 xff0c 比如 xff1a
  • [STM32F1]关于STM32F103芯片按钮机制串口发送程序

    就是目前遇到一个项目 xff0c 是监视各个按钮状态并将各个按钮的状态发送至上位机 xff0c 上位机进行整体程序逻辑动作 一般情况下串口发送可以直接一直发送给上位机 xff0c 通过解析不同数据位0和1的状态可以知道具体哪个按钮按下 xf
  • [uCOS/RTOS]freertos——创建任务(一)

    FreeRTOS操作系统学习 前言 FreeRTOS操作系统的学习正式开始 一 了解FreeRTOS FreeRTOS共有32个优先级 xff08 0 31 xff09 使用时0和31不使用 优先级规则 xff1a 数字越大优先级越高 任务
  • 基于FreeRTOS的UART空闲中断框架设计

    设计背景 xff1a 针对大部分国产低端MCU ARM CortexM0 来说 xff0c 并没有空闲中断 xff0c 此时就需要一个定时器Timer配合来完成此任务 对于UART接受不定长数据 xff0c 空闲中断还是非常实用的 xff0
  • 2、可迭代对象与迭代器

    1 Iterable 可迭代对象 概念 xff1a python中能够使用for循环遍历的都是可迭代对象 1 常见的可迭代对象 1 1 序列如 xff1a list str tuple range 1 2 非序列 xff1a dict se
  • 帮你分清嵌入式与单片机

    从事计算机和或电子行业相关领域工作的朋友 xff0c 一般都听说过单片机和嵌入式 但是要问单片机和嵌入式两者之间有什么联系 xff0c 大多数人都不能很好的解释清楚 想要弄清楚嵌入式和单片机有什么联系 xff0c 首先就要弄明白什么是嵌入式
  • [MM32生态]Python,让嵌入式应用开发更便捷、更高效、更专注

    前言 前面分享了基于PikaScript如何在MM32平台上部署Python开发环境的帖子 xff0c 实现了Python基础开发环境的部署 xff0c 可以通过串口终端软件在线编写Python xff0c 然后直接运行得到结果 通过Pyt
  • [STM32]STM32移植freemodbus实现modbusTCP

    上次说到采用STM32F1移植了FreeModbus协议栈进行开发实现ModBus RTU协议来进行一些线圈寄存器的控制 xff08 继电器开关 xff09 和一些保持寄存器的读写 xff08 参数配置灯 xff09 xff0c 这次说一下
  • FR8012HAQ利用ADC实现检测电池电压检测的解决方案

    今天要跟大家分享的是FR8012HAQ利用ADC实现检测电池电压检测的解决方案 FR8012HAQ是富芮坤的一款通用蓝牙芯片 特性介绍如下图 xff1a 我们再来看FR8012HAQ的PMU xff0c 它强大的地方还在于内置了充电模块 F
  • [单片机芯片]CH32V307驱动单总线温湿度传感器DHT22

    手头有一个DHT22温湿度传感器和CH32V307开发板 xff0c 可玩性极强 DHT22是已校准的数字温湿度传感器 xff0c 用于检测环境温湿度 xff0c 采用DHT22 AM2302 xff0c 标准单总线接口 拥有比常见的DHT
  • RT_Thread好用吗? RT_Thread成国内最成熟开源RTOS?

    RT Thread 是一款主要由中国开源社区主导开发的开源实时操作系统 许可证GPLv2 实时线程操作系统不仅仅是一个单一的实时操作系统内核 xff0c 它也是一个完整的应用系统 xff0c 包含了实时 嵌入式系统相关的各个组件 xff1a
  • [技术讨论]知识科普のARM和STM32之间的纠葛

    一 ARM和STM32的关系 ARM和STM32是两个不同的概念 xff0c ARM是一家英国公司 xff0c 专注于设计和许可处理器架构 xff0c 而STM32是ST公司基于ARM Cortex M内核的一系列微控制器产品 ARM Co
  • 嵌入式经历了哪些发展阶段?这些嵌入式法则你都了解吗?

    为增进大家对嵌入式的认识 xff0c 本文将对嵌入式发展阶段以及嵌入式中的一些法则予以介绍 嵌入式已经是现在的主流系统以及开发手段之一 xff0c 嵌入式工程师更是占据了一席之地 为增进大家对嵌入式的认识 xff0c 本文将对嵌入式发展阶段
  • 【技术分享】GD32硬件I2C调试中的问题与解决过程-续

    使用GD32303C EVAL开发板和MPL3115A2模块测量气压或高度数据 xff0c 两者间使用硬件I2C进行通讯 上次调试发现官方例程 xff08 单一I2C读写功能 xff09 可以正常读写MPL芯片的寄存器 xff0c 而我建立