STM32F1的IIC硬件调试之EV5,EV6,EV7,EV8,EV9

         以前接触的都是车规级芯片,最近了解了一下STM32F1,用到了其IIC。听说STM32F1库函数的IIC有许多坑,很多人都推荐用IO模拟IIC,只能说这很“51”。上手两天发现,官方库函数的IIC是可以用的,这里介绍一下我解题思路。

        初调IIC,很容易陷入库函数中的死循环,为啥呢。EV5,EV6,EV7是啥,死循环干什么;查询了芯片手册,对症下药,一步一步调试,对症下药。

        芯片手册中有IIC的传送时序图,很重要。

主要介绍一下主模式,从模式可以下载芯片手册了解。

主模式涉及到了EV5,EV6,EV7,EV8,EV9等;【从模式涉及到了EV1,EV2,EV3,EV4等】

EV5:    SB=1,读SR1然后将地址写入DR寄存器将清除该事件。

EV6:    ADDR=1,读SR1然后读SR2将清除该事件。

EV6_1:  没有对应的事件标志,只适用于接受1个字节的情况。恰好在EV6之后,要清除响应和停止条件的产生位。

EV_7:  RxNE=1,读DR寄存器清除该事件。

EV7_1:  RxNE=1,读DR寄存器清除该事件。设置ACK=0和STOP请求。

EV8_1:  TxE=1,移位寄存器空,数据寄存器空,写DR寄存器。

EV8:    TxE=1,移位寄存器非空,数据寄存器空,写入DR寄存器将清除该事件。

EV8_2:  TxE=1,BTF=1,请求设置停止位。TxE,BTF位由硬件在产生停止条件时清除。

EV9: ADDR10=1, 读SR1然后写入DR寄存器将清除该事件。

EV_9:ADDR10=1,读SR1然后写入DR寄存器将清除该事件。

注:EV5,EV6,EV9,EV8_1,EV8_2事件拉低SCL低的事件,直到对应的软件序列结束。

EV7,EV8的软件序列必须在当前字节传输结束前完成。

EV6_1       或EV7_1的软件序列必须在当前传输字节的ACK脉冲之前完成。

 

测试一段时间,没有出现陷入死循环的情况;

考虑安全性和可靠性,可以加入软件循环跳出,以防万一。

 

 

 

以下是使用STM32F103C8T6的I2C例程。该例程使用了STM32CubeMX和Keil uVision5进行开发。在使用前,请确保已经正确配置了您的硬件和软件环境。 1. 在STM32CubeMX中配置I2C 首先,打开STM32CubeMX并创建一个新的工程。选择您的MCU型号,并启用I2C。 在“Pinout & Configuration”选项卡中,将SCL和SDA引脚分别分配给I2C1模块。 在“Configuration”选项卡中,启用I2C,并选择适当的时钟速度和I2C模式。根据您的需求,选择“主机模式”或“从模式”。 在“NVIC Settings”选项卡中,启用I2C中断并设置优先级。 最后,生成代码并导出到Keil uVision5。 2. 编写I2C例程 在Keil uVision5中打开生成的代码,并编写I2C例程。 ```c #include "stm32f1xx_hal.h" /* I2C handle structure */ I2C_HandleTypeDef hi2c1; /* I2C address */ #define DEVICE_ADDR 0x68 void I2C_Init(void) { /* Enable I2C clock */ __HAL_RCC_I2C1_CLK_ENABLE(); /* Configure I2C GPIOs */ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* Configure I2C */ hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; HAL_I2C_Init(&hi2c1); } void I2C_Write(uint8_t addr, uint8_t *data, uint16_t len) { HAL_I2C_Master_Transmit(&hi2c1, DEVICE_ADDR, data, len, HAL_MAX_DELAY); } void I2C_Read(uint8_t addr, uint8_t *data, uint16_t len) { HAL_I2C_Master_Receive(&hi2c1, DEVICE_ADDR, data, len, HAL_MAX_DELAY); } void I2C_IRQHandler(void) { HAL_I2C_EV_IRQHandler(&hi2c1); HAL_I2C_ER_IRQHandler(&hi2c1); } ``` 在以上代码中,我们首先定义了I2C句柄结构体`hi2c1`。然后定义了I2C地址为0x68的设备地址。 接下来,我们实现了一个I2C初始化函数`I2C_Init()`,该函数用于配置I2C GPIO和I2C模块。我们使用GPIOB的6号引脚和7号引脚作为SCL和SDA。 然后我们实现了I2C写函数`I2C_Write()`和I2C读函数`I2C_Read()`。这些函数使用了HAL库中的HAL_I2C_Master_Transmit()和HAL_I2C_Master_Receive()函数来进行数据传输。在这些函数中,我们使用了设备地址和MAX_DELAY参数,以保证传输的可靠性。 最后,我们实现了一个I2C中断处理函数`I2C_IRQHandler()`,该函数使用了HAL库中的HAL_I2C_EV_IRQHandler()和HAL_I2C_ER_IRQHandler()函数来处理I2C事件和错误。 3. 调用I2C函数 在您的应用程序中,您可以使用以下代码来调用I2C函数: ```c uint8_t data[4]; I2C_Init(); /* Write data to I2C device */ data[0] = 0x01; data[1] = 0x02; I2C_Write(DEVICE_ADDR, data, 2); /* Read data from I2C device */ I2C_Read(DEVICE_ADDR, data, 4); ``` 在以上代码中,我们首先初始化了I2C模块。然后,我们使用I2C_Write()函数将数据写入I2C设备,并使用I2C_Read()函数从I2C设备读取数据。 希望这个例程可以帮助到您。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值