STM32 I2C EEPROM学习笔记

Wesley13
• 阅读 460

开发板:野火指南者(STM32F103VE)

STM32库版本:STM32F10x_StdPeriph_Lib_V3.5.0

IDE:KEIL5(代码编写很不方便,只在编译的时候用到)

代码编写工具:Source Insight 4.0(跟读代码、编写代码的最佳工具)

使用到的串口:USART1

使用到的I2C:I2C1

EEPROM型号:AT24C02

硬件原理图:

STM32 I2C EEPROM学习笔记 STM32 I2C EEPROM学习笔记

1. 新建user_i2c.c、user_i2c.h、user_usart.c、user_usart.h、main.c 5个文件,并从STM32官方库的例子中将stm32f10x_it.c、stm32f10x_it.h、stm32f10x_conf.h拷贝到自己的工程目录下。

2. 在user_i2c.h中添加如下代码

STM32 I2C EEPROM学习笔记 STM32 I2C EEPROM学习笔记

 1 #ifndef __USER_I2C_H
 2 #define __USER_I2C_H
 3 
 4 #include "stm32f10x.h"
 5 #include "stdio.h"
 6 
 7 
 8 #define I2C_SPEED        400000        //用于配置I2传输速率
 9 #define    I2C_STM32_ADDRESS    0x0A    //用于配置STM32F103VE自身的I2C地址
10 #define    EEPROM_ADDRESS        0xA0    //用于配置EEPROM的地址
11 #define    TIMEOUT        0x00100000        //用于延时用
12 #define EEPROM_PAGE_SIZE    8        //EEPROM页大小宏
13 
14 
15 void user_I2C_GPIO_Config(void);    //I2C对应GPIO PIN脚配置函数
16 void user_I2C_Config(void);        //I2C配置函数
17 void user_I2C_EEPROM_Write_Byte(uint8_t * data, uint8_t writeAddress);        //EEPROM单字节写函数
18 void user_I2C_EEPROM_Read_Bytes(uint8_t * data, uint8_t readAddress,uint32_t readNums);        //EEPROM N个数据读函数
19 int user_timeout(uint32_t I2C_EVENT, int error_id);        //延时函数
20 void user_I2C_EEPROM_Write_Page(uint8_t * data, uint8_t writeAddress, uint32_t WriteNums);        //EEPROM页写入函数
21 void user_I2C_EEPROM_Write_Bytes(uint8_t * data, uint8_t writeAddress, uint32_t WriteNums);        //EEPROM N个数据写入函数
22 void user_I2C_EEPROM_Standby(void);        //检查EEPROM是否处于待机状态
23 
24 
25 
26 #endif

View Code

2. 在user_i2c.c中添加如下代码

STM32 I2C EEPROM学习笔记 STM32 I2C EEPROM学习笔记

  1 #include "user_i2c.h"
  2 
  3 //I2C对应GPIO PIN脚配置函数
  4 void user_I2C_GPIO_Config(void)
  5 {
  6     GPIO_InitTypeDef I2C_GPIO_SCL_PB6,I2C_GPIO_SDA_PB7;
  7 
  8     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  9 
 10     I2C_GPIO_SCL_PB6.GPIO_Mode = GPIO_Mode_AF_OD;
 11     I2C_GPIO_SCL_PB6.GPIO_Pin = GPIO_Pin_6;
 12     I2C_GPIO_SCL_PB6.GPIO_Speed = GPIO_Speed_50MHz;
 13 
 14     I2C_GPIO_SDA_PB7.GPIO_Mode = GPIO_Mode_AF_OD;
 15     I2C_GPIO_SDA_PB7.GPIO_Pin = GPIO_Pin_7;
 16     I2C_GPIO_SDA_PB7.GPIO_Speed = GPIO_Speed_50MHz;
 17 
 18     GPIO_Init(GPIOB, &I2C_GPIO_SCL_PB6);
 19     GPIO_Init(GPIOB, &I2C_GPIO_SDA_PB7);
 20 
 21 }
 22 
 23 //I2C配置函数
 24 void user_I2C_Config(void)
 25 {
 26     I2C_InitTypeDef I2C_Config;
 27 
 28     RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
 29 
 30     I2C_Config.I2C_Ack = I2C_Ack_Enable;
 31     I2C_Config.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
 32     I2C_Config.I2C_ClockSpeed = I2C_SPEED;
 33     I2C_Config.I2C_DutyCycle = I2C_DutyCycle_2;
 34     I2C_Config.I2C_Mode = I2C_Mode_I2C;
 35     I2C_Config.I2C_OwnAddress1 = I2C_STM32_ADDRESS;
 36 
 37     I2C_Init(I2C1, &I2C_Config);
 38     I2C_Cmd(I2C1, ENABLE);
 39     
 40 
 41 }
 42 
 43 //EEPROM单字节写函数
 44 void user_I2C_EEPROM_Write_Byte(uint8_t * data, uint8_t writeAddress)
 45 {
 46     //产生开始信号
 47     I2C_GenerateSTART(I2C1, ENABLE);
 48     if(user_timeout(I2C_EVENT_MASTER_MODE_SELECT, 1) == -1)
 49     {
 50         return;
 51     }
 52 
 53     //发送EEPROM地址
 54     I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
 55     if(user_timeout(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, 2) == -1)
 56     {
 57         return;
 58     }
 59 
 60     //发送要写入到EEPROM的地址
 61     I2C_SendData(I2C1, writeAddress);
 62     if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 3) == -1)
 63     {
 64         return;
 65     }
 66 
 67     //发送写入的数据
 68     I2C_SendData(I2C1, *data);
 69     if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 4) == -1)
 70     {
 71         return;
 72     }
 73 
 74     //产生结束信号
 75     I2C_GenerateSTOP(I2C1, ENABLE);
 76     
 77 
 78 }
 79 
 80 //EEPROM页写入函数
 81 void user_I2C_EEPROM_Write_Page(uint8_t * data, uint8_t writeAddress, uint32_t WriteNums)
 82 {
 83 
 84     uint32_t time = TIMEOUT;
 85 
 86     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))    //检查EEPROM是否处于忙碌状态
 87     {
 88         if(time == 0)
 89         {
 90             printf("function:user_I2C_EEPROM_Write_Page->I2C_GetFlagStatus time out!\n");
 91             return;
 92         }
 93         time--;
 94     }
 95 
 96     I2C_GenerateSTART(I2C1, ENABLE);
 97     if(user_timeout(I2C_EVENT_MASTER_MODE_SELECT, 11) == RESET)
 98     {
 99         return;
100     }
101 
102     I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
103     if(user_timeout(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, 12))
104     {
105         return;
106     }
107 
108     I2C_SendData(I2C1, writeAddress);
109     if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 13))
110     {
111         return;
112     }
113 
114     while(WriteNums)
115     {
116         I2C_SendData(I2C1, *data);
117         data++;
118         WriteNums--;
119 
120         if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 14))
121         {
122             return;
123         }
124 
125         
126     }
127 
128 
129 
130     I2C_GenerateSTOP(I2C1, ENABLE);
131 
132 
133 }
134 
135 //EEPROM N个数据写入函数
136 void user_I2C_EEPROM_Write_Bytes(uint8_t * data, uint8_t writeAddress, uint32_t WriteNums)
137 {
138     uint32_t tempAddress = 0,tempPageData = 0,tempLessPageData = 0;        //tempAddress用来保存写入的地址是否对齐,tempPageData用来保存数据可以分多少页写入,tempLessPageData用来保存不足一页数据的个数
139 
140     tempAddress = writeAddress % EEPROM_PAGE_SIZE;
141     tempPageData = WriteNums / EEPROM_PAGE_SIZE;
142     tempLessPageData = WriteNums % EEPROM_PAGE_SIZE;
143 
144     //分两种情况,一是要写入的地址刚好地址对齐,另一种则是不对齐,则转到else中代码处理
145     if(tempAddress == 0)
146     {
147         if(tempLessPageData == 0)    //分两种情况,一是数据刚好整页写入,二是有不足一页的数据,第二种情况转到else中处理
148         {
149             while(tempPageData)        //数据整页整页写入
150             {
151                 user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE);
152                 user_I2C_EEPROM_Standby();        //多个字节写入,必须要调用此函数,检查EEPROM设备是否处于就绪状态,以便下次进行写入
153                 writeAddress += EEPROM_PAGE_SIZE;
154                 data += EEPROM_PAGE_SIZE;
155                 tempPageData--;
156             }
157         }
158         else
159         {
160             while(tempPageData)        //若有不足一页的情况,先将能整页写入的数据写入,让后再写入剩下不足一页的数据
161             {
162                 user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE);
163                 user_I2C_EEPROM_Standby();
164                 writeAddress += EEPROM_PAGE_SIZE;
165                 data += EEPROM_PAGE_SIZE;
166                 tempPageData--;
167             }
168             user_I2C_EEPROM_Write_Page(data, writeAddress, tempLessPageData);
169             user_I2C_EEPROM_Standby();
170         }
171     }
172     else
173     {
174         user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE - tempAddress);
175         user_I2C_EEPROM_Standby();
176         writeAddress += EEPROM_PAGE_SIZE - tempAddress;
177         data += EEPROM_PAGE_SIZE - tempAddress;
178 
179         tempPageData = (WriteNums - (EEPROM_PAGE_SIZE - tempAddress)) / EEPROM_PAGE_SIZE;        //地址补齐后,需要重新计算剩下的数据需要多少页写完
180         tempLessPageData = (WriteNums - (EEPROM_PAGE_SIZE - tempAddress)) % EEPROM_PAGE_SIZE;    //地址补齐后,需要重新计算剩下的数据有多少数据不足一页
181 
182         if(tempLessPageData == 0)
183         {
184             while(tempPageData)
185             {
186                 user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE);
187                 user_I2C_EEPROM_Standby();
188                 writeAddress += EEPROM_PAGE_SIZE;
189                 data += EEPROM_PAGE_SIZE;
190                 tempPageData--;
191             }
192             
193         }
194         else
195         {
196             while(tempPageData)
197             {
198                 user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE);
199                 user_I2C_EEPROM_Standby();
200                 writeAddress += EEPROM_PAGE_SIZE;
201                 data += EEPROM_PAGE_SIZE;
202                 tempPageData--;
203             }
204             user_I2C_EEPROM_Write_Page(data, writeAddress, tempLessPageData);
205             user_I2C_EEPROM_Standby();
206         }
207 
208         
209     }
210 
211 
212 
213 }
214 
215 //检查EEPROM是否处于待机状态
216 void user_I2C_EEPROM_Standby(void)
217 {
218 
219 
220     I2C_GenerateSTART(I2C1, ENABLE);
221         
222     I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
223 
224     while (I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002 == 0)
225     {
226         I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
227         
228     }
229 
230 
231     I2C_ClearFlag(I2C1, I2C_FLAG_AF);
232 
233     I2C_GenerateSTOP(I2C1, ENABLE);
234     
235 }
236 
237 
238 
239 //EEPROM N个数据读函数
240 void user_I2C_EEPROM_Read_Bytes(uint8_t * data, uint8_t readAddress,uint32_t readNums)
241 {
242     uint32_t time = TIMEOUT;
243 
244     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
245     {
246         
247         if(time == 0)
248         {
249             printf("function:user_I2C_EEPROM_Read_Bytes->I2C_GetFlagStatus time out!\n");
250             return;
251         }
252         time--;
253     }
254     
255     time = TIMEOUT;
256     while(time--);    //此延时时间非常关键,在读写时,写完之后必须要等待足够长的时间才能开始读取数据,否则读取不到数据至少要在这里有足够多的延时时间,具体原因不知
257 
258     
259 
260     I2C_GenerateSTART(I2C1, ENABLE);
261     if(user_timeout(I2C_EVENT_MASTER_MODE_SELECT, 5) == -1)
262     {
263         return;
264     }
265 
266 
267     I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
268     if(user_timeout(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, 6) == -1)
269     {
270         return;
271     }
272 
273     I2C_Cmd(I2C1, ENABLE);
274 
275     I2C_SendData(I2C1, readAddress);
276     if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 7) == -1)
277     {
278         return;
279     }
280 
281     I2C_GenerateSTART(I2C1, ENABLE);
282     if(user_timeout(I2C_EVENT_MASTER_MODE_SELECT, 8) == -1)
283     {
284         return;
285     }
286 
287     //这里第二次发送EEPROM地址,还是0xA0,而非0xA1
288     I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);
289     if(user_timeout(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, 9) == -1)
290     {
291         return;
292     }
293 
294     while(readNums)
295     {
296         if(readNums == 1)
297         {
298             I2C_AcknowledgeConfig(I2C1, DISABLE);
299             I2C_GenerateSTOP(I2C1, ENABLE);
300         }
301     
302         //这条检查语句一定要放在I2C_ReceiveData()之前,否则无法读取不到数据
303         if(user_timeout(I2C_EVENT_MASTER_BYTE_RECEIVED, 10) == -1)
304         {
305             return;
306         }
307 
308         *data = I2C_ReceiveData(I2C1);
309         data++;
310 
311         
312         readNums--;
313 
314     }
315 
316     //使能Ack响应
317     I2C_AcknowledgeConfig(I2C1, ENABLE);
318 
319 }
320 
321 
322 //延时函数
323 int user_timeout(uint32_t I2C_EVENT, int error_id)
324 {
325     int timeout = TIMEOUT;
326 
327     while(I2C_CheckEvent(I2C1, I2C_EVENT) == RESET)
328     {
329         if(timeout == 0)
330         {
331             printf("Time out! error id is %d\n",error_id);
332             return -1;
333         }
334 
335         timeout--;
336     }
337         
338     return 0;
339 }

View Code

3. 在user_usart.h中添加如下代码

STM32 I2C EEPROM学习笔记 STM32 I2C EEPROM学习笔记

 1 #ifndef __USER_USART_H
 2 #define __USER_USART_H
 3 
 4 #include "stm32f10x.h"
 5 #include "stdio.h"
 6 
 7 
 8 
 9 void user_USART_GPIO_Config(void);        //USART GPIO PIN配置函数
10 void user_USART_Config(void);        //USART配置函数
11 int fputc(int data, FILE * file);        //重写fputc函数,支持printf与USART的关联
12 int fgetc(FILE * file);        //重写fgetc函数,支持scanf与USART的关联
13 
14 
15 #endif

View Code

4. 在user_usart.c中添加如下代码

STM32 I2C EEPROM学习笔记 STM32 I2C EEPROM学习笔记

 1 #include "user_usart.h"
 2 
 3 //USART GPIO PIN配置函数
 4 void user_USART_GPIO_Config(void)
 5 {
 6     GPIO_InitTypeDef USART_GPIO_TX_PA9,USART_GPIO_RX_PA10;
 7 
 8     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 9 
10     USART_GPIO_TX_PA9.GPIO_Mode = GPIO_Mode_AF_PP;
11     USART_GPIO_TX_PA9.GPIO_Pin = GPIO_Pin_9;
12     USART_GPIO_TX_PA9.GPIO_Speed = GPIO_Speed_50MHz;
13 
14     USART_GPIO_RX_PA10.GPIO_Mode = GPIO_Mode_IN_FLOATING;
15     USART_GPIO_RX_PA10.GPIO_Pin = GPIO_Pin_10;
16 
17     GPIO_Init(GPIOA, &USART_GPIO_TX_PA9);
18     GPIO_Init(GPIOA, &USART_GPIO_RX_PA10);
19 
20     
21 }
22 
23 //USART配置函数
24 void user_USART_Config(void)
25 {
26     USART_InitTypeDef USART_Config;
27 
28     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
29 
30     USART_Config.USART_BaudRate = 115200;
31     USART_Config.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
32     USART_Config.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
33     USART_Config.USART_Parity = USART_Parity_No;
34     USART_Config.USART_StopBits = USART_StopBits_1;
35     USART_Config.USART_WordLength = USART_WordLength_8b;
36 
37     USART_Init(USART1, &USART_Config);
38     USART_Cmd(USART1, ENABLE);
39     
40 }
41 
42 
43 //重写fputc函数,支持printf与USART的关联
44 int fputc(int data, FILE * file)
45 {
46     USART_SendData(USART1, (uint8_t)data);
47 
48     while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
49 
50     return data;
51 }
52 
53 //重写fgetc函数,支持scanf与USART的关联
54 int fgetc(FILE * file)
55 {
56     while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
57 
58     return USART_ReceiveData(USART1);
59 }

View Code

5. 在main.c中添加如下代码

STM32 I2C EEPROM学习笔记 STM32 I2C EEPROM学习笔记

 1 #include "stm32f10x.h"
 2 #include "user_usart.h"
 3 #include "user_i2c.h"
 4 
 5 
 6 #define        BUFFSIZE        30
 7 
 8 
 9 
10 int main(void)
11 {
12 
13     uint8_t w_buff[BUFFSIZE],r_buff[BUFFSIZE];
14     uint8_t i;
15 
16 
17     user_USART_GPIO_Config();
18     user_USART_Config();
19 
20     user_I2C_GPIO_Config();
21     user_I2C_Config();
22 
23     for(i = 0; i < BUFFSIZE; i++)
24     {
25         w_buff[i] = i + 1;
26     }
27 
28 
29 
30     user_I2C_EEPROM_Write_Bytes((uint8_t *)&w_buff, 0, 30);
31     user_I2C_EEPROM_Read_Bytes((uint8_t *)&r_buff, 0, 30);
32 
33 
34 
35     printf("==================start=====================\n");
36 
37 
38 
39     for(i = 0; i < BUFFSIZE; i++)
40     {
41         printf("r_buff[%d] = %d\n",i,r_buff[i]);
42     }
43     
44 
45 
46 
47 
48 
49     while(1);
50 
51     
52     return 0;
53 }

View Code

总结:

1. EEPROM在初始化对应的GPIO Pin脚时,需要都配置为复用开漏输出GPIO_Mode_AF_OD

2. 页写入函数不能单独使用,需要放在N个字节写入函数中调用,具体原因不知

3. 多个数据写入时,需要使用I2C_ReadRegister函数检查EEPROM是否处于就绪状态

4. 读操作动作,写、读要发送的地址,都是写入时的EEPROM地址0xA0,非0xA1

实验代码:

链接:https://pan.baidu.com/s/1VhF74nfzdVJnj8KHPmz2kw
提取码:4816

点赞
收藏
评论区
推荐文章
技术小男生 技术小男生
4个月前
linux环境jdk环境变量配置
1:编辑系统配置文件vi/etc/profile2:按字母键i进入编辑模式,在最底部添加内容:JAVAHOME/opt/jdk1.8.0152CLASSPATH.:$JAVAHOME/lib/dt.jar:$JAVAHOME/lib/tools.jarPATH$JAVAHOME/bin:$PATH3:生效配置
光头强的博客 光头强的博客
4个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
Jacquelyn38 Jacquelyn38
1年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
小森森 小森森
4个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
Wesley13 Wesley13
1年前
STM32串口通信USART学习笔记
1\.实验环境:开发板:野火指南者(STM32F103VE)STM32库版本:STM32F10x\_StdPeriph\_Lib\_V3.5.0IDE:KEIL5(代码编写很不方便,只在编译的时候用到)代码编写工具:SourceInsight4.0(跟读代码、编写代码的最佳工具)使用到的串口:USART1硬件原理图:!(
Wesley13 Wesley13
1年前
MySQL查询按照指定规则排序
1.按照指定(单个)字段排序selectfromtable_nameorderiddesc;2.按照指定(多个)字段排序selectfromtable_nameorderiddesc,statusdesc;3.按照指定字段和规则排序selec
Stella981 Stella981
1年前
Angular material mat
IconIconNamematiconcode_add\_comment_addcommenticon<maticonadd\_comment</maticon_attach\_file_attachfileicon<maticonattach\_file</maticon_attach\
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
helloworld_34035044 helloworld_34035044
6个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为