ESP32 I2C外设的使用 读写AT24C04 FreeRTOS嵌入式实时操作系统思想

今天我们通过使用ESP32的I2C外设来进行对EEPROM的读写操作,本次我们使用FreeRTOS进行创建任务进程,而不是通过While死循环顺序执行。在乐鑫提供的SDK中已经包含了FreeRTOS相关代码文件,我们直接使用即可;


在此之间,在此提醒熟读乐鑫提供的ESP-IDF编程指南,以及AT24C04的相关芯片手册。

目录

  • ESP32 I2C外设的使用 读写AT24C04 FreeRTOS嵌入式实时操作系统思想
  • I2C简介
  • 一、AT24C04
    • 1.简介
    • 2.程序设计
  • 二、FreeRTOS
    • 1.简单理解嵌入式实时操作系统
    • 2.创建任务

I2C简介

这里直接Copy百度百科了
IIC 简介
IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接
微控制器及其外围设备。它是由数据线 SDA 和时钟 SCL 构成的串行总线,可发送和接收数据。
在 CPU 与被控 IC 之间、 IC 与 IC 之间进行双向传送, 高速 IIC 总线一般可达 400kbps 以上。
I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答
信号。
开始信号: SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。
结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,
表示已收到数据。 CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号, CPU 接
收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为
受控单元出现故障。
I2C的时序图就不贴出来了;


提示:以下是本篇文章正文内容,下面案例可供参考

一、AT24C04

1.简介

AT24C04是Ateml公司的4Kb得电可擦除存储芯片,采用两线串行的总线和单片机通讯,电压最低可以到2.5V,额定电流为1mA,静态电流10uA(5.5V),芯片内的资料可以在断电的情况下保存100年,而且采用8 脚的 封装,使用方便。

我们来看AT24C04的电路图:
这里WP引脚接到了地,我们可以对整个512个字节进行读写操作。

2.程序设计

首先建立AT24C04.c和.h文件,引入将要使用到的头文件:

#include "AT24C04.h"
#include "esp_log.h"
#include "driver/i2c.h"
#include "freertos/FreeRTOS.h"

编辑AT24C04的头文件:

#define AT24C04_SCL_PIN GPIO_NUM_12
#define AT24C04_SDA_PIN GPIO_NUM_13#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */#define ACK_CHECK_EN 0x1  /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0       /*!< I2C ack value */
#define NACK_VAL 0x1      /*!< I2C nack value */#define CHIPADDR 0x50

对于I2C外设的初始化,可参考乐鑫提供的I2C示例,也可参考IDF编程指南:

这里我们把I2C配置为主机模式,与STM32不同的是ESP32配置好I2C后一定要记得注册外设;

/*** @name   AT24C04_Init.* @brief  AT24C04初始化* @param  None* @retval None*/
void AT24C04_Init(void)
{i2c_config_t i2c_configstructure;i2c_configstructure.mode = I2C_MODE_MASTER;             //配置为I2C主机模式i2c_configstructure.scl_io_num = AT24C04_SCL_PIN;       //at24c04 scl引脚号i2c_configstructure.scl_pullup_en = GPIO_PULLUP_ENABLE; //使能上拉i2c_configstructure.sda_io_num = AT24C04_SDA_PIN;       //at24c04 sda引脚号i2c_configstructure.sda_pullup_en = GPIO_PULLUP_ENABLE; //使能上拉i2c_configstructure.master.clk_speed = 100000;          //配置i2c CLK频率i2c_configstructure.clk_flags = 0;i2c_param_config(I2C_NUM_0, &i2c_configstructure); //配置i2c初始化参数i2c_driver_install(I2C_NUM_0, i2c_configstructure.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}

至于这里的CLK频率如何确定,我们可以通过i2c_config_t这个结构体查看,规定时钟频率不能超过1MHz;不同芯片模组有不同,具体频率需要根据所使用的的模组进行确定。

/*** @brief I2C initialization parameters*/
typedef struct{i2c_mode_t mode;     /*!< I2C mode */int sda_io_num;      /*!< GPIO number for I2C sda signal */int scl_io_num;      /*!< GPIO number for I2C scl signal */bool sda_pullup_en;  /*!< Internal GPIO pull mode for I2C sda signal*/bool scl_pullup_en;  /*!< Internal GPIO pull mode for I2C scl signal*/union {struct {uint32_t clk_speed;     /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */} master;                   /*!< I2C master config */struct {uint8_t addr_10bit_en;  /*!< I2C 10bit address mode enable for slave mode */uint16_t slave_addr;    /*!< I2C address for slave mode */} slave;                    /*!< I2C slave config */};uint32_t clk_flags;             /*!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice*/
} i2c_config_t;

然后我们编辑对AT24C04进行读写操作的函数:

/*** @name   AT24C04_ReadByte.* @brief  AT24C04读取一个字节* @param  ReadAddr: 开始读取的起始地址* @retval 读取到的数据*/
uint8_t AT24C04_ReadByte(uint16_t ReadAddr)
{esp_err_t ret = 0;uint8_t data = 0;i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();i2c_master_start(i2c_cmd);i2c_master_write_byte(i2c_cmd, 0xA0 + ((ReadAddr / 256) << 1), ACK_CHECK_EN);i2c_master_write_byte(i2c_cmd, ReadAddr % 256, ACK_CHECK_EN);i2c_master_start(i2c_cmd);i2c_master_write_byte(i2c_cmd, ((CHIPADDR << 1) | I2C_MASTER_READ), ACK_CHECK_EN);i2c_master_read_byte(i2c_cmd, &data, NACK_VAL);i2c_master_stop(i2c_cmd);ret = i2c_master_cmd_begin(I2C_NUM_0, i2c_cmd, 1000 / portTICK_PERIOD_MS);i2c_cmd_link_delete(i2c_cmd);return data;
}/*** @name   AT24C04_WriteByte.* @brief  AT24C04写入一个字节* @param  WriteAddr: 开始写入的起始地址* @param  DatatoWrite: 需要写入的数据* @retval None*/
void AT24C04_WriteByte(uint16_t WriteAddr, uint8_t DatatoWrite)
{esp_err_t ret = 0;i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();i2c_master_start(i2c_cmd);i2c_master_write_byte(i2c_cmd, 0XA0 + ((WriteAddr / 256) << 1), ACK_CHECK_EN); //发送器件地址0XA0,写数据i2c_master_write_byte(i2c_cmd, WriteAddr % 256, ACK_CHECK_EN);                 //发送低地址i2c_master_write_byte(i2c_cmd, DatatoWrite, ACK_CHECK_EN);i2c_master_stop(i2c_cmd);ret = i2c_master_cmd_begin(I2C_NUM_0, i2c_cmd, 1000 / portTICK_RATE_MS);i2c_cmd_link_delete(i2c_cmd);vTaskDelay(10 / portTICK_RATE_MS);
}/*** @name   AT24C04_Check.* @brief  检查AT24C04是否正常* @param  ReadAddr: 开始读取的起始地址* @retval 1: 检测失败* @retval 0: 检测成功*/
uint8_t AT24C04_Check(void)
{uint8_t temp;temp = AT24C04_ReadByte(511);ESP_LOGI(TAG, "AT24C04_Check : %02X \r\n", temp);if (temp == 0x55){return 0;}else{AT24C04_WriteByte(511, 0x55);temp = AT24C04_ReadByte(511);if (temp == 0X55)return 0;}return 1;
}/*** @name   AT24C04_ReadData.* @brief  在AT24CXX里面的指定地址开始读出指定个数的数据* @param  ReadAddr :开始读出的地址 对24c04为0~511* @param  pBuffer: 数据数组首地址* @param  NumToRead:要读出数据的个数* @retval None*/
void AT24C04_ReadData(uint16_t ReadAddr, uint8_t *pBuffer, uint16_t NumToRead)
{while (NumToRead){*pBuffer++ = AT24C04_ReadByte(ReadAddr++);NumToRead--;}
}/*** @name   AT24C04_WriteData.* @brief  在AT24CXX里面的指定地址开始写入指定个数的数据* @param  WriteAddr: 开始写入的地址 对24c04为0~511* @param  pBuffer: 数据数组首地址* @param  NumToWrite: 要写入数据的个数* @retval None*/
void AT24C04_WriteData(uint16_t WriteAddr, uint8_t *pBuffer, uint16_t NumToWrite)
{while (NumToWrite--){AT24C04_WriteByte(WriteAddr, *pBuffer);WriteAddr++;pBuffer++;}
}

别忘了在头文件中进行声明;
到这里基本上对AT24C04的操作代码就基本编辑完毕,本次实验还使用到的ESP提供的日志库进行打印输出调试信息;理解较为容易,这里不再赘述。

二、FreeRTOS

1.简单理解嵌入式实时操作系统

我们在学习51单片机的时候,就已经接触过了中断,那么中断不同于程序顺序执行的思想,通过中断,我们可以跳出正在执行的程序,来处理发生中断的事件,通过触发中断事件,来执行中断回调函数,以解决一些原程序之外的由外部触发的突发事件,常见的中断有看门狗中断,外部中断,定时器中断等等;和计算机不同的是,简单的普通性能的单片机只有一个核心,程序执行的方式则是顺序执行,那么在学习中断的时候我们了解到了中断优先级的概念,在中断优先级当中又细分为了抢占优先级和子优先级,以此来处理不同条件触发中断时的先后顺序,通过中断是否可以达到近似任务同时执行的效果呢?因此有了嵌入式实时操作系统,如何来理解实时操作系统和原本的顺序执行程序的区别呢?通俗易懂的来说,就像一位母亲正在给小孩子喂饭,这个时候领导打电话过来,那么按照顺序执行的理念,要么先接完电话,再喂饭,要么喂完饭再接电话;这样是否会造成无论谁先执行,另一方都会处在一直等待的状态呢;必须等待上一个任务执行完成后,才能执行下一个任务;那么用嵌入式实时操作系统的理念来处理这件事呢。
我们通过一张图来理解:
套入示例来理解,这位母亲先讲两句电话,然后为几口饭,再讲几句电话,如此交替运行,即可达到类似两件事同时做的效果。在FreeRTOS内,我们直接使用提供的函数进行任务创建即可;注意事件的优先级;
这里对FreeRTOS描述的不是那么专业,可自行查询FreeRTOS的文档了解;

2.创建任务

我这里使用的是ESP32-S2,只有一个核心,不需要进行指定核心运行,如果是双核的ESP32可使用xTaskCreatePinnedToCore来指定核心;
我们先编写事件函数:

/*** @brief  AT24C04_ReadWriteTestTask.* @param  None* @retval None*/
void AT24C04_ReadWriteTestTask(void *arg)
{AT24C04_Init();while (1){while (AT24C04_Check()){ //检测不到24c04ESP_LOGI(TAG, "24C04 Check Failed!\r\n");vTaskDelay(1000 / portTICK_RATE_MS);ESP_LOGI(TAG, "Please Check!      \r\n");vTaskDelay(1000 / portTICK_RATE_MS);}ESP_LOGI(TAG, "Start Write 24C04....\r\n");AT24C04_WriteData(0, (uint8_t *)TEXT_Buffer, SIZE);ESP_LOGI(TAG, "24C04 Write Finished!\r\n"); //提示传送完成ESP_LOGI(TAG, "Start Read 24C04.... \r\n");AT24C04_ReadData(0, datatemp, SIZE);ESP_LOGI(TAG, "The Data Readed is: %s \r\n", datatemp); //提示传送完成ESP_LOGI(TAG, "Test ok\n");vTaskDelay(5000 / portTICK_RATE_MS);}
}

还使用到了之间的Led,那么我们为Led也创建一个事件,让它一直闪烁:

/*** @brief  Led_BlinkTask.* @param  None* @retval None*/
void Led_BlinkTask(void *arg)
{Led_Init();while (1){ESP_LOGI(TAG, "Led On\n");gpio_set_level(BLINK_GPIO, 0);vTaskDelay(1000 / portTICK_PERIOD_MS);ESP_LOGI(TAG, "Led Off\n");gpio_set_level(BLINK_GPIO, 1);vTaskDelay(1000 / portTICK_PERIOD_MS);}
}

然后我们为两个事件创建进程,分配内存空间:

ESP_LOGI(TAG, "APP Start......");
ESP_ERROR_CHECK(nvs_flash_init());
xTaskCreate(&AT24C04_ReadWriteTestTask, //pvTaskCode"sensorTask",               //pcName4096,                       //usStackDepthNULL,                       //pvParameters2,                          //uxPriorityNULL                        //pxCreatedTask);
xTaskCreate(Led_BlinkTask, "Led_Blink", 2048, NULL, 5, NULL);

编译后烧录到开发板中,打开串口中断查看打印的信息。

可以看到读写成功


[ESP32]学习笔记05相关推荐

  1. ESP32 单片机学习笔记 - 05 - AP/Smart Config

    ESP32 单片机学习笔记 - 05 - AP/Smart Config 终于把感觉必要的基础外设学完了,开始学esp32的主要特色功能--物联网~~?(大概) 一.WIFI热点 AP模式 编程指南: ...

  2. ESP32学习笔记( VSCode + ESP-IDF环境) 3 ——GPIO相关的简单外设驱动

    1.如何在VSCode和ESP-IDF的环境下创建工程 说实话,这是我用ESP-IDF在VSCode环境下最不喜欢的事情,在一顿CSDN和百度之后,很多大佬博主都推荐使用VSCode,通过官方示例来进 ...

  3. JavaWeb黑马旅游网-学习笔记05【分类数据展示功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  4. JavaWeb-综合案例(用户信息)-学习笔记05【分页查询功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb-综合案例(用户信息)-学习笔记01[列表查询] JavaWeb-综合案例(用户信息)-学习笔记02[登录功能] JavaWeb-综合案 ...

  5. JavaScript学习笔记05【高级——DOM对象】

    w3school 在线教程:https://www.w3school.com.cn JavaScript学习笔记01[基础--简介.基础语法.运算符.特殊语法.流程控制语句][day01] JavaS ...

  6. MySQL学习笔记05【多表操作、三大范式、数据库的备份和还原】

    MySQL 文档-黑马程序员(腾讯微云):https://share.weiyun.com/RaCdIwas 1-MySQL基础.pdf.2-MySQL约束与设计.pdf.3-MySQL多表查询与事务 ...

  7. opencv学习笔记05

    原创:opencv学习笔记05 OpenCV-Python教程:40.ORB https://www.jianshu.com/p/49a84ddef11d ORB最重要的事情是它是OpenCV实验室出 ...

  8. 关于esp32蓝牙模块的使用——esp32学习笔记

    关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 零.前言 一.经典蓝牙BT 二.低功耗蓝牙B ...

  9. ESP32学习笔记(1)——搭建环境、编译烧写(Windows+VS Code)

    Espressif-IDE 环境搭建参看 ESP32学习笔记(50)--搭建环境.编译烧写(Windows+Espressif-IDE) 一.搭建环境 1.1 官方资料 ESP-IDF 编程指南 1. ...

最新文章

  1. BGP local-preference MED属性实验
  2. 【APP接口开发】chrome浏览器DHC工具安装使用(亲测有效)
  3. 春节后面试别人的经历总结之一,好岗位分享给还在找工作中的软件开发爱好者们【转】...
  4. win102004优化_win10 2004系统电脑出现玩命运2掉帧的问题
  5. 【项目调研+论文阅读】基于医学文献的实体抽取(NER)方法研究 day5
  6. 电力电气自动计算excel表格大全【共46份】
  7. 侯捷老师英中繁简术语对照表
  8. 访达前往文件夹_MacOS实用技巧之Finder(访达)的使用
  9. 东北大学材料成型工艺学中期末复习
  10. arcgis打开Excel文件显示没有注册类的解决方案
  11. 伦敦金实时行情今日变化多少?
  12. 赵小楼:《天道》《遥远的救世主》深度解析(22)丁元英为什么不问肖亚文以后有什么打算?
  13. 赛灵思 Xilinx Versal 自适应计算加速平台嵌入式设计教程
  14. 如何学习解剖学的简单方法
  15. coursera-dl 报错 AttributeError (‘HTMLParser’ object has no attribute ‘unescape’)
  16. 海思HI3516板子初体验
  17. SQL实现一对多、多对多建表与查询
  18. H3C SE 教程笔记——构建安全优化的广域网(下)
  19. java集合框架笔记
  20. VLC media player组播测试使用总结

热门文章

  1. Python 3.12 目标:还可以更快!
  2. git和coding 基本操作
  3. Python:【4】利用讯飞开放平台实现语音识别
  4. delegate用法
  5. 前端后端一起成长激励的句子
  6. 2018巅峰极客writeup(Misc)
  7. CC00042.CloudKubernetes——|KuberNetes二进制部署.V20|5台Server|——|kubernetes配置|生产环境关键性配置|
  8. 部分RFID安全技术
  9. 解决simnow客户端登录报CTP:客户端认证失败
  10. Bios读文件与Grub(bootload)和initrd和内核对文件系统驱动的支持