开发板:野火指南者(STM32F103VE)
STM32库版本:STM32F10x_StdPeriph_Lib_V3.5.0
IDE:KEIL5(代码编写很不方便,只在编译的时候用到)
代码编写工具:Source Insight 4.0(跟读代码、编写代码的最佳工具)
使用到的串口:USART1
使用到的I2C:I2C1
EEPROM型号:AT24C02
硬件原理图:
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中添加如下代码
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中添加如下代码
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中添加如下代码
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中添加如下代码
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中添加如下代码
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
实验代码: