xiaolinen

  • 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定位器》,观看 课程介绍

学过的课程

最近访客

< 1/2 >

统计信息

已有34人来访过

  • 芯积分:532
  • 好友:1
  • 主题:12
  • 回复:30
  • 课时:--
  • 资源:--

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言