跟其他外设一样, STM32提供了I2C的初始化结构体,详情如下:
typedef struct{
uint32_t I2C_ClockSpeed; //设置SCL时钟频率,此值要低于400khz
uint16_t I2C_Mode; //指定工作模式,可选I2C模式或者SMBUS模式
uint16_t I2C_DutyCycle; //指定时钟占空比,可选2/1或者16/9
uint16_t I2C_OwnAddress1; //指定自身的I2C设备地址,即STM32主机地址
uint16_t I2C_Ack; //使能或者关闭响应(一般都要使能)
uint16_t I2C_AcknowledgeAddress; //指定地址的长度,可以是7位地址或者10位地址
}I2C_InitTypeDef;
软件编程要点:
(1)配置通讯使用的目标引脚为开漏模式
(2)使能I2C外设的时钟
(3)配置I2C外设的模式,地址、速率等参数并使能I2C外设
(4)编写基本的I2C按字节收发的函数
(5)编写EEPROM存储内容的函数
(6)编写测试程序,对读写数据进行校验。
首先为了提高代码的移植性,对用到的GPIO和外设使用宏定义
#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 I2C_Speed 400000
#define I2Cx_OWN_ADDRESS7 0x0A
#define I2C_PageSize 8
#define EEPROM_ADDR 0xA0
初始化I2C的GPIO引脚
static void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EEPROM_I2C_APBxClock_FUN(EEPROM_I2C_CLK,ENABLE);
EEPROM_I2C_GPIO_APBxClock_FUN(EEPROM_I2C_GPIO_CLK,ENABLE);
GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SCL_PIN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(EEPROM_I2C_SCL_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SDA_PIN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(EEPROM_I2C_SDA_PORT,&GPIO_InitStruct);
}
配置I2C的工作模式
1 /**
2 * @brief I2C 工作模式配置
3 * @param 无
4 * @retval 无
5 */
6 static void I2C_Mode_Configu(void)
7 {
8 I2C_InitTypeDef I2C_InitStructure;
9
10 /* I2C 配置 */
11 I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
12
13 /* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */
14 I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
15
16 I2C_InitStructure.I2C_OwnAddress1 =I2Cx_OWN_ADDRESS7;
17 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
18
19 /* I2C 的寻址模式 */
20 I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
21
22 /* 通信速率 */
23 I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
24
25 /* I2C 初始化 */
26 I2C_Init(EEPROM_I2Cx, &I2C_InitStructure);
27
28 /* 使能 I2C */
29 I2C_Cmd(EEPROM_I2Cx, ENABLE);
30 }
31
32
33 /**
34 * @brief I2C 外设(EEPROM)初始化
35 * @param 无
36 * @retval 无
37 */
38 void I2C_EE_Init(void)
39 {
40 I2C_GPIO_Config();
41
42 I2C_Mode_Configu();
43
44 /* 根据头文件 i2c_ee.h 中的定义来选择 EEPROM 要写入的设备地址 */
45 /* 选择 EEPROM Block0 来写入 */
46 EEPROM_ADDRESS = EEPROM_Block0_ADDRESS;
47 }
这段程序很好理解,其实就是把I2C外设通讯时钟引脚占空比设置为2/1,使能响应功能,使用7位地址I2C_OWN_ADDRESS7以及速率配置I2C_Speed。最后,调用库函数I2C_Init把这些配置写到相应的寄存器里面,并调用I2C_Cmd函数使能I2C外设。
向EEPROM写入一个字节的数据
初始化后,使用I2C外设,向EEPROM写入一个字节
1
2 /***************************************************************/
3 /*通讯等待超时时间*/
4 #define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
5 #define I2CT_LONG_TIMEOUT ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))
6
7 /**
8 * @brief I2C 等待事件超时的情况下会调用这个函数来处理
9 * @param errorCode:错误代码,可以用来定位是哪个环节出错.
10 * @retval 返回 0,表示 IIC 读取失败.
11 */
12 static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
13 {
14 /* 使用串口 printf 输出错误信息,方便调试 */
15 EEPROM_ERROR("I2C 等待超时!errorCode = %d",errorCode);
16 return 0;
17 }
18 /**
19 * @brief 写一个字节到 I2C EEPROM 中
20 * @param pBuffer:缓冲区指针
21 * @param WriteAddr:写地址
22 * @retval 正常返回 1,异常返回 0
23 */
24 uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)
25 {
26 /* 产生 I2C 起始信号 */
27 I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
28
29 /*设置超时等待时间*/
30 I2CTimeout = I2CT_FLAG_TIMEOUT;
31 /* 检测 EV5 事件并清除标志*/
32 while (!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
33 {
34 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
35 }
36
37 /* 发送 EEPROM 设备地址 */
38 I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS,
39 I2C_Direction_Transmitter);
40
41 I2CTimeout = I2CT_FLAG_TIMEOUT;
42 /* 检测 EV6 事件并清除标志*/
43 while (!I2C_CheckEvent(EEPROM_I2Cx,
44 I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
45 {
46 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
47 }
48
49 /* 发送要写入的 EEPROM 内部地址(即 EEPROM 内部存储器的地址) */
50 I2C_SendData(EEPROM_I2Cx, WriteAddr);
51
52 I2CTimeout = I2CT_FLAG_TIMEOUT;
53 /* 检测 EV8 事件并清除标志*/
54 while (!I2C_CheckEvent(EEPROM_I2Cx,
55 I2C_EVENT_MASTER_BYTE_TRANSMITTED))
56 {
57 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
58 }
59 /* 发送一字节要写入的数据 */
60 I2C_SendData(EEPROM_I2Cx, *pBuffer);
61
62 I2CTimeout = I2CT_FLAG_TIMEOUT;
63 /* 检测 EV8 事件并清除标志*/
64 while (!I2C_CheckEvent(EEPROM_I2Cx,
65 I2C_EVENT_MASTER_BYTE_TRANSMITTED))
66 {
67 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
68 }
69
70 /* 发送停止信号 */
71 I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
72
73 return 1;
74 }
这里由于一些时间问题,我找到一篇很详细的博客说明I2C读写EEPROM。
STM32系统学习——I2C (读写EEPROM)_Yuk丶的博客-CSDN博客_i2c读写eeprom
个人认为I2C还是比较简单的,只需要根据对应的时序图,完全可以写出代码,其中最为关键的一点就是STM32的速度相较于EEPROM来说是非常快的,写入数据到EEPROM还需要等待一段时间,作为EEPROM的内部时序完成写操作,AT24C02里面其实介绍了这个时序叫做Acknowledge Polling(应答轮询)。
评论(0)
您还未登录,请登录后发表或查看评论