Fillmore

  • 2020-07-31
  • 发表了主题帖: 零基础做温湿度监控上位机

    本帖最后由 Fillmore 于 2020-7-31 18:29 编辑 前言 很多网友问我,写上位机用什么语言好。C#,C++,python这些都可以写,但是你必须要把你用的语言的一些基础知识学完之后才能动手去写一个上位机。这会耗费你一些时间。 那么有什么办法快速方法写一个上位机监控软件,答案是有的。今天来介绍一个好工具--MicroLab,零代码写上位机监控软件。这个软件是网友--FM,他花了一年多的时间写出来了。为了更好的展示,结合RT-Thread方便的软件包生态,快速搭建一套温湿度监控设备。 环境:RT-Thread studio + GND board + MicroLab软件 GND board是自己DIY的一款板子,下面链接是关于GND board的描述。 GND Studio 开发板   RT-Thread软件包   GND board上带在DHT11温湿度传感器,RT-Thread有相应的软件包,直接利用简单快捷。在RT-Thread studio添加DHT11的软件包。   修改DHT11连接对应的IO口     MicroLab软件   MicroLab是一个功能集成度比较高的软件。 下载地址: https://gitee.com/fengmeitech/Micro-Lab 功能列表: 序号 功能 1 全新的串口/网络调试服务及关联模块 2 发送历史永久保存及支持别名 3 串口/网络动态指令编程 4 串口/网络虚拟示波器 5 超级计算器 6 ModBus RTU计算器 7 DTU 8 串口-网络分裂器 9 应用影子分身 10 众多开发辅助工具及资料 下面展示一下该软件的部分界面。   移植MicroLab协议到rtt工程   MicroLab支持串口传输和网络传输,在这里的演示都是基于串口传输的。所以需要用到串口,所以我们需要配置相对应的串口。   初始化响应的串口,波特率:115200。     MircoLib的组态协议。     移植组态协议到RTT。 移植虚拟示波器协议到RTT。   虚拟示波器演示 我们在下位写一个正弦波的程序进行测试。 效果:   温湿度软件演示   首先设计画布。   下位机上报数据代码。   效果:   下位机的代码链接:https://gitee.com/rice_chen_1/picture/tree/master/micro_lab/rice_MicroLab  

  • 2020-07-21
  • 发表了主题帖: RT-Thread操作系统μC/OS-III兼容层

    1 概述
    这是一个针对RT-Thread操作系统的μCOS-III操作系统兼容层,可以让基于美国Micriμm公司的μCOS-III操作系统的项目快速迁移到RT-Thread操作系统上。
    1.1 本兼容层适合于: 老项目需要从μCOS-III操作系统向RT-Thread操作系统迁移 之前学习过μCOS-III操作系统,意图转向学习RT-Thread国产操作系统。本兼容层可以帮您用已有的μCOS-III编程经验和习惯快速将项目跑起来,日后再慢慢深入熟悉RT-Thread的API函数,逐步向RT-Thread过度。降低您的学习门槛和时间成本。有了本兼容层,对RT-Thread API以及编程风格的不熟悉再也不是您学习RT-Thread的阻力! 1.2 版本详细信息 组件名称 版本号 RT-Thread nano 3.1.3 μC/OS-III 3.03.00 μC/CPU 1.30.00 μC/LIB 1.37.02 1.3 官网
    RT-Thread: https://www.rt-thread.org/ 文档中心: https://www.rt-thread.org/document/site/tutorial/nano/an0038-nano-introduction/   μCOS-III:https://www.micrium.com/ 文档中心: https://doc.micrium.com/display/kernel304/uC-OS-III+Documentation+Home
    2 使用 2.1 Keil-MDK仿真工程 本仿真工程是基于STM32F103RB平台。 Keil工程路径:RT-Thread-wrapper-of-uCOS-III\rt_thread_3.1.3-ucosiii_3.03-wrapper\rt-thread-3.1.3\bsp\stm32f103-msh-628\Project.uvprojx 需要提前安装好RT-Thread Nano-3.1.3 Keil支持包: https://www.rt-thread.org/download/mdk/RealThread.RT-Thread.3.1.3.pack 注意:调试串口使用的是USART2,不是USART1 2.2 迁移步骤 将uCOS-III_Wrapper文件夹内的所有文件都加入到你的工程中,最好保持原有文件夹的结构。相较于原版μCOS-III增加了os_rtwrap.c文件,负责对RT-Thread和μCOS-III的转换提供支持。 浏览一下μC-CPU/cpu.h文件,看一下头文件中的定义是否符合你的CPU,一般不需要改这个文件 浏览一下μCOS-III/os.h文件,看一下错误代码,这个错误代码和原版μCOS-III是有一定区别的。 注意: 请勿随意打开注释掉的错误码枚举体成员, 如果用户使用到了这些注释掉的成员,则会在迁移时编译报错,用以提醒用户这些错误代码在兼容层已经不可用。 软件定时器:μCOS-III原版的软件定时器回调函数是两个参数,本兼容层由于RT-Thread的回调函数仅为一个参数,因此改为一个参数(详见μCOS-III/os.h)。 μCOS-III原版软件定时器回调函数定义: 1typedef  void  (*OS_TMR_CALLBACK_PTR)(void *p_tmr, void *p_arg); 本兼容层软件定时器回调函数定义: 1typedef  void (*OS_TMR_CALLBACK_PTR)(void *parameter); 配置os_cfg.h和os_cfg_app.h 每个选项的配置说明和原版μCOS-III一致,若有不同,我已经在注释中有所解释。 原版μCOS-III配置说明可参见: a)《嵌入式实时操作系统μC/OS-III》北京航空航天大学出版社 宫辉等译 邵贝贝审校 b) Micriμm公司μCOS-III在线文档: https://doc.micrium.com/display/kernel304/uC-OS-III+Features+os_cfg.h 2.3 os_cfg.h配置文件 1#define  OS_CFG_DBG_EN  1     /* Enable (1) debug code/variables  */   该宏定义定义是否启用兼容层调试,建议在第一次迁移时打开,因为在兼容层内部,一部分uCOS-III原版功能没有实现,如果用户用到了这部分没有实现的功能,将会通过调试的方式输出,予以提示。用户务必对业务逻辑予以修改。 1#define  OS_CFG_TMR_TASK_RATE_HZ 100u /* Rate for timers (100 Hz Typ.) */ 在原版μCOS-III中,该宏定义定义了软件定时器的时基信号,这与RT-Thread的软件定时器有本质的不同,在RT-Thread中,软件定时器的时基信号就等于OS ticks。因此为了能够将μCOS-III软件定时器时间参数转为RT-Thread软件定时器的时间参数,需要用到该宏定义。请使该宏定义与原工程使用μCOS-III时的该宏定义参数一致。 2.4 os_cfg_app.h配置文件 该文件仅保留了OS tick频率的配置、定时器任务以及统计任务的配置。其他配置项本兼容层用不到(例如中断任务),予以删除。 2.5 运行 1#include <os.h> /*头文件保持和原版μCOS-III相同*/ 2 3int main(void) /*RT-Thread main线程*/ 4{ 5    OS_ERR err; 6 7    OSInit(&err);                                   /*uCOS-III操作系统初始化*/ 8 9    OSStart(&err);                                  /*开始运行uCOS-III操作系统*/ 10 11#if OS_CFG_APP_HOOKS_EN > 0u 12    App_OS_SetAllHooks();                           /*设置钩子函数*/ 13#endif   14 15#if OS_CFG_STAT_TASK_EN > 0u 16    OSStatTaskCPUUsageInit(&err);                   /*统计任务*/     17    OSStatReset(&err);                              /*复位统计数据*/     18#endif       19 20} 2.6 注意 由于μCOS-III支持8、16、32位CPU,而RT-Thread支持32、64位CPU,因此本兼容层仅能对基于32位CPU的已有工程进行兼容。 μCOS-III的任务堆栈大小单位是sizeof(CPU_STK),而RT-Thread的线程堆栈大小单位是Byte,虽然在兼容层已经做了转换,但是在填写时一定要注意,所有涉及到μCOS-III的API、宏定义全部是按照μCOS-III的标准,即堆栈大小为sizeof(CPU_STK),切勿混搭!这种错误极其隐晦,一定要注意!下面是混搭的错误示例: 1ALIGN(RT_ALIGN_SIZE) 2static rt_uint8_t thread2_stack[1024];//错误:混搭RT-Thread的数据类型定义线程堆栈 3 4OSTaskCreate(&thread2, 5             (CPU_CHAR*)"thread2", 6             thread2_entry,     7             RT_NULL, 8             THREAD_PRIORITY, 9             thread2_stack, 10             sizeof(thread2_stack)/10,//任务堆栈深度限位(错误:这个参数的单位是sizeof(CPU_STK)) 11             sizeof(thread2_stack),//任务堆栈大小(错误:这个参数的单位是sizeof(CPU_STK)) 12             0, 13             THREAD_TIMESLICE, 14             0, 15             OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, 16             &err); 下面是正确写法: 1#define THREAD_STACK_SIZE       256 //正确,要通过宏定义单独定义堆栈大小,单位为sizeof(CPU_STK) 2ALIGN(RT_ALIGN_SIZE) 3    static CPU_STK thread2_stack[THREAD_STACK_SIZE];//正确,使用uCOS-III自己的数据类型定义任务堆栈 4 5OSTaskCreate(&thread2, 6             (CPU_CHAR*)"thread2", 7             thread2_entry, 8             RT_NULL, 9             THREAD_PRIORITY, 10             thread2_stack, 11             THREAD_STACK_SIZE/10,//任务堆栈深度限位(正确) 12             THREAD_STACK_SIZE,//任务堆栈大小(正确) 13             0, 14             THREAD_TIMESLICE, 15             0, 16             OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, 17             &err); 切勿将RT-Thread和μCOS-III的API混搭使用。 例如RTT中的rt_thread_suspend / rt_thread_resume仅支持一次挂起/解挂;而μCOS-III的OSTaskSuspend / OSTaskResume是支持嵌套挂起/解挂的,为此需要继承struct rt_thread结构体并在其基础上增加成员.SuspendCtr变量实现该功能。若采用rt_thread_init初始化线程,该函数并不会管理μCOS-III兼容层的成员变量,.SuspendCtr也不会创建和初始化,若此时调用OSTaskSuspend / OSTaskResume函数试图指向.SuspendCtr成员变量,将会访问非法内存地址(因为rt_thread_init初始化的线程.SuspendCtr成员变量根本不存在)! 兼容层取消了原版μCOS-III中的时间戳功能 在μCOS-III中,时间戳主要用于测量中断关闭时间,以及任务单次执行时间以及最大时间等涉及到精度较高的时长测量。该特性在μCOS-II以及RT-Thread中均没有,因此不予实现。 μCOS-III原版定时器回调函数是在定时器线程中调用的,而非在中断中调用,因此要使用μCOS-III兼容层的软件定时器,需要将rtconfig.h中的宏定义RT_USING_TIMER_SOFT置1。 3 接口 3.1 没有实现兼容的API(仅2个) 虽然RT-Thread没有任务内建消息队列、任务内建信号量、任务内建寄存器机制,但是本兼容层均已实现,可以正常兼容。但由于RT-Thread没有提供相关接口,以下μCOS-III API无法兼容: 1void  OSTaskChangePrio (OS_TCB *p_tcb, OS_PRIO prio_new, OS_ERR *p_err); 2void  OSTaskTimeQuantaSet (OS_TCB *p_tcb, OS_TICK time_quanta, OS_ERR *p_err); 3.2 钩子函数 μCOS-III的钩子函数仅对μCOS-III兼容层负责。 即如果你注册了OSTaskDelHook函数,他仅会在调用OSTaskDel函数时被调用,不会在调用rt_thread_detach函数时被调用(这个由RTT的钩子函数负责)。这样做是为了层次分明,防止μCOS-III兼容层插手RT-Thread内部事务。 μCOS-III的钩子函数在两个文件中实现:os_cpu_c.c和os_app_hooks.c。按照μCOS-III的思想,os_cpu_c.c提供原始的钩子函数(即这些钩子函数被相应的函数直接调用),该文件以及其内部的钩子函数是移植工程师编写的内容,应用工程师不应该操作这个文件的内容,os_cpu_c.c文件的钩子函数提供相应的函数指针供os_app_hooks.c文件内的钩子函数注册和使用,这个文件内的钩子函数应用工程师是可以操作的。换句话说,我们有什么需要在钩子函数中调用的函数,应该放在os_app_hooks.c文件中。 以下原版μCOS-III钩子函数将予以取消,由RT-Thread接管相关钩子函数接管: 1void          OSTaskReturnHook          (OS_TCB *p_tcb); 2void          OSTaskSwHook              (void); 3void          OSTimeTickHook            (void); 同时,上述钩子函数对应的应用级钩子函数也被取消: 1void  App_OS_TaskReturnHook (OS_TCB  *p_tcb); 2void  App_OS_InitHook (void);/*按照手册要求OS初始化的钩子函数不应该出现在应用层,在3.03版本中出现应该是失误,在3.08版本中已经将该应用级钩子函数取消*/ 3void  App_OS_TaskSwHook (void); 4void  App_OS_TimeTickHook (void); 3.3 统计任务(OS_StatTask()、os_stat.c) 在μCOS-III中,统计任务是一个系统任务,通过OS_CFG_STAT_TASK_EN宏决定是否开启,可以在系统运行时做一些统计工作。例如统计总的CPU使用率(0.00% - 100.00%)、各任务的CPU使用率(0.00% - 100.00%)以及各任务的堆栈使用量。从内核版本V3.03.00起,CPU的利用率用一个0-10000之间的整数表示(对应0.00% - 100.00%)。 但是RT-Thread并没有统计任务,因此需要创建一个任务来兼容原版μCOS-III的统计任务,完成上述功能。该统计任务会在兼容层初始化时自动创建,用户无需干预。用户仅需调用OSStatTaskCPUUsage全局变量即可获取当前的CPU使用率,CPU使用率的计算策略和原版μCOS-III完全一致。 目前统计任务实现的功能:
    计算全局CPU使用率 计算每个任务的任务堆栈使用情况(当 OS_CFG_DBG_EN 和 OS_CFG_STAT_TASK_STK_CHK_EN 为1)
    注意:一旦开启统计任务,则该优先级强烈建议不要被其他任务使用,统计任务的优先级总是为OS_CFG_PRIO_MAX-2u 。 特别感谢:感谢armink大神在2018年7月14日在idle.c文件中增加了hook list功能,使RT-Thread空闲任务回调函数可以注册最多RT_IDEL_HOOK_LIST_SIZE个,而非只能注册一个。若没有该功能,本兼容层的空闲任务将无法实现。 3.4 全局变量 目前,本兼容层可以使用以下μCOS-III原版全局变量(位于os.h)。这些全局变量的具体含义请参见2.2节中所列举出的参考资料。 1#define          OSSchedLockNestingCtr      rt_critical_level()         /* Lock nesting level                         */ 2#define          OSIntNestingCtr            rt_interrupt_get_nest()     /* Interrupt nesting level                    */ 3#define          OSTCBCurPtr                ((OS_TCB*)rt_thread_self()) /* Pointer to currently running TCB           */ 4                                                                        /* PRIORITIES ------------------------------- */ 5#define          OSPrioCur                  rt_current_priority         /* Priority of current task                   */ 6#define          OSPrioTbl                  rt_thread_priority_table 7 8#if OS_CFG_APP_HOOKS_EN > 0u 9OS_EXT           OS_APP_HOOK_TCB            OS_AppTaskCreateHookPtr;    /* Application hooks                          */ 10OS_EXT           OS_APP_HOOK_TCB            OS_AppTaskDelHookPtr; 11OS_EXT           OS_APP_HOOK_VOID           OS_AppIdleTaskHookPtr; 12OS_EXT           OS_APP_HOOK_VOID           OS_AppStatTaskHookPtr; 13#endif 14 15OS_EXT            OS_STATE                  OSRunning;                  /* Flag indicating that kernel is running     */ 16 17#ifdef OS_SAFETY_CRITICAL_IEC61508 18OS_EXT            CPU_BOOLEAN               OSSafetyCriticalStartFlag;  /* Flag indicating that all init. done        */ 19#endif 20 21#if OS_CFG_MEM_EN > 0u 22#if OS_CFG_DBG_EN > 0u 23OS_EXT            OS_MEM                   *OSMemDbgListPtr; 24#endif 25OS_EXT            OS_OBJ_QTY                OSMemQty;                   /* Number of memory partitions created        */ 26#endif 27                                                                        /* TASKS ------------------------------------ */ 28#if OS_CFG_DBG_EN > 0u 29OS_EXT            OS_TCB                   *OSTaskDbgListPtr; 30#endif 31OS_EXT            OS_OBJ_QTY                OSTaskQty;                  /* Number of tasks created                    */ 32#if OS_CFG_TASK_REG_TBL_SIZE > 0u 33OS_EXT            OS_REG_ID                 OSTaskRegNextAvailID;       /* Next available Task Register ID            */ 34#endif 35#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u 36OS_EXT            OS_TICK                   OSSchedRoundRobinDfltTimeQuanta; 37OS_EXT            CPU_BOOLEAN               OSSchedRoundRobinEn;        /* Enable/Disable round-robin scheduling      */ 38#endif 39                                                                        /* IDLE TASK -------------------------------- */ 40OS_EXT            OS_IDLE_CTR               OSIdleTaskCtr;               41 42#if OS_CFG_STAT_TASK_EN > 0u                                            /* STATISTICS ------------------------------- */ 43OS_EXT            CPU_BOOLEAN               OSStatResetFlag;            /* Force the reset of the computed statistics */ 44OS_EXT            OS_CPU_USAGE              OSStatTaskCPUUsage;         /* CPU Usage in %                             */ 45OS_EXT            OS_CPU_USAGE              OSStatTaskCPUUsageMax;      /* CPU Usage in % (Peak)                      */ 46OS_EXT            OS_TICK                   OSStatTaskCtr; 47OS_EXT            OS_TICK                   OSStatTaskCtrMax; 48OS_EXT            OS_TICK                   OSStatTaskCtrRun; 49OS_EXT            CPU_BOOLEAN               OSStatTaskRdy; 50OS_EXT            OS_TCB                    OSStatTaskTCB; 51#endif 4 联系方式&致谢
    感谢RT-Thread工程师Willian Chan的技术支持: https://github.com/willianchanlovegithub 感谢RT-Thread工程师yangjie的技术支持: https://github.com/yangjie11   维护:Meco Man 联系方式:jiantingman@foxmail.com 主页:https://github.com/mysterywolf/RT-Thread-wrapper-of-uCOS-III

  • 2020-07-08
  • 回复了主题帖: 开源 4G模块 CMUX+PPP 拨号上网 详解

    455850258 发表于 2020-6-30 15:31 **** 作者被禁止或删除 内容自动屏蔽 ****
    ?公众号、吗

  • 回复了主题帖: rt-thread studio 1.1.2开始支持 雅特力芯片了。

  • 2020-06-01
  • 加入了学习《新版RTOS教程:15天入门RT-Thread内核》,观看 线程的创建

  • 加入了学习《新版RTOS教程:15天入门RT-Thread内核》,观看 动态内存堆的使用

  • 回复了主题帖: RT-Thread应用实战-TI温湿度HDC1000软件包设计与制作

    666 作者可以认识下吗?

  • 2020-05-29
  • 发表了主题帖: 开源 4G模块 CMUX+PPP 拨号上网 详解

    本帖最后由 Fillmore 于 2020-5-29 18:35 编辑 一、环境搭建 硬件:野火STM32F429 V1 + EC20 软件:RT-THREAD V4.0.1 + MDK 5.26 + STM32 CUBEMX RT-Thread软件包版本: 1.CMUX - latest 2.ppp_Device – latest 3.AT_Device – 1.6.0   开放工程以及详细操作步骤请见附件!

  • 2020-05-12
  • 加入了学习《RT-Thread Nano学习教程》,观看 RT-Thread Nano-IWDT-设备驱动框架入门

  • 2020-04-27
  • 回复了主题帖: RT-Thread设备框架学习——by ID.LODA

    啥也不说,这个真是太棒了

  • 2020-04-23
  • 回复了主题帖: RT-Thread系统内核简介

    沙发  楼主的分享很地道

  • 2020-04-10
  • 发表了主题帖: 水墨屏桌面台历

    本帖最后由 Fillmore 于 2020-4-10 13:50 编辑 平时经常会有一些事情忘记,比如今天几号,星期几,哪天有什么事情要做。有时候写在本子上,有时候记在微信里,但有时候连记在哪里都忘记了……   为了应对这个情况,我制作了一款带有备忘录的台历放在工作台上,提醒自己需要做的事情。   功能描述 作品功能列表:显示阳历日期、显示阴历日期、显示周几、显示纪念日、显示当日备忘事项、显示每日精选句子、点击触摸按键可以选择日期、标记待做事项状态、可通过MQTT将备忘事项传到云端、通过二维码对设备配网连接WIFI。   作品使用的软件包列表 1、EasyFlash软件包 EasyFlash是一款开源的轻量级嵌入式Flash存储器库,方便开发者更加轻松的实现基于Flash存储器的常见应用开发。 负责部分配置参数的存储。 2、fal软件包 FAL (Flash Abstraction Layer) Flash 抽象层,是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API 。 参数保存操作依赖此包。 3、airkissOpen软件包 腾讯WiFi设备一键配网协议,可以方便的对设备进行WIFI的网络配置。 帮我解决了联网一系列的烦恼,可以专注于完成业务逻辑。 4、netutils软件包 RT-Thread 可用的全部网络小工具集合,我用到了其中网络时间同步的功能。可以是设备连上网络之后,自动同步时间,让我不用关心时间是否准确。 5、pahomqtt软件包 在 Eclipse paho-mqtt源码包的基础上设计的一套 MQTT 客户端程序。 与服务器通信依赖此包,可以让我很轻松的与自己的MQTT服务器进行通信,而不必关心中间的实现细节与过程,能够把更多的工作中心放到业务处理中。 6、cJSON软件包 一款经典的json格式工具包,在C语言中通过API接口可以轻松解析和封装JSON数据。 通过MQTT传输的数据均是通过cJSON进行处理,结构化的数据更简洁直观,能把更多的注意力放到业务逻辑中。 7、u8g2-c 软件包 u8g2是一个单色屏驱动,通过它我顺利驱动了墨水屏。利用其丰富的API,我将数据生动的展现在墨水屏上。 8、【新增】lunar_calendar软件包 lunar_calendar 是一个将公历日期转换成阴历的工具软件包。 日期一般有阴历有阳历两种历法,软件包仓库并没有这个软件包,而作为一款台历,没有阴历会显得非常不完整。所以,我将阳历阴历转换的代码封装成了一款软件包,也方便为今后更多的朋友更简单的进行阴阳历的转换。 9、【新增】bs8116a软件包 bs8116a 是合泰的bs8116a-3的触摸按键芯片的中断使用软件包。 我在这个项目中使用到了bs8116a-3触摸按键,但是软件仓库中的按键软件包并不能完美实现我的需求,所以我就自己实现了相关的业务。这个软件包的其中一个特点就是可以同时使用多路i2c,连接多个bs8116触摸芯片。并且通过中断的形式,减少了系统资源消耗。通过相关参数的配置,可以达到低功耗的效果。 以上软件包均可以在以下地址找到(请复制至外部浏览器打开):http://packages.rt-thread.org/ 硬件框图 软件设计 逻辑流程图 大概的流程如上,主要分为上电,配网,以及点击按键。每个部分逻辑上都比较简单,就没有详细展开。 架构 这里放置了我的主要代码,共11个文件,c文件8个,头文件2个,编译文件1个。   1applications 2├── SConscript      /* 编译文件 */ 3├── defines.h       /* 变量 函数声明 */ 4├── init.c          /* 初始化 */ 5├── keyboard.c      /* 键盘相关处理 */ 6├── logic.c         /* 逻辑处理 */ 7├── main.c          /* 主函数 */ 8├── mqtt.c          /* MQTT操作相关 */ 9├── network.c       /* 网络操作相关 */ 10├── qrcode_array.h  /* 配网二维码数组 */ 11├── screen.c        /* 屏幕操作相关 */ 12└── timer.c         /* 计时器 */   简单介绍一下: defines.h 1#define MAX_TODO_SIZE  8 2typedef struct { 3    char content[64]; 4    short status; 5} TODO; 6typedef struct { 7    char date[16]; 8    TODO todo_list[MAX_TODO_SIZE]; 9    char festival[64]; 10    char words[256]; 11} CALENDAR; 12extern CALENDAR calendar_data; 此处定义了我传输数据的结构体,大部分数据都是存储在这个结构体中。 init.c 1/* 集中初始化 */ 2void init_all(void) { 3    fal_init(); 4    easyflash_init(); 5    init_mq(); 6    init_sem(); 7    init_keyboard(); 8    hwtimer_init(); 9} 这个函数将各个部分放置到一起,按照顺序逐个初始化。 对应的源码均已开源。   Web操作界面 代码地址 bs8116a-latest 代码地址:https://github.com/illusionlee/bs8116a.git lunar_calendar-latest  代码地址:https://github.com/illusionlee/lunar_calendar.git 项目代码地址:https://github.com/illusionlee/desk_calendar.git   本期分享来自RT-Thread软件包大赛一等奖获得者李硕

  • 发表了主题帖: 你在使用什么嵌入式操作系统?

  • 2020-03-19
  • 回复了主题帖: 使用RTT Studio DIY 迷你桌面时钟(一)| 基于STM32芯片创建HelloWorld工程

    modoyu4ylp0o 发表于 2020-3-7 12:11 第六步串口不出来“hello...”,不知道什么原因,其它都出来了
    问题解决了吗

  • 2020-03-14
  • 发表了日志: 基于RT-Thread的SPIFI调试总结

  • 发表了主题帖: 基于RT-Thread的SPIFI调试总结

    背景 此文用以复盘基于RT-Thread的SPIFI调试过程。由此总结相关知识,形成相应知识的经验总结。 硬件平台 MCU:LPC54606 NorFLash芯片:W25Q256 SPI接口:SPIFI 开始之前面临的问题 由于刚刚接触RT-Thread个把月左右的时间,SPIFI(甚至SPI)相关基础也不是太牢靠。在开始之前一直有几个疑惑。 SPIFI与SPI什么关系? RT-Thread中只看到了SPI的驱动架构,没有看到SPIFI的,SPIFI怎么加入RT-Thread? 刚开始简直是一头雾水,看了官网关于SPI的相关文档,找了论坛里与SPI相关的讨论,均未找到明确的答案。 开始有头绪和线索,是从cychen的回帖开始的。在此非常感谢cychen的热心帮助,多次回帖甚至将自己已经完成的驱动分享给我。后来我才想到他也是最早在论坛里发帖讨论LPC54608下的SPIFI问题的人。在cycchen提示可参考STM32 QSPI以及分享给我的代码的提示下,现在已经搞明白了相关问题并调试通过。现在做个总结也分享给大家。 SPIFI与SPI什么关系? SPIFI本质上就是SPI,只不过换了个名字和一堆NXP自己的SPIFI寄存器而已。 RT-Thread中只看到了SPI的驱动架构,没有看到SPIFI的,SPIFI怎么加入RT-Thread? 要将SPIFI将入SPI架构,需要将SPIFI的数据与QSPI的数据结构进行相互转换即可。下面将总结关键的数据结构与关键接口。理解了这些也就理解了如何进行驱动挂接。 关键数据结构 spifi_command_t 1/*! @brief SPIFI command structure */ 2typedef struct _spifi_command 3{ 4    uint16_t dataLen;                 /*!< How many data bytes are needed in this command. */ 5    bool isPollMode;                  /*!< For command need to read data from serial flash */ 6    spifi_data_direction_t direction; /*!< Data direction of this command. */ 7    uint8_t intermediateBytes;        /*!< How many intermediate bytes needed */ 8    spifi_command_format_t format;    /*!< Command format */ 9    spifi_command_type_t type;        /*!< Command type */ 10    uint8_t opcode;                   /*!< Command opcode value */ 11} spifi_command_t; 该结构体是在NXP提供的官方驱动中的数据结构,该结构根据SPIFI的command register来构造。用来通知MCU当前发送的SPI指令的相关参数。 rt_qspi_message 1struct rt_qspi_message 2{ 3    struct rt_spi_message parent; 4    /* instruction stage */ 5    struct 6    { 7        rt_uint8_t content; 8        rt_uint8_t qspi_lines; 9    } instruction; 10    /* address and alternate_bytes stage */ 11    struct 12    { 13        rt_uint32_t content; 14        rt_uint8_t size; 15        rt_uint8_t qspi_lines; 16    } address, alternate_bytes; 17    /* dummy_cycles stage */ 18    rt_uint32_t dummy_cycles; 19    /* number of lines in qspi data stage, the other configuration items are in parent */ 20    rt_uint8_t qspi_data_lines; 21}; 该数据结构为RT-Thread用于设置SPI相关指令参数的数据结构。可以看出该数据结构继承自rt_spi_message。在此基础上又定义了QSPI特有的参数,包括指令结构(指令内容/数据宽度),地址结构(地址内容/长度/数据宽度),空时钟数量,收发数据的宽度。 该数据结构是RT-Thread对SPI指令进行的抽象,我们在不用的硬件平台上实现驱动时就是要将这个抽象的数据结构转化为具体硬件平台的数据结构(不同芯片厂商所提供驱动的数据结构),然后交由芯片驱动进行操作。这应当是RT-Thread 驱动的关键。 转化方法 1void spifi_send_cmd(SPIFI_Type *base, struct rt_qspi_message *message) 2{ 3    spifi_command_t spifi_command; 4    RT_ASSERT(message != RT_NULL); 5    /*发送或接收的数据长度*/ 6    spifi_command.dataLen = message->parent.length; 7    spifi_command.isPollMode = false; 8 9    /*根据message中的rcv_buf/send_buf填写dirction*/ 10    if (message->parent.recv_buf) 11        spifi_command.direction = kSPIFI_DataInput; 12    else  13        spifi_command.direction = kSPIFI_DataOutput; 14 15    /* 16    根据message中的instruction.qspi_lines/address.qspi_lines/message->qspi_data_lines 17    完成对spifi_command.format的设置 18    PS:完成对spifi_command下的format是对指令、地址、数据传输时使用的数据宽度进行设置 19    */ 20    /* 21    instruction/address/qspi_data_lines均不为2(dual双线模式)或4(quad四线模式) 22    指令、地址、数据传输时均使用单线制 23    */ 24    if (message->instruction.qspi_lines != 2 && 25        message->instruction.qspi_lines != 4 && 26            message->address.qspi_lines != 2 && 27            message->address.qspi_lines != 4 &&  28            message->qspi_data_lines != 2 && 29            message->qspi_data_lines != 4) 30    { 31        spifi_command.format = kSPIFI_CommandAllSerial;  32    } 33    else if (message->qspi_data_lines == 4 &&  34            message->address.qspi_lines != 4 &&  35            message->instruction.qspi_lines != 4) 36    {/*仅date使用quad模式*/ 37        spifi_command.format = kSPIFI_CommandDataQuad; 38    } 39    else if (message->instruction.qspi_lines == 1 &&  40            message->address.qspi_lines != 1 &&  41            message->qspi_data_lines != 1) 42    {/*仅指令部分使用串行模式*/ 43        spifi_command.format = kSPIFI_CommandOpcodeSerial; 44    } 45    else  46    {/*所有均使用quad模式*/ 47        spifi_command.format = kSPIFI_CommandAllQuad; 48    } 49    /* 50    intermediateBytes对应着dummy cycles, 51    但是注意intermediateBytes的单位是byte 52    dummy cycles单位是bit 53    同时,注意他们之间的关系不能单纯的使用dummy_cycles / 8 54    对于dual或者quad模式应当使用dummy_cycles*message->address.qspi_lines / 8; 55    否则将导致dual或者quad模式下的读写错误 56    */ 57    if(message->address.qspi_lines!=0) 58        spifi_command.intermediateBytes = message->dummy_cycles*message->address.qspi_lines / 8; 59    else 60        spifi_command.intermediateBytes = message->dummy_cycles / 8; 61    /*命令*/ 62    spifi_command.opcode = message->instruction.content; 63 64    /* 65    根据message中指令和地址的线宽,设置spifi中的type 66    */ 67    if (message->instruction.qspi_lines != 0) 68    { 69        if (message->address.qspi_lines == 0) 70            spifi_command.type = kSPIFI_CommandOpcodeOnly; 71        else if (message->address.size == 8) 72            spifi_command.type = kSPIFI_CommandOpcodeAddrOneByte; 73        else if (message->address.size == 16) 74            spifi_command.type = kSPIFI_CommandOpcodeAddrTwoBytes; 75        else if (message->address.size == 24) 76            spifi_command.type = kSPIFI_CommandOpcodeAddrThreeBytes; 77        else 78            spifi_command.type = kSPIFI_CommandOpcodeAddrFourBytes;      79    } else { 80        if (message->address.size == 24) 81            spifi_command.type = kSPIFI_CommandNoOpcodeAddrThreeBytes; 82        else if (message->address.size == 32)  83            spifi_command.type = kSPIFI_CommandNoOpcodeAddrFourBytes; 84    } 85    if (message->address.qspi_lines) 86        SPIFI_SetCommandAddress(base, message->address.content); 87    SPIFI_SetCommand(base, &spifi_command); 88} 这部分不再多说,注意以上代码中的注释就好,已经做了详细说明。 RT-Thread数据结构思想
    这部分与SPI无关,是SPI实现中的副产品,仅仅是我在实现SPI这部分功能中对RT-Thread数据结构的一点理解,记录于此。其实这部分在RT-Thread的官方文档中已经有说明,只是接触个把月来,到现在才有较深刻的认识,所以有必要在此记录一下,以帮助自己和他人理解RT-Thread的世界观。
    RT-Thread的虽然是C语言的,但是其数据结构的设计大量的使用了面向对象的思想。比如rt_object,这是整个RT-Thread最基础的一个类,在rt-object的基础上,派生出了大量其他类。 通过上面的图我们看到,RT-Thread中,将所有用于进程间同步与通信的数据结构抽象出了一个ipc结构,在这个结构的基础上,再派生出了sem/mutex/event/mb/mq 而上面的数据结构中,在我们在驱动实现中,最多使用到的是rt_device。 拿spi举例,有一个基本的数据结构rt_qspi_device: 该数据结构定义如下: 1struct rt_qspi_device 2{  3    struct rt_spi_device parent; /*继承自rt_spi_device*/ 4    struct rt_qspi_configuration config; 5    void (*enter_qspi_mode)(struct rt_qspi_device *device); 6    void (*exit_qspi_mode)(struct rt_qspi_device *device); 7}; 从数据结构定义可以看出该数据结构继承自rt_spi_device。 再看rt_spi_device 1/** 2 * SPI Virtual BUS, one device must connected to a virtual BUS 3 */ 4struct rt_spi_device 5{ 6    struct rt_device parent; 7    struct rt_spi_bus *bus; 8    struct rt_spi_configuration config; 9    void   *user_data; 10}; 这里我们看出该数据结构作为一个device继承了rt_device的一些特性,同时,这个device挂载在spi总线上,其也包含了rt_spi_bus的特性; 这里的user_data,是用来让我们挂载不同芯片特性的,比如不同芯片的总线操作基地址,总线配置等等。 在我们的代码里,这个指针指向了lpc_spifi 1struct lpc_spifi 2{ 3    SPIFI_Type *base;       /*SPIFI寄存器基地址*/ 4    struct rt_qspi_configuration *cfg; 5}; 明白了这些,我们就能够明白RT-Thread是如何组织不同的数据结构,不同的驱动结构,以及如何将不同的芯片特有操作集成到一个统一的驱动框架下的。 关键接口 在实现了关键数据结构以后,对于SPI只需要实现两个基本API就可以了。 1static struct rt_spi_ops lpc_spifi_ops =  2{ 3    configure,  4    spixfer 5};  即实现SPI的配置与数据传输。 spixfer是所有数据传输的接口,任何的数据传输都需要最后都需要使用这个接口。 这里有几个经验总结一下:
    当我们调试过程中遇到问题,总是调试不通的时候,可以使用官方例程或者我们在其他地方跑通的程序放到configure中进行测试。 比如本次调试中,我一直怀疑初始化部分有问题,于是就将例程直接放入configure中进行调试,直接发现例程的配置是可以运行起来的,在此基础上,再将例程进行修改优化即可。 在实现本部分时,一直有个疑惑,下面这个ops的数据结构中,两个接口的第二个参数是rt_spi_configuration。但我要传入的参数是rt_qspi_configuration,当时一直都想不明白如何传入该参数,毕竟这个ops是rtt定义好的,我不能修改他。
    1/** 2 * SPI operators 3 */ 4struct rt_spi_ops 5{ 6    rt_err_t (*configure)(struct rt_spi_device *device, struct rt_spi_configuration *configuration); 7    rt_uint32_t (*xfer)(struct rt_spi_device *device, struct rt_spi_message *message); 8}; 后来看到STM32 QSPI的实现代码,明白了如何实现。实现代码如下: 1static rt_uint32_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message) 2{ 3    uint8_t val; 4    rt_size_t i; 5    rt_size_t len = 0; 6    RT_ASSERT(device != RT_NULL); 7    RT_ASSERT(device->bus != RT_NULL); 8    RT_ASSERT(device->bus->parent.user_data != RT_NULL); 9    struct lpc_spifi *spifi = (struct lpc_spifi *)(device->bus->parent.user_data);   10    struct lpc_sw_spifi_cs *cs = device->parent.user_data;  11    struct rt_qspi_message *qspi_message = (struct rt_qspi_message *)message; 12    if(message->cs_take) 13    { 14 15    } 16 17    const rt_uint8_t *sndb = message->send_buf; 18    rt_uint8_t *rcvb = message->recv_buf; 19    rt_size_t length = message->length; 20    /* send data */ 21    if (sndb) 22    { 23        spifi_send_cmd(spifi->base, qspi_message); 24        if(qspi_message->parent.length != 0) 25        { 26            for (i = 0; i < length; i++) 27            { 28                SPIFI_WriteDataByte(spifi->base, *sndb); 29                sndb++; 30            } 31            len = length; 32        } 33        else 34        { 35            len = 1; 36        }    37    }  38    else if (rcvb) 39    {    40        spifi_send_cmd(spifi->base, qspi_message); 41 42        for (i = 0; i < length; i++) 43        {    44            val = SPIFI_ReadDataByte(spifi->base); 45            *rcvb = val; 46            rcvb++; 47        } 48        len = length; 49    } 50    if (message->cs_release) 51    { 52    } 53    return len;54 }   我们传入的是一个指针,这个指针是可以进行类型转换的,使用指针的类型转换,可以有效的进行数据结构的切换。 当然,这对我们定义的数据结构也是有要求的,即我们上面提到的面向对象的定义方法,且数据结构的公共部分一定是放在数据结构的起始的。 总结 以上,就是本次开发的一些经验总结。感谢大家的阅读!

  • 2020-03-09
  • 回复了主题帖: 记录下基于Socket实现一个跨平台MQTT客户端,并做成RTT软件包

    okhxyyo 发表于 2020-3-9 00:17 谢谢分享,这个很不错呀
    谢谢版主

  • 2020-03-06
  • 发表了主题帖: 记录下基于Socket实现一个跨平台MQTT客户端,并做成RTT软件包

    kawaii-mqtt软件包是一个基于socket API之上的跨平台MQTT客户端 基于socket API的MQTT客户端,拥有非常简洁的API接口,以极少的资源实现QOS2的服务质量,并且无缝衔接了mbedtls加密库。此仓库是专门为RT-Thread做的软件包,原始仓库位于:https://github.com/jiejieTop/mqttclient   kawaii 是“卡哇伊”的谐音,单词的原意也是可爱的意思,一个很可爱的程序员制作的可爱的软件包 ღ( ´・ᴗ・` )~   使用本软件包有哪些优势? 1 基于标准BSD socket之上开发,只要是兼容BSD socket的系统均可使用。 2 稳定:无论是掉线重连,丢包重发,都是严格遵循MQTT协议标准执行,除此之外对大数据量的测试无论是收是发,都是非常稳定(一次发送135K数据,3秒一次),高频测试也是非常稳定(7个主题同时收发,每秒一次,也就是1秒14个mqtt报文,服务质量QoS0、QoS1、QoS2都有)。因为作者以极少的资源设计了记录机制,对采用QoS1服务质量的报文必须保证到达一次,当发布的主题(qos1、qos2都适用)没有被服务器收到时会自动重发,而对QoS2服务质量的报文保证有且只有处理一次(如果不相信它稳定性的同学可以自己去修改源码,专门为QoS2服务质量去做测试,故意不回复PUBREC包,让服务器重发QoS2报文,且看看客户端是否有且只有处理一次),而对于掉线重连的稳定性,这种则是基本操作了,没啥好说的,在自动重连后还会自动重新订阅主题,保证主题不会丢失,因此在测试中稳定性极好。 3 轻量级:整个代码工程极其简单,不使用mbedtls情况下,占用资源极少,作者曾使用esp8266模组与云端通信,整个工程代码消耗的RAM不足15k(包括系统占用的开销,对数据的处理开销,而此次还是未优化的情况下,还依旧完美保留了掉线重连的稳定性,但是对应qos1、qos2服务质量的报文则未做测试,因为STM32F103C8T6芯片资源实在是太少了,折腾不起)。 4 无缝衔接mbedtls加密传输,让网络传输更加安全,而且接口层完全不需要用户理会,无论是否加密,kawaii-mqtt对用户提供的API接口是没有变化的,这就很好的兼容了一套代应用层的码可以加密传输也可以不加密传输。 5 拥有极简的API接口,总的来说,kawaii-mqtt的配置都有默认值,基本无需配置都能使用的,也可以随意配置,对配置都有健壮性检测,这样子设计的API接口也是非常简单。 6 有非常好的代码风格与思想:整个代码采用分层式设计,代码实现采用异步处理的思想,降低耦合,提高性能,具体体现在什么地方呢?很简单,目前市面上很多MQTT客户端发布主题都是要阻塞等待ack,这是非常暴力的行为,阻塞当前线程等待服务器的应答,那如果我想要发送数据怎么办,或者我要重复检测数据怎么办,你可能会说,指定阻塞时间等待,那如果网络延迟,ack迟迟不来,我就白等了吗,对于qos1、qos2的服务质量怎么办,所以说这种还是要异步处理的思想,我发布主题,那我发布出去就好了,不需要等待,对于qos1、qos2服务质量的MQTT报文,如果服务器没收到,那我重发就可以,这种重发也是异步的处理,完全不会阻塞当前线程。 7 MQTT协议支持主题通配符“#”、“+”。 8 订阅的主题与消息处理完全分离,让编程逻辑更加简单易用,用户无需理会错综复杂的逻辑关系。 9 kawaii-mqtt内部已实现保活处理机制,无需用户过多关心理会,用户只需专心处理应用功能即可。 10 无缝衔接salof:它是一个同步异步日志输出框架,在空闲时候输出对应的日志信息,也可以将信息写入flash中保存,方便调试。 11 不对外产生依赖。   整体框架 拥有非常明确的分层框架 

  • 2020-02-24
  • 回复了主题帖: 使用RTT Studio DIY 迷你桌面时钟(一)| 基于STM32芯片创建HelloWorld工程

    ltbytyn 发表于 2020-2-21 19:31 这个不错,我最近也在玩。
    你也在用RT-Thread Studio?

  • 2020-02-21
  • 发表了日志: 使用RTT Studio DIY 迷你桌面时钟(一)| 基于STM32芯片创建HelloWorld工程

最近访客

< 1/4 >

统计信息

已有33人来访过

  • 芯币:153
  • 好友:--
  • 主题:29
  • 回复:39
  • 课时:--
  • 资源:1

留言

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


现在还没有留言