STM32F4IO口模拟多个IIC方案

STM32F4IO口模拟多个IIC方案


一、问题来源

最近在做一个比赛要用到6个激光测距传感器(VL53L0),原计划用Modbus协议的,但是由于不知名原因(可能是线太长了,PCB板子没有加终端电阻),总是只能驱动一个激光,加上第二个激光就会读不出来数据,所以打算用IIC协议,但是正点原子IIC协议的底层函数是用宏实现的,只能支持一个IIC,激光模块上也没有片选口,只能采用6个不同的IIC控制6个激光的方案或者使用TI的TCA9548AIIC拓展芯片的方法来解决,我采用的是6个不同的IIC控制6个激光的方案。

二、IIC相对于Modbus的协议的优势

Modbus协议区分两组不同的命令的方式是靠延时,激光模块的厂家给的例程两组命令间隔100ms这样我600ms才能轮询一边所有激光,速度有些慢,而IIC协议来通信只需要间隔20ms的激光传感器最快测距等待时间读取传感器的值就可以了,如果配置传感器为连续测量模式我几乎就是20ms就可以获得6个传感器的数据。速度相对于Modbus协议快了很多。但是Modbus的CRC校验可以保证数据的准确性,IIC不能。

三、解决方法

下面是我想到的几种方法
1、使用函数指针,使用不同的IIC前更改指针指向的函数,但是这种方法要建立很多函数或者宏,比较麻烦。
2、使用##更改宏,根据不同的传入参数达到使用不同函数的效果,但是我没有成功。
3、使用结构体数组,更改原有的宏和底层函数,调用不同的结构体来实现IIC之间的转换。

四 具体实现方法

1、首先建立一个结构体,内容分别是SCL和SDA的GPIO和引脚。

typedef struct
{
   
	GPIO_TypeDef *SCL_GPIO;
	uint32_t SCL_Pin;
	
	GPIO_TypeDef *SDA_GPIO;
	uint32_t SDA_Pin;
	
}LDC_IIC;

2、然后初始化结构体数组,这里要把keil的模式调成C99模式,这时串口的printf函数那里会报错,在前面加一个void就可以了。

LDC_IIC LDC_VL53L0X[6]={
   [0]={
   .SCL_GPIO=GPIOE,.SCL_Pin=GPIO_Pin_13,.SDA_GPIO=GPIOE,.SDA_Pin=GPIO_Pin_15},
	                      [1]={
   .SCL_GPIO=GPIOG,.SCL_Pin=GPIO_Pin_2,.SDA_GPIO=GPIOG,.SDA_Pin=GPIO_Pin_4},
	                      [2]={
   .SCL_GPIO=GPIOB,.SCL_Pin=GPIO_Pin_0,.SDA_GPIO=GPIOB,.SDA_Pin=GPIO_Pin_2},
	                      [3]={
   .SCL_GPIO=GPIOC,.SCL_Pin=GPIO_Pin_0,.SDA_GPIO=GPIOC,.SDA_Pin=GPIO_Pin_2},
	                      [4]={
   .SCL_GPIO=GPIOG,.SCL_Pin=GPIO_Pin_9,.SDA_GPIO=GPIOG,.SDA_Pin=GPIO_Pin_11},
	                      [5]
  • 9
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果I2C总线上没有设备响应,那么使用HAL库中的`HAL_I2C_IsDeviceReady()`函数查询设备地址可能会超时,导致程序阻塞。为了避免这种情况,可以使用IO模拟实现I2C设备地址查询。 具体实现方法如下: 1. 配置I2C总线的SDA和SCL引脚为输出模式,并且将SDA引脚输出高电平,SCL引脚输出低电平。 2. 依次向I2C地址的每一位发送1,每发送一位后都要查询SDA引脚的电平状态。如果SDA引脚的电平状态为0,则说明有设备响应,该位为0;如果SDA引脚的电平状态为1,则说明没有设备响应,该位为1。 3. 完成地址发送后,需要再次查询SDA引脚的电平状态。如果SDA引脚的电平状态为0,则说明有设备响应,该地址存在;如果SDA引脚的电平状态为1,则说明没有设备响应,该地址不存在。 以下是一个使用IO模拟I2C设备地址查询的示例代码: ``` #define I2C_ADDRESS 0xA0 // 要查询的I2C地址 // 配置I2C总线的SDA和SCL引脚 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 将SDA引脚输出高电平,SCL引脚输出低电平 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); // 依次向I2C地址的每一位发送1,并查询SDA引脚的电平状态 for (uint8_t i = 0; i < 8; i++) { // 发送1 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); HAL_Delay(1); // 查询SDA引脚的电平状态 uint8_t bit = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == GPIO_PIN_RESET ? 0 : 1; // 将查询结果写入I2C地址的对应位 I2C_ADDRESS |= bit << i; } // 查询SDA引脚的电平状态,判断地址是否存在 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); HAL_Delay(1); uint8_t deviceExists = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == GPIO_PIN_RESET; if (deviceExists) { printf("Device found at address 0x%X\n", I2C_ADDRESS); } else { printf("Device not found at address 0x%X\n", I2C_ADDRESS); } ``` 需要注意的是,使用IO模拟I2C设备地址查询需要自行实现I2C的协议,如何发送起始位、停止位,如何发送数据和查询SDA引脚的电平状态等,需要仔细参考I2C的协议和具体设备的手册来实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值