赞
踩
因为拿不到套件跑的liteos_m内核的源码,所以一些涉及到深层次的东西目前还不清楚原理。
本文主要是列一下多任务编程过程中踩到的坑和遇到的问题,一是为了和大家分享一些经验,另一个也是希望有人能指出我的错误。闭门造车,画地为牢可不好。
直入主题吧~~~~
1. 任务栈溢出
任务内容如下
static void *Thread2(const char *arg)
{
(void)arg;
uint32_t kernelTimerCnt = 0;
while(1)
{
kernelTimerCnt = osKernelGetSysTimerCount();
printf("2 = %u\n",kernelTimerCnt);
usleep(3000);
}
return NULL;
}
创建任务时属性设置如下
#define TASK_STACK_SIZE 512
attr.name = "thread2";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = TASK_STACK_SIZE;
attr.priority = osPriorityBelowNormal1;
if (osThreadNew((osThreadFunc_t)Thread2, NULL, &attr) == NULL) {
printf("[os_test] Falied to create Thread2!\n");
}
在创建任务之前我特意查了一下任务栈空间的最小需求
printf("[os_test] g_taskMinStkSize = %u\n",g_taskMinStkSize);
输出结果:[os_test] g_taskMinStkSize = 384
而我的任务很简单对栈的需求不会太高,所以我设置栈空间大小为512,但是运行后报错了,如下
即栈溢出了。当我把栈空间增大后,问题解决。虽然问题很简单的解决了,但是系统对任务栈空间的需求让我感觉有些太大了。因为不清楚内部实现,所以也不清楚栈空间中都存储了哪些东西。
2. 无法修改tick周期
在做机器人系统底层设计的时候往往需要较快的响应速度,所以要求任务之间的切换要尽量快,不清楚套件中liteos_m的任务切换方式是什么,不过一般来说应该是和tick周期有关,所以我这里想要看一下当前的tick周期,然后尝试修改。
获取tick周期(频率)和系统定时器周期(频率)
uint32_t timerFreq,tickFreq;
timerFreq = osKernelGetSysTimerFreq();
tickFreq = osKernelGetTickFreq();
printf("[os_test] timerFreq = %u, tickFreq = %u\n",timerFreq,tickFreq);
输出:timerFreq = 160000000, tickFreq = 100
如上,系统定时器频率是160MHz,tick频率是100Hz
然后我开始查找配置接口,位于vendor\hisi\hi3861\hi3861\platform\os\Huawei_LiteOS\targets\hi3861v100\include\target_config.h中,内容如下
#if (LOSCFG_LIB_CONFIGURABLE == YES)
extern UINT32 g_minusOneTickPerSecond;
#define LOSCFG_BASE_CORE_TICK_PER_SECOND (g_minusOneTickPerSecond + 1) /* tick per sencond plus 1 */
#else
#define LOSCFG_BASE_CORE_TICK_PER_SECOND (1000UL)
#endif
因为在编译的时候定义了LIB_CONFIGURABLE,所以配置tick频率的内容如下
extern UINT32 g_minusOneTickPerSecond;
#define LOSCFG_BASE_CORE_TICK_PER_SECOND (g_minusOneTickPerSecond + 1) /* tick per sencond plus 1 */
然后开始查找g_minusOneTickPerSecond,通过查找编译的map和asm文件发现这个变量不在可修改的源文件中,应该是在未开放的代码部分。而且并没有提供修改该变量的接口。不过没关系,既然可以使用extern声明,那么我们就可以使用,于是在创建任务之前我修改了g_minusOneTickPerSecond=999,即期望设置tick频率为1000.可想而知,起始tick的频率并没有改变,依然是100.很正常,因为内核应该会使用这个信息来配置定时器中断的周期,所以这种配置是需要在初始化内核之前完成的。
那就继续查找,看一下内核初始化是在哪里进行,我只要在此之前修改这个变量就好了。
通过asm发现了系统启动过程中函数调用的关系:start_flash_data_loop --》 main --》 AppInit --》app_main。而前三个函数都没有对外开放,我们看不到内容,更无法修改。那就只能看一看app_main了,很遗憾,这里已经进行到应用级别了,内核初始化早已经完成。
从以上内容看,我应该是无法修改tick的周期了。(理论上编译的时候不要定义LIB_CONFIGURABLE,然后应该就可以通过修改配置文件vendor\hisi\hi3861\hi3861\platform\os\Huawei_LiteOS\targets\hi3861v100\include\target_config.h中的宏定义来修改tick周期。但我并不像去改变编译参数,所以并没有这样做。有时间这样试一试。修改编译参数:vendor\hisi\hi3861\hi3861\build\make_scripts\config.mk)
3. 任务之间的切换时间很奇怪
如下两个任务,Thread1的优先级为osPriorityBelowNormal,Thread2的优先级为osPriorityBelowNormal1。即Thread2优先级高于Thread1。
static void *Thread1(const char *arg)
{
(void)arg;
uint32_t kernelTimerCnt = 0;
while(1)
{
kernelTimerCnt = osKernelGetSysTimerCount();
printf("1 = %u\n",kernelTimerCnt);
usleep(2000);
}
return NULL;
}
static void *Thread2(const char *arg)
{
(void)arg;
uint32_t kernelTimerCnt = 0;
while(1)
{
kernelTimerCnt = osKernelGetSysTimerCount();
printf("2 = %u\n",kernelTimerCnt);
usleep(3000);
}
return NULL;
}
对应这样的两个任务我做了两个测试
测试一:
Thread1不休眠,一直运行,即屏蔽 usleep(2000);
查看输出,很明显的当Thread2需要运行时,会打断Thread1,当Thread2执行完休眠之后会跳回Thread1。
系统运行一段时间后就会复位。
以上测试结果基本上可以说明如下几个问题:
1. Thread2的优先级高于Thread1,当Thread2就绪时会抢占资源。
2. 因为Thread1一直没有释放资源,所以优先级低于Thread1的任务都无法运行,这会导致看门狗复位。
3. Thread2的输出周期并不是期望的3ms,而是10ms,是的,和tick的周期相同
测试二:
在测试一的基础上,不屏蔽usleep(2000);即代码如上。
输出情况如下
可以看到从Thread1切换到Thread2,耗时约1600000 / 160000000 * 1000 =10ms.
而从Thread2切换到Thread1,耗时约3500 / 160000000 * 1000 =0.02ms.
然后我调换了两个任务的优先级,从输出结果看,两个任务之间的切换耗时也出现了调换。
也就是说,从低优先级任务切换到高优先级任务时,即使高优先级任务已经就绪,但是最快时间也要一个tick周期的时间,或许是因为系统任务的切换是在每次tick中断时进行的,而从高优先级任务切换到低优先级任务,却可以马上切换,这个有些不理解了。
这个现象真的是让人难以理解~~~~~
总的来说,多任务下无法设置tick周期,任务切换时间如此奇怪,这种问题在对实时性要求比较高的机器人底层系统中是无法接受的。
对于以上问题只是个人见解,有错误的地方还请指出;
有些疑问甚至没有给出答案,有明白的大佬希望能够不吝赐教。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。