EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。EEPROM 可以在电脑上或专用设备上擦除已有信息,重新编程。一般用在即插即用。
AT24C02是一个2K位串行CMOS E2PROM, 内部含有256个8位字节,CATALYST公司的先进CMOS技术实质上减少了器件的功耗。AT24C02有一个16字节页写缓冲器。该器件通过IIC总线接口进行操作,有一个专门的写保护功能。
模块来源
规格参数
工作电压:1.8V-5.5V
工作电流:最大3mA
通信接口:IIC
内存:2048位
时钟速度:5V时最大1000Khz,其余为400Khz
以上信息见厂家资料文件
移植过程
查看资料
AT24C02设备地址
上图是AT24CXX的设备地址(第一行的为AT24C02,它的容量为2K),我们发现AT24CXX整个系列芯片的地址高四位都相同,都是1010,这四位是由生产商固化在芯片内部,无法改变。
AT24C02地址的低三位(不包括读写位)对应芯片的三个引脚,也就是说这三位是可以人为设定的,23=8,所以一条I2C总线上可以挂载8个AT24C02。
AT24C02的地址为7位二进制数,下图中最后一位是读写位(数据方向位),1 表示读数据,0 表示写数据。
这样,7位设备地址加1位读写位,构成I2C的寻址数据。I2C 总线的寻址过程中,通常在起始条件后的第一个字节决定了主机选择哪一个从机,该字节的最后一位决定数据传输方向。
AT24C02读写:AT24C02的存储空间为2K位(256字节),在对其进行写数据时,最小写入单位为字节(Byte),最大写入单位为页(Page),AT24C02页大小为 16 Byte。
字节写
在字节写模式下,主器件发送起始信号和从器件地址信息(R/W 位置零)给从器件,在从器件送回应答信号后,主器件发送 AT24WC01/02/04/08/16 的字节地址,主器件在收到从器件的应答信号后,再发送数据到被寻址的存储单元。AT24WC01/02/04/08/16 再次应答,并在主器件产生停止信号后开始内部数据的擦写,在内部擦写过程中,AT24WC01/02/04/08/16 不再应答主器件的任何请求。
页写
用页写,AT24WC01 可一次写入 8 个字节数据,AT24WC02/04/08/16 可以一次写入 16 个字节的数据,页写操作的启动和字节写一样,不同在于传送了一字节数据后并不产生停止信号,主器件被允许发送 P(AT24WC01 P=7;AT24WC02/04/08/16 P=15)个额外的字节。每发送一个字节数据后 AT24WC01/02/04/08/16 产生一个应答位并将字节地址低位加 1,高位保持不变。
如果在发送停止信号之前主器件发送超过P+1个字节,地址计数器将自动翻转,先前写入的数据被覆盖。
接收到P+1字节数据和主器件发送的停止信号后,AT24CXXX启动内部写周期将数据写到数据区,所有接收的数据在一个写周期内写入AT24WC01/02/04/08/16。
当前地址读
AT24WC01/02/04/08/16 的地址计数器内容为最后操作字节的地址加 1。也就是说 如果上次读/写的操作地址为 N,则立即读的地址从地址 N+1 开始。如果 N=E(这里对 24WC01 E=127;对 24WC02 E=255;对 24WC04 E=511;对 24WC08 E=1023;对 24WC16 E=2047)则计数器将翻转到 0 且继续输出数据。AT24WC01/02/04/08/16 接收到从器件地址信号后(R/W 位置 1),它首先发送一个应答信号,然后发送一个 8 位字节数据。主器件不需发送一个应答信号,但要产生一个停止信号。
选择读(随机读)
选择性读操作允许主器件对寄存器的任意字节进行读操作,主器件首先通过发送起始信号、从器件地址和它想读取的字节数据的地址执行一个伪写操作。在 AT24WC01/02/04/08/16 应答之后,主器件重新发送起始信号和从器件地址,此时 R/W 位置 1,AT24WC01/02/04/08/16 响应并发送应答信号,然后输出所要求的一个 8 位字节数据,主器件不发送应答信号但产生一个停止信号。
连续读
连续读操作可通过立即读或选择性读操作启动。在 AT24WC01/02/04/08/16 发送完一个 8 位字节数据后,主器件产生一个应答信号来响应,告知 AT24WC01/02/04/08/16 主器件要求更多的数据,对应每个主机产生的应答信号 AT24WC01/02/04/08/16 将发送一个 8 位数据字节。当主器件不发送应答信号而发送停止位时结束此操作。
从 AT24WC01/02/04/08/16 输出的数据按顺序由 N 到 N+1 输出。读操作时地址计数器在 AT24WC01/02/04/08/16 整个地址内增加,这样整个寄存器区域在可在一个读操作内全部读出。当读取的字节超过 E(对于 24WC01 E=127;对 24WC02 E=255; 对 24WC04 E=511;对 24WC08 E=1023;对 24WC16 E=2047)计数器将翻转到零并继续输出数据字节。
引脚选择
这里选择的引脚见引脚接线表
代码移植
下载为大家准备的驱动代码文件夹,复制到自己工程中\luban-lite\application\rt-thread\helloworld\user-bsp
文件夹下
提示
如果未找到 user-bsp
这个文件夹,说明你未进行模块移植的前置操作。请转移到手册使用必要操作(点击跳转)中进行必要的配置操作!!!
接下来打开自己的工程,开始修改Kconfig文件。
1、在 VSCode 中打开 application\rt-thread\helloworld\Kconfig 文件
2、在该文件的 #endif
前面添加该模块的 Kconfig路径语句
# AT24C02-EEPROM存储器
source "application/rt-thread/helloworld/user-bsp/at24c02-eeprom-module/Kconfig"
2
menuconfig操作
1、我们 双击 luban-lite
文件夹下的 win_env.bat
脚本打开env工具:
2、输入以下命令列出所有可用的默认配置:
scons --list-def
3、选择 d13x_JLC_rt-thread_helloworld
这个配置!这个是我们衡山派开发板的默认配置!输入以下命令即可:
scons --apply-def=7
或者
scons --apply-def=d13x_JLC_rt-thread_helloworld_defconfig
这两个命令作用是一样的,一个是 文件名 ,一个是 编号 !!!
4、输入以下命令进入menuconfig菜单
scons --menuconfig
进入以下界面:
5、选中 Porting code using the LCKFB module
按
Y
选中按
N
取消选中方向键
左右
调整 最下面菜单的选项方向键
上下
调整 列表的选项
回车
执行最下面菜单的选项
6、回车进入 Porting code using the LCKFB module
菜单
7、按方向键 上下
选中 Using AT24C02 EEPROM module
后按 Y
键,看到前面括号中出现一个 *
号,就可以下一步了。
8、按方向键 左右
选中 <Save>
然后一路回车
,然后 退出
即可
编译
我们 保存并退出menuconfig菜单 之后,输入以下命令进行编译:
scons
或
scons -j16
-j 用来选择参与编译的核心数: 我这里是选择16
大家可以根据自己的电脑来选择
核心越多编译越快
如果写的数量高于电脑本身,那么就自动按照最高可用的来运行!
镜像烧录
编译完成之后会在 \luban-lite\output\d13x_JLC_rt-thread_helloworld\images
文件夹下生成一个 d13x_JLC_v1.0.0.img
镜像文件!
然后我们烧录镜像,具体的教程请查看:镜像烧录(点击跳转🚀)
到这里完成了,请移步到 最后一节 进行移植验证。
工程代码解析
bsp_at24c02.c
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include <inttypes.h>
#include <finsh.h>
#include "hal_adcim.h"
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_log.h"
#include "hal_gpai.h"
#include "aic_hal_gpio.h"
#include "hal_i2c.h"
#include "bsp_at24c02.h"
#define I2C_BUS_NAME "i2c0" /* I2C总线设备名称 */
#define SLAVE_ADDR 0x50 /* 器件地址 */
static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C总线设备句柄 */
/* 发送数据 */
static rt_err_t write_data(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *data)
{
struct rt_i2c_msg msgs;
msgs.addr = SLAVE_ADDR;
msgs.flags = RT_I2C_WR;
msgs.buf = data;
msgs.len = len;
/* 调用I2C设备接口传输数据 */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
/* 读取数据 */
static rt_err_t read_data(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf)
{
struct rt_i2c_msg msgs;
msgs.addr = SLAVE_ADDR; // 器件地址
msgs.flags = RT_I2C_RD; // 读写标志的设定
msgs.buf = buf; // 发送缓存区地址
msgs.len = len; // 发送的字节长度
/* 调用I2C设备接口传输数据 */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
/**********************************************************
* 函 数 名 称:AT24C02_Write_Byte
* 函 数 功 能:向指定地址写入一个字节数据
* 传 入 参 数: Addr_Buff - 地址
* Data_Buff - 发送的数据
* 函 数 返 回:RT_OK:完成 -RT_ERROR:错误
* 作 者:LCKFB
* 备 注:
**********************************************************/
int AT24C02_Write_Byte(uint8_t Addr, uint8_t Data)
{
uint8_t Send_Buff[2] = {Addr, Data};
/* 调用write_data函数发送数据 */
if (write_data(i2c_bus, 2, Send_Buff) != RT_EOK)
{
LOG_E("%s-->[ write_data Error ]", __FUNCTION__);
return -RT_ERROR;
}
return RT_EOK;
}
/**********************************************************
* 函 数 名 称:AT24C02_Read_Data
* 函 数 功 能:从指定地址读取一个字节数据
* 传 入 参 数: Addr_Buff - 地址
* Data_Buff - 接收数据的地址
* 函 数 返 回:RT_OK:完成 -RT_ERROR:错误
* 作 者:LCKFB
* 备 注:
**********************************************************/
int AT24C02_Read_Byte(uint8_t Addr, uint8_t *RecvData)
{
/* 发送地址 */
if(RT_EOK != write_data(i2c_bus, 1, &Addr))
{
LOG_E("%s-->[write_data] Error !!",__FUNCTION__);
return -RT_ERROR;
}
/* 读取数据 */
if(RT_EOK != read_data(i2c_bus, 1, RecvData))
{
LOG_E("%s-->[read_data] Error !!",__FUNCTION__);
return -RT_ERROR;
}
return RT_EOK;
}
/**********************************************************
* 函 数 名 称:AT24C02_Init
* 函 数 功 能:初始化AT24C02
* 传 入 参 数:无
* 函 数 返 回:RT_OK:完成 -RT_ERROR:错误
* 作 者:LCKFB
* 备 注:
**********************************************************/
int AT24C02_Init(void)
{
/* 查找I2C总线设备,获取I2C总线设备句柄 */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(I2C_BUS_NAME);
if(i2c_bus == RT_NULL)
{
LOG_E("no device: %s",I2C_BUS_NAME);
return -RT_ERROR;
}
else
{
rt_kprintf("\nfind device: %s\n",I2C_BUS_NAME);
}
return RT_EOK;
}
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
bsp_at24c02.h
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#ifndef __BSP_AT24C02_H__
#define __BSP_AT24C02_H__
#include "stdio.h"
int AT24C02_Init(void);
int AT24C02_Write_Byte(uint8_t Addr, uint8_t Data);
int AT24C02_Read_Byte(uint8_t Addr, uint8_t *RecvData);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Kconfig
这个是一个menuconfig中的选项,如果在菜单中选中该选项,就会在rtconfig.h
中定义一个语句,用来if判断条件编译之类的。
config LCKFB_AT24C02_EEPROM_MODULE
bool "Using AT24C02 EEPROM module"
select AIC_USING_I2C0
default n
help
More information is available at: https://wiki.lckfb.com/
2
3
4
5
6
SConscript
自动化构建文件,如果定义了 LCKFB_AT24C02_EEPROM_MODULE
和 USING_LCKFB_TRANSPLANT_CODE
就自动编译当前目录下的文件!!
Import('RTT_ROOT')
Import('rtconfig')
import rtconfig
from building import *
cwd = GetCurrentDir()
CPPPATH = [cwd]
src = []
if GetDepend('LCKFB_AT24C02_EEPROM_MODULE') and GetDepend('USING_LCKFB_TRANSPLANT_CODE'):
src = Glob(os.path.join(cwd, '*.c'))
group = DefineGroup('lckfb-at24c02-eeprom-module', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
test_at24c02_eeprom_module.c
这个文件定义了一个处理AT24C02 EEPROM模块的函数test_at24c02_eeprom_module
,它通过一系列的写和读操作来验证AT24C02 EEPROM芯片的功能。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_hal_gpio.h"
#include "bsp_at24c02.h"
static void test_at24c02_eeprom_module(int argc, char **argv)
{
int ret = 0;
uint8_t recv_buff[2] = {0};
/* 初始化 */
if(RT_EOK != AT24C02_Init())
{
LOG_E("AT24C02_Init Error !!");
return;
}
rt_kprintf("\nStart Run AT24C02 Module--->\n");
//向0地址写入数据48
ret = AT24C02_Write_Byte(0, 48);
if(ret != RT_EOK)
{
LOG_E("%s-->[AT24C02_Write_Byte]",__FUNCTION__);
}
rt_kprintf("\nWrite to the [0] address: 48\n");
rt_thread_mdelay(5);
//向8地址写入数据66
ret = AT24C02_Write_Byte(8, 66);
if(ret != RT_EOK)
{
LOG_E("%s-->[AT24C02_Write_Byte]",__FUNCTION__);
}
rt_kprintf("\nWrite to the [8] address: 66\n");
rt_thread_mdelay(5);
//从0地址读取数据到recv_buff[0]
ret = AT24C02_Read_Byte(0, &recv_buff[0]);
if(ret != RT_EOK)
{
LOG_E("%s-->[AT24C02_Read_Byte]",__FUNCTION__);
}
rt_thread_mdelay(5);
//从8地址读取数据到recv_buff[1]
AT24C02_Read_Byte(8, &recv_buff[1]);
if(ret != RT_EOK)
{
LOG_E("%s-->[AT24C02_Read_Byte]",__FUNCTION__);
}
rt_thread_mdelay(5);
rt_thread_mdelay(50);
//输出recv_buff查看数据是否正确
rt_kprintf("\n\n================================\n");
rt_kprintf("recv_buff[0] = %d\n",recv_buff[0]);
rt_kprintf("recv_buff[1] = %d\n",recv_buff[1]);
rt_kprintf("\n================================\n\n");
rt_kprintf("\nThe AT24C02 EEPROM module is finished running !!\n");
}
// 导出函数为命令
MSH_CMD_EXPORT(test_at24c02_eeprom_module, run AT24C02-EEPROM module);
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
函数逻辑
- 定义了一个整型变量
ret
用于存储函数返回状态,以及一个recv_buff
数组用于存储读取到的数据。 - 调用
AT24C02_Init
函数初始化AT24C02 EEPROM模块。 - 向地址0写入字节值48,然后延时5毫秒以确保写入完成。
- 向地址8写入字节值66,然后延时5毫秒以确保写入完成。
- 从地址0读取字节值到
recv_buff[0]
,然后延时5毫秒以确保读取完成。 - 从地址8读取字节值到
recv_buff[1]
,然后延时5毫秒以确保读取完成。 - 延时50毫秒后,打印
recv_buff
数组的内容以验证写入和读取的数据是否一致。 - 打印完成信息,表明AT24C02 EEPROM模块测试运行结束。
此外,该文件还定义了一个命令test_at24c02_eeprom_module
,用于运行AT24C02 EEPROM模块的测试函数。
test_at24c02_eeprom_module
命令执行test_at24c02_eeprom_module
函数,该函数初始化AT24C02 EEPROM模块,执行写入和读取操作,并打印结果以验证EEPROM的功能。
以下是代码中的关键点:
AT24C02_Init
函数用于初始化AT24C02 EEPROM模块。AT24C02_Write_Byte
函数用于向EEPROM的指定地址写入一个字节。AT24C02_Read_Byte
函数用于从EEPROM的指定地址读取一个字节。aicos_mdelay
函数用于实现毫秒级的延时。LOG_E
宏用于打印错误信息。MSH_CMD_EXPORT
宏用于将函数导出为命令行接口,使得用户可以在命令行中直接调用这些函数。
通过在命令行中输入test_at24c02_eeprom_module
,用户可以运行AT24C02 EEPROM模块的测试函数,它会初始化模块,执行写入和读取操作,并打印结果以验证EEPROM的功能。如果写入和读取的数据一致,则表明EEPROM模块工作正常。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_hal_gpio.h"
#include "bsp_at24c02.h"
static void test_at24c02_eeprom_module(int argc, char **argv)
{
int ret = 0;
uint8_t recv_buff[2] = {0};
/* 初始化 */
if(RT_EOK != AT24C02_Init())
{
LOG_E("AT24C02_Init Error !!");
return;
}
rt_kprintf("\nStart Run AT24C02 Module--->\n");
//向0地址写入数据48
ret = AT24C02_Write_Byte(0, 48);
if(ret != RT_EOK)
{
LOG_E("%s-->[AT24C02_Write_Byte]",__FUNCTION__);
}
rt_kprintf("\nWrite to the [0] address: 48\n");
rt_thread_mdelay(5);
//向8地址写入数据66
ret = AT24C02_Write_Byte(8, 66);
if(ret != RT_EOK)
{
LOG_E("%s-->[AT24C02_Write_Byte]",__FUNCTION__);
}
rt_kprintf("\nWrite to the [8] address: 66\n");
rt_thread_mdelay(5);
//从0地址读取数据到recv_buff[0]
ret = AT24C02_Read_Byte(0, &recv_buff[0]);
if(ret != RT_EOK)
{
LOG_E("%s-->[AT24C02_Read_Byte]",__FUNCTION__);
}
rt_thread_mdelay(5);
//从8地址读取数据到recv_buff[1]
AT24C02_Read_Byte(8, &recv_buff[1]);
if(ret != RT_EOK)
{
LOG_E("%s-->[AT24C02_Read_Byte]",__FUNCTION__);
}
rt_thread_mdelay(5);
rt_thread_mdelay(50);
//输出recv_buff查看数据是否正确
rt_kprintf("\n\n================================\n");
rt_kprintf("recv_buff[0] = %d\n",recv_buff[0]);
rt_kprintf("recv_buff[1] = %d\n",recv_buff[1]);
rt_kprintf("\n================================\n\n");
rt_kprintf("\nThe AT24C02 EEPROM module is finished running !!\n");
}
// 导出函数为命令
MSH_CMD_EXPORT(test_at24c02_eeprom_module, run AT24C02-EEPROM module);
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
移植验证
我们使用串口调试,将 USB转TTL模块 连接到衡山派开发板上面!!
具体的教程查看:串口调试(点击跳转🚀)
串口波特率默认为
115200
我们在输入下面的命令运行该模块的线程:
输入的时候按下
TAB键
会进行命令补全!!
test_at24c02_eeprom_module
模块上电效果: