提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
这是记录rtthread 学习之路的汇总
提示:以下是本篇文章正文内容,下面案例可供参考
一、方法论是什么?
一开始的一些内核部分的知识过于理论,可以在平时实际问题中进行学习!先不用深究,可以把rtthread 在keil上仿真的示例源码弄懂即可!!
以及搭配cubemx+rtthread studio 的方式快速开发具体工程,利用组件+软件包对实际开发工程拓展
目前学设备和驱动篇!分析rtthread标准源码
使用rtthread studio 验证具体学习的知识
二、基本步骤
基于RT-Thread studio开发换将创建的工程,时钟默认为内部时钟,如果希望使用外部时钟,如何配置呢?
rtthread studio外部时钟配置
选择相应芯片,使用cubemx 的debug 和 sys 、usart1 。通过配置HSE,使用cubemx生成的systemconfig 替换driver下的drv.clk.c里面的system_config (注意默认生成的例程使用到了usart1,当使用到cubemx生成工程后,需要对usart1进行配置)
生成工程选择Makefile 而不是MDK
通过输出,可以看到时钟被配置成HSE
RtThread 操作系统原子stm32学习
代码参考:led点亮,key中断控制
#include <rtdevice.h>
#define LED_IO GET_PIN(E,5)
#define LED_I1 GET_PIN(B,5)
#define KEY1_IO GET_PIN(E,3)
#define KEY0_IO GET_PIN(E,4)
int flag=0;
//回调函数
void KEY_CallBack_Function(void *args)
{
flag=!flag;
rt_pin_write(LED_IO, flag);
}
int main(void)
{
rt_pin_mode(LED_IO, PIN_MODE_OUTPUT);
rt_pin_mode(LED_I1, PIN_MODE_OUTPUT);
rt_pin_mode(KEY1_IO,PIN_MODE_INPUT);
rt_pin_mode(KEY0_IO,PIN_MODE_INPUT_PULLUP);
rt_pin_write(LED_IO, 1);//默认关灯
rt_pin_attach_irq(KEY1_IO, PIN_IRQ_MODE_FALLING , KEY_CallBack_Function, RT_NULL);
rt_pin_irq_enable(KEY1_IO, PIN_IRQ_ENABLE);
while(1){
//rt_kprintf("key:%d\n",rt_pin_read(KEY0_IO));
rt_pin_mode(LED_I1, PIN_MODE_OUTPUT);
rt_thread_mdelay(500);
rt_pin_mode(LED_I1, PIN_MODE_OUTPUT);
rt_thread_mdelay(500);
}
return RT_EOK;
}
连按,不连按
长按(2s)和短按
按键KEY0长按2秒之后,LED1点亮,松开后再长按2秒,LED1熄灭。按键KEY0按下不到2秒松开,LED0点亮,再按一次,LED0熄灭
/* 按键同时支持长按和短按 */
int main(void)
{
int i = 0, j = 0;
int key_up = 1;
rt_uint16_t t = 0;
/* 把LED引脚设置为输出 */
rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
/* 把KEY引脚设置为输入 */
rt_pin_mode(KEY0_PIN, PIN_MODE_INPUT);
rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT);
rt_pin_mode(KEY2_PIN, PIN_MODE_INPUT);
rt_pin_mode(KEY_UP_PIN, PIN_MODE_INPUT);
/* 先把两个灯都关掉 */
rt_pin_write(LED0_PIN, PIN_HIGH);
rt_pin_write(LED1_PIN, PIN_HIGH);
while (1)
{
/* 读取KEY0引脚电平 */
if (rt_pin_read(KEY0_PIN) == PIN_LOW)//按键按下时为低电平
{
rt_thread_mdelay(10);//延时消抖
t++;
if (t == 200)
{//长按两秒
j = ~j;
if(j)
{
rt_pin_write(LED1_PIN, PIN_LOW);//点亮LED1
}
else
{
rt_pin_write(LED1_PIN, PIN_HIGH);//熄灭LED1
}
}
}
else if(rt_pin_read(KEY0_PIN) != PIN_LOW)//按键松开
{
if(t >=3 && t<= 200)
{//短按
i = ~i;
if(i)
{
rt_pin_write(LED0_PIN, PIN_LOW);//点亮LED0
}
else
{
rt_pin_write(LED0_PIN, PIN_HIGH);//熄灭LED0
}
}
t = 0;
}
}
}
启用Rthread studio 的Terminal 可以开启串口
使用xterm来进行msh的使用(要求main里面的while(1)需要延时期间会挂起线程不占用cpu资源)
2.IO设备框架
简单的设备–无需经过设备驱动层,直接注册到IO设备管理层,
复杂的设备 --> 设备驱动层–>IO设备管理层
创建的设备示例先注册到对应的设备驱动框架,再有设备驱动框架往IO设备管理层进行注册
drv.usart.c --> serial.c -->
在RT-Thread中,设备也是一种内核对象,和以前说的线程,IPC机制,定时器等对象一样,有自己的对象控制块
设备对象控制块
设备类型 —字符、块、其他设备类型
设备注册 flag --属于什么状态的设备,可读、可写、收发等
设备访问 open_flag —设备访问的时候,需要使用这个 open_flag 来判断需要对设备进行什么操作,是读?还是写? 还是发送等。。。
/** rtdef.h
* Device structure
*/
struct rt_device
{
struct rt_object parent; /**< inherit from rt_object */
enum rt_device_class_type type; /**< device type */
rt_uint16_t flag; /**< device flag */
rt_uint16_t open_flag; /**< device open flag */
rt_uint8_t ref_count; /**< reference count */
rt_uint8_t device_id; /**< 0 - 255 */
/* device call back */
rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);
#ifdef RT_USING_DEVICE_OPS
const struct rt_device_ops *ops;
#else
/* common device interface */
rt_err_t (*init) (rt_device_t dev);
rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag);
rt_err_t (*close) (rt_device_t dev);
rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
rt_err_t (*control)(rt_device_t dev, int cmd, void *args);
#endif
#if defined(RT_USING_POSIX)
const struct dfs_file_ops *fops;
struct rt_wqueue wait_queue;
#endif
void *user_data; /**< device private data */
};
device.c
设备创建 = 初始化+操作方法 vs 设备销毁
设备注册 (到IO设备管理器,应用程序才可访问) vs 设备注销
注销只是把这个 设备对象结构体 从管理链表中去掉,并不会释放这个对象结构体的空间,要释放空间需要调用销毁设备函数。
查找设备
初始化设备
打开和关闭设备
UART 设备模型 是复习理解 RT-Thread I/O 设备模型的完美设备。
设备驱动层和设备驱动框架层
在 RT-Thread 操作系统中,对UART设备的初始化,可以理解为就是对 stm32_uart 结构体对象 的初始化
int rt_hw_usart_init(void)
{
rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct stm32_uart);
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
rt_err_t result = 0;
stm32_uart_get_dma_config();
for (int i = 0; i < obj_num; i++)
{
uart_obj[i].config = &uart_config[i];
uart_obj[i].serial.ops = &stm32_uart_ops;
uart_obj[i].serial.config = config;
/* register UART device */
result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
RT_DEVICE_FLAG_RDWR
| RT_DEVICE_FLAG_INT_RX
| RT_DEVICE_FLAG_INT_TX
| uart_obj[i].uart_dma_flag
, NULL);
RT_ASSERT(result == RT_EOK);
}
return result;
}
board.c(rt_hw_board_init–>hw_board_init 完成板子的bsp初始化,其内部使用宏来管理pin设备和usat设备等通rtthread setting 组件一致)
rt_hw_usart_init–> (usart初始化)
rt_hw_serial_register(设备驱动框架,其内部调用rt_device_register)
–>rt_device_register(通用注册函数,用于将其注册到IO设备管理器)
串口RX —我们正常的项目使用中,一般都是 中断接收 或者 DMA 接收,
基本上不会使用 轮询接收的方式
在 RT-Thread 系统中,我们常用信号量或者消息队列 来标志是否接收到串口数据,这样的好处是当没有数据的时候,会将数据处理线程挂机,让出CPU资源
串口TX — 轮询 方式发送。
/轮询方式发送,中断接收orDMA接收/
RT-thread相关教程汇总–env
开发环境搭建!rtthread源码版本下载4.0.3(可通过rtthread studio快速获取)
尽量使用env开发工具来增添移除文件,使用keil来增添时,下一次使用env将会移除
UART 设备的设备驱动框架层,是有定义了 UART 设备自己的控制块,其继承了rt_device的内容,同时还增加了 UART 设备特有的一些配置,操作,回调函数之类的内容
pin设备
pin设备的对象结构体在pin.h中有定义,struct rt_device_pin
rt_hw_pin_init()会调用 rt_device_pin_register 函数进行 PIN 设备的初始化
在hw_board_ini进行对rt_hw_pin_init()对引脚进行配置,通过宏开启是否对该组件进行配置
rt_hw_pin_init()内部使用rt_device_pin_register设备驱动框架层注册,
rt_device_pin_register使用rt_device_register注册到IO设备管理层,在pin.c中,提供了应用层访问硬件的接口
应用层是通过设备驱动框架层的rt_pin_get 、rt_pin_read 、rt_pin_write 来控制硬件
ADC设备
ADC 通道 MCU一般都有 ADC 引脚,将需要检测的模拟量连接至对应的 IO 口,做好配置就能使用
ADC 分辨率
STM32F1xx 系列的芯片来说,他们的 ADC 最大支持12位
一个ADC设置为 12 位的分辨率,2 的12 次方 = 4096,简单来说就是这个 ADC 设备把他的量程分为 4096 份:0 ~ 4095 最大值 4095 就等于他量程的最大值
STM32 ADC的量程为 0~ 3.3V,如果读到 4095 的ADC 值,就表示读到的电压为 3.3V
ADC设备操作函数
实际操作
I/O 设备模型之ADC设备
SPI设备
SPI 通讯有4种模式,由 CPOL (时钟的极性)和 CPHA (时钟的相位)决定:
挂载SPI设备,
spi设备操作:读取墨水屏驱动ID,
RT-thread SPI SFUD读写W25Q128
SFUD库源移植: sfud-通用的spi-flash库
基于hal库的使用,printf的重定向支持,
wds_rtthread study
开始篇目:先从设备驱动程序学习!
-
统一设备接口,使用设备驱动框架层()
-
PIN驱动分析
-
I2C设备
-
块设备驱动程序
核心要点就是:
写入数据到pos位置,由此读该pos位置可以得到写入的数据,
不同于字符设备主要体现在控制功能,如led点亮熄灭操作。块设备传输数据的单位为扇区,具有存储功能
描述磁盘容量信息:有多少个扇区,扇区大小为多少,可擦除的块有多大 -
文件系统
POSIX 标准定义了操作系统应该为应用程序提供的接口标准,是 IEEE 为要在各种 UNIX 操作系统上运行的软件而定义的一系列 API 标准的总称
DFS虚拟文件系统向用户应用程序提供POSIX接口层,POSIX接口层是给开发者使用的接口 函数层。开发者使用POSIX接口层提供的POSIX API进行文件操作,无需关心文件系统是 如何实现的,也无需关心数据是存放在哪个存储设备中。
提炼点:参考文章
5.1.挂载点
windows 下插入一个U盘,盘符信息、文件系统格式信息、块设备驱动程序(读、写、擦除设备操作)
对于rtthread或linux系统来说,没有盘符的概念,而是一个树状结构的文件目录格式,相当与将某一设备挂载在某一个目录下(该目录等价于设备之于操作系统明确的实际位置)
以linux 操作系统举例:
5.1.挂载点、文件系统、块设备三者之间的关系
提升篇目: 链表与对象管理器的学习
内核知识学习
stm32基础+cubemx+硬件
STM32程序编写风格:
把所有用到的头文件存放到一个include.h 中,后期所有的.c 文件皆包含该头文件,举例如下:
用法,效果修饰凡是test.h 声明的函数,其他文件就可以直接去使用
在test.c中定义一个变量,同时在test.h 中声明,让其他文件调用
总结
提示:这里对文章进行总结:
env 工具的学习
参考了很多教程,来制作bsp,可以参考官网的bsp教程or这篇,生成对应芯片的mdk 配置,只需要在board文件中修改。可以对比rtthread studio 生成的工程文件rtconfig.h来比对是否正确完成bsp制作
只要注意几点:
- cubemx 配置基本的rcc sys usart 选项,并且在生成工程不选择单独生成.c 和.h 选项
- 修改适合与当前芯片的flash和ram容量信息
- 在修改完成cubemx后,修改Kconfig 选项,换成自己的BSP对应芯片,其他有需要可以按实际情况修改!!! 这个主要是依照自己板载的外设,来具体添加,在使用env 工具,命令menuconfig来使能rtconfig.h 相应的宏来完成外设的初始化
- 修改链接脚本适合于MDK工程
- 在 env 界面输入命令 menuconfig 对工程进行配置,并生成新的 rtconfig.h 文件
- 使用 env 工具输入命令
scons --target=mdk5
- 特别要注意在rtconfig.h 文件中需要开启组件自动初始化,对RT_USING_COMPONENTS_INIT进行宏定义!! 只需要明白一点,使用组件必须先声明这个宏,否则比如Finsh组件使用无效(敲回车不能交互)
其它注意事项:参考官网bsp教程
cubemx每次生成只需要保留4这个文件就可以
目前就是使用env 配置工程时使用不太熟悉,没有在rtconfig.h中包含
#define RT_USING_COMPONENTS_INIT,需要手动添加!!!!!!
下一步是需要将工程生成位置变得便携,完成env工具的学习
使用命令,scons --dist 生成需要的模版文件,打开project.uvprojx。
结合单独驱动的.c 和.h 完成往rtthread 工程中移植!!目前想到的主要是完成路径的包含,以及注意延时函数的替换上
env 完成e-paper的软件架构–wifi
我选择的是usart2,并且开启了esp8266线程
menuconfig 其实就是完成对stm32f1xxx_hal_conf.h的宏定义的取消注释
可以在MDK工程全局搜索这个 #define __STM32F1xx_HAL_CONF_H,找到这个文件,通过完成相应宏的定义,来简化定义kconfig的注释!这个主要是为了快速开发!!,但是后续应该就是对Kconfig文件的修改
rtthread 完成任意芯片工程的驱动!!!–最基本的外设驱动能力
开发模式:cubemx + 修改kconfig + env配置 + 简单的.c 和.h移植(SConscript)
kconfig 主要修改成开发板所包含的硬件驱动外设,完成menuconfig的设置
寻找一个通用的Kconfig 来修改,如F1参考已给的工程例程
rtthread studio 学习:只需要rtthread setting 和 cubemx setting
学习高级的内核部分知识优化驱动性能,理解rtthread 操作系统元源码!!!!