继上一篇笔记,编译好STM32的裸机程序,能点亮LED灯了。
下一步就是启动liteos_m内核了。
不过为了更好的调试代码,需要先把printf重定向到串口,基于gcc的printf重定向和Keil不一样。
直接新建printf.c,在里面重写printf函数即可。
int printf(char const *fmt, ...)
{
char buf[256];
va_list ap;
va_start(ap, fmt);
int len = vsnprintf_s(buf, sizeof(buf), sizeof(buf) - 1, fmt, ap);
UartWrite(buf, len);
va_end(ap);
return len;
}
UartWrite是写串口的函数,基于HAL库写串口驱动我就不介绍了。
记得在printf之前要初始化UART和相关的GPIO。
这里有2个细节需要注意:
1.写串口没有临界区保护,多线程printf会有竞争风险,等kernel跑起来了,到时候加上mutex。
2.格式化字符串的buf长度256,是在stack上开辟的,注意调用线程的stack空间要给够。
liteos_m内核已经完成了ARM cortex-m4的适配,理论上所有基于cortex-m4的MCU都能快速使用。
我们只需要在main函数调用LOS_KernelInit()和LOS_Start()可以了。
启动之前自己创建一个线程,用来闪灯。
static void LED_Blink(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = 0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
printf("enter led blink.\n");
while(1)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
LOS_TaskDelay(250);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
LOS_TaskDelay(250);
}
}
static void InitTask(void)
{
UINT32 taskID = 0;
TSK_INIT_PARAM_S stTask = {0};
stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)LED_Blink;
stTask.uwStackSize = 1024;
stTask.pcName = "led";
stTask.usTaskPrio = 1;
LOS_TaskCreate(&taskID, &stTask);
printf("task id=%d\n", taskID);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
UartInit();
printf("init...\n");
LOS_KernelInit();
InitTask();
LOS_Start();
printf("BUG!!!\n");
while(1);
}
运气好的话,编译下去,可以看到串口打印,同时LED也会闪烁。
init...
entering kernel init...
task id=2
Entering scheduler
enter led blink.
如果没有跑起来的话,可以检查一下:
1. 进入kernel/liteos_m目录,执行make menuconfig
试试修改相关配置项。
2. 检查LOSCFG_PLATFORM_HWI有没有定义为1
liteos_m有接管中断向量表的功能,需要开启这个宏。
这样一来我们就不用修改startup.s里面的中断向量函数。
3.检查printf函数能不能正常工作
printf不正常好像也会影响程序运行。
内核跑起来之后,我们可以开启shell控制台。
这个控制台是liteos_m提供的,需要手动开启,里面有几个基本命令。
开启配置后,在main函数里面调用shell初始化:
LosShellInit();
OsShellInit();
//注册自定义的命令reboot使实现重启功能
osCmdReg(CMD_TYPE_EX, "reboot", 0,(CMD_CBK_FUNC)cmd_reboot);
static void cmd_reboot(UINT32 argc, const CHAR **argv)
{
printf("reboot...\n");
HAL_NVIC_SystemReset();
}
初始化完了还不能用,还要提供一个串口读取字符串的函数:uint8_t UartGetc(void)
这个函数从串口返回1个字符,没有收到数据时返回0。
光是这样还不行,它还需要我们在串口中断里面发送一个信号:
LOS_EventWrite(&g_shellInputEvent, 0x1);
g_shellInputEvent是在shell代码里面定义的全局变量,用来阻塞shell线程。
当收到g_shellInputEvent事件后,shell线程调用UartGetc来接收输入。
由于liteos_m接管了中断向量表,所以我们UART的中断函数要用:
LOS_HwiCreate(USART1_IRQn, 0, 1, (HWI_PROC_FUNC)uart_irq, 0);
这个函数来注册。
好了,终于写好代码,我们编译下载,不出意外可以看到串口打印.
输入help可以看到命令列表。
init...
entering kernel init...
task id=3
Entering scheduler
enter led blink.
OHOS #
OHOS #
OHOS # help
*******************shell commands:*************************
cat cd cp date free help ls memusage
mkdir pwd reboot rm rmdir task touch
OHOS #
OHOS # reboot
reboot...
init...
entering kernel init...
task id=3
Entering scheduler
enter led blink.
OHOS #
试了下mkdir、ping等命令都不能用,下一步要实现文件系统和网络,让这几个命令正常工作。