OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 53|回复: 0

《ESP32-P4开发指南— V1.0》第二十三章 IIC_EEPROM实验

[复制链接]

1227

主题

1241

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5280
金钱
5280
注册时间
2019-5-8
在线时间
1347 小时
发表于 4 天前 | 显示全部楼层 |阅读模式
第二十三章 IIC_EEPROM实验

1)实验平台:正点原子DNESP32P4开发板

2)章节摘自【正点原子】ESP32-P4开发指南— V1.0

3)购买链接:https://detail.tmall.com/item.htm?id=873309579825

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子DNESP32S3开发板技术交流群:132780729


2.jpg

3.png

本章将学习ESP32-P4的硬件IIC接口去驱动24C02器件。在本章节,实现和24C02之间的双向通信,并把结果显示在LCD模块上。
本章分为如下几个小节:
23.1 24C02介绍
23.2 硬件设计
23.3 程序设计
23.4 下载验证


23.1 24C02介绍
关于IIC的描述,请回顾第十九章IIC_EXIO实验,本小节主要介绍24C02,首先科普一下何为EEPROM存储器?
其实EEPROM全程是“电可擦除可编程只读存储器”,即“Electrically Erasable Programmable Read-Only Memory”,特性就是数据掉电不丢失。
24C02是一个2K bit的串行EEPROM存储器,内部含有256个字节。在24C02里面还有一个8字节的页写缓冲器。该设备的通信方式IIC,通过其SCL和SDA与其他设备通信,芯片的引脚图如下图所示。

第二十三章 IIC414.png
图23.1.1 24C02引脚图

上图的WP引脚是写保护引脚,接高电平只读,接地允许读和写,我们的板子设计是把该引脚接地。每一个设备都有自己的设备地址,24C02也不例外。前面提及到有7位寻址、11位寻址,这里的位数就是设备地址位数,24C02的设备地址就是7位的,具体格式如下图所示。

第二十三章 IIC561.png
图23.1.2 24C02地址格式

24C02的设备地址是包括不可编程部分和可编程部分,不可编程部分也就是“1010”,可编程部分是根据上图的硬件引脚A0、A1和A2所决定。根据我们的板子设计,A0、A1和A2均接地处理,所以24C02设备地址为“1010000”即0x50。
这里还会涉及到24C02通信地址的概念,通信地址就是写操作地址和读操作地址,简单来说,就是设备地址和一个读写位的配合。上图中的地址格式最后一位R/W用于设置数据的传输方向,即读操作/写操作,0是写操作,1是读操作,所以24C02的读操作地址为:0xA1(0x50 << 1 | 1),写操作地址为:0xA0(0x50 << 1 | 0)。
下面把实验中到的数据传输时序讲解一下,分别是对24C02的写时序和读时序。24C02写时序图如下图所示。


第二十三章 IIC926.png
图23.1.3 24C02写时序图

上图展示的主机向24C02写操作时序图,主机在IIC总线发送第1个字节的数据为24C02的写操作地址0xA0(设备地址0x50 << 1 | 0),用于寻找总线上找到24C02,在获得24C02的应答信号之后,继续发送第2个字节数据,该字节数据是24C02的内存地址,再等到24C02的应答信号,主机继续发送第3字节数据,这里的数据即是写入在第2字节内存地址的数据。主机完成写操作后,可以发出停止信号,终止数据传输。
上面的写操作只能单字节写入到24C02,效率比较低,所以24C02有页写入时序,大大提高了写入效率,下面看一下24C02页写时序图,如下图所示。


第二十三章 IIC1228.png
图23.1.4 24C02页写时序

在单字节写时序时,每次写入数据时都需要先写入设备的内存地址才能实现,在页写时序中,只需要告诉24C02第一个内存地址1,后面数据会按照顺序写入到内存地址2,内存地址3等,大大节省了通信时间,提高了时效性。因为24C02每次只能8bit数据,所以它的页大小也就是1字节。页写时序的操作方式跟上面的单字节写时序差不多,所以不作过多解释了。参考以上说明去理解页写时序。
说完两种写入方式之后,下图是关于24C02的读时序。


第二十三章 IIC1458.png
图23.1.5 24C02读时序图

24C02读取数据的过程是一个复合的时序,其中包含写时序和读时序。先看第一个通信过程,这里是写时序,起始信号产生后,主机发送24C02的写操作地址0xA0(设备地址0x50 << 1 | 0),获取从机应答信号后,接着发送需要读取的内存地址;在读时序中,起始信号产生后,主机发送24C02的读操作地址0xA1(设备地址0x50 << 1 | 1),获取从机应答信号后,接着从机返回刚刚在写时序中内存地址的数据,以字节为单位传输在总线上,假如主机获取数据后返回的是应答信号,那么从机会一直传输数据,当主机发出的是非应答信号并以停止信号发出为结束,从机就会结束传输。

23.2 硬件设计

23.2.1 例程功能
每按下KEY1,MCU通过IIC总线向24C02写入数据,通过按下KEY0来控制24C02读取数据。同时在LCD上面显示相关信息。LED0闪烁用于提示程序正在运行。

23.2.2 硬件资源
1)LED灯
        LED        0        - IO51
2)RGBLCD / MIPILCD(引脚太多,不罗列出来)
3)XL9555
        IIC_INT        - IO36
        IIC_SDA        - IO33
        IIC_SCL        - IO32
        EXIO_8        - KEY0
        EXIO_9        - KEY1
4)24C02
        IIC_SDA        - IO33
        IIC_SCL        - IO32

23.2.3 原理图
24C02器件相关原理图,如下图所示。

第二十三章 IIC2082.png
图23.2.3.1 24C02硬件原理图

23.3 程序设计
IIC外设驱动已经在第十九章19.3.1小节做了说明,这里就不再赘述了。

23.3.1 程序流程图

第二十三章 IIC2191.png
图23.3.1.1 IIC_EEPROM实验程序流程图

23.3.2 程序解析
在14_iic_eeprom例程中,作者在14_iic_eeprom\components\BSP路径下新建了1个文件夹AT24C02,并且需要更改CMakeLists.txt内容,以便在其他文件上调用。
IIC驱动代码跟19.3.3小节的IIC驱动代码部分是一样的,这里就不再赘述了,直接对at24c02驱动做介绍。
1. AT24C02驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。AT24CXX驱动源码包括两个文件:at24c02.c和at24c02.h。
下面先解析at24c02.h的程序。对AT24C02的器件地址和AT24C02芯片最大地址做了相关定义。
  1. <font size="3">#define AT_ADDR     0x50                /* 24c02设备地址 */</font>
  2. <font size="3">#define AT24C02     255</font>
复制代码
接下来,解析一下at24c02.c的程序,首先来看一下AT24C02器件的初始化函数at24c02_init,代码如下:
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief             初始化AT24C02</font>
  3. <font size="3"> * [url=home.php?mod=space&uid=271674]@param[/url]             无</font>
  4. <font size="3"> * @retval             ESP_OK:初始化成功</font>
  5. <font size="3"> */</font>
  6. <font size="3">esp_err_t at24c02_init(void)</font>
  7. <font size="3">{</font>
  8. <font size="3">    /* 未调用myiic_init初始化IIC */</font>
  9. <font size="3">    if (bus_handle == NULL)</font>
  10. <font size="3">    {</font>
  11. <font size="3">        ESP_ERROR_CHECK(myiic_init());</font>
  12. <font size="3">    }</font>

  13. <font size="3">    i2c_device_config_t eeprom_i2c_dev_conf = {</font>
  14. <font size="3">        .dev_addr_length = I2C_ADDR_BIT_LEN_7,  /* 从机地址长度 */</font>
  15. <font size="3">        .scl_speed_hz    = IIC_SPEED_CLK,       /* 传输速率 */</font>
  16. <font size="3">        .device_address  = AT_ADDR,             /* 从机7位的地址 */</font>
  17. <font size="3">    };</font>
  18. <font size="3">    /* I2C总线上添加AT24C02设备 */</font>
  19. <font size="3">ESP_ERROR_CHECK(i2c_master_bus_add_device(        bus_handle, &eeprom_i2c_dev_conf, </font>
  20. <font size="3">&eeprom_handle));</font>

  21. <font size="3">    return ESP_OK;</font>
  22. <font size="3">}</font>
复制代码
在AT24C02初始化函数中,首先对eeprom_i2c_dev_conf变量的成员进行赋值,设置AT24C02的地址长度、设备地址以及传输速率,然后调用i2c_master_bus_add_device函数对AT24C02设备进行初始化。
下面先看一下at24c02_write_one_byte函数,实现在AT24C02芯片指定地址写入一个数据,代码如下:
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief                 在AT24C02指定地址写入一个数据</font>
  3. <font size="3"> * @param             addr: 写入数据的目的地址</font>
  4. <font size="3"> * @param             data: 要写入的数据</font>
  5. <font size="3"> * @retval            无</font>
  6. <font size="3"> */</font>
  7. <font size="3">void at24c02_write_one_byte(uint8_t addr, uint8_t data)</font>
  8. <font size="3">{</font>
  9. <font size="3">uint8_t send_buf[2] = {0};</font>

  10. <font size="3">    send_buf[0] = addr % 256;</font>
  11. <font size="3">    send_buf[1] = data;</font>

  12. <font size="3">    ESP_ERROR_CHECK(i2c_master_transmit(eeprom_handle, send_buf, 2, -1));</font>

  13. <font size="3">    /* 由于AT24C02写入过慢,需延迟10ms左右时间 */</font>
  14. <font size="3">    esp_rom_delay_us(10000);</font>
  15. <font size="3">}</font>
复制代码
该函数的实现,主要调用i2c_master_transmit函数。在这里需要进行数据整合,把写入数据的目的地址和要写入的数据重新存放到一个buf。在这里需要注意存放顺序,写入数据的目的地址要在要写入数据的前面,这样子通过i2c_master_transmit函数发送出去的数据才符合AT24C02的写数据操作。这里需要注意:EEPROM写入比较慢,所以需要延时10ms,等待eeprom写入完毕。
继续看一下at24c02_read_one_byte,实现从AT24C02内存地址读取数据,代码如下:
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief             在AT24C02指定地址读出一个数据</font>
  3. <font size="3"> * @param            addr: 开始读数的地址</font>
  4. <font size="3"> * @retval           读到的数据</font>
  5. <font size="3"> */</font>
  6. <font size="3">uint8_t at24c02_read_one_byte(uint8_t addr)</font>
  7. <font size="3">{</font>
  8. <font size="3">    uint8_t data = 0;</font>
  9. <font size="3">ESP_ERROR_CHECK(i2c_master_transmit_receive(eeprom_handle, &addr, 1, &data,</font>
  10. <font size="3">        1, -1));</font>
  11. <font size="3">    return data;</font>
  12. <font size="3">}</font>
复制代码
该函数的实现主要调用i2c_master_transmit_receive函数。从AT24C02的addr内存地址处读出data数据,作为函数返回值返回。
有了基本的读写函数接口,就可以写一个比较简单的检测函数at24c02_check,用来测试IIC总线上是否存在24C02或者说器件是否正常,代码如下所示。
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief            检查AT24C02是否正常</font>
  3. <font size="3"> * [url=home.php?mod=space&uid=60778]@note[/url]              检测原理: 在器件的末地址写如0X55, 然后再读取, 如果读取值为0X55</font>
  4. <font size="3"> *                    则表示检测正常. 否则,则表示检测失败.</font>
  5. <font size="3"> * @param             无</font>
  6. <font size="3"> * @retval             检测结果</font>
  7. <font size="3"> *                    0: 检测成功</font>
  8. <font size="3"> *                     1: 检测失败</font>
  9. <font size="3"> */</font>
  10. <font size="3">uint8_t at24c02_check(void)</font>
  11. <font size="3">{</font>
  12. <font size="3">    uint8_t temp;</font>
  13. <font size="3">    uint16_t addr = AT24C02;</font>

  14. <font size="3">    temp = at24c02_read_one_byte(addr);     /* 避免每次开机都写AT24CXX */</font>

  15. <font size="3">    if (temp == 0X55)                       /* 读取数据正常 */</font>
  16. <font size="3">    {</font>
  17. <font size="3">        return 0;</font>
  18. <font size="3">    }</font>
  19. <font size="3">    else                                    /* 排除第一次初始化的情况 */</font>
  20. <font size="3">    {</font>
  21. <font size="3">        at24c02_write_one_byte(addr, 0X55); /* 先写入数据 */</font>
  22. <font size="3">        temp = at24c02_read_one_byte(255);  /* 再读取数据 */</font>
  23. <font size="3">        if (temp == 0X55)</font>
  24. <font size="3">        {</font>
  25. <font size="3">            return 0;</font>
  26. <font size="3">        }</font>
  27. <font size="3">    }</font>

  28. <font size="3">    return 1;</font>
  29. <font size="3">}</font>
复制代码
在这里,就是利用EEPROM芯片掉电不丢失的特性,在第一次写入了某个值之后,再去读一下看是否写入成功,这种方式就可以去检测芯片是否可以正常工作。
有时候操作单位往往不是单个字节,所以这里我们也提供了多字节写和多字节读的函数接口,代码如下所示。
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief             在AT24C02里面的指定地址开始读出指定个数的数据</font>
  3. <font size="3"> * @param             addr    : 开始读出的地址 对24c02为0~255</font>
  4. <font size="3"> * @param            pbuf    : 数据数组首地址</font>
  5. <font size="3"> * @param             datalen : 要读出数据的个数</font>
  6. <font size="3"> * @retval            无</font>
  7. <font size="3"> */</font>
  8. <font size="3">void at24c02_read(uint8_t addr, uint8_t *pbuf, uint8_t datalen)</font>
  9. <font size="3">{</font>
  10. <font size="3">    while (datalen--)</font>
  11. <font size="3">    {</font>
  12. <font size="3">        *pbuf++ = at24c02_read_one_byte(addr++);</font>
  13. <font size="3">    }</font>
  14. <font size="3">}</font>

  15. <font size="3">/**</font>
  16. <font size="3"> * @brief             在AT24C02里面的指定地址开始写入指定个数的数据</font>
  17. <font size="3"> * @param             addr    : 开始写入的地址 对24c02为0~255</font>
  18. <font size="3"> * @param             pbuf    : 数据数组首地址</font>
  19. <font size="3"> * @param            datalen : 要写入数据的个数</font>
  20. <font size="3"> * @retval            无</font>
  21. <font size="3"> */</font>
  22. <font size="3">void at24c02_write(uint8_t addr, uint8_t *pbuf, uint8_t datalen)</font>
  23. <font size="3">{</font>
  24. <font size="3">    while (datalen--)</font>
  25. <font size="3">    {</font>
  26. <font size="3">        at24c02_write_one_byte(addr, *pbuf);</font>
  27. <font size="3">        addr++;</font>
  28. <font size="3">        pbuf++;</font>
  29. <font size="3">    }</font>
  30. <font size="3">}</font>
复制代码
以上两个函数都是基于单个字节读和单个字节写函数实现的,这里就不多讲了。
3. CMakeLists.txt文件
本例程的功能实现主要依靠IIC驱动和AT24C02驱动。要在main函数中,成功调用AT24C02文件中的内容,就得需要修改BSP文件夹下的CMakeLists.txt文件,修改如下:
  1. <font size="3">set(src_dirs</font>
  2. <font size="3">                   LED</font>
  3. <font size="3">                        LCD</font>
  4. <font size="3">                   MYIIC</font>
  5. <font size="3">                  XL9555</font>
  6. <font size="3">                  AT24C02)</font>

  7. <font size="3">set(include_dirs</font>
  8. <font size="3">                  LED</font>
  9. <font size="3">                        LCD</font>
  10. <font size="3">                   MYIIC</font>
  11. <font size="3">                  XL9555</font>
  12. <font size="3">                  AT24C02)</font>

  13. <font size="3">set(requires</font>
  14. <font size="3">                   driver</font>
  15. <font size="3">                   esp_lcd</font>
  16. <font size="3">                   esp_common)</font>

  17. <font size="3">idf_component_register(        SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})</font>

  18. <font size="3">component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)</font>
复制代码
4. main.c驱动代码
在main.c里面编写如下代码。
  1. <font size="3">void app_main(void)</font>
  2. <font size="3">{</font>
  3. <font size="3">    esp_err_t ret;</font>
  4. <font size="3">    uint8_t key = 0;</font>
  5. <font size="3">    uint16_t i = 0;</font>
  6. <font size="3">    uint8_t datatemp[TEXT_SIZE];</font>

  7. <font size="3">    ret = nvs_flash_init();     /* 初始化NVS */</font>
  8. <font size="3">    if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)</font>
  9. <font size="3">    {</font>
  10. <font size="3">        ESP_ERROR_CHECK(nvs_flash_erase());</font>
  11. <font size="3">        ESP_ERROR_CHECK(nvs_flash_init());</font>
  12. <font size="3">    }</font>

  13. <font size="3">    led_init();                         /* LED初始化 */</font>
  14. <font size="3">    lcd_init();                         /* LCD屏初始化 */</font>
  15. <font size="3">    xl9555_init();                      /* 初始化按键 */</font>
  16. <font size="3">    at24c02_init();                     /* 初始化24CXX */</font>

  17. <font size="3">    lcd_show_string(30, 50,  200, 16, 16, "ESP32-P4", RED);</font>
  18. <font size="3">    lcd_show_string(30, 70,  200, 16, 16, "EEPROM TEST", RED);</font>
  19. <font size="3">    lcd_show_string(30, 90,  200, 16, 16, "ATOM@ALIENTEK", RED);</font>
  20. <font size="3">    lcd_show_string(30, 110, 200, 16, 16, "KEY1:Write  KEY0:Read", RED);</font>

  21. <font size="3">    while (at24c02_check())     /* 检测不到24c02 */</font>
  22. <font size="3">    {</font>
  23. <font size="3">        lcd_show_string(30, 130, 200, 16, 16, "24C02 Check Failed!", RED);</font>
  24. <font size="3">        vTaskDelay(pdMS_TO_TICKS(500));</font>
  25. <font size="3">        lcd_show_string(30, 130, 200, 16, 16, "Please Check!      ", RED);</font>
  26. <font size="3">        vTaskDelay(pdMS_TO_TICKS(500));</font>
  27. <font size="3">        LED0_TOGGLE();          </font>
  28. <font size="3">    }</font>

  29. <font size="3">    lcd_show_string(30, 130, 200, 16, 16, "24C02 Ready!", RED);</font>

  30. <font size="3">    while (1)</font>
  31. <font size="3">    {</font>
  32. <font size="3">        key = xl9555_key_scan(0);   /* 按键扫描  */</font>

  33. <font size="3">        if (key == KEY1_PRES)       /* KEY1按下,写入24C02 */</font>
  34. <font size="3">        {</font>
  35. <font size="3">            lcd_fill(0, 150, 239, 319, WHITE);                                           /* 清除显示 */</font>
  36. <font size="3">            lcd_show_string(30, 150, 200, 16, 16,"Start Write 24C02....", BLUE);</font>
  37. <font size="3">            at24c02_write(0, (uint8_t *)g_text_buf, TEXT_SIZE);        /* 写数据 */</font>
  38. <font size="3">            lcd_show_string(30, 150, 200, 16, 16,"24C02 Write Finished!", BLUE);</font>
  39. <font size="3">        }</font>

  40. <font size="3">        if (key == KEY0_PRES)       /* KEY0按下,读取字符串并显示 */</font>
  41. <font size="3">        {</font>
  42. <font size="3">            lcd_show_string(30, 150, 200, 16, 16,"Start Read 24C02.... ", BLUE);</font>
  43. <font size="3">            at24c02_read(0, datatemp, TEXT_SIZE);                                   /* 读取数据 */ </font>
  44. <font size="3">            lcd_show_string(30, 150, 200, 16, 16,"The Data Readed Is:  ", BLUE);</font>
  45. <font size="3">            lcd_show_string(30, 170, 200, 16, 16, (char *)datatemp, BLUE);      </font>
  46. <font size="3">        }</font>

  47. <font size="3">        i++;</font>

  48. <font size="3">        if (i == 20)</font>
  49. <font size="3">        {</font>
  50. <font size="3">            LED0_TOGGLE();</font>
  51. <font size="3">            i = 0;</font>
  52. <font size="3">        }</font>

  53. <font size="3">        vTaskDelay(pdMS_TO_TICKS(10));</font>
  54. <font size="3">    }</font>
  55. <font size="3">}</font>
复制代码
在app_main函数中,初始化AT24C02后,检测AT24C02是否存在。若AT24C02正常,则会不断等待按键输入。通过KEY0去读取0地址存放的数据并把数据显示在LCD上;通过KEY1向0地址处写入g_text_buf数据并在LCD上显示传输中,完成后并显示“24C02 Write Finished!”。

23.4 下载验证
将程序下载到开发板后,可以看到LED0不停闪烁,提示程序已经在运行了,先按下KEY1写入数据,然后再按KEY0读取数据,最终LCD显示的内容如图23.4.1所示:

第二十三章 IIC9053.png
图23.4.1 IIC_EEPROM实验程序运行效果图
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-1-2 11:46

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表