当前位置: 首页 > news >正文

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录

  • 概述
  • HelloWorld 工程
    • C/C++配置
    • 编译器主配置
    • Makefile脚本
    • 烧录器主配置
    • 运行结果
    • 程序调用栈
  • 任务管理实验
    • 实验结果
    • osal 系统适配层
    • osal_task_create
  • 其他实验
    • 实验源码
    • 内存管理实验
    • 互斥锁实验
    • 信号量实验
  • CMISIS接口实验
    • 还是得JlINK
    • CMSIS 简介
    • LiteOS->CMSIS
    • 任务间消息交互
    • 执行结果
  • 其他

概述

本实验基于LiteOS Studio 工具进行物联网终端的开发,使用LiteOS操作系统进行物联网开发板的控制。实验主要目的:

  • 掌握LiteOS Studio的使用
  • 掌握LiteOS操作系统任务的使用
  • 掌握LiteOS操作系统内存管理的使用
  • 掌握LiteOS操作系统互斥量和信号量使用
  • 熟悉LCD屏幕的使用 (在实验4中)
  • 熟悉开发板的LED和按键使用 (在实验4中)
  • OSAL接口使用
  • CMSIS接口使用(CMSIS任务和消息队列接口使用等)

@History
在进行本实验前,请先阅读 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink>#,文中较详细的介绍了LiteOS Studio的搭建和使用方法,文中我们也提及了LiteOS工程(LiteOS_Lab_HCIP),但没有使用它,而是直接使用了BearPi-IoT_Std_LiteOS 源码。为了贴合实验指导书的步骤,我们在这里选用 LiteOS_Lab_HCIP源码。

在写本文前,我已经尝试了不同形式的物联网开发IDE,分别如下几篇文章中:
#<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink>#
#<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + IoT Link 插件>#
#<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + GitBash + GCC工具>#

如果主要目标是为了完成HCIP-IOT实验,我是建议选用指导书中指定的LiteOS Studio开发环境,不仅是因为贴合指导书中的步骤,减少不必要的麻烦。华为云IoTDA中提及的VSCode + IoT Link 模式,由于要使用低版本的VSCode,让人很不爽,配置和调试操作都体验不咋地。相比较而言,LiteOS Studio 虽然是基于VSCode的,但其与你已经安装的VSCode不会产生环境变量或软件安装层次的冲突,算是优势吧。

HelloWorld 工程

请参见 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink># 完成此部分实验。
在这里插入图片描述

C/C++配置

c_cpp_properties.json 是 VSCode 中 C/C++ 扩展的核心配置文件,主要用于配置代码分析和开发环境,确保 IntelliSense(智能代码补全、错误检查等)能够准确理解项目结构和编译器行,需要作出如下修改,
在这里插入图片描述

编译器主配置

关于目标板卡配置、组件配置、编译器,参见前文提到的 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink>#,不再赘述。编译器配置下的Makefile脚本选择H:\HuaWeiYun\LiteOS_Lab_HCIP\targets\STM32L431_BearPi\GCC下的Makefile文件。
在这里插入图片描述

Makefile脚本

这块在HCIP-IoT实验手册中并没有提及。Makefile(脚本)最终编译哪个示例工程,是由LiteOS_Lab_HCIP\targets\STM32L431_BearPi.config文件决定的。
在这里插入图片描述
最简单的改变该文件的方法是,从\Demos各示例程序文件夹下拷贝defaults.sdkconfig全部内容,当然,也可以借助menuconfig和genconfig生成,那就稍微复杂些了,可以参考本专栏下其他文章。

烧录器主配置

烧录器、调试器配置,参见前文提到的 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + IoT Link 插件>#,采用ST-Link+OpenOCD模式。烧录文件要编译成功后才有哦。但还是建议使用JLink模式。
在这里插入图片描述
但还是建议使用JLink模式。这一节使用ST-Link+OpenOCD,体验很差劲。
在这里插入图片描述

运行结果

小熊派开发板的这个串口,感觉不太顶用,我就拔插过两三次,感觉就有接触不良的情况偶发啦。
在这里插入图片描述

在这里插入图片描述

程序调用栈

LiteOS_Lab_HCIP\targets\STM32L431_BearPi\Src\main.c

int main(void)
{UINT32 uwRet = LOS_OK;HardWare_Init();uwRet = LOS_KernelInit();if (uwRet != LOS_OK)  {return LOS_NOK;}extern void shell_uart_init(int baud);shell_uart_init(115200);link_test();  //第一步(void)LOS_Start();return 0;
}static int link_test() {int ret = -1;UINT32 uwRet = LOS_OK;UINT32  handle;TSK_INIT_PARAM_S task_init_param;memset (&task_init_param, 0, sizeof (TSK_INIT_PARAM_S));task_init_param.uwArg = (unsigned int)NULL;task_init_param.usTaskPrio = 2;task_init_param.pcName =(char *) "link_main";task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)link_main; //第二步task_init_param.uwStackSize = 0x1000;uwRet = LOS_TaskCreate(&handle, &task_init_param);if(LOS_OK == uwRet){ret = 0;}return ret;
}

LiteOS_Lab_HCIP\iot_link\link_main.c

int link_main(void *args) {...
#ifdef CONFIG_LINKDEMO_ENABLEextern int standard_app_demo_main(void);(void) standard_app_demo_main();   //第三步
#endif...
}

LiteOS_Lab_HCIP\targets\STM32L431_BearPi\Demos\hello_world_demo\hello_world_demo.c

//任务入口函数
static int app_hello_world_entry()   {while (1)    {printf("Hello World! This is BearPi!\r\n");osal_task_sleep(4*1000);}
}//第四步
int standard_app_demo_main()  {osal_task_create("helloworld",app_hello_world_entry,NULL,0x400,NULL,2);return 0;
}

任务管理实验

新添加任务入口函数并创建任务

void *task1 = NULL, *task2 = NULL; int num = 0;
//添加任务2
static int hcip_iot_task(void) {while (1)    {printf("This is task2!\r\n");#if 1if (num == 3) {osal_task_kill(task1);}#endifosal_task_sleep(4*1000);}
}//宏控制的本项目下的任务创建接口/每个示例下该函数声明相同
int standard_app_demo_main() {task1 = osal_task_create("helloworld",app_hello_world_entry,NULL,0x400,NULL,2);//新创建任务task2 = osal_task_create("task2",hcip_iot_task,NULL,0x400,NULL,2);return 0;
}

实验结果

任务添加测试, 两个任务同时运行,
在这里插入图片描述
如上,任务1和任务2同时执行,同时打印输出。

任务删除测试,
在这里插入图片描述
如上,任务1被关闭后,周任务2在执行。

几个注意事项:
1、在编译或重新编译前前,要先关闭串口,否则可能使得IDE卡主,不能执行编译过程,必须得重启软件。
2、在烧录器配置烧录.hex文件时,遇到过显示烧录成功(且复位过)但是不生效的情况,可以多尝试几次或配置烧录.bin文件。

osal 系统适配层

在这里我们简单看看osal适配层的实现方法,后期我们会单独讲解。
在这里插入图片描述
个人觉得osal与cmsis有些类似,在上述os目录下,osal和linux/ucos_ii等系统目录平级,cmsis目录在liteos之下,如下图,
在这里插入图片描述
OSAL定义统一的系统调用接口(如线程管理、通信),使应用无需关注底层内核(如LiteOS与Linux的差异)。例如,鸿蒙通过OSAL层兼容Linux与LiteOS内核。 CMSIS 为Cortex处理器提供标准外设访问API(如寄存器映射)及RTOS接口(CMSIS-RTOS2),确保代码可跨RTOS(如FreeRTOS、RTX5)复用。

osal_task_create

//osal 操作系统适配层函数
void* osal_task_create(const char *name,int (*task_entry)(void *args), void *args,int stack_size,void *stack,int prior) {void *ret = NULL;if((NULL != s_os_cb) &&(NULL != s_os_cb->ops) &&(NULL != s_os_cb->ops->task_create)) {ret = s_os_cb->ops->task_create(name, task_entry,args,stack_size,stack,prior);}return ret;
}

osal_task_create 函数调用堆栈分析,
在这里插入图片描述

其他实验

互斥锁、内存管理、信号量实验,统一参见如下代码。

实验源码

#define SWITCH_TEST_HELLO  0
#define SWITCH_TEST_TASK   0
#define SWITCH_TEST_MUTEX  1
#define SWITCH_TEST_MEM    0
#define SWITCH_TEST_SEMP   0//任务句柄
void *task1 = NULL, *task2 = NULL; int num = 0;#if SWITCH_TEST_MUTEX
//需要保护的公共资源
uint32_t public_value = 0;
//互斥锁
osal_mutex_t public_value_mutex;
#endif
//信号量实验
osal_semp_t sync_semp = NULL;#if SWITCH_TEST_HELLO
static int app_hello_world_entry()
{while (1){printf("Hello World! This is BearPi!\r\n");osal_task_sleep(4*1000);}
}
#endif#if SWITCH_TEST_TASK
static int hcip_iot_task(void) {while (1)    {printf("This is task2, num:%d \r\n", ++num);#if 1if (num == 3) {osal_task_kill(task1);}#endifosal_task_sleep(4*1000);}
}
#endif#if SWITCH_TEST_MUTEX
//互斥锁/任务1入口
static int mutex_task1_entry() {while (1) {if (true == osal_mutex_lock(public_value_mutex)) {printf("task1: lock a mutex.\r\n");public_value += 10;printf("task1: public_value = %ld.\r\n", public_value);printf("task1: sleep...\r\n");osal_task_sleep(10); //msprintf("task1: continue...\r\n");printf("task1: unlock a mutex.\r\n");osal_mutex_unlock(public_value_mutex);if (public_value > 60) break; } //if} //whilereturn 0;
}//互斥锁/任务2入口
static int mutex_task2_entry() {while (1) {if (true == osal_mutex_lock(public_value_mutex)) {printf("task2: lock a mutex.\r\n");public_value += 5;printf("task2: public_value = %ld.\r\n", public_value);printf("task2: unlock a mutex.\r\n");osal_mutex_unlock(public_value_mutex);if (public_value > 50) break; #if 0  //task2 not sleeposal_task_sleep(10); //ms#endif} //if} //whilereturn 0;
}
#endif#if SWITCH_TEST_MEM
//内存管理实验/任务1
static int mem_access_task_entry() {uint32_t i = 0;          //for looksize_t mem_size = 0;     //uint8_t *mem_ptr = NULL; //内存块指针//loopwhile (1) {//每次循环申请的块大小扩一倍mem_size = 1 << i++;//执行申请操作mem_ptr = osal_malloc(mem_size);//successif (NULL != mem_ptr) {printf("access %d bytes memory success!\r\n", mem_size);osal_free(mem_ptr);mem_ptr = NULL;printf("free memory success!\r\n");}else {printf("access %d bytes memory failed!\r\n", mem_size);return 0;}}return 0;
}
#endif//信号量实验
#if SWITCH_TEST_SEMP
//信号量实验/任务1
static int semp_task1_entry() {printf("task1: post a semp.\r\n");osal_semp_post(sync_semp);printf("task1: end.\r\n");
}//信号量实验/任务1
static int semp_task2_entry() {printf("task2: watting for a semp...\r\n");osal_semp_pend(sync_semp);printf("task2: access a semp.\r\n");
}
#endif//示例初始化函数
int standard_app_demo_main() {
//原HelloWorld
#if SWITCH_TEST_HELLOosal_task_create("helloworld",app_hello_world_entry,NULL,0x400,NULL,2);
#endif//互斥锁实验
#if SWITCH_TEST_MUTEX//创建互斥锁osal_mutex_create(&public_value_mutex);//创建任务 //const char *name,int (*task_entry)(void *args), void *args,int stack_size,void *stack,int priortask1 = osal_task_create("mutex_task1", mutex_task1_entry, NULL, 0x400, NULL, 12);//创建任务 //const char *name,int (*task_entry)(void *args), void *args,int stack_size,void *stack,int priortask2 = osal_task_create("mutex_task2", mutex_task2_entry, NULL, 0x400, NULL, 11);
#endif//内存实验
#if SWITCH_TEST_MEM//创建任务task2 = osal_task_create("mem_task", mem_access_task_entry, NULL, 0x400, NULL, 11);
#endif//信号量实验
#if SWITCH_TEST_SEMP//创建信号量 /数量1初始值0osal_semp_create(&sync_semp, 1, 0);//任务1优先级低,负责释放信号量task1 = osal_task_create("semp_task1", semp_task1_entry, NULL, 0x400, NULL, 12);//任务2优先级高,先进入等待/申请信号量的状态task2 = osal_task_create("semp_task2", semp_task2_entry, NULL, 0x400, NULL, 11);
#endifreturn 0;
}

内存管理实验

在这里插入图片描述

互斥锁实验

在这里插入图片描述
如上实验结果,如果高优先级的任务不睡眠,则低优先级任务必要要等到高优先级任务退出后才有机会执行。

信号量实验

在这里插入图片描述
这里,semp_task2_entry优先级高,会先执行,进入pend等待信号量状态,函数会使得当前任务进入阻塞状态,从而让出 CPU 资源给其他任务使用,如这里优先级稍低的semp_task1_entry任务。待task1释放信号量后,task2被唤醒继续执行。

CMISIS接口实验

与FreeRTOS一样,LiteOS也支持CMSIS,这简直是福利。在以下目录 LiteOS_Lab_HCIP\iot_link\os\liteos\cmsis
在这里插入图片描述

还是得JlINK

前面几个小实验,纯粹是为了体验OpenOCD模式,但真的很难用啊。在进行CMSIS实验时,哈哈也不知道是咋鼓捣的,基于STLink+OpenOCD的调试环境,它之间罢工了。在配置文件、OpenOCD版本等方向尝试修复无果。
在这里插入图片描述
于是乎,我又乖乖的将板载的STLink刷成了JLink,该过程参考 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink># 文章。这个二次烧录Jlink固件的过程,也很崎岖…
1、在 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + IoT Link 插件>#文中,提到了使用 STLinkReflash 将JLink 刷回 STLink 也不顺利。
2、在第一步中,通过STM32 ST-LINK Utility升级了调试器固件。
3、在上述两步基础上,再尝试使用 STLinkReflash 将STLink刷成JLink是失败的。后来,我重新安装了 STlink驱动、更换了usb接口、重启过电脑,等一系列组合拳下来,竟然成功啦。哈哈,不想再尝试了。就按照Studio官方建议,这么用吧。
4、J-Link 固件内置 J-Link GDB Server,可直接与调试工具(如 LiteOS Studio 的 GDB 客户端)通信,无需中间层协议转换。这种直接集成减少了调试链路的复杂性,提高运行效率。刷写后的 ST-Link(J-Link OB)可实现高达 1.8MHz 的下载速率,显著快于 OpenOCD + ST-Link 的组合。ST-Link没有内置GDB服务,因此要借助外部的openocd.exe做GDB服务器。

CMSIS 简介

随着 32 位处理器在嵌入式市场需求量逐渐增多,各家芯片公司推出新型芯片,伴随而
来的是开发工具、软件兼容以及代码移植等问题。在这种情况下,各个硬件平台的供应商都
寻求易于使用且高效的解决方案,其中,ARM 与 Atmel、IAR、KEIL、SEGGER 和 ST 等诸
多芯片和软件工具厂商合作,发布了一套 CMSIS 标准。
CMSIS(Cortex Microcontroller Software Interface Standard),即 ARM Cortex 微控制器软
件接口标准。CMSIS 标准提供了内核和外围设备、实时操作系统和中间组件之间的通用 API
接口,从而简化了软件的重复使用,缩短了微控制器开发人员的学习时间,并缩短了新设备
的上市时间。下图是 ARM 公司的 CMSIS 标准结构框图:
在这里插入图片描述
其中,CMSIS-CORE 层定义了 Cortex-M 以及 Cortex-A 处理器(Cortex-A5/A7/A9)内核
和外围设备的标准化 API。CMSIS-Pack 层包含了 CMSIS-Driver 驱动框架、CMSIS-DSP 相关
库、CMSIS-RTOS 操作系统 API、中间件 API 和 Peripheral HAL 层 API 等。根据 CMSIS 的标准,ARM 公司整合并提供了 CMSIS 软件包模板。基于 ARM 提供的 CMSIS 软件包模板,ST 官方结合自己芯片的差异进行了修改,并将其整合到了 STM32Cube 固件包中的 CMSIS 文件夹里。

LiteOS->CMSIS

除了ST的HAL支持外,LiteOS也要提供支持,以osThreadNew为例,

//cmsis_liteos2.c /定义在cmsis_os2.h中
osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr) {...uwRet = LOS_TaskCreate(&uwTid, &stTskInitParam);...
}

上述 LOS_TaskCreate 实现在 LiteOS_Lab_HCIP\iot_link\os\liteos\base\core 内核中。在osal层的任务创建函数 osal_task_create,其最后也要调用上述 LOS_TaskCreate 内核实现。 该函数的实现,我们不再深入。

任务间消息交互

#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <osal.h>
#include <cmsis_os.h> //CMSIS_OS_VER==2#define SWITCH_TEST_HELLO  0
#define SWITCH_TEST_TASK   0
#define SWITCH_TEST_MUTEX  0
#define SWITCH_TEST_MEM    0
#define SWITCH_TEST_SEMP   0
#define SWITCH_TEST_CMSIS  1//cmsis接口
#if SWITCH_TEST_CMSIS
//消息队列句柄 /void*
osMessageQueueId_t cmsis_queue;
//消息队列消息
typedef struct cmsis_msg {int a;int b;
} TMsg;///typedef void (*osThreadFunc_t) (void *argument);
//任务1 /发送消息 
static void cmsis_task1_entry(void *argument) {TMsg tMsg = {0, 0};while (1) {//tMsg.a += 1;tMsg.b += 2;//(osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout)osMessageQueuePut(cmsis_queue, &tMsg, 0, 10);//打印发送的消息printf("Send Msg a:%d b:%d\r\n", tMsg.a, tMsg.b);//睡眠osal_task_sleep(1*1000);//任务退出if (tMsg.a > 100) break;}
}///typedef void (*osThreadFunc_t) (void *argument);
//任务2 /接收消息
static void cmsis_task2_entry(void *argument) {TMsg tMsg; uint8_t msg_prio = 0;while (1) {//osStatus_t osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout)osMessageQueueGet(cmsis_queue, (void*)&tMsg, &msg_prio, osWaitForever);//打印收到的消息printf("Recv Msg a:%d b:%d\r\n", tMsg.a, tMsg.b);}
}
#endifint standard_app_demo_main() {
//cmsis接口
#if SWITCH_TEST_CMSIS//创建消息队列/osMessageQueueId_t osMessageQueueNew (uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr);cmsis_queue = osMessageQueueNew (5, sizeof(TMsg), NULL);const osThreadAttr_t thread_attr1 = {.name = "MyThread1",         // 线程名称(调试用).stack_size = 1024,         // 栈大小(字节).priority = osPriorityAboveNormal5  };const osThreadAttr_t thread_attr2 = {.name = "MyThread2",         // 线程名称(调试用).stack_size = 1024,         // 栈大小(字节).priority = osPriorityAboveNormal5};//任务1/发送消息 /osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)osThreadId_t thread1 = osThreadNew (cmsis_task1_entry, NULL, &thread_attr1); //任务2接收消息 /osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)osThreadId_t thread2 = osThreadNew (cmsis_task2_entry, NULL, &thread_attr2);//printf("Hello cmsis!\r\n");
#endifreturn 0;
}

执行结果

在这里插入图片描述

其他

怎么说呢?挺不顺的,一定要有耐心。

http://www.xdnf.cn/news/900253.html

相关文章:

  • halcon 透视矩阵
  • Opencv中的addweighted函数
  • OpenCV 图像色彩空间转换与抠图
  • VTK如何让部分单位不可见
  • uniapp 安卓 APP 后台持续运行(保活)的尝试办法
  • 【Veristand】Veristand环境安装教程-Linux RT / Windows
  • OpenCV为图像添加边框
  • MVC与MVP设计模式对比详解
  • PCB特种工艺应用扩展:厚铜、高频与软硬结合板
  • 加法c++
  • 【驱动】Orin NX恢复备份失败:does not match the current board you‘re flashing onto
  • 【网络安全】Qt免杀样本分析
  • 令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
  • ReadWriteLock(读写锁)和 StampedLock
  • tpc udp http
  • 自动化提示生成框架(AutoPrompt)
  • 零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
  • AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
  • 【AUTOSAR COM CAN】CanSM模块的实现与应用解析
  • 对象存储Ozone EC应用和优化
  • 大语言模型提示词(LLM Prompt)工程系统性学习指南:从理论基础到实战应用的完整体系
  • 装饰模式(Decorator Pattern)重构java邮件发奖系统实战
  • leetcode_206 反转链表
  • PDF转Markdown/JSON软件MinerU最新1.3.12版整合包下载
  • 元图CAD:一键解锁PDF转CAD,OCR技术赋能高效转换
  • 网络安全逆向分析之rust逆向技巧
  • 不到 2 个月,OpenAI 火速用 Rust 重写 AI 编程工具。尤雨溪也觉得 Rust 香!
  • 三十四、面向对象底层逻辑-SpringMVC九大组件之FlashMapManager接口设计哲学
  • C#学习第28天:内存缓存和对象池化
  • vscode使用系列之快速生成html模板