STM32F1与STM32CubeIDE编程实例-设备驱动-EEPROM-AT24C256驱动

EEPROM-AT24C256驱动

1、EEPROM与AT24C256介绍

EEPROM(电可擦除可编程只读存储器)是用户可修改的只读存储器(ROM),可以通过施加高于正常电压的电压反复擦除和重新编程(写入)。 与 EPROM 芯片不同,EEPROM 不需要从计算机中取出即可进行修改。 但是,EEPROM 芯片必须整体擦除和重新编程,而不是选择性地擦除和重新编程。 它还具有有限的寿命——也就是说,它可以重新编程的次数被限制在数万或数十万次。 在计算机使用过程中经常重新编程的 EEPROM 中,EEPROM 的寿命可能是一个重要的设计考虑因素。

一种特殊形式的 EEPROM 是闪存,它使用正常的 PC 电压进行擦除和重新编程。

AT24C128/256 提供 131,072/262,144 位串行电可擦除可编程只读存储器 (EEPROM),组织为 16,384/32,768 个字,每个字为 8 位。 该设备的级联功能允许多达 4 个设备共享一个公共的两线总线。 该器件针对许多工业和商业应用进行了优化,在这些应用中,低功率和低电压操作是必不可少的。 这些器件采用节省空间的 8 引线 JEDEC PDIP、8 引线 JEDEC SOIC、8 引线 EIAJSOIC、8 引线 MAP (24C128)、8 引线 TSSOP、8 引线 SOIC 阵列封装和 8 焊球 dBGA2 封装 . 此外,整个系列提供 2.7V(2.7V 至 5.5V)和 1.8V(1.8V 至 3.6V)版本。

在这里插入图片描述

关于AT24Cxx的驱动在前面的文章做了详细的描述,请参考:

2、AT24C256配置

开发环境搭建、系统时钟配置、调试配置及串口配置,请参考:

2.1 AT24C256的I2C配置

在这里插入图片描述

各项设置完成后,保存并生成代码。

3、AT24C256驱动实现

3.1 基本定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef \_AT24CXX\_H
#define \_AT24CXX\_H

#include "i2c.h"

#ifdef \_\_cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>

typedef struct {
uint16\_t id;
I2C_HandleTypeDef\* i2c_handle;
uint16\_t size_kb;
uint8\_t device_address;
uint16\_t p_size; // 页面大小
bool is_locked;
}at24cxx\_t;

// 初始化
bool at24cxx\_init(at24cxx\_t\* device);
// 判断设备是否已经连接
bool at24cxx\_is\_connected(at24cxx\_t\* device);
// 在指定地址写数据
bool at24cxx\_write(at24cxx\_t\* device,uint16\_t address, uint8\_t \*data, size\_t len, uint32\_t timeout);
// 从指定地址读数据
bool at24cxx\_read(at24cxx\_t\* device,uint16\_t address, uint8\_t \*data, size\_t len, uint32\_t timeout);
// 芯片擦除
bool at24cxx\_erase\_chip(at24cxx\_t\* device);

#ifdef \_\_cplusplus
}
#endif

#endif


3.2 驱动功能实现

3.2.1 导入相关头文件
1
2
3
4
5
// at24cxx.c
#include "at24cxx.h"
#include "main.h"
#include "i2c.h"

3.2.2 设备初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bool at24cxx\_init(at24cxx\_t \*device) {
switch (device->size_kb) {
case 1:
case 2:
device->p_size = 8;
break;
case 4:
case 8:
case 16:
device->p_size = 16;
break;
default:
device->p_size = 32;
}
device->is_locked = false;
return true;
}


3.2.2 判断设备是否连接
1
2
3
4
5
6
7
8
9
// at24cxx.c
bool at24cxx\_is\_connected(at24cxx\_t \*device) {
if (HAL\_I2C\_IsDeviceReady(device->i2c_handle, device->device_address, 2,
100) == HAL_OK)
return true;
else
return false;
}

函数HAL_I2C_IsDeviceReady用于判断I2C设备是否已经连接。如果已经连接,则返回true;否则,返回false。

3.2.3 写数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// at24cxx.c
bool at24cxx\_write(at24cxx\_t \*device, uint16\_t address, uint8\_t \*data,
size\_t len, uint32\_t timeout) {
if (device->is_locked)
return false;

device->is_locked = true;
uint16\_t w;
uint32\_t startTime = HAL\_GetTick();
uint16\_t dev_address, mem_address, mem_add_size;
mem_add_size = I2C_MEMADD_SIZE_8BIT;
while (1) {
w = device->p_size - (device->device_address % device->p_size);
if (w > len) {
w = len;
}
if (device->size_kb == 1 || device->size_kb == 2) {
dev_address = device->device_address;
mem_address = address;
} else if (device->size_kb == 4) {
dev_address = device->device_address | ((address & 0x0100) >> 7);
mem_address = (address & 0xff);
} else if (device->size_kb == 8) {
dev_address = device->device_address | ((address & 0x0300) >> 7);
mem_address = (address & 0xff);
} else if (device->size_kb == 16) {
dev_address = device->device_address | ((address & 0x0700) >> 7);
mem_address = (address & 0xff);
} else {
dev_address = device->device_address;
mem_address = address;
mem_add_size = I2C_MEMADD_SIZE_16BIT;
}
if (HAL\_I2C\_Mem\_Write(device->i2c_handle, dev_address, mem_address,
mem_add_size, data, w, 100) == HAL_OK) {
HAL\_Delay(10);
len -= w;
data += w;
address += w;
if (len == 0) {
device->is_locked = false;
return true;
}
if (HAL\_GetTick() - startTime >= timeout) {
device->is_locked = false;
return false;
}
} else {
device->is_locked = false;
return false;
}
}
}

在上面代码中,主要步骤如下:

  • 1)通过储存页面大小和写入地址计算储存地址偏移值。
  • 2)使用HAL_I2C_Mem_Write函数写数据,并计算超时。
  • 3)重复1)和第2)步,直到指定数据长度写完。
3.2.3 读取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// at24cxx.c
bool at24cxx\_read(at24cxx\_t \*device, uint16\_t address, uint8\_t \*data,
size\_t len, uint32\_t timeout) {
if (device->is_locked)
return false;
device->is_locked = true;
uint16\_t dev_address, mem_address, mem_add_size;
mem_add_size = I2C_MEMADD_SIZE_8BIT;

if (device->size_kb == 1 || device->size_kb == 2) {
dev_address = device->device_address;
mem_address = address;
} else if (device->size_kb == 4) {
dev_address = device->device_address | ((address & 0x0100) >> 7);
mem_address = (address & 0xff);
} else if (device->size_kb == 8) {
dev_address = device->device_address | ((address & 0x0300) >> 7);
mem_address = (address & 0xff);
} else if (device->size_kb == 16) {
dev_address = device->device_address | ((address & 0x0700) >> 7);
mem_address = (address & 0xff);
} else {
dev_address = device->device_address;
mem_address = address;
mem_add_size = I2C_MEMADD_SIZE_16BIT;
}

if (HAL\_I2C\_Mem\_Read(device->i2c_handle, dev_address, mem_address,
mem_add_size, data, len, timeout) == HAL_OK) {
device->is_locked = false;
return true;
} else {
device->is_locked = false;
return false;
}
}

3.2.4 储存擦除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// at24cxx.c
bool at24cxx\_erase\_chip(at24cxx\_t \*device) {
const uint8\_t eraseData[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF\
, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF };
uint32\_t bytes = 0;
while (bytes < (device->size_kb\* 128)) {
if (at24cxx\_write(device, bytes, (uint8\_t\*) eraseData,
sizeof(eraseData), 100) == false)
return false;
bytes += sizeof(eraseData);
}
return true;
}

3.2.5 主程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// main.c
/\* USER CODE END Header \*/
/\* Includes ------------------------------------------------------------------\*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"

/\* Private includes ----------------------------------------------------------\*/
#include "AT24CXX/at24cxx.h"
#include <stdio.h>
/\* USER CODE END Includes \*/

/\* Private typedef -----------------------------------------------------------\*/
/\* USER CODE BEGIN PTD \*/
/\* USER CODE END PTD \*/

/\* Private define ------------------------------------------------------------\*/
/\* USER CODE BEGIN PD \*/
/\* USER CODE END PD \*/

/\* Private macro -------------------------------------------------------------\*/
/\* USER CODE BEGIN PM \*/

/\* USER CODE END PM \*/

/\* Private variables ---------------------------------------------------------\*/

/\* USER CODE BEGIN PV \*/

bool writeStatus = false;
bool readStatus = false;
bool eraseStatus = false;
uint8\_t wData[] = "Hello World 123";
uint8\_t rData[25];
/\* USER CODE END PV \*/

/\* Private function prototypes -----------------------------------------------\*/
void SystemClock\_Config(void);
/\* USER CODE BEGIN PFP \*/

/\* USER CODE END PFP \*/

/\* Private user code ---------------------------------------------------------\*/
/\* USER CODE BEGIN 0 \*/
at24cxx\_t at24cxx;
/\* USER CODE END 0 \*/

/\*\*
\* @brief The application entry point.
\* @retval int
\*/
int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_I2C1\_Init();
MX\_USART1\_UART\_Init();
/\* USER CODE BEGIN 2 \*/
printf("AT24C256 EPPROM DEMO \r\n");
at24cxx.i2c_handle = &hi2c1;
at24cxx.id = 1;
at24cxx.device_address = 0xA0;
at24cxx.size_kb = 256; // 256Kb
at24cxx\_init(&at24cxx);
if(at24cxx\_is\_connected(&at24cxx)){
if(at24cxx\_erase\_chip(&at24cxx)){
printf("at24c256 erased\r\n");
}else{
printf("at24c256 erase failed\r\n");
}
if(at24cxx\_write(&at24cxx,MEM_ADDR, wData, 15, 100)){
printf("write data:%s\r\n",wData);
}else{
printf("write data@0x00 failed\r\n");
}
if(at24cxx\_read(&at24cxx, MEM_ADDR, rData, 15, 100)){
printf("read data from@0x00:%s\r\n ",rData);
}else{
printf("read data failed\r\n");
}
}else{
printf("at24c256 not connected\r\n");
}
/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
}
/\* USER CODE END 3 \*/
}


4、运行结果

在这里插入图片描述

文章来源: https://iotsmart.blog.csdn.net/article/details/125238855