IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> [ESP32]学习笔记05 -> 正文阅读

[嵌入式][ESP32]学习笔记05

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

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


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


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())
        { //检测不到24c04
            ESP_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",               //pcName
            4096,                       //usStackDepth
            NULL,                       //pvParameters
            2,                          //uxPriority
            NULL                        //pxCreatedTask
    );
xTaskCreate(Led_BlinkTask, "Led_Blink", 2048, NULL, 5, NULL);

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

在这里插入图片描述

可以看到读写成功


  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-25 12:22:20  更:2021-08-25 12:22:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 3:13:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码