RT-Thread操作系统(2)
目录
RT-Thread操作系统(2)
设备驱动
IO设备模型框架
PIN设备(控制LED灯)
软件包开发
DHT11的使用
自动初始化机制
串口
LCD
LVGL
连接阿里云和服务器
设备驱动
IO设备模型框架
-- RT-Thread 提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。
应用程序通过 I/O 设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层 I/O 硬件设备进行数据(或控制)交互。
I/O 设备管理层实现了对设备驱动程序的封装**。应用程序通过图中的"I/O设备管理层"提供的标准接口访问底层设备,设备驱动程序的升级、更替不会对上层应用产生影响。这种方式使得设备的硬件操作相关的代码能够独立于应用程序而存在,双方只需关注各自的功能实现,
设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。
设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。它负责创建和注册 I/O 设备,对于操作逻辑简单的设备,可以不经过设备驱动框架层,直接将设备注册到 I/O 设备管理器中,使用序列图如下图所示,主要有以下 2 点:
设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过 rt_device_register() 接口注册到 I/O 设备管理器中。
应用程序通过 rt_device_find() 接口查找到设备,然后使用 I/O 设备管理接口来访问硬件。
PIN设备(控制LED灯)
-- 首先来看pin设备(也就是GPIO)
- 输入输出的模式
- pin设备的管理接口
-- 参考例程:
#define LED2_PIN GET_PIN(E,2)static rt_thread_t tid1 = RT_NULL;static void thread1_entry(void *parameter)
{rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);for (; ; ){rt_pin_write(LED2_PIN, PIN_LOW);rt_thread_mdelay(500);rt_pin_write(LED2_PIN, PIN_HIGH);rt_thread_mdelay(500);}}int main(void)
{int count = 1;tid1 = rt_thread_create("thread1",thread1_entry, RT_NULL,512,25, 5);/* 如果获得线程控制块,启动这个线程 */if (tid1 != RT_NULL)rt_thread_startup(tid1);while (count++){LOG_D("Hello RT-Thread!");rt_thread_mdelay(1000);}return RT_EOK;
}
-- 效果:LED2 闪烁
软件包开发
- 1、软件包的使用(会报错)
- 2、有自动初始化机制(不用在主函数中调用)
- 3、设备的注册,使用流程
DHT11的使用
-- 搜索DHT11软件包并添加
-- 添加过后,点击ctrl+s保存
-- 回到工程,就能看到DHT11软件包已经在工程中了
-- 更改DHT11的引脚,改成我们使用的开发板对应的原理图上的引脚
-- 并且要更改头文件
-- 我们可以看到DHT11的代码中有关于设备的操作
-- 我们可以看到工程中使用了自动初始化
-- 编译,下载,效果图
自动初始化机制
-- 这里为了更好的理解这个自动初始化,我们将本来写在main函数中的代码,写成一个函数,然后使用自动初始化的机制。
-- 在main函数下面写一个函数,将上面我们写过的线程创建和启动写在这个函数中,然后宏定义声明,这样就算我们不在main中调用,我们也可以执行。
-- 源代码:
int main(void)
{int count = 1;tid1 = rt_thread_create("thread1",thread1_entry, RT_NULL,512,25, 5);/* 如果获得线程控制块,启动这个线程 */if (tid1 != RT_NULL)rt_thread_startup(tid1);while (count++){LOG_D("Hello RT-Thread!");rt_thread_mdelay(1000);}return RT_EOK;
}
-- 更改为自动初始化后的代码
int main()
{while (count++){LOG_D("Hello RT-Thread!");rt_thread_mdelay(1000);}return RT_EOK;
}void aaa(void)
{tid1 = rt_thread_create("thread1",thread1_entry, RT_NULL,512,25, 5);/* 如果获得线程控制块,启动这个线程 */if (tid1 != RT_NULL)rt_thread_startup(tid1);
}
INIT_APP_EXPORT(aaa);
串口
-- 我们可以看到串口设备也是这几个函数
-- 1、查找设备
- 用程序根据串口设备名称获取设备句柄,进而可以操作串口设备,查找设备函数如下所示,
rt_device_t rt_device_find(const char* name);
-- 2、打开设备
-- 通过设备句柄,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
- oflags 参数支持下列取值 (可以采用或的方式支持多种取值):
#define RT_DEVICE_FLAG_STREAM 0x040 /* 流模式 */
/* 接收模式参数 */
#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中断接收模式 */
#define RT_DEVICE_FLAG_DMA_RX 0x200 /* DMA 接收模式 */
/* 发送模式参数 */
#define RT_DEVICE_FLAG_INT_TX 0x400 /* 中断发送模式 */
#define RT_DEVICE_FLAG_DMA_TX 0x800 /* DMA 发送模式 */
- 串口数据接收和发送数据的模式分为 3 种:中断模式、轮询模式、DMA 模式。在使用的时候,这 3 种模式只能选其一,若串口的打开参数 oflags 没有指定使用中断模式或者 DMA 模式,则默认使用轮询模式。
-- 3、控制串口设备
- 通过控制接口,应用程序可以对串口设备进行配置,如波特率、数据位、校验位、接收缓冲区大小、停止位等参数的修改。控制函数如下所示:
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
- 控制参数结构体 struct serial_configure 原型如下
struct serial_configure
{rt_uint32_t baud_rate; /* 波特率 */rt_uint32_t data_bits :4; /* 数据位 */rt_uint32_t stop_bits :2; /* 停止位 */rt_uint32_t parity :2; /* 奇偶校验位 */rt_uint32_t bit_order :1; /* 高位在前或者低位在前 */rt_uint32_t invert :1; /* 模式 */rt_uint32_t bufsz :16; /* 接收数据缓冲区大小 */rt_uint32_t reserved :4; /* 保留位 */
};
-- 4、串口设备使用实例
-- 可以查看默认的参数判断要不要更改参数
-- 这里没有写注册函数的原因是因为串口2在系统中就有,所以不用写
- usart2222.c
/** Copyright (c) 2006-2021, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2024-11-04 皮某人 the first version*/
#include "USART2222.h"
#include <rtthread.h>
#include "board.h"
#include <rtdevice.h>/* 用于接收消息的信号量 */
static rt_device_t serial;
struct rt_semaphore rx_sem;/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */rt_sem_release(&rx_sem);return RT_EOK;
}struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 初始化配置参数 */static void serial_thread_entry(void *parameter)
{char ch[100];while (1){rt_sem_take(&rx_sem, RT_WAITING_FOREVER);/* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */while (rt_device_read(serial, 0, ch, 1)){/* 读取到的数据通过串口错位输出 */rt_device_write(serial, 0, &ch, 1);}// rt_device_write(serial, 0, ch, 1);// rt_thread_mdelay(10);}
}void api_usart2_init(void)
{serial = rt_device_find("uart2");/* 初始化信号量 */rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);/* step2:修改串口配置参数 */config.baud_rate = BAUD_RATE_9600; //修改波特率为 9600config.data_bits = DATA_BITS_8; //数据位 8config.stop_bits = STOP_BITS_1; //停止位 1config.bufsz = 128; //修改缓冲区 buff size 为 128config.parity = PARITY_NONE; //无奇偶校验位/* step3:控制串口设备。通过控制接口传入命令控制字,与控制参数 */rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);/* 以中断接收及轮询发送模式打开串口设备 */rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);/* 设置接收回调函数 */rt_device_set_rx_indicate(serial, uart_input);//rt_device_open(serial, RT_DEVICE_FLAG_STREAM);//流模式rt_device_write(serial, 0, "123456789",5);rt_thread_t thread = rt_thread_create("serial",serial_thread_entry, RT_NULL,512, 25, 10);if (thread != RT_NULL){rt_thread_startup(thread);}}
INIT_APP_EXPORT(api_usart2_init);
-- 编译,出现错误
-- 需要在board.h中宏定义串口2(系统默认只定义了串口1)
-- 查看线程状态
-- 这个串口2发送的状态需要外接一个串口转usb查看,否则无法查看,这个主要是学会如果创建新文件和一个设备是如何创建和使用的。
-- 在程序执行中如果出现错误,检查一下是不是头文件没有添加
LCD
-- 先将LCD的官方例程添加到工程中
-- 然后回到工程中点击”刷新“即可更新到工程中
-- 编译,出现错误
-- 点击错误信息,跳转到错误的代码中,发现是FSMC没有配置的问题
-- 找不到FSMC,但是我们知道他的本质是扩容
-- 再次编译,发现还有错误,需要将有关fsmc的代码,注释掉
-- 现在来看这个LCD的官方例程
- 首先是注册设备
-- 然后是查找设备,设备初始化,之后开始应用设备
LVGL
-- 添加lcd的作用就是为了添加lvgl
-- 首先添加软件包,ctrl+s保存
-- 编译之后看有没有错误,如果还有错误,记得点亮
-- 后来就可以看到工程中有软件包的相关代码
连接阿里云和服务器
-- 打开软件包,保存
--
-- 还需要配置
- 配置参数
-- 在board.h中添加usart3的宏定义(根据单片机的原理图)
-- 在main.c中添加引脚定义和执行函数
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>#include <rtdevice.h>
#include <board.h>#define ESP_PIN GET_PIN(E,6)int main(void)
{int count = 1;rt_pin_mode(ESP_PIN , PIN_MODE_OUTPUT);rt_pin_write(ESP_PIN , PIN_HIGH);while (count++){LOG_D("Hello RT-Thread!");rt_thread_mdelay(1000);}return RT_EOK;
}
-- 编译,出现显示检测不到指令的问题
-- 我们想知道设备是否连接成功,可以发送help获取指令,然后通过指令查看
-- 我们现在使用的是4.0.2版本,需要更改内核版本到4.1.1,就可以实现
-- 可以直接在当前工程中切换版本,但是会造成很多错误,因为在之前的版本加的软件包是契合之前那个版本的,所以最好是新建工程
-- 再次编译,我们这里因为加了lvgl,导致内粗过大,程序很可能会卡死,所以我们最好测试wiif的时候将lvgl的初始化代码注释掉
-- 安装新版本(4.1.1的芯片型号)
-- 同样添加软件包,之后点击配置项
-- 填写阿里云的账号信息
-- 选择mqtt的例子
-- 配置过后,保存,配置串口3的宏定义和在main.c中写调用代码,编译,就会连接上阿里云