开发板:野火指南者(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 1 #ifndef __USER_I2C_H 2 2 #define __USER_I2C_H 3 3 4 4 #include "stm32f10x.h" 5 5 #include "stdio.h" 6 6 7 7 8 8 #define I2C_SPEED 400000 //用于配置I2传输速率 9 9 #define I2C_STM32_ADDRESS 0x0A //用于配置STM32F103VE自身的I2C地址 1010 #define EEPROM_ADDRESS 0xA0 //用于配置EEPROM的地址 1111 #define TIMEOUT 0x00100000 //用于延时用 1212 #define EEPROM_PAGE_SIZE 8 //EEPROM页大小宏 1313 1414 1515 void user_I2C_GPIO_Config(void); //I2C对应GPIO PIN脚配置函数 1616 void user_I2C_Config(void); //I2C配置函数 1717 void user_I2C_EEPROM_Write_Byte(uint8_t * data, uint8_t writeAddress); //EEPROM单字节写函数 1818 void user_I2C_EEPROM_Read_Bytes(uint8_t * data, uint8_t readAddress,uint32_t readNums); //EEPROM N个数据读函数 1919 int user_timeout(uint32_t I2C_EVENT, int error_id); //延时函数 2020 void user_I2C_EEPROM_Write_Page(uint8_t * data, uint8_t writeAddress, uint32_t WriteNums); //EEPROM页写入函数 2121 void user_I2C_EEPROM_Write_Bytes(uint8_t * data, uint8_t writeAddress, uint32_t WriteNums); //EEPROM N个数据写入函数 2222 void user_I2C_EEPROM_Standby(void); //检查EEPROM是否处于待机状态 2323 2424 2525 2626 #endif
View Code
2. 在user_i2c.c中添加如下代码

1 1 #include "user_i2c.h" 2 2 3 3 //I2C对应GPIO PIN脚配置函数 4 4 void user_I2C_GPIO_Config(void) 5 5 { 6 6 GPIO_InitTypeDef I2C_GPIO_SCL_PB6,I2C_GPIO_SDA_PB7; 7 7 8 8 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); 9 9 10 10 I2C_GPIO_SCL_PB6.GPIO_Mode = GPIO_Mode_AF_OD; 11 11 I2C_GPIO_SCL_PB6.GPIO_Pin = GPIO_Pin_6; 12 12 I2C_GPIO_SCL_PB6.GPIO_Speed = GPIO_Speed_50MHz; 13 13 14 14 I2C_GPIO_SDA_PB7.GPIO_Mode = GPIO_Mode_AF_OD; 15 15 I2C_GPIO_SDA_PB7.GPIO_Pin = GPIO_Pin_7; 16 16 I2C_GPIO_SDA_PB7.GPIO_Speed = GPIO_Speed_50MHz; 17 17 18 18 GPIO_Init(GPIOB, &I2C_GPIO_SCL_PB6); 19 19 GPIO_Init(GPIOB, &I2C_GPIO_SDA_PB7); 20 20 21 21 } 22 22 23 23 //I2C配置函数 24 24 void user_I2C_Config(void) 25 25 { 26 26 I2C_InitTypeDef I2C_Config; 27 27 28 28 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); 29 29 30 30 I2C_Config.I2C_Ack = I2C_Ack_Enable; 31 31 I2C_Config.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; 32 32 I2C_Config.I2C_ClockSpeed = I2C_SPEED; 33 33 I2C_Config.I2C_DutyCycle = I2C_DutyCycle_2; 34 34 I2C_Config.I2C_Mode = I2C_Mode_I2C; 35 35 I2C_Config.I2C_OwnAddress1 = I2C_STM32_ADDRESS; 36 36 37 37 I2C_Init(I2C1, &I2C_Config); 38 38 I2C_Cmd(I2C1, ENABLE); 39 39 40 40 41 41 } 42 42 43 43 //EEPROM单字节写函数 44 44 void user_I2C_EEPROM_Write_Byte(uint8_t * data, uint8_t writeAddress) 45 45 { 46 46 //产生开始信号 47 47 I2C_GenerateSTART(I2C1, ENABLE); 48 48 if(user_timeout(I2C_EVENT_MASTER_MODE_SELECT, 1) == -1) 49 49 { 50 50 return; 51 51 } 52 52 53 53 //发送EEPROM地址 54 54 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); 55 55 if(user_timeout(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, 2) == -1) 56 56 { 57 57 return; 58 58 } 59 59 60 60 //发送要写入到EEPROM的地址 61 61 I2C_SendData(I2C1, writeAddress); 62 62 if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 3) == -1) 63 63 { 64 64 return; 65 65 } 66 66 67 67 //发送写入的数据 68 68 I2C_SendData(I2C1, *data); 69 69 if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 4) == -1) 70 70 { 71 71 return; 72 72 } 73 73 74 74 //产生结束信号 75 75 I2C_GenerateSTOP(I2C1, ENABLE); 76 76 77 77 78 78 } 79 79 80 80 //EEPROM页写入函数 81 81 void user_I2C_EEPROM_Write_Page(uint8_t * data, uint8_t writeAddress, uint32_t WriteNums) 82 82 { 83 83 84 84 uint32_t time = TIMEOUT; 85 85 86 86 while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) //检查EEPROM是否处于忙碌状态 87 87 { 88 88 if(time == 0) 89 89 { 90 90 printf("function:user_I2C_EEPROM_Write_Page->I2C_GetFlagStatus time out!\n"); 91 91 return; 92 92 } 93 93 time--; 94 94 } 95 95 96 96 I2C_GenerateSTART(I2C1, ENABLE); 97 97 if(user_timeout(I2C_EVENT_MASTER_MODE_SELECT, 11) == RESET) 98 98 { 99 99 return; 100100 } 101101 102102 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); 103103 if(user_timeout(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, 12)) 104104 { 105105 return; 106106 } 107107 108108 I2C_SendData(I2C1, writeAddress); 109109 if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 13)) 110110 { 111111 return; 112112 } 113113 114114 while(WriteNums) 115115 { 116116 I2C_SendData(I2C1, *data); 117117 data++; 118118 WriteNums--; 119119 120120 if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 14)) 121121 { 122122 return; 123123 } 124124 125125 126126 } 127127 128128 129129 130130 I2C_GenerateSTOP(I2C1, ENABLE); 131131 132132 133133 } 134134 135135 //EEPROM N个数据写入函数 136136 void user_I2C_EEPROM_Write_Bytes(uint8_t * data, uint8_t writeAddress, uint32_t WriteNums) 137137 { 138138 uint32_t tempAddress = 0,tempPageData = 0,tempLessPageData = 0; //tempAddress用来保存写入的地址是否对齐,tempPageData用来保存数据可以分多少页写入,tempLessPageData用来保存不足一页数据的个数 139139 140140 tempAddress = writeAddress % EEPROM_PAGE_SIZE; 141141 tempPageData = WriteNums / EEPROM_PAGE_SIZE; 142142 tempLessPageData = WriteNums % EEPROM_PAGE_SIZE; 143143 144144 //分两种情况,一是要写入的地址刚好地址对齐,另一种则是不对齐,则转到else中代码处理 145145 if(tempAddress == 0) 146146 { 147147 if(tempLessPageData == 0) //分两种情况,一是数据刚好整页写入,二是有不足一页的数据,第二种情况转到else中处理 148148 { 149149 while(tempPageData) //数据整页整页写入 150150 { 151151 user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE); 152152 user_I2C_EEPROM_Standby(); //多个字节写入,必须要调用此函数,检查EEPROM设备是否处于就绪状态,以便下次进行写入 153153 writeAddress += EEPROM_PAGE_SIZE; 154154 data += EEPROM_PAGE_SIZE; 155155 tempPageData--; 156156 } 157157 } 158158 else 159159 { 160160 while(tempPageData) //若有不足一页的情况,先将能整页写入的数据写入,让后再写入剩下不足一页的数据 161161 { 162162 user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE); 163163 user_I2C_EEPROM_Standby(); 164164 writeAddress += EEPROM_PAGE_SIZE; 165165 data += EEPROM_PAGE_SIZE; 166166 tempPageData--; 167167 } 168168 user_I2C_EEPROM_Write_Page(data, writeAddress, tempLessPageData); 169169 user_I2C_EEPROM_Standby(); 170170 } 171171 } 172172 else 173173 { 174174 user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE - tempAddress); 175175 user_I2C_EEPROM_Standby(); 176176 writeAddress += EEPROM_PAGE_SIZE - tempAddress; 177177 data += EEPROM_PAGE_SIZE - tempAddress; 178178 179179 tempPageData = (WriteNums - (EEPROM_PAGE_SIZE - tempAddress)) / EEPROM_PAGE_SIZE; //地址补齐后,需要重新计算剩下的数据需要多少页写完 180180 tempLessPageData = (WriteNums - (EEPROM_PAGE_SIZE - tempAddress)) % EEPROM_PAGE_SIZE; //地址补齐后,需要重新计算剩下的数据有多少数据不足一页 181181 182182 if(tempLessPageData == 0) 183183 { 184184 while(tempPageData) 185185 { 186186 user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE); 187187 user_I2C_EEPROM_Standby(); 188188 writeAddress += EEPROM_PAGE_SIZE; 189189 data += EEPROM_PAGE_SIZE; 190190 tempPageData--; 191191 } 192192 193193 } 194194 else 195195 { 196196 while(tempPageData) 197197 { 198198 user_I2C_EEPROM_Write_Page(data, writeAddress, EEPROM_PAGE_SIZE); 199199 user_I2C_EEPROM_Standby(); 200200 writeAddress += EEPROM_PAGE_SIZE; 201201 data += EEPROM_PAGE_SIZE; 202202 tempPageData--; 203203 } 204204 user_I2C_EEPROM_Write_Page(data, writeAddress, tempLessPageData); 205205 user_I2C_EEPROM_Standby(); 206206 } 207207 208208 209209 } 210210 211211 212212 213213 } 214214 215215 //检查EEPROM是否处于待机状态 216216 void user_I2C_EEPROM_Standby(void) 217217 { 218218 219219 220220 I2C_GenerateSTART(I2C1, ENABLE); 221221 222222 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); 223223 224224 while (I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002 == 0) 225225 { 226226 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); 227227 228228 } 229229 230230 231231 I2C_ClearFlag(I2C1, I2C_FLAG_AF); 232232 233233 I2C_GenerateSTOP(I2C1, ENABLE); 234234 235235 } 236236 237237 238238 239239 //EEPROM N个数据读函数 240240 void user_I2C_EEPROM_Read_Bytes(uint8_t * data, uint8_t readAddress,uint32_t readNums) 241241 { 242242 uint32_t time = TIMEOUT; 243243 244244 while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) 245245 { 246246 247247 if(time == 0) 248248 { 249249 printf("function:user_I2C_EEPROM_Read_Bytes->I2C_GetFlagStatus time out!\n"); 250250 return; 251251 } 252252 time--; 253253 } 254254 255255 time = TIMEOUT; 256256 while(time--); //此延时时间非常关键,在读写时,写完之后必须要等待足够长的时间才能开始读取数据,否则读取不到数据至少要在这里有足够多的延时时间,具体原因不知 257257 258258 259259 260260 I2C_GenerateSTART(I2C1, ENABLE); 261261 if(user_timeout(I2C_EVENT_MASTER_MODE_SELECT, 5) == -1) 262262 { 263263 return; 264264 } 265265 266266 267267 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); 268268 if(user_timeout(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, 6) == -1) 269269 { 270270 return; 271271 } 272272 273273 I2C_Cmd(I2C1, ENABLE); 274274 275275 I2C_SendData(I2C1, readAddress); 276276 if(user_timeout(I2C_EVENT_MASTER_BYTE_TRANSMITTED, 7) == -1) 277277 { 278278 return; 279279 } 280280 281281 I2C_GenerateSTART(I2C1, ENABLE); 282282 if(user_timeout(I2C_EVENT_MASTER_MODE_SELECT, 8) == -1) 283283 { 284284 return; 285285 } 286286 287287 //这里第二次发送EEPROM地址,还是0xA0,而非0xA1 288288 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver); 289289 if(user_timeout(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, 9) == -1) 290290 { 291291 return; 292292 } 293293 294294 while(readNums) 295295 { 296296 if(readNums == 1) 297297 { 298298 I2C_AcknowledgeConfig(I2C1, DISABLE); 299299 I2C_GenerateSTOP(I2C1, ENABLE); 300300 } 301301 302302 //这条检查语句一定要放在I2C_ReceiveData()之前,否则无法读取不到数据 303303 if(user_timeout(I2C_EVENT_MASTER_BYTE_RECEIVED, 10) == -1) 304304 { 305305 return; 306306 } 307307 308308 *data = I2C_ReceiveData(I2C1); 309309 data++; 310310 311311 312312 readNums--; 313313 314314 } 315315 316316 //使能Ack响应 317317 I2C_AcknowledgeConfig(I2C1, ENABLE); 318318 319319 } 320320 321321 322322 //延时函数 323323 int user_timeout(uint32_t I2C_EVENT, int error_id) 324324 { 325325 int timeout = TIMEOUT; 326326 327327 while(I2C_CheckEvent(I2C1, I2C_EVENT) == RESET) 328328 { 329329 if(timeout == 0) 330330 { 331331 printf("Time out! error id is %d\n",error_id); 332332 return -1; 333333 } 334334 335335 timeout--; 336336 } 337337 338338 return 0; 339339 }
View Code
3. 在user_usart.h中添加如下代码

1 1 #ifndef __USER_USART_H 2 2 #define __USER_USART_H 3 3 4 4 #include "stm32f10x.h" 5 5 #include "stdio.h" 6 6 7 7 8 8 9 9 void user_USART_GPIO_Config(void); //USART GPIO PIN配置函数 1010 void user_USART_Config(void); //USART配置函数 1111 int fputc(int data, FILE * file); //重写fputc函数,支持printf与USART的关联 1212 int fgetc(FILE * file); //重写fgetc函数,支持scanf与USART的关联 1313 1414 1515 #endif
View Code
4. 在user_usart.c中添加如下代码

1 1 #include "user_usart.h" 2 2 3 3 //USART GPIO PIN配置函数 4 4 void user_USART_GPIO_Config(void) 5 5 { 6 6 GPIO_InitTypeDef USART_GPIO_TX_PA9,USART_GPIO_RX_PA10; 7 7 8 8 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 9 9 1010 USART_GPIO_TX_PA9.GPIO_Mode = GPIO_Mode_AF_PP; 1111 USART_GPIO_TX_PA9.GPIO_Pin = GPIO_Pin_9; 1212 USART_GPIO_TX_PA9.GPIO_Speed = GPIO_Speed_50MHz; 1313 1414 USART_GPIO_RX_PA10.GPIO_Mode = GPIO_Mode_IN_FLOATING; 1515 USART_GPIO_RX_PA10.GPIO_Pin = GPIO_Pin_10; 1616 1717 GPIO_Init(GPIOA, &USART_GPIO_TX_PA9); 1818 GPIO_Init(GPIOA, &USART_GPIO_RX_PA10); 1919 2020 2121 } 2222 2323 //USART配置函数 2424 void user_USART_Config(void) 2525 { 2626 USART_InitTypeDef USART_Config; 2727 2828 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); 2929 3030 USART_Config.USART_BaudRate = 115200; 3131 USART_Config.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 3232 USART_Config.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; 3333 USART_Config.USART_Parity = USART_Parity_No; 3434 USART_Config.USART_StopBits = USART_StopBits_1; 3535 USART_Config.USART_WordLength = USART_WordLength_8b; 3636 3737 USART_Init(USART1, &USART_Config); 3838 USART_Cmd(USART1, ENABLE); 3939 4040 } 4141 4242 4343 //重写fputc函数,支持printf与USART的关联 4444 int fputc(int data, FILE * file) 4545 { 4646 USART_SendData(USART1, (uint8_t)data); 4747 4848 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); 4949 5050 return data; 5151 } 5252 5353 //重写fgetc函数,支持scanf与USART的关联 5454 int fgetc(FILE * file) 5555 { 5656 while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); 5757 5858 return USART_ReceiveData(USART1); 5959 }
View Code
5. 在main.c中添加如下代码

1 1 #include "stm32f10x.h" 2 2 #include "user_usart.h" 3 3 #include "user_i2c.h" 4 4 5 5 6 6 #define BUFFSIZE 30 7 7 8 8 9 9 1010 int main(void) 1111 { 1212 1313 uint8_t w_buff[BUFFSIZE],r_buff[BUFFSIZE]; 1414 uint8_t i; 1515 1616 1717 user_USART_GPIO_Config(); 1818 user_USART_Config(); 1919 2020 user_I2C_GPIO_Config(); 2121 user_I2C_Config(); 2222 2323 for(i = 0; i < BUFFSIZE; i++) 2424 { 2525 w_buff[i] = i + 1; 2626 } 2727 2828 2929 3030 user_I2C_EEPROM_Write_Bytes((uint8_t *)&w_buff, 0, 30); 3131 user_I2C_EEPROM_Read_Bytes((uint8_t *)&r_buff, 0, 30); 3232 3333 3434 3535 printf("==================start=====================\n"); 3636 3737 3838 3939 for(i = 0; i < BUFFSIZE; i++) 4040 { 4141 printf("r_buff[%d] = %d\n",i,r_buff[i]); 4242 } 4343 4444 4545 4646 4747 4848 4949 while(1); 5050 5151 5252 return 0; 5353 }
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