开发板:野火指南者(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
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 }
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
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 }
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 }
总结:
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