andeyqi 发表于 2023-10-18 20:55:22

【Telink-泰凌微电子 B91通用开发套件】(二)FreeRtos 适配SHELL

本帖最后由 andeyqi 于 2023-10-18 22:40 编辑

1.简介:

   MCU 开发项目中经常会使用RTOS,FreeRtos 是个使用比较广泛的RTOS,B91 的sdk 里面已经适配了freertos 的demo 工程,这就意味着我们不需要做额外的适配工作就可以把FreeRtos 跑起来,SDK的demo 工程如下,demo 程序创建了连个task去翻转板上的led,我们打开如下工程烧录后会发现板上的LED 按照期望的方式闪烁显示了说明RTOS 的任务已经正常调度运行起来了。



在开发调试过程中经常会需要触发某些测试函数来验证功能是否正常工作,这时候我们可以在代码中写死测试函数不过这样不是很灵活,我们可以在项目中实现个shell,通过shell命令触发对应的函数这样就会方便很多,可以在程序异常的时候通过命令查看特定的运行状态,那开始我们今天的主题适配shell。
2.UART 适配
2.1 串口驱动适配本次shell 的适配物理层使用的是uart 设备,要想适配shell 首先要将物理层适配好,对于串口的驱动我们实际也是不需要单独自己从0编写,sdk里的uart_demo 示例程序已经可以串口输出数据和回显接收到的字符,我们只需要把如下文件加入到工程编译并在main 函数内调用对应的user_init函数即可完成uart 的串口适配,至此我们没有额外对应一行代码已经完成了freertos的适配和uart 的驱动适配,真有种真香的感觉。

因为shell 的输出使用的是标准的printf 函数,串口驱动已经适配好了我们只要把串口的输出和printf的底层以来的实现绑定起来即可完成printf 函数的功能,sdk 使用的是gcc的编译环境printf 的底层实现依赖函数为_write 函数,我们在该函数内调用uart 的输出函数即可完成printf函数的对接,对应实现代码如下:#include "uart.h"
__attribute__((used)) int _write(int fd, const unsigned char *buf, int size)
{
    (void) fd;
    int    i;
    for (i = 0; i < size; i++){
      uart_send_byte(UART0,buf);
    }
    return i;
}至此我们只是写入以上代码就可以在工程里使用printf 函数了,我们可以在每个task 内追加打印来验证printf 函数的功能在task 内添加如下打印函数:

log 已经按照预测的方式输出了,同时也再次验证freertos 已经被正常的调度起来了。
2.2 添加 shell
shell 是作为一个任务接口解析串口命令输出信息和使用者交互,本地创建shell 任务,shell 的实现细节在此就不过多描述了,shell任务的代码块如下。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "littleshell.h"
#include <stdint.h>
#include <stdbool.h>
#include "FreeRTOS.h"
#include "task.h"
#include "usart_adapter.h"

#define PRINTF printf
#define CONSOLE_FROME "#"
#define NEW_LINE      "\r\n"

#define MSG(msg)UART_Transmit(1,msg,strlen(msg))

#pragma section="FSymTab"

static struct littleshell_syscall * _syscall_table_begin = NULL;
static struct littleshell_syscall * _syscall_table_end = NULL;

static struct little_shell _shell;
static struct little_shell * shell = &_shell;

uint8_t uartgetchar(uint8_t* pdata);

unsigned int hello(char argc,char ** argv)
{
    PRINTF("hello word.\r\n");
    return 1;
}

const struct littleshell_syscall shell_cmd[] =
{
                {"hello","test hello",hello},
                {"hello1","test hello",hello}
};


void littleshell_system_function_init(const void* begin, const void* end)
{
    _syscall_table_begin = (struct littleshell_syscall*) shell_cmd;
    _syscall_table_end = (struct littleshell_syscall*) shell_cmd+(sizeof(shell_cmd)/sizeof(shell_cmd));
}


void littleshell_system_init(void)
{
#if defined (__ICCARM__)
    littleshell_system_function_init(__section_begin("FSymTab"),
                               __section_end("FSymTab"));
#elif defined (__GNUC__)
    extern const int __fsymtab_start;
    extern const int __fsymtab_end;
    littleshell_system_function_init(&__fsymtab_start, &__fsymtab_end);
#endif
}

struct littleshell_syscall* littleshell_syscall_lookup(const char* name)
{
    struct littleshell_syscall* index;

    for (index = _syscall_table_begin; index < _syscall_table_end; index++)
    {
      if (strcmp(index->name, name) == 0)
            return index;
    }
    return NULL;
}

static int str_common(const char *str1, const char *str2)
{
    const char *str = str1;

    while ((*str != 0) && (*str2 != 0) && (*str == *str2))
    {
      str ++;
      str2 ++;
    }

    return (str - str1);
}

static void shell_auto_complete(char *prefix)
{
    struct littleshell_syscall* index;
    const char *name_ptr, *cmd_name;
    int length, min_length;

    min_length = 0;
    name_ptr = NULL;
   
    PRINTF(NEW_LINE);
    if (*prefix == '\0')
    {
#if ( LTSHELL_USING_SHOW_HELP == 1 )
      PRINTF("shell commands:"NEW_LINE);
      for (index = _syscall_table_begin; index < _syscall_table_end; index++)
      {
            PRINTF("%s - %s"NEW_LINE, index->name, index->desc);
      }
#endif
      PRINTF("%s%s", CONSOLE_FROME, prefix);
      return;
    }

    for (index = _syscall_table_begin; index < _syscall_table_end; index++)
    {
      if (strncmp(index->name, prefix,strlen(prefix)) == 0)
      {
            cmd_name = index->name;
            if(min_length == 0)
            {
               /* set name_ptr */
               name_ptr = cmd_name;
               /* set initial length */
               min_length = strlen(name_ptr);
            }
            length =str_common(name_ptr, cmd_name);
            if (length < min_length)
                min_length = length;
            PRINTF("%s"NEW_LINE, cmd_name);
      }
    }
    /* auto complete string */
    if (name_ptr != NULL)
    {
      strncpy(prefix, name_ptr, min_length);
    }
    PRINTF("%s%s", CONSOLE_FROME, prefix);
    return ;
}
   
/*** Parse command line
* return pointer to start of new command line
*/
static void littleshell_parse_args(const char *cmd, struct littleshell_arg *arg)
{
    char *p;

    if (arg) {
      arg->argc = 0;
      if (cmd) {
            p = arg->_buf;
            while (*cmd && arg->argc < MAX_CLI_ARGS_NUM && (p - arg->_buf < MAX_CLI_ARGS_BUF_LEN)) {
                while(*cmd && (*cmd == ' ' || *cmd == '\t'))/* Ignore spaces and backspace */
                  cmd++;

                arg->argv = p;/* Write command point values */
                while (*cmd && (*cmd != ' ' && *cmd != '\t') && (p - arg->_buf < MAX_CLI_ARGS_BUF_LEN)) {/* Write command buffer */                     
                        *p++ = *cmd++;
                }
                *p++ = '\0';

                if (*(arg->argv) == '\0') /* anomaly detection */
                  break;
                arg->argc++;
            }
      }
    }
}
   
/*** filter out leading and tailing spaces, discard comments
* return pointer to start of new command line
*/
   
int littleshell_interpret(const char *line)
{
    struct littleshell_arg arg = {0};
//const struct cli_command *cmd;

    struct littleshell_syscall* fun_index = NULL;
    littleshell_parse_args(line, &arg);

    fun_index = littleshell_syscall_lookup(arg.argv);
    if(fun_index)
    {
      fun_index->func(arg.argc,arg.argv);
    }
    //PRINTF("[%d][%s]\n",arg.argc,arg._buf);
    return 0;
}

   
/*** filter out leading and tailing spaces, discard comments
* return pointer to start of new command line
*/
static char * littleshell_process_line(char *p)
{
    char *s;
    char *x;

    if (!p)
      return NULL;
    // skip leading spaces
    while (p && (*p == ' ' || *p == '\t'))
      p++;
    for (s = x = p; *p; x++, p++) {
      switch(*p) {
      case '\\':
            p++;
            if (*p) {
                switch(*p) {
                case 'n':
                  *x = '\n';
                  break;
                case 'r':
                  *x = '\r';
                  break;
                case 't':
                  *x = '\t';
                  break;
                case 'b':
                  *x = '\b';
                  break;
                default:
                  if (*p >= '0' && *p <= '9')
                        *x = *p - '0';
                  else
                        *x = *p;
                  break;
                }
            }
            break;
      default:
            if (*p == '\r' || *p == '\n' || *p == '#') *p = '\0';
            *x = *p;
            break;
      }
      if (*p == 0)
            break;
    }
    // trim tailing spaces
    p--;
    while (p > s && (*p == ' ' || *p == '\t'))
      *p-- = '\0';
    return s;
}   

#ifdef LTSHELL_USING_HISTORY

static void shell_handle_history(struct little_shell *shell)
{

    PRINTF("\033[2K\r");
    PRINTF("%s%s", CONSOLE_FROME, shell->line);
}

static void shell_push_history(struct little_shell *shell)
{
    if (shell->line_position != 0)
    {
      /* push history */
      if (shell->history_count >= MAX_HISTROY_NUMS)
      {
            /* if current cmd is same as last cmd, don't push */
            if (memcmp(&shell->cmd_history, shell->line, MAX_CLI_ARGS_BUF_LEN))
            {
                /* move history */
                int index;
                for (index = 0; index < MAX_HISTROY_NUMS - 1; index ++)
                {
                  memcpy(&shell->cmd_history,
                           &shell->cmd_history, MAX_CLI_ARGS_BUF_LEN);
                }
                memset(&shell->cmd_history, 0, MAX_CLI_ARGS_BUF_LEN);
                memcpy(&shell->cmd_history, shell->line, shell->line_position);

                /* it's the maximum history */
                shell->history_count = MAX_HISTROY_NUMS;
            }
      }
      else
      {
            /* if current cmd is same as last cmd, don't push */
            if (shell->history_count == 0 || memcmp(&shell->cmd_history, shell->line, MAX_CLI_ARGS_BUF_LEN))
            {
                shell->current_history = shell->history_count;
                memset(&shell->cmd_history, 0, MAX_CLI_ARGS_BUF_LEN);
                memcpy(&shell->cmd_history, shell->line, shell->line_position);

                /* increase count and set current history position */
                shell->history_count ++;
            }
      }
    }
    shell->current_history = shell->history_count;
}

#endif
/*** read a line datas from command line.
*/

void littleshell_main_entry(void *pvParameters)
{
    char *p;
    littleshell_system_init();
   
    PRINTF("%s",CONSOLE_FROME);
    while(1)
    {   
      uint8_t ch = 100;
      //if (linelen >= sizeof(line))
      //    continue;
      if((shell_uart_getchar(&ch)) != 0)
      {
            /*
             * handle control key
             * up key: 0x1b 0x5b 0x41
             * down key: 0x1b 0x5b 0x42
             * right key:0x1b 0x5b 0x43
             * left key: 0x1b 0x5b 0x44
             */
            if (ch == 0x1b)
            {
                shell->stat = WAIT_SPEC_KEY;
                continue;
            }
            else if (shell->stat == WAIT_SPEC_KEY)
            {
                if (ch == 0x5b)
                {
                  shell->stat = WAIT_FUNC_KEY;
                  continue;
                }
   
                shell->stat = WAIT_NORMAL;
            }
            else if (shell->stat == WAIT_FUNC_KEY)
            {
                shell->stat = WAIT_NORMAL;
   
                if (ch == 0x41) /* up key */
                {
#ifdefLTSHELL_USING_HISTORY
                  /* prev history */
                  if (shell->current_history > 0)
                        shell->current_history --;
                  else
                  {
                        shell->current_history = 0;
                        continue;
                  }
   
                  /* copy the history command */
                  memcpy(shell->line, &shell->cmd_history,
                           MAX_CLI_ARGS_BUF_LEN);
                  shell->line_curpos = shell->line_position = strlen(shell->line);
                  shell_handle_history(shell);
#endif
                  continue;
                }
                else if (ch == 0x42) /* down key */
                {
#ifdef LTSHELL_USING_HISTORY
                  /* next history */
                  if (shell->current_history < shell->history_count - 1)
                        shell->current_history ++;
                  else
                  {
                        /* set to the end of history */
                        if (shell->history_count != 0)
                            shell->current_history = shell->history_count - 1;
                        else
                            continue;
                  }
   
                  memcpy(shell->line, &shell->cmd_history,
                           MAX_CLI_ARGS_BUF_LEN);
                  shell->line_curpos = shell->line_position = strlen(shell->line);
                  shell_handle_history(shell);
#endif
                  continue;
                }
                else if (ch == 0x44) /* left key */
                {
                  if (shell->line_curpos)
                  {
                        PRINTF("\b");
                        shell->line_curpos --;
                  }
   
                  continue;
                }
                else if (ch == 0x43) /* right key */
                {
                  if (shell->line_curpos < shell->line_position)
                  {
                        PRINTF("%c", shell->line);
                        shell->line_curpos ++;
                  }
   
                  continue;
                }
            }

            /* received null or error */
            if (ch == '\0' || ch == 0xFF) continue;
            /* handle tab key */
            else if (ch == '\t')
            {
                int i;
                /* move the cursor to the beginning of line */
                for (i = 0; i < shell->line_curpos; i++)
                  PRINTF("\b");

                /* auto complete */
                shell_auto_complete(&shell->line);
                /* re-calculate position */
                shell->line_curpos = shell->line_position = strlen(shell->line);

                continue;
            }
            /* handle backspace key */
            else if (ch == 0x7f || ch == 0x08)
            {
                /* note that shell->line_curpos >= 0 */
                if (shell->line_curpos == 0)
                  continue;

                shell->line_position--;
                shell->line_curpos--;

                if (shell->line_position > shell->line_curpos)
                {
                  int i;

                  memmove(&shell->line,
                               &shell->line,
                               shell->line_position - shell->line_curpos);
                  shell->line = 0;

                  PRINTF("\b%s\b", &shell->line);

                  /* move the cursor to the origin position */
                  for (i = shell->line_curpos; i <= shell->line_position; i++)
                        PRINTF("\b");
                }
                else
                {
                  PRINTF("\b \b");
                  shell->line = 0;
                }

                continue;
            }

            /* handle end of line, break */
            if (ch == '\r' || ch == '\n')
            {
                if (shell->line_position != 0)
                {
                  shell_push_history(shell);
                  p = littleshell_process_line(shell->line);
                  if (*p)
                  {
                        PRINTF(NEW_LINE);

                        littleshell_interpret(p);
                        //PRINTF("[%s]\n",p);
                        //TODO deal with strings,end of deal echo console string
                        
                  }
                }
                PRINTF(NEW_LINE);
                PRINTF(CONSOLE_FROME);

                memset(shell->line, 0, sizeof(shell->line));
                shell->line_curpos = shell->line_position = 0;
                continue;
            }

            /* it's a large line, discard it */
            if (shell->line_position >= MAX_CLI_ARGS_BUF_LEN)
                shell->line_position = 0;

            /* normal character */
            if (shell->line_curpos < shell->line_position)
            {
                int i;

                memmove(&shell->line,
                           &shell->line,
                           shell->line_position - shell->line_curpos);
                shell->line = ch;
                PRINTF("%s", &shell->line);

                /* move the cursor to new position */
                for (i = shell->line_curpos; i < shell->line_position; i++)
                  PRINTF("\b");
            }
            else
            {
                shell->line = ch;
                PRINTF("%c", ch);
            }

            ch = 0;
            shell->line_position ++;
            shell->line_curpos++;
            if (shell->line_position >= MAX_CLI_ARGS_BUF_LEN)
            {
                /* clear command line */
                shell->line_position = 0;
                shell->line_curpos = 0;
            }   
      }
      else
      {
            vTaskDelay(100);
      }
    }   
}



3 测试验证
为了验证shell 的工作状态本地追加led 命令用于控制led 的点亮熄灭,led on/off 打开/关闭led,对应代码如下:unsigned int led(char argc,char ** argv)
{
      /*init gpio*/
      reg_gpio_pb_oen &= ~ GPIO_PB7;
      reg_gpio_pb_oen &= ~ GPIO_PB4;

      if(argc != 2)
                return 1;

      if(strcmp(argv,"on") == 0)
      {
                reg_gpio_pb_out |= GPIO_PB7;
                reg_gpio_pb_out |= GPIO_PB4;
      }
      else if(strcmp(argv,"off") == 0)
      {
                reg_gpio_pb_out &= ~GPIO_PB7;
                reg_gpio_pb_out &= ~GPIO_PB4;
      }

      return 0;
};
串口输入led on/off 已经实现了led 的点亮熄灭控制符合预期效果如下,后续就可以实现测试命令使用串口和板子进行交互。



==================资料分割线==================官方的sdk文档及文档整理至如下路径,里面的所有资料也都可以从官方的wiki获取https://gitee.com/andeyqi/telink-b91

























页: [1]
查看完整版本: 【Telink-泰凌微电子 B91通用开发套件】(二)FreeRtos 适配SHELL