RTT有一个非常好的功能就是调试界面了,但是移植finish组件需要使用RTT的驱动文件,个人表示只喜欢用内核,不想用RTT的方式写驱动,所以自己改写了一下RTT自带的finish功能,可以不使用RTT的drv。
基本能实现自己想要的功能,基本思路就是把RTT各种命令的函数及其调用的函数复制到我们自己的文件夹,然后串口中断后保存串口的数据,当点击回车时比较我们输入的字符跟定义的命令字符,然后执行相应的操作。
移植RTT的内核到nucleo144-F7,参照火哥的书籍移植,移植后配置好RTT的rt_kpruntf()函数,然后就可以自己配置finish了
1、把命令对应的函数及其调用的函数复制到我们自己的文件里面,基本都在rtt文件夹的cmd.c(各种list_)和msh_cmd.c(ls,echo等),可以用查找以下两个函数的方式查找内核中的命令,因为rtt的命令都是通过这两个函数导入到内核里面FINSH_FUNCTION_EXPORT()
MSH_CMD_EXPORT()
这样就可以查到内核里面有哪些命令,然后把命令的实现函数(跟命令一个名字)复制过来。
- const char *cmd_list[MAX_CMD] = { "ps",
- "free",
- "list_thread",
- "list_sem",
- "list_timer",
- "list_mutex",
- "list_mailbox",
- "list_msgqueue",
- "list_event",
- "list_fevent",
- "list_mempool",
- "help",
- "reboot",
- "shutdown_thread",
- "reset_thread",
- "led3_open",
- "led3_close",
- "show_version"
- /*add cmd here*/};
-
- static int object_name_maxlen(struct rt_list_node *list)
- {
- struct rt_list_node *node;
- struct rt_object *object = NULL;
- int max_length = 0, length;
- rt_enter_critical();
- for (node = list->next; node != list; node = node->next)
- {
- object = rt_list_entry(node, struct rt_object, list);
- length = rt_strlen(object->name);
- if (length > max_length) max_length = length;
- }
- rt_exit_critical();
- if (max_length > RT_NAME_MAX || max_length == 0) max_length = RT_NAME_MAX;
- return max_length;
- }
- rt_inline void object_split(int len)
- {
- while (len--) rt_kprintf("-");
- }
- static long _list_thread(struct rt_list_node *list)
- {
- int maxlen;
- rt_uint8_t *ptr;
- struct rt_thread *thread;
- struct rt_list_node *node;
- maxlen = object_name_maxlen(list);
- rt_kprintf("%-*.s pri status sp stack size max used left tick error\n", maxlen, "thread"); object_split(maxlen);
- rt_kprintf( " --- ------- ---------- ---------- ------ ---------- ---\n");
- for (node = list->next; node != list; node = node->next)
- {
- rt_uint8_t stat;
- thread = rt_list_entry(node, struct rt_thread, list);
- rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->name, thread->current_priority);
- stat = (thread->stat & RT_THREAD_STAT_MASK);
- if (stat == RT_THREAD_READY) rt_kprintf(" ready ");
- else if (stat == RT_THREAD_SUSPEND) rt_kprintf(" suspend");
- else if (stat == RT_THREAD_INIT) rt_kprintf(" init ");
- else if (stat == RT_THREAD_CLOSE) rt_kprintf(" close ");
- ptr = (rt_uint8_t *)thread->stack_addr;
- while (*ptr == '#')ptr ++;
- rt_kprintf(" 0x%08x 0x%08x %02d%% 0x%08x %03d\n",
- thread->stack_size + ((rt_uint32_t)thread->stack_addr - (rt_uint32_t)thread->sp),
- thread->stack_size,
- (thread->stack_size - ((rt_uint32_t) ptr - (rt_uint32_t) thread->stack_addr)) * 100
- / thread->stack_size,
- thread->remaining_tick,
- thread->error);
- }
- return 0;
- }
- long list_thread(void)
- {
- struct rt_object_information *info;
- info = rt_object_get_information(RT_Object_Class_Thread);
- return _list_thread(&info->object_list);
- }
复制代码 代码太多,就贴了一段。
2、测试一下自己复制的各个命令是否有效,自己在main文件里面执行各个命令对应的函数,这里只移植了部分命令,有一些需要修改config文件,
- list_thread();
- list_sem();
- list_event();
- list_mutex();
- list_mailbox();
- list_msgqueue();
- list_memheap();
- list_mempool();
- list_timer();
复制代码 然后查看串口的输出。
3、搞定了命令对应的函数已经串口打印正确,然后就是获取串口的输入执行对应的命令,首先要定义一个字符串数组保存我们的命令用来跟串口的数据比较等到要执行的命令函数,
- const char *cmd_list[MAX_CMD] = { "ps",
- "free",
- "list_thread",
- "list_sem",
- "list_timer",
- "list_mutex",
- "list_mailbox",
- "list_msgqueue",
- "list_event",
- "list_fevent",
- "list_mempool",
- "help",
- "reboot",
- "shutdown_thread",
- "reset_thread",
- "led3_open",
- "led3_close",
- "show_version"
- /*add cmd here*/};
复制代码 我这里就添加了以上命令。
4、获取串口的数据参考原子的串口函数,当从串口收到回车符时,就判断输入的命令。
- //串口3中断服务程序
- void USART3_IRQHandler(void)
- {
- uint8_t Res;
- #ifdef SUPPORT_RTT
- /* enter interrupt */
- rt_interrupt_enter();
- #endif
-
- if((__HAL_UART_GET_FLAG(&Uart3Handle,UART_FLAG_RXNE)!=RESET)) //接收中断(接收到的数据必须是0x0d结尾)
- {
- HAL_UART_Receive(&Uart3Handle,&Res,1,1000); //保存收到的数据
- HAL_UART_Transmit(&Uart3Handle,&Res,1,1000); //将输入的数据原封不动的打印到串口
- if(Res == 0x7f) //DEL键,如果按了del键,不仅要删除putty的显示,还要删除刚刚输入的命令的最后一个字符
- {
- USART_RX_BUF[(USART_RX_STA&0X3FFF)-1] = '\0'; //删除最后一个字符
- USART_RX_STA--; //数量减一
- }
- else
- {
- if((USART_RX_STA&0x8000)==0)//接收未完成
- {
- if(Res==0x0d)USART_RX_STA|=0x8000; //收到了oxod
- else
- {
- USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; //保存数据
- USART_RX_STA++; //数量加一
- if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
- }
- }
- }
- }
- HAL_UART_IRQHandler(&Uart3Handle);
- #ifdef SUPPORT_RTT
- /* enter interrupt */
- rt_interrupt_enter();
- #endif
- }
复制代码 这里USART_RX_STA类似一个寄存器操作,USART_RX_BUF保存串口数据,当收到回车,相应的标志位会被置位,会有一个线程来处理串口的数据。
5、新建一个线程用来处理串口输入的数据。线程函数如下
- static void msh_thread_entry(void *parameter)
- {
- uint8_t cmd;
- uint8_t md_value;
- rt_kprintf("\r\nmsh>");
- while(1)
- {
- if(USART_RX_STA&0x8000)
- {
- if(USART_RX_BUF[0]!='\0')
- {
- rt_kprintf("\r\n");
- for(cmd=0;cmd<=MAX_CMD;cmd++)
- if(rt_strcmp((const char*)USART_RX_BUF,cmd_list[cmd])==0)
- break;
- switch(cmd)
- {
- case PS:list_thread();break;
-
- case FREE:rt_kprintf("Not yet open\r\n");break;
-
- case LIST_THREAD:list_thread();break;
-
- case LIST_SEM:list_sem();break;
-
- case LIST_TIMER:list_timer();break;
-
- case LIST_MUTEX:list_mutex();break;
-
- case LIST_MAILBOX:list_mailbox();break;
-
- case LIST_MSGQUEUE:list_msgqueue();break;
-
- case LIST_EVENT:list_event();break;
-
- case LIST_FEVENT:rt_kprintf("Not yet open\r\n");break;
-
- case LIST_MEMPOOL:rt_kprintf("Not yet open\r\n");break;
-
- case REBOOT:HAL_NVIC_SystemReset();break;
-
- case SHUTDOWN_THREAD:rt_kprintf("Not yet open\r\n");break;
-
- case RESET_THREAD:rt_kprintf("Not yet open\r\n");break;
-
- case LED3_OPEN://HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);rt_kprintf("LED3 IS OPEN\r\n");break;
- md_value=0x01;
- rt_mb_send_wait(test_mail,(rt_uint32_t)md_value,0);
- break;
-
- case LED3_CLOSE://HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);rt_kprintf("LED3 IS CLOSE\r\n");break;
- md_value=0;
- rt_mb_send_wait(test_mail,(rt_uint32_t)md_value,0);
- break;
-
- case SHOW_VERSION:
- rt_show_version();
- break;
-
- case HELP:
- for(uint8_t i=0;i<(MAX_CMD-1);i++)
- rt_kprintf("%s\r\n",cmd_list[i]);
- break;
- case OTHER_CMD:
- break;
-
- default:rt_kprintf("command not found!\r\n");break;
- }
- }
- rt_kprintf("\r\nmsh>");
- rt_memset(USART_RX_BUF,0,sizeof(USART_RX_BUF));
- USART_RX_STA=0;
- }
- rt_thread_delay(5);
- }
- }
复制代码 就是判断串口的数据,执行相应的命令,这个函数里面有邮箱的发送函数,为了测试用的,会有一个线程专门接收邮箱的数据然后控制LED。然后新建对应的线程就可以操作了。
目前仅支持删除键,上下左右TAB键什么都不支持。
然后添加命令只需添加命令数组,加大MAX_CMD,在命令处理线程里面增加相应的操作。
使用这种方法,对于任何系统,裸机都可以实现shell。
我在main里面定义了5个线程,线程一 LED1闪烁、线程二消息队列发送发送控制LED2的消息,线程三消息队列接收接收线程二的消息并控制LED2、线程四 模拟MSH、线程五邮箱接收MSH线程发来的控制LED3的邮箱内容,涉及了线程的创建,消息队列,邮箱定时器等。nucleo144-F7可以直接下载。
如果文章中或者代码中有什么错误,希望大家指正,对于RTT的理解,有疑问的也希望大家指导。
DEMO.zip
(2.62 MB, 下载次数: 45)
|