I2C扫描从设备方法
说明
这几天在研究0.96寸OLED屏;但是一直都点不亮,同事建议用I2C扫描从设备地址,如是写了这个函数;虽然最终确定不是软件问题,但是解题思路很好,因此记录下来;MCU是STM32F103VE.
思路
I2C发送从设备地址,检测哪些地址有应答,则把这些地址答应出来,即可知道此I2C挂载了多少从设备;
代码
I2C_SCAN.H
#ifndef __I2C_SCAN_H
#define __I2C_SCAN_H
#include "stm32f10x.h"
#include "stm32f10x_i2c.h"
/**************************I2C参数定义,I2C1或I2C2********************************/
#define EEPROM_I2Cx I2C1
#define EEPROM_I2C_APBxClock_FUN RCC_APB1PeriphClockCmd
#define EEPROM_I2C_CLK RCC_APB1Periph_I2C1
#define EEPROM_I2C_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define EEPROM_I2C_GPIO_CLK RCC_APB2Periph_GPIOB
#define EEPROM_I2C_SCL_PORT GPIOB
#define EEPROM_I2C_SCL_PIN GPIO_Pin_6
#define EEPROM_I2C_SDA_PORT GPIOB
#define EEPROM_I2C_SDA_PIN GPIO_Pin_7
/*等待超时时间*/
#define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))
/*信息输出*/
#define EEPROM_DEBUG_ON 0
#define EEPROM_INFO(fmt,arg...) printf("<<-EEPROM-INFO->> "fmt"\n",##arg)
#define EEPROM_ERROR(fmt,arg...) printf("<<-EEPROM-ERROR->> "fmt"\n",##arg)
#define EEPROM_DEBUG(fmt,arg...) do{\
if(EEPROM_DEBUG_ON)\
printf("<<-EEPROM-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
}while(0)
void I2C_SCAN_Init(void);
//主函数调用下面函数,如果检测地址0~200中间是否有外设的地址,把200写入进去即可
uint32_t I2C_SCAN(uint32_t TEST_ADDRESS);
#endif /*__BSP_I2C_24C02_H*/
I2C_SCAN.C
#include "i2c_scan.h"
static __IO uint32_t I2CTimeout = I2CT_LONG_TIMEOUT;
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode);
static void I2C_GPIO_Config(void)
{
//PB6=SCL;PB7=SDA I2C1
GPIO_InitTypeDef GPIO_InitStructure;
//使能GPIOB时钟;使能I2C时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
//复用开漏输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
//复用开漏输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
static void I2C_Mode_Config(void)
{
I2C_InitTypeDef I2C_InitStructure;
//设置速度为400Khz
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
//占空比1:2
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
//指定自身地址
I2C_InitStructure.I2C_OwnAddress1 = 0x10;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
//设备地址七位
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1,&I2C_InitStructure);
}
void I2C_SCAN_Init(void)
{
I2C_GPIO_Config();
I2C_Mode_Config();
}
uint32_t I2C_SCAN(uint32_t TEST_ADDRESS)
{
while(TEST_ADDRESS>0)
{
/* Send STRAT condition */
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Send EEPROM address for write */
I2C_Send7bitAddress(EEPROM_I2Cx, TEST_ADDRESS, I2C_Direction_Transmitter);
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((--I2CTimeout) == 0) break;
}
if(I2CTimeout>0) printf("%#x\n",TEST_ADDRESS);
/* Send STOP condition */
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
TEST_ADDRESS--;
}
}
/**
* @brief Basic management of the timeout situation.
* @param errorCode:错误代码,可以用来定位是哪个环节出错.
* @retval 返回0,表示IIC读取失败.
*/
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
{
/* Block communication and all processes */
EEPROM_ERROR("I2C 等待超时!errorCode = %d",errorCode);
return 0;
}
最终打印效果&不解的地方
本次挂载了两个从设备,一个是24C02地址为0XA0;一个是OLED,地址为0X78;但最终打印出来的数据如下:
0xaf
0xae
0xad
0xac
0xab
0xaa
0xa9
0xa8
0xa7
0xa6
0xa5
0xa4
0xa3
0xa2
0xa1
0xa0
0x79
0x78
不知道为什么会打印这么多;知道的讲一下哦!