- 2023-10-26
-
回复了主题帖:
来PK:最适合物联网开发的连接
虽然这个事情本来就是仁者见仁智者见智的事情,但是个人感觉蓝牙更好一些;原因就是功耗低!
- 2023-10-25
-
回复了主题帖:
【EETALK】你平时会忘事吗?项目管理软件有了解吗?看看大家怎么说
跟帖,大佬们有好的软件的话,也尝试一下
- 2023-10-18
-
回复了主题帖:
有奖话题:你知道的,最能打的国产RISC-V芯片有哪些?
一,重点考虑生态问题,碰到问题是否能快速的找到解决办法;
二,推荐沁恒的CH583,提供BLE,目前用着挺不错;
三,最直观的理由:(1)价格相对便宜;(2)周围有一群一起使用的开发者,能够一起讨论问题;
- 2023-10-01
-
回复了主题帖:
读《RT-Thread设备驱动开发指南》--- 第五篇
wangerxian 发表于 2023-9-30 09:21
应该是标准版RTThread的才有这个组件吧?
是的
- 2023-09-28
-
回复了主题帖:
读《RT-Thread设备驱动开发指南》--- 第五篇
wangerxian 发表于 2023-9-28 17:07
都没太关注这个PM电源管理组件,它会在MCU不工作的时候进入休眠吗?
是的,在MCU需要的情况下,使MCU休眠
-
回复了主题帖:
阅读打卡终点站:25-27章——《RT-Thread设备驱动开发指南》
1,set_config,suspend,wakeup三个操作方法可以不实现
2,RT_CAN_EVENT_RX_IND;RT_CAN_EVENT_TX_DONE;RT_CAN_EVENT_TX_FAIL;RT_CAN_EVENT_RX_TIMEOUT;RT_CAN_EVENT_RXOF_IND;
3,在进入与退出中断时,需要调用rt_interrupt_enter()和rt_interrupt_leave()
-
回复了主题帖:
阅读打卡第五站:20-24章——《RT-Thread设备驱动开发指南》
1,PM电源管理框架中定义了六种休眠模式,分别是:
PM_SLEEP_MODE_NONE
PM_SLEEP_MODE_IDLE
PM_SLEEP_MODE_LIGHT
PM_SLEEP_MODE_DEEP
PM_SLEEP_MODE_STANDBY
PM_SLEEP_MODE_SHUTDOWN
2,录音设备和播音设备两种
3,MIC设备的flag一般位RT_DEVICE_FLAG_RDONLY,为只读类型
4,WLAN管理框架部分和WLAN私有工作队列部分
5,第一:创建WLAN设备;第二:实现WLAN设备的操作方法;第三:注册WLAN设备;
学到了WLAN的开发步骤和工作流程,提高了对WLAN设备的认识。
-
发表了主题帖:
读《RT-Thread设备驱动开发指南》--- 第五篇
本帖最后由 xiaolinen 于 2023-9-28 15:28 编辑
读《RT-Thread设备驱动开发指南》--- PM设备驱动的学习
第一部分:认识PM组件
PM组件采用分层的设计理念,分离架构和芯片相关的部分,提取公共部分作为核心,既保证应用层调用的接口统一性,又实现底层驱动适配的灵活性。框架结构图如下:
个人拙见:
1)PM组件是一个管理框架,我们应该根据实际情况使用,并不是梏桎,要求用上rt-thread就一定要用PM组件;
2)既然是一个框架,就需要我们进行适配,根据应用层的业务需求,进行修改和验证,从而实现功耗最优。
3)PM组件框架不仅仅适用于rt-thred,同样可以移植到其他的rtos上去。
第二部分:使用PM组件
在ENV工具中,开启PM组件,如图:
文件存放位置:
第三部分:部分代码和实验现象
代码:
/*
功能:深度睡眠
*/
void pm_bsp_enter_deepsleep(struct rt_pm *pm)
{
HAL_SysTickDisable();
if (pm->run_mode == PM_RUN_MODE_LOW_SPEED)
{
/* Enter LP SLEEP Mode, Enable low-power regulator */
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER,PMU_LOWDRIVER_ENABLE,WFI_CMD);
}
else
{
/* Enter SLEEP Mode, Main regulator is ON */
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER,PMU_LOWDRIVER_ENABLE,WFI_CMD);
}
HAL_SysTickEnable();
}
/*
功能:深度睡眠1
*/
void pm_bsp_enter_deepsleep_1(struct rt_pm *pm)
{
HAL_SysTick_IRQ_Disable();
if (pm->run_mode == PM_RUN_MODE_LOW_SPEED)
{
/* Enter LP SLEEP Mode, Enable low-power regulator */
pmu_to_deepsleepmode_1(PMU_LDO_LOWPOWER,PMU_LOWDRIVER_ENABLE,WFI_CMD);
}
else
{
/* Enter SLEEP Mode, Main regulator is ON */
pmu_to_deepsleepmode_1(PMU_LDO_LOWPOWER,PMU_LOWDRIVER_ENABLE,WFI_CMD);
}
HAL_SysTick_IRQ_Enable();
}
/*
功能:深度睡眠2
*/
void pm_bsp_enter_deepsleep_2(struct rt_pm *pm)
{
rt_base_t level;
//level = rt_hw_interrupt_disable();
HAL_SysTick_IRQ_Disable();
if (pm->run_mode == PM_RUN_MODE_LOW_SPEED)
{
/* Enter LP SLEEP Mode, Enable low-power regulator */
pmu_to_deepsleepmode_2(PMU_LDO_LOWPOWER,PMU_LOWDRIVER_ENABLE,WFI_CMD);
}
else
{
/* Enter SLEEP Mode, Main regulator is ON */
pmu_to_deepsleepmode_2(PMU_LDO_LOWPOWER,PMU_LOWDRIVER_ENABLE,WFI_CMD);
}
//system_lowpower_set(SCB_LPM_DEEPSLEEP);
HAL_SysTick_IRQ_Enable();
// rt_hw_interrupt_enable(level);
}
/*
功能:待机模式
*/
void pm_bsp_enter_standby(struct rt_pm *pm)
{
pmu_to_standbymode(WFI_CMD);
}
实现现象:
设备正常运行工作状态:165mA;
设备休眠状态(外接传感器掉电操作):27uA;
总结:
调设备功耗是一个技术活,又是一个体力活,调功耗必然会遇到各种各样的问题,坐的住,耐下心来认真干,问题才会越来越少,切记不要一会儿心态崩了,一会儿心态又崩了,心态再崩多少次也没用,毕竟活是你的,你需要干完!!!现在呢,有了PM框架,不能说完全解决了我们的问题,但是至少为我们指明了部分解决方向;个人认为:rt-thread的精髓不仅仅在于它的系统,更多的还是它的组件,这些组件提供便利的同时,也向我们展示着一种编程思想。RT-Thread是一个优美的产品,值得我们去深入学习和充分应用。
-
回复了主题帖:
读《RT-Thread设备驱动开发指南》--- 第四篇
秦天qintian0303 发表于 2023-9-28 08:48
直接实践了,效果相当不错
实践出真知嘛
-
回复了主题帖:
读《RT-Thread设备驱动开发指南》--- 第四篇
Jacktang 发表于 2023-9-28 07:39
FlashDB数据库看来是个重点
确实是一个重点,以前公司用easyflash,现在转到flashdbl
- 2023-09-27
-
发表了主题帖:
读《RT-Thread设备驱动开发指南》--- 第四篇
本帖最后由 xiaolinen 于 2023-9-27 15:36 编辑
读《RT-Thread设备驱动开发指南》--- FAL组件,ulog日志,flashDB组件的使用
前言:
本次测试是基于国民技术的N32WB452LEQ6芯片开展的,前期工作主要是在RT-Thread4.1.1版本上增加了该芯片的BSP。
第一部分:ulog日志
1.1) ulog日志组件架构图,如下:
前端:我们在应用程序中调用到的一些函数接口。
核心:这一部分对传进来的日志进行出来,根据开发者的设置,进行过滤和格式化。
后端:这里就是记录或者显示日志的地方,可以通过串口打印处理,也可以通过SPI写入外置的FLASH芯片中。
1.2)配置
使能ulog组件
配置部分选项
注:本次使用需要使用RTC功能,故使用了软件RTC,开启如下:
1.3)常用API接口
第二部分:SFUD
2.1)SFUD是一款开源的串行 SPI Flash 通用驱动库,具有支持SPI/QSPI接口,面向对象(同时支持多个flash对象),可灵活性裁剪,拓展性强的特性。且Fal硬件抽象层基于SFUD万能驱动库实现!!!
2.2)使能和配置如下
2.3)具体使用
#define LOG_TAG "flash"
#define LOG_LVL LOG_LVL_DBG
#include <ulog.h>
#include "ph_public.h"
static int rt_hw_spi_flash_init(void)
{
static struct rt_spi_device *spi_device = RT_NULL;
spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
if(spi_device == RT_NULL){
LOG_E("Failed to malloc the spi device!\r\n");
return -RT_ENOMEM;
}
static struct n32_spi_cs spi_cs;
spi_cs.GPIOx = GPIOA;
spi_cs.GPIO_Pin = GPIO_PIN_4;
if(rt_spi_bus_attach_device(spi_device,SPI_FLASH_DEVICE_NAME,SPI_BUS_NAME,(void*)&spi_cs) != RT_EOK){//在SPI总线上附加一个设备
LOG_E("Failed to attach the %s device!\r\n",SPI_BUS_NAME);
return -RT_ERROR;
}
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_spi_flash_init);
static int rt_hw_spi_flash_with_sfud_init(void)
{
if (RT_NULL == rt_sfud_flash_probe(SPI_FLASH_CHIP, SPI_FLASH_DEVICE_NAME))
{
return RT_ERROR;
};
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_with_sfud_init);
第三部分:FAL
3.1)FAL是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API (框架图如下所示),并具有以下特性:
3.1.1)支持静态可配置的分区表,并可关联多个 Flash 设备;
3.1.2)分区表支持自动装载 。避免在多固件项目,分区表被多次定义的问题;
3.1.3)代码精简,对操作系统无依赖 ,可运行于裸机平台,比如对资源有一定要求的 Bootloader;
3.1.4)统一的操作接口。保证了文件系统、OTA 等对 Flash 有一定依赖的组件,底层 Flash 驱动的可重用性;
3.1.5)自带基于 Finsh/MSH 的测试命令,可以通过 Shell 按字节寻址的方式操作(读写擦) Flash 或分区,方便开发者进行调试、测试;
3.2)使能和配置
3.3)主要工作:
3.1.1)定义flash设备,包括片内flash和片外的spi flash.
3.1.2)定义flash设备表,在fal_dfg.h记录。
3.1.3)定义flash分区表:
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WORD, "bootload", "n32_onchip", 0, 128*1024, 0}, \
{FAL_PART_MAGIC_WORD, "app", "n32_onchip", 128*1024, 384*1024, 0}, \
{FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 0, 256*1024, 0}, \
{FAL_PART_MAGIC_WORD, "factory", NOR_FLASH_DEV_NAME, 256*1024, 256*1024, 0}, \
{FAL_PART_MAGIC_WORD, "cmb_log", NOR_FLASH_DEV_NAME, 512*1024, 1024*1024, 0}, \
{FAL_PART_MAGIC_WORD, "kvdb_param", NOR_FLASH_DEV_NAME, 1536*1024, 1024*1024, 0}, \
{FAL_PART_MAGIC_WORD, "tsdb_data", NOR_FLASH_DEV_NAME, 2560*1024, 4096*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
第四部分:flashDB
4.1)FlashDB是一款可以运行在MCU上的轻量级的开源嵌入式数据库,专注于提供嵌入式产品的数据存储方案,有键值数据库和时序数据库两种,可以用来保存产品参数,也可以用来保存传感器的采集数据,比如温度和湿度。
4.2)使能和配置
4.3)具体使用
/************** FLASHDB相关结构体 ***************/
struct DATA_STORE_TIME
{
int tem;//温度
int hum;//湿度
};
static struct fdb_kvdb kvdb = { 0 };
struct DATA_STORE_TIME sensor_value = {0,0};
// 数据库 数据表单
static struct fdb_default_kv_node default_kv_table[] = {
{"data_store_time", &sensor_value, sizeof(struct DATA_STORE_TIME)}, /* int array type KV */
};
/*
功能:flashdb相关初始化
*/
int ph_flashdb_init(void)
{
fal_init();
fdb_err_t result = FDB_NO_ERR;
struct fdb_default_kv default_kv;
default_kv.kvs = default_kv_table;
default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);
result = fdb_kvdb_init(&kvdb, "deviceparam", "kvdb_param", &default_kv, NULL);
if (result != FDB_NO_ERR){
LOG_E("fdb_kvdb_init error,please check ...\n");
return RT_ERROR;
}
return RT_EOK;
}
INIT_APP_EXPORT(ph_flashdb_init);
/*
功能:flashdb测试
*/
void ph_flashdb_test(void)
{
fdb_err_t result = FDB_NO_ERR;
struct fdb_blob kvdb_blob;
fdb_kv_get_blob(&kvdb, "data_store_time", fdb_blob_make(&kvdb_blob, &sensor_value, sizeof(struct DATA_STORE_TIME)));
if(kvdb_blob.saved.len > 0){
LOG_I("get the 'data_store_time' value is: sensor_value.tem = %d,sensor_value.hum = %d \n", sensor_value.tem,sensor_value.hum);
}else{
LOG_E("================== ph_read_data_collection_time_from_db error ==================\n");
}
sensor_value.tem++;
sensor_value.hum += 2;
result = fdb_kv_set_blob(&kvdb, "data_store_time", fdb_blob_make(&kvdb_blob, &sensor_value, sizeof(struct DATA_STORE_TIME)));
if (result != FDB_NO_ERR){
LOG_E("================= ph_write_data_collection_TIME error =====================\n");
}
}
第五部分:效果打印
[14:03:38]01-01 08:00:00 I/flash main: get the 'data_store_time' value is: sensor_value.tem = 10,sensor_value.hum = 19
[14:03:45]01-01 08:00:00 I/flash main: get the 'data_store_time' value is: sensor_value.tem = 11,sensor_value.hum = 21
[14:03:46]01-01 08:00:00 I/flash main: get the 'data_store_time' value is: sensor_value.tem = 12,sensor_value.hum = 23
[14:03:47]01-01 08:00:00 I/flash main: get the 'data_store_time' value is: sensor_value.tem = 13,sensor_value.hum = 25
整体工程如下,欢迎多多指正!!!
- 2023-09-17
-
回复了主题帖:
阅读打卡第四站:15-19章——《RT-Thread设备驱动开发指南》
1,fetch_data,control两种方法。
2,需要一把锁对读,写,擦等操作进行保护。
3,CRC设备,RNG设备,HASH设备,CRYP设备等。
4,擦除的最小单位是一个块大小。
-
发表了主题帖:
读《RT-Thread设备驱动开发指南》---第三篇
本帖最后由 xiaolinen 于 2023-9-17 16:41 编辑
读《RT-Thread设备驱动开发指南》--- SENSOR设备驱动开发
第一部分:了解SENSOR设备
SENSOR设备就是我们常见的种种传感器,它的作用不言而喻,就是感知外界的各种参数,为系统运行提供数据。SENSOR设备也是I/O设备中的一种,适用I/O设备框架。本次使用到的是MPU6050传感器,其可以用来测试设备的加速度和角速度。
第二部分:SENSOR设备涉及到的函数接口
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);
3,读设备
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
4,控制设备
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
5,接收回调函数
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
6,关设备
rt_err_t rt_device_close(rt_device_t dev);
第三部分:实验测试用的软件包以及现象
1,软件包:mpu6xxx,该软件包依赖于RT-Thread4.00+,serson组件,IIC/SPI驱动;
2,配置选择如下图:
Enable MPU6xxx acce: 配置开启加速度计功能
Enable MPU6xxx gyro: 配置开启陀螺仪功能
Enable MPU6xxx mag: 配置开启磁力计功能
3,主要函数:
3.1初始化:
struct mpu6xxx_device *mpu6xxx_init(const char *dev_name, rt_uint8_t param)
3.2获取加速度数据:
rt_err_t mpu6xxx_get_accel(struct mpu6xxx_device *dev, struct mpu6xxx_3axes *accel)
3.3获取陀螺仪数据:
rt_err_t mpu6xxx_get_gyro(struct mpu6xxx_device *dev, struct mpu6xxx_3axes *gyro)
4,实验现象打印:
accel.x = 11458, accel.y = 10464, accel.z = 4222
gyro.x = 79 gyro.y = 22, gyro.z = 13
accel.x = 11308, accel.y = 10432, accel.z = 4178
gyro.x = 79 gyro.y = 23, gyro.z = 13
accel.x = 11436, accel.y = 10520, accel.z = 4238
gyro.x = 80 gyro.y = 23, gyro.z = 15
accel.x = 11458, accel.y = 10502, accel.z = 4140
gyro.x = 79 gyro.y = 24, gyro.z = 14
accel.x = 11410, accel.y = 10398, accel.z = 4232
gyro.x = 78 gyro.y = 22, gyro.z = 14
accel.x = 11480, accel.y = 10492, accel.z = 4235
gyro.x = 79 gyro.y = 23, gyro.z = 15
- 2023-09-02
-
回复了主题帖:
阅读打卡第三站:9-14章——《RT-Thread设备驱动开发指南》
1,控制看门狗设备的操作有:(1)获取溢出时间:RT_DEVICE_CTRL_WDT_GET_TIMEOUT;(2)设置溢出时间:RT_DEVICE_CTRL_WDT_SET_TIMEOUT;(3)获取剩余时间:RT_DEVICE_CTRL_WDT_GET_TIMELEFT;(4)喂狗:RT_DEVICE_CTRL_WDT_KEEPALIVE;(5)启动看门狗:RT_DEVICE_CTRL_WDT_START;(6)停止看门狗:RT_DEVICE_CTRL_WDT_STOP。
2,宏定义为:RT_USING_WDT。
3,设备名:sdx,如:sd0,sd1...等。
4,需对接在rt_size_t (*touch_readpoint)(struct rt_touch_device *touch, void *buf, rt_size_t touch_num)接口上。
- 2023-08-30
-
发表了主题帖:
读《RT-Thread设备驱动开发指南》---第二篇
本帖最后由 xiaolinen 于 2023-8-30 22:19 编辑
读《RT-Thread设备驱动开发指南》--- TOUCH设备驱动开发
第一部分:了解Touch层级结构
1)什么是Touch设备:
Touch设备就是触摸设备,是目前嵌入式人机交互领域常用的输入设备。
2)Touch的层级结构:
如图所示,即是Touch设备的层级结构示意:
注:图中只体现出的硬件层的IIC总线,其实也可以通过SPI总线实现!
2.1)图中的应用层主要是开发者自己编写的应用代码,调用统一的I/O设备操作函数,实现自己的业务需求。
2.2)I/O设备管理层主要是为设备驱动框架提供统一的操作接口,即rt_device_find,rt_device_open,rt_device_control等。
2.3)Touch设备驱动框架层是对触摸设备基本功能的抽象,与硬件无关,需要开发者自行实现这部分操作方法,比如:rt_touch_info;另外,Touch设备属于I/O设备的一类,同样需要进行设备注册操作,接口为:rt_hw_touch_register。
2.4)Touch设备驱动层就是针对具体的触摸芯片编写的,开发者可借助RT-Thread提供的软件包快速实现功能,本次实验就会用到XPT2046相关软件包。
2.5)硬件层就是对应不同厂家,不同型号的触摸芯片,如:IIC总线的FT5426,GT911等;SPI总线的XPT2046,HR2046等。
第二部分:Touch设备涉及到的函数接口
查找设备
rt_device_t rt_device_find(const char* name);
打开设备
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
读设备
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
控制设备
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
设置接收回调函数
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
关闭设备
rt_err_t rt_device_close(rt_device_t dev);
第三部分:实验用到的相关软件包以及效果
3.1)软件包添加路径:
注:该软件包依赖:RT-Thread 4.0+,SPI设备驱动程序,PIN设备驱动程序
3.2)查找设备:
#define PRO_TOUCH_EQU "xpt0"
rt_device_t touch = rt_device_find(PRO_TOUCH_EQU);
if (touch == RT_NULL)
{
LOG_E("can't find device: %s\n", PRO_TOUCH_EQU);
return;
}
3.3)打开设备:
if (rt_device_open(touch, RT_DEVICE_FLAG_INT_RX) != RT_EOK)
{
LOG_E("open %s failed!\n",PRO_TOUCH_EQU);
return;
}
3.4)读设备:
if (rt_device_read(touch, 0, &read_data, 1) == 1)
{
LOG_I("x = %d,y = %d\n",read_data.x_coordinate,read_data.y_coordinate);
}
3.5)打印输出:
操作为触摸屏进行校准后,手指长按一个点,不动。
ph_xpt2046: x = 3189,y = 1994
ph_xpt2046: x = 3199,y = 1977
ph_xpt2046: x = 3199,y = 1979
ph_xpt2046: x = 3213,y = 1972
ph_xpt2046: x = 3247,y = 1991
ph_xpt2046: x = 3247,y = 1979
ph_xpt2046: x = 3247,y = 1967
ph_xpt2046: x = 3247,y = 1971
ph_xpt2046: x = 3247,y = 1955
ph_xpt2046: x = 3199,y = 1973
ph_xpt2046: x = 3199,y = 1990
ph_xpt2046: x = 3199,y = 2007
ph_xpt2046: x = 3167,y = 2039
ph_xpt2046: x = 3071,y = 2019
ph_xpt2046: x = 2943,y = 1911
ph_xpt2046: x = 2735,y = 1871
ph_xpt2046: x = 2523,y = 1865
ph_xpt2046: x = 2399,y = 1853
ph_xpt2046: x = 2295,y = 1839
- 2023-08-20
-
回复了主题帖:
阅读打卡第二站:2-8章——《RT-Thread设备驱动开发指南》
1)12C总线设备从机地址被定义在:struct rt_i2c_msg中;
struct rt_i2c_msg{
rt_uint16_t addr; /*从机地址*/
rt_uint16_t flags; /*读,写标志等*/
rt_uint16_t len; /*读写数据字节数*/
rt_uint8_t *buf; /*读写数据缓冲区指针*/
}
2)系统中存在SPI总线设备后,可以借助挂载SPI从设备的接口,通过rt_hw_spi_device_attach()函数挂载一个SPI外设(SPI从设备)。
3)在不考虑系统内存的情况下,HWTIMER硬件定时器的个数也不是无限的;因为HWTIMER硬件定时器是和硬件MCU定时器一一绑定的,其可用的定时器数量受限于硬件MCU本身定时器的数量,一般在3~15个。
4)pwm设备中,control方法中的arg:是PWM设备的配置参数,对PWM设备操作时,需要传入的设置参数包括PWM设备的通道号,波形周期和脉冲宽度。参数类型定义如下所示:
struct rt_pw_configuration
{
rt_uint32_t channel; /*PWM通道:0~n*/
rt_uint32_t period; /*PWM周期时间(单位为ns)*/
rt_uint32_t pluse; /*PWM脉冲宽度时间(单位为ns)*/
}
5)RTC驱动依赖libc库吗?
:书中8.5章节驱动配置中提到需要使能RT_USING_LIBC,故目前认为是需要依赖。
- 2023-08-16
-
发表了主题帖:
读《RT-Thread设备驱动开发指南》---第一篇
本帖最后由 xiaolinen 于 2023-8-20 17:34 编辑
读《RT-Thread设备驱动开发指南》--- I/O设备框架
前言:
首先,作为一个正在工作中学习和使用RT-Thread的开发者,非常感谢论坛和RT-Thread提供这么一个机会,让自己拿到这本书,从而进行进一步的学习,也祝平台和RT-Thread的发展更上一层楼!!!此外,帖子只是自己的学习笔记,不足之处,请多多指教。
第一部分:I/O设备模型认知
I/O设备框架是什么:设备框架就是针对某一类外设,抽象出来一套统一的操作方法以及接入标准。注:所说的I/O设备指的是输入/输出设备,不是GPIO(不要问为什么在这里写这么一句,因为我有一个朋友曾经就有这样的疑惑)。
I/O设备框架有什么:I/O设备框架位于硬件和应用层之间,总共分为三层,从上到下分别是I/O设备管理层,设备驱动框架层,设备驱动层。具体如下如所示:
图中提到的应用层:也就是我们自己的业务逻辑程序,比如:众所周知的main.c文件。在这里,我们通过调用I/O设备管理层的函数接口,实现我们想要的功能,比如:点个灯。
图中提到的I/O设备管理层:在这一层,RT-Thread实现对设备驱动程序的封装,对应着工程中的device.c文件。实现对上为应用层提供统一的函数接口,使得设备驱动程序升级时,不会对应用层产生影响;对下通过设备驱动框架层操作硬件的动作。此外,在这一层包含rt_device_find,open,read,write,close,register等管理接口!
图中提到的设备驱动框架:在这一层进行对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现,也就是求同存异,最大力度的统一格式再使用!!!比如:serial.c,adc.c等文件。
图中提到出的设备驱动层:在这一层通过程序,实现对硬件设备的访问功能。负责I/O设备的创建和注册功能;比如:drv_gpio.c,drv_adc.c,drv_usart.c等文件。
这部分的注册功能有两种实现方式:
1)使用I/O设备管理接口直接注册,通过rt_device_register()接口实现;流程如下图所示:
2)通过设备驱动框架层提供的注册函数进行注册,注册函数名一般为rt_hw_xxx_register(),其后,设备驱动框架层的注册函数调用rt_device_register()接口,实现注册功能;流程如下图所示:
图中提到出的硬件:比如:单片机自带的外设,外接的传感器,FLASH芯片等等。
I/O设备框架的优势是什么:
一开始自己也认为在裸机程序中,直接调用驱动程序,多么直接,多么干脆,为什么要费劲巴拉的使用实时系统呢?为什么还要了解这个I/O设备框架呢?但是,经历了这几年的换芯潮之后,现在已经转变了自己的想法,不得不说有一个统一的框架确实香!RT-Thread实时系统降低了代码的耦合性,复杂性,提高了系统的可靠性,易维护性,在这其中呢,因为有I/O设备框架的存在,不管使用哪一款MCU,只要应用层调用的对设备操作的函数保持不变,那么开发人员就只需要修改驱动代码,从而节省一部分时间,去做其他有意义的事情。
第二部分:I/O设备模型使用准备
I/O设备管理接口:
1)创建设备:
rt_device_t rt_device_create(int type, int attach_size);
2)销毁设备:
void rt_device_destroy(rt_device_t device);
3)注册设备:
rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags);
4)注销设备:
rt_err_t rt_device_unregister(rt_device_t dev);
5)查找设备:
rt_device_t rt_device_find(const char* name);
6)初始化设备:
rt_err_t rt_device_init(rt_device_t dev);
7)打开设备:
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
8)关闭设备:
rt_err_t rt_device_close(rt_device_t dev);
9)控制设备:
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
10)读设备:
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos,void* buffer, rt_size_t size);
11)写设备:
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos,const void* buffer, rt_size_t size);
12)数据发送回调函数:
rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));
13)数据接收回调函数:
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
注:上面只是记录了函数接口,参数和详细说明,请移步RT-Thread官网:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/device
第三部分:I/O设备模型实操(UART)
使用流程:注册-》查找-》打开-》读取或发送
1)注册串口设备:
注:串口的参数配置保存在uart_obj数组中;INIT_BOARD_EXPORT(rt_hw_usart_init)为自动初始化(关于RT-Thread自动初始化机制更多了解,请移步:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/basic/basic?id=rt-thread-%E8%87%AA%E5%8A%A8%E5%88%9D%E5%A7%8B%E5%8C%96%E6%9C%BA%E5%88%B6);
/*
功能:设置所需串口的参数,注册串口
*/
int rt_hw_usart_init(void)
{
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
rt_err_t result = 0;
stm32_uart_get_dma_config();
for (rt_size_t i = 0; i < sizeof(uart_obj) / sizeof(struct stm32_uart); i++)
{
/* init UART object */
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;
}
INIT_BOARD_EXPORT(rt_hw_usart_init);
2)查找设备:
#define PRO_SENSOR_UART "uart1"
/*
功能:查找传感器串口
*/
rt_device_t device = rt_device_find(PRO_SENSOR_UART);
if (!device){
LOG_E("find %s failed!\n", PRO_SENSOR_UART);
return RT_ERROR;
}
3)打开设备
/*
功能:以RT_DEVICE_FLAG_DMA_RX方式打开串口
*/
if (rt_device_open(device, RT_DEVICE_FLAG_DMA_RX) != RT_EOK){
LOG_E(" %s device open error,please check ...", PRO_SENSOR_UART);
}
4)设置数据接收回调函数:
/*
功能:传感器串口的接受回调函数
*/
static rt_err_t sensor_uart_recv_callfunc(rt_device_t dev, rt_size_t size)
{
if(size > 0){
rt_sem_release(&rx_sem);
}
return RT_EOK;
}
/*
功能:设置串口的回调函数
*/
if(rt_device_set_rx_indicate(device, sensor_uart_recv_callfunc) != RT_EOK){
LOG_E("uart_recv callfunc set error ,please check ...");
return false;
}
5)接收数据解析函数:
注:因为传感器部分使用到了软件包Agile Modbus,所以串口接收到数据后,调用了下面的函数进行解析:
bool pro_sensormcu_mosbus_prase(rt_uint8_t *inbuf,rt_uint16_t length)
{
rt_uint16_t hold_register[HOLD_REGISTER_NUM];
ctx->read_bufsz = length;
rt_memcpy(ctx->read_buf,inbuf,ctx->read_bufsz);
int rc = agile_modbus_deserialize_read_registers(ctx, ctx->read_bufsz, hold_register);
if (rc < 0) {
LOG_W("Receive failed.");
if (rc != -1)
LOG_W("Error code:%d", -128 - rc);
LOG_I("Hold Registers:");
for (int i = 0; i < HOLD_REGISTER_NUM; i++){
LOG_I("Register [%d]: 0x%04X", i, hold_register[i]);
}
return false;
}
LOG_I("Receive success Hold Registers:");
for (int i = 0; i < HOLD_REGISTER_NUM; i++){
LOG_I("Register [%d]: 0x%04X", i, hold_register[i]);
}
return true;
}
6)采集到的数据记录:
ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x026C
ph_sensortwomcu1: Register [1]: 0x011A
ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x026B
ph_sensortwomcu1: Register [1]: 0x0119
ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x0268
ph_sensortwomcu1: Register [1]: 0x0118
ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x0249
ph_sensortwomcu1: Register [1]: 0x0118
ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x024A
ph_sensortwomcu1: Register [1]: 0x0117
结束语:
这是阅读《RT_Thread设备驱动开发指南》的第一篇笔记,结合外接的温湿度传感器,进行了一个小实验,不足之处,还请大家多多指正!
- 2023-08-15
-
回复了主题帖:
阅读打卡第一站:IO设备框架基础知识——《RT-Thread设备驱动开发指南》
1.RT-Thread 设备基类是什么?
:rt_device。
2.RT-Thread为设备提供了哪些操作方法?
:init(),open(),close(),read(),write(),control()。
3.设备名的最大长度是什么指定的?
:由RT_NAME_MAX决定 。
4.设备注册失败,可能的原因是什么?
:1)设备对象为空;2)设备名已经注册。
- 2023-08-04
-
回复了主题帖:
读书入围名单:《RT-Thread设备驱动开发指南》
个人信息无误,确认可以完成阅读计划和打卡任务
- 2023-07-20
-
加入了学习《物联网NBIoT定位器》,观看 课程介绍