GD32VF103 - I2C调试的坑

本文记录了一位工程师初次接触I2C总线的调试过程,包括遇到的问题和解决方法。从SPI转向I2C是因为资源限制,调试过程中发现示波器的重要性,以及在没有示波器时如何通过寄存器检查进行调试。作者强调了I2C操作的阻塞特性,并分享了如何检查状态寄存器和错误处理的经验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

I2C

第一次使用I2C总线,虽然之前已经了解过,大概知道这个总线是如何工作的,但没真正去实现过,一般器件都支持spi,调试和连接都简单明了,需要修改的代码也不多,多数能够从以前的代码拷过来,再对照一下官方的示例修改一下就能用。这回实在是spi总线不够用,如果每个器件都占用一个spi,需要四组spi才行,分cs的话,就要重新梳理两个器件之间的占用,纠结了一阵子,还是开始了I2C总线的调试之旅。

调试的准备

如果新手入坑,在购买示波器的时候,一定要买一个好用且精度高一些的。不然就会像我一样,根本捕捉不到I2C的信号,500ms下能轻松捕捉到,但是分辨率太差,没啥用,使用软件模拟I2C的时候,又要不停调试的调软件延时,而且达不到预想的精度。

做了一块demo板,但是没仔细审核,把I2C上拉电阻接地了,于是用跳线和夹子做的总线,结果扑街了,各种错误,后面用洞洞板焊了电路,跳线上去,它居然工作了。

I2C的精髓是阻塞

每个操作都需要等待硬件对相应的状态位置位,才能进行下一步的操作。
使用start信号后,要检查I2C_FLAG_SBSEND是否置位,要等多久才能检查到置位呢,不知道,所以需要死循环,但如果没有ACK,就卡在这里了。所以需要在死循环中检查是否发生N-ACK的错误,或者用定时回调查看是否发生了N-ACK错误,从而将相应的线程stop掉。回调检查可以检查多种错误,如果在阻塞的死循环里把所有的可能发生的错误检查都检查一遍,不仅浪费时间,代码也会臃肿。

检查状态寄存器

在没有示波器和逻辑分析仪的情况下,唯有祭出寄存器大法了,虽然可以用mega2560或者uno写一个现成但没直接查寄存器来得简单明了。
固件库里有寄存器地址转换的宏定义:

#define REG32(addr)                  (*(volatile uint32_t *)(uint32_t)(addr))
#define REG16(addr)                  (*(volatile uint16_t *)(uint32_t)(addr))
#define REG8(addr)                   (*(volatile uint8_t *)(uint32_t)(addr))

使用这些宏就能访问相应的寄存器,I2C的固件库里还进一步把状态寄存器进行封装

/* registers definitions */
#define I2C_CTL0(i2cx)                REG32((i2cx) + 0x00U)      /*!< I2C control register 0 */
#define I2C_CTL1(i2cx)                REG32((i2cx) + 0x04U)      /*!< I2C control register 1 */
#define I2C_SADDR0(i2cx)              REG32((i2cx) + 0x08U)      /*!< I2C slave address register 0*/
#define I2C_SADDR1(i2cx)              REG32((i2cx) + 0x0CU)      /*!< I2C slave address register */
#define I2C_DATA(i2cx)                REG32((i2cx) + 0x10U)      /*!< I2C transfer buffer register */
#define I2C_STAT0(i2cx)               REG32((i2cx) + 0x14U)      /*!< I2C transfer status register 0 */
#define I2C_STAT1(i2cx)               REG32((i2cx) + 0x18U)      /*!< I2C transfer status register */
#define I2C_CKCFG(i2cx)               REG32((i2cx) + 0x1CU)      /*!< I2C clock configure register */
#define I2C_RT(i2cx)                  REG32((i2cx) + 0x20U)      /*!< I2C rise time register */

所以可以直接访问相关的寄存器,比如查看地址寄存器是否存放了正确的从机地址,格式是否正确。查看收发缓存是否存放正确的数据等等。

获取状态位

i2c_flag_get()是调试中使用最多、且最有用的。它能获取已经定义的状态指示、错误指示,虽然可以直接读整个状态寄存器,但直接查询到位是真的香。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值