当前位置:   article > 正文

STM32页读页写AT24CXX(HAL库 模拟IIC)

STM32页读页写AT24CXX(HAL库 模拟IIC)

参考文章:

这里附上一篇看到写得很好的大佬的文章:
STM32F407单片机通用24CXXX读写程序(KEIL),兼容24C系列存储器(24C01到24C512),支持存储器任意地址跨页连续读写多个页

 AT24C32/64官方手册:AT24C32/64

一、AT24CXX容量表

二、AT24CXX寻址方式

三、AT24CXX时序图

1.字节写

2.页写

3.当前地址读

4.顺序读

 5.随机读

四、代码


一、AT24CXX容量表

型号容量(bit)容量(byte)页数每页字节数(byte)
AT24C011K128168
AT24C022K256328
AT24C044K5123216
AT24C088K10246416
AT24C1616K204812816
AT24C3232K409612832
AT24C6464K819225632
AT24C128128K1638425664
AT24C256256K3276851264
AT24C512512K65536512128

二、AT24CXX寻址方式

型号WORD ADDRESS(bit)型号WORD ADDRESS(bit)
AT24C017AT24C3212
AT24C028AT24C6413
AT24C049AT24C12814
AT24C0810AT24C25615
AT24C1611AT24C51216

三、AT24CXX时序图

1.字节写

2.页写

         AT24CXX内部是有分页的,根据型号不同,页数不同,每页字节数不同。连续写入数据的时候,内部指针会+1,当内部指针移动到当前页末的时候,就会自动移动到当前页头部,再往里写数据的时候就会覆盖掉之前的数据。

        如果想要连续写多页数据,那就需要去判断是否需要翻页,如果地址是在另一页,就需要重新发送字节写的时序。

3.当前地址读

4.顺序读

 5.随机读

        顺序读是从当前地址开始读,那么随机读搭配顺序读即可以读取任意地址。随机读就是先发送写命令,让EEPROM将指针移动到要读取的位置,然后主机发送起始条件,发送从机地址(读写位为读),即开始顺序读。

四、代码

这里附上的代码是基于STM32 HAL库,模拟IIC读写EEPROM,对AT24CXX系列通用。

bsp_at24cxx.c

  1. /** BSP_AT24CXX.C EEPROM AT24CXX/FM24CXX驱动
  2. *
  3. * @author Dai Zu<zhangruilin@163.com>
  4. * @date 2024/4/10
  5. *
  6. */
  7. /* BSP头文件 */
  8. #include "BSP_AT24CXX.h"
  9. /* 宏定义 */
  10. #define AT24CXX_ADDR 0xA0 // 从机地址
  11. // #define AT24C01 {128, 8, AT24CXX_ADDR}
  12. // #define AT24C02 {256, 8, AT24CXX_ADDR}
  13. // #define AT24C04 {512, 16, AT24CXX_ADDR}
  14. // #define AT24C08 {1024, 16, AT24CXX_ADDR}
  15. // #define AT24C16 {2048, 16, AT24CXX_ADDR}
  16. // #define AT24C32 {4096, 32, AT24CXX_ADDR}
  17. #define AT24C64 {8192, 32, AT24CXX_ADDR}
  18. // #define AT24C128 {16384, 64, AT24CXX_ADDR}
  19. // #define AT24C256 {32768, 64, AT24CXX_ADDR}
  20. // #define AT24C512 {65536, 128, AT24CXX_ADDR}
  21. /* EEPROM结构体 */
  22. struct AT24CXX_TYPE
  23. {
  24. uint32_t size; // 容量,单位(字节)
  25. uint8_t pageSize; // 每页字节数
  26. uint8_t addr; // 从机地址
  27. };
  28. struct AT24CXX_TYPE EEPROM_TYPE= AT24C64;
  29. #define IIC_Soft 1 // 软件IIC
  30. #if IIC_Soft
  31. /* 使用STM32Hal库,以下是IIC接口,移植IIC时只需要实现以下接口即可 */
  32. #define IIC_SCL_PORT EEP_SCL_GPIO_Port
  33. #define IIC_SDA_PORT EEP_SDA_GPIO_Port
  34. #define IIC_SCL_PIN EEP_SCL_Pin
  35. #define IIC_SDA_PIN EEP_SDA_Pin
  36. #define IIC_SCL_SET HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET)
  37. #define IIC_SCL_RESET HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET)
  38. #define IIC_SDA_SET HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET)
  39. #define IIC_SDA_RESET HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET)
  40. #define READ_SDA HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN)
  41. void IIC_SDA_Dir(uint8_t dir);
  42. /* IO方向 */
  43. enum IIC_SDA_DIR
  44. {
  45. IIC_SDA_OUTPUT = 0,
  46. IIC_SDA_INPUT
  47. };
  48. enum IIC_ACK
  49. {
  50. ACK = 0,
  51. NACK = 1
  52. };
  53. /**
  54. * @brief 初始化IIC相关的外设
  55. */
  56. void IIC_MSP_Init(void)
  57. {
  58. }
  59. /**
  60. * @brief 初始化IIC
  61. */
  62. void IIC_Init(void)
  63. {
  64. IIC_MSP_Init();
  65. }
  66. /**
  67. * @brief 设置SDA的方向
  68. * @param dir IIC_SDA_DIR
  69. * IIC_SDA_OUTPUT or IIC_SDA_INPUT
  70. */
  71. void IIC_SDA_Dir(uint8_t dir)
  72. {
  73. if (dir == IIC_SDA_INPUT)
  74. {
  75. IIC_SDA_SET;
  76. }
  77. }
  78. void Delay_us(uint32_t us)
  79. {
  80. __IO uint32_t Delay = us * 48 / 8;//(SystemCoreClock / 8U / 1000000U)
  81. //见stm32f1xx_hal_rcc.c -- static void RCC_Delay(uint32_t mdelay)
  82. do
  83. {
  84. __NOP();
  85. }
  86. while (Delay --);
  87. }
  88. /**
  89. * @brief 产生IIC起始信号
  90. * SCL高电平期间,SDA产生下降沿
  91. */
  92. void IIC_Start(void)
  93. {
  94. IIC_SDA_Dir(IIC_SDA_OUTPUT);
  95. IIC_SDA_SET;
  96. IIC_SCL_SET;
  97. Delay_us(4);
  98. IIC_SDA_RESET;
  99. Delay_us(4);
  100. IIC_SCL_RESET;
  101. }
  102. /**
  103. * @brief 产生IIC停止信号
  104. * SCL高电平期间,SDA产生上升沿
  105. */
  106. void IIC_Stop(void)
  107. {
  108. IIC_SDA_Dir(IIC_SDA_OUTPUT);
  109. IIC_SCL_RESET;
  110. IIC_SDA_RESET;
  111. Delay_us(4);
  112. IIC_SCL_SET;
  113. Delay_us(4);
  114. IIC_SDA_SET;
  115. }
  116. /**
  117. * @brief 等待应答信号
  118. * @return ACK or NACK
  119. */
  120. uint8_t IIC_Wait_Ack(void)
  121. {
  122. uint8_t ucErrTime=0;
  123. IIC_SDA_Dir(IIC_SDA_INPUT);
  124. IIC_SDA_SET;Delay_us(1);
  125. IIC_SCL_SET;Delay_us(1);
  126. while(READ_SDA)
  127. {
  128. ucErrTime++;
  129. if(ucErrTime>250)
  130. {
  131. IIC_Stop();
  132. return NACK;
  133. }
  134. }
  135. IIC_SCL_RESET;
  136. return ACK;
  137. }
  138. /**
  139. * @brief 产生ACK应答
  140. */
  141. void IIC_Ack(void)
  142. {
  143. IIC_SCL_RESET;
  144. IIC_SDA_Dir(IIC_SDA_OUTPUT);
  145. IIC_SDA_RESET;
  146. Delay_us(2);
  147. IIC_SCL_SET;
  148. Delay_us(2);
  149. IIC_SCL_RESET;
  150. }
  151. /**
  152. * @brief 不应答
  153. */
  154. void IIC_NAck(void)
  155. {
  156. IIC_SCL_RESET;
  157. IIC_SDA_Dir(IIC_SDA_OUTPUT);
  158. IIC_SDA_SET;
  159. Delay_us(2);
  160. IIC_SCL_SET;
  161. Delay_us(2);
  162. IIC_SCL_RESET;
  163. }
  164. /**
  165. * @brief IIC发送一个字节
  166. */
  167. void IIC_Send_Byte(uint8_t txd)
  168. {
  169. uint8_t t;
  170. IIC_SDA_Dir(IIC_SDA_OUTPUT);
  171. IIC_SCL_RESET;
  172. for(t=0;t<8;t++)
  173. {
  174. if ((txd&0x80)>>7)
  175. {
  176. IIC_SDA_SET;
  177. }
  178. else
  179. {
  180. IIC_SDA_RESET;
  181. }
  182. txd<<=1;
  183. Delay_us(2);
  184. IIC_SCL_SET;
  185. Delay_us(2);
  186. IIC_SCL_RESET;
  187. Delay_us(2);
  188. }
  189. }
  190. /**
  191. * @brief 读一个字节
  192. * @param ack 是否发送应答
  193. * 0-发送应答,1-不发送应答
  194. */
  195. uint8_t IIC_Read_Byte(unsigned char ack)
  196. {
  197. unsigned char i,receive=0;
  198. IIC_SDA_Dir(IIC_SDA_INPUT);
  199. for(i=0;i<8;i++ )
  200. {
  201. IIC_SCL_RESET;
  202. Delay_us(2);
  203. IIC_SCL_SET;
  204. receive<<=1;
  205. if(READ_SDA)receive++;
  206. Delay_us(1);
  207. }
  208. if (ack)
  209. {
  210. IIC_NAck();
  211. }
  212. else
  213. {
  214. IIC_Ack();
  215. }
  216. return receive;
  217. }
  218. /**
  219. * @brief 初始化
  220. */
  221. void AT24CXX_Init(void)
  222. {
  223. IIC_Init();
  224. }
  225. /**
  226. * @brief 从指定地址读出一个数据
  227. * @param ReadAddr 数据地址
  228. * @retval 读取到的数据
  229. */
  230. uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr)
  231. {
  232. uint8_t temp=0;
  233. IIC_Start();
  234. if(EEPROM_TYPE.size>2048)
  235. {
  236. IIC_Send_Byte(EEPROM_TYPE.addr); //发送写命令
  237. IIC_Wait_Ack();
  238. IIC_Send_Byte(ReadAddr>>8);//发送高地址
  239. }else
  240. {
  241. IIC_Send_Byte(EEPROM_TYPE.addr+((ReadAddr/256)<<1)); //发送器件地址,写数据
  242. }
  243. IIC_Wait_Ack();
  244. IIC_Send_Byte(ReadAddr%256); //发送低地址
  245. IIC_Wait_Ack();
  246. IIC_Start();
  247. IIC_Send_Byte(EEPROM_TYPE.addr+1);
  248. IIC_Wait_Ack();
  249. temp=IIC_Read_Byte(1);
  250. IIC_Stop();
  251. return temp;
  252. }
  253. /**
  254. * @brief 向指定地址写入一个字节
  255. * @param WriteAddr 数据地址
  256. * @param DataToWrite 要写入的数据
  257. */
  258. void AT24CXX_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite)
  259. {
  260. IIC_Start();
  261. if(EEPROM_TYPE.size>2048)
  262. {
  263. IIC_Send_Byte(EEPROM_TYPE.addr); //发送写命令
  264. IIC_Wait_Ack();
  265. IIC_Send_Byte(WriteAddr>>8);//发送高地址
  266. }else
  267. {
  268. IIC_Send_Byte(EEPROM_TYPE.addr+((WriteAddr/256)<<1)); //发送器件地址,写数据
  269. }
  270. IIC_Wait_Ack();
  271. IIC_Send_Byte(WriteAddr%256); //发送低地址
  272. IIC_Wait_Ack();
  273. IIC_Send_Byte(DataToWrite);
  274. IIC_Wait_Ack();
  275. IIC_Stop();
  276. HAL_Delay(10);
  277. }
  278. /**
  279. * @brief 向指定地址写入16位或32位数据
  280. * @param WriteAddr 数据地址
  281. * @param DataToWrite 要写入的数据
  282. * @param Len 2字节或4字节
  283. */
  284. void AT24CXX_WriteLenByte(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len)
  285. {
  286. uint8_t t;
  287. for(t=0;t<Len;t++)
  288. {
  289. AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
  290. }
  291. }
  292. /**
  293. * @brief 从指定地址读取16位或32位的数据
  294. * @param WriteAddr 数据地址
  295. * @param Len 2字节或4字节
  296. * @retval 读出的数据
  297. */
  298. uint32_t AT24CXX_ReadLenByte(uint16_t ReadAddr,uint8_t Len)
  299. {
  300. uint8_t t;
  301. uint32_t temp=0;
  302. for(t=0;t<Len;t++)
  303. {
  304. temp<<=8;
  305. temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);
  306. }
  307. return temp;
  308. }
  309. /**
  310. * @brief 检查EEPROM是否正常
  311. * @retval 1-检测失败 0-成功
  312. */
  313. uint8_t AT24CXX_Check(void)
  314. {
  315. uint8_t temp;
  316. temp=AT24CXX_ReadOneByte(255);
  317. if(temp==0X55)return 0;
  318. else//排除第一次初始化的情况
  319. {
  320. AT24CXX_WriteOneByte(255,0X55);
  321. temp=AT24CXX_ReadOneByte(255);
  322. if(temp==0X55)return 0;
  323. }
  324. return 1;
  325. }
  326. /**
  327. * @brief 连续读
  328. * @param addr 数据地址
  329. * @param data 存储位置
  330. * @param length 读取长度
  331. *
  332. */
  333. void AT24CXX_SequentialRead(uint16_t addr,uint8_t *data,uint16_t length)
  334. {
  335. if (length == 0)
  336. {
  337. return;
  338. }
  339. IIC_Start();
  340. if(EEPROM_TYPE.size>2048)
  341. {
  342. IIC_Send_Byte(EEPROM_TYPE.addr); //发送写命令
  343. IIC_Wait_Ack();
  344. IIC_Send_Byte(addr>>8);//发送高地址
  345. }else
  346. {
  347. IIC_Send_Byte(EEPROM_TYPE.addr+((addr/256)<<1)); //发送器件地址,写数据
  348. }
  349. IIC_Wait_Ack();
  350. IIC_Send_Byte(addr%256); //发送低地址
  351. IIC_Wait_Ack();
  352. IIC_Start();
  353. IIC_Send_Byte(EEPROM_TYPE.addr+1);
  354. IIC_Wait_Ack();
  355. *data++=IIC_Read_Byte(0);
  356. while(--length)
  357. {
  358. *data++=IIC_Read_Byte(0);
  359. }
  360. IIC_Stop();
  361. }
  362. /**
  363. * @brief 页写
  364. * @param addr 数据地址
  365. * @param data 数据指针
  366. * @param length 要写入数据的个数
  367. */
  368. void AT24CXX_PageWrite(uint16_t addr,uint8_t *data,uint16_t length)
  369. {
  370. if (length==0 || addr>=EEPROM_TYPE.size)
  371. {
  372. return;
  373. }
  374. IIC_Start();
  375. if(EEPROM_TYPE.size>2048)
  376. {
  377. IIC_Send_Byte(EEPROM_TYPE.addr); // 发送写命令
  378. IIC_Wait_Ack();
  379. IIC_Send_Byte(addr>>8); // 发送高地址
  380. }else
  381. {
  382. IIC_Send_Byte(EEPROM_TYPE.addr+((addr/256)<<1)); //发送器件地址,写数据
  383. }
  384. IIC_Wait_Ack();
  385. IIC_Send_Byte(addr%256); // 发送低地址
  386. IIC_Wait_Ack();
  387. for (uint16_t i = 0; i < length; i++)
  388. {
  389. IIC_Send_Byte(data[i]);
  390. IIC_Wait_Ack();
  391. addr++;
  392. if (addr >= EEPROM_TYPE.size) // 内存已满
  393. {
  394. break;
  395. }
  396. if ((addr)%EEPROM_TYPE.pageSize == 0) // 满页
  397. {
  398. IIC_Stop();
  399. HAL_Delay(10);
  400. IIC_Start();
  401. if(EEPROM_TYPE.size>2048)
  402. {
  403. IIC_Send_Byte(EEPROM_TYPE.addr); // 发送写命令
  404. IIC_Wait_Ack();
  405. IIC_Send_Byte(addr>>8); // 发送高地址
  406. }else
  407. {
  408. IIC_Send_Byte(EEPROM_TYPE.addr+((addr/256)<<1)); //发送器件地址,写数据
  409. }
  410. IIC_Wait_Ack();
  411. IIC_Send_Byte(addr%256); // 发送低地址
  412. IIC_Wait_Ack();
  413. }
  414. }
  415. IIC_Stop();
  416. HAL_Delay(10);
  417. }
  418. #elif
  419. #endif

BSP_AT24CXX.h

  1. /** BSP_AT24CXX.h EEPROM AT24CXX/FM24CXX驱动
  2. *
  3. * @author Dai Zu<zhangruilin@163.com>
  4. * @date 2024/4/10
  5. *
  6. */
  7. #ifndef __BSP_AT24Cxx_H
  8. #define __BSP_AT24Cxx_H
  9. #include "main.h"
  10. uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr);
  11. void AT24CXX_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite);
  12. void AT24CXX_WriteLenByte(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len);
  13. uint32_t AT24CXX_ReadLenByte(uint16_t ReadAddr,uint8_t Len);
  14. void AT24CXX_SequentialRead(uint16_t addr,uint8_t *data,uint16_t length);
  15. void AT24CXX_PageWrite(uint16_t addr,uint8_t *data,uint16_t length);
  16. uint8_t AT24CXX_Check(void);
  17. void AT24CXX_Init(void);
  18. #endif

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/404295
推荐阅读
相关标签
  

闽ICP备14008679号