• RT-Thread 组件 FinSH 使用时遇到的问题


    一、FinSH 的移植与使用问题

    FinSH组件输入无反应的问题

    • 现象:当打开 finsh 组件后,控制台会打相应的信息,如下图说是:
     \ | /
    - RT -     Thread Operating System
     / | \     3.1.5 build Jul  6 2022
     2006 - 2020 Copyright by rt-thread team
    do components initialization.
    initialize rti_board_end:0 done
    initialize finsh_system_init:0 done
    RT-Thread Start......
    msh >
    
    

    但是输入回车或任意内容时,无反应。当然导致这样的现象有两种可能,一种可能是未正常开启相应的配置,第二种是未对接收字符的函数进行实现,具体看下面内容

    • FinSH 组件在 rtconfig.h 中的配置,如下所示:
    /* --------------------------------- RT-Thread 内核部分 --------------------------------- */
     
    /* 表示内核对象的名称的最大长度,若代码中对象名称的最大长度大于宏定义的长度,
     * 多余的部分将被截掉。*/
    #define RT_NAME_MAX    8
    
    /* 字节对齐时设定对齐的字节个数。常使用 ALIGN(RT_ALIGN_SIZE) 进行字节对齐。*/
    #define RT_ALIGN_SIZE   4
    
    /* 定义系统线程优先级数;通常用 RT_THREAD_PRIORITY_MAX-1 定义空闲线程的优先级 */
    #define RT_THREAD_PRIORITY_MAX  32
    
    /* 定义时钟节拍,为 100 时表示 100 个 tick 每秒,一个 tick 为 10ms */
    #define RT_TICK_PER_SECOND  1000
    
    /* 检查栈是否溢出,未定义则关闭 */
    #define RT_USING_OVERFLOW_CHECK
    
    /* 定义该宏开启 debug 模式,未定义则关闭 */
    #define RT_DEBUG
    
    /* 开启 debug 模式时:该宏定义为 0 时表示关闭打印组件初始化信息,定义为 1 时表示启用 */
    #define RT_DEBUG_INIT 1
    
    /* 开启 debug 模式时:该宏定义为 0 时表示关闭打印线程切换信息,定义为 1 时表示启用 */
    #define RT_DEBUG_THREAD 0
    
    /* 定义该宏表示开启钩子函数的使用,未定义则关闭 */
    //#define RT_USING_HOOK
    
    /* 定义了空闲线程的栈大小 */
    #define IDLE_THREAD_STACK_SIZE 1024
    
    折叠

    FinSH 移植

    FinSH 组件使用有三种种方式,如下:

    1. 通过 rt_hw_console_getchar() 函数获取控制台数据
      FinSH 线程的使用方式主要是通过实现rt_hw_console_getchar()函数,获取控制台输入的数据,具体方式看我之前的笔记,STM32 移植 RT-Thread 标准版的 FinSH 组件

    2. 通过外设驱动中的 数据流(stm32_getc函数) 获取控制台数据
      具体实现方式见UART外设的移植,稍后我也会将我移植的过程发出来,有需要的可以看我之后的笔记。

    3. 通过外设驱动中的 中断方式 获取控制台数据
      中断的方式包涵了DMA的方式获取控制台数据。

    注意: 第二和第三中方式其实都是通过RT-Thread中的外设驱动获取的,这里我为啥会将 数据流和中断 分开说明,是因为他们之间会引入一个新的问题,具体见之后的流程

    二、设备为空问题

    • 现象:msh >(dev != RT_NULL) assertion failed at function:rt_device_read, line number:320

    • 原因:出现这个现象主要是在注册设备的时候导致的,在注册设备的时候才用了数据流的方式回去了数据,如下所示:

      /* 配置串口设备 */
      result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
                                         RT_DEVICE_OFLAG_RDWR 
                                         | RT_DEVICE_FLAG_INT_RX  
                                         | RT_DEVICE_FLAG_INT_TX
                                         , NULL);
      
      

      相应的配置宏如下所示:

      #define RT_DEVICE_FLAG_RDONLY       0x001 /* 只读 */
      #define RT_DEVICE_FLAG_WRONLY       0x002 /* 只写  */
      #define RT_DEVICE_FLAG_RDWR         0x003 /* 读写  */
      #define RT_DEVICE_FLAG_REMOVABLE    0x004 /* 可移除  */
      #define RT_DEVICE_FLAG_STANDALONE   0x008 /* 独立   */
      #define RT_DEVICE_FLAG_SUSPENDED    0x020 /* 挂起  */
      #define RT_DEVICE_FLAG_STREAM       0x040 /* 流模式  */
      #define RT_DEVICE_FLAG_INT_RX       0x100 /* 中断接收 */
      #define RT_DEVICE_FLAG_DMA_RX       0x200 /* DMA 接收 */
      #define RT_DEVICE_FLAG_INT_TX       0x400 /* 中断发送 */
      #define RT_DEVICE_FLAG_DMA_TX       0x800 /* DMA 发送 */
      
      

      认真思考的小伙伴就会存在一个疑问,为啥将数据接收注册为 流模式 会导致设备为空了,可以猜测在某处导致了设备丢失,我们仔细找一下代码就会发现在shell.c文件中,通过了中断的方式打开了设备,如下图所示:

      现在原因找到了,那么解决方式有两种,如下所示:

    解决办法

    1. 将注册设备时,改为中断接收的方式注册设备

    2. 将shell.c文件中的发开方式改为流模式即可,只需要将 RT_DEVICE_FLAG_INT_RX 屏蔽,如下所示:

      void finsh_set_device(const char *device_name)
      {
      	rt_device_t dev = RT_NULL;
      
      	RT_ASSERT(shell != RT_NULL);
      	dev = rt_device_find(device_name);
      	if (dev == RT_NULL)
      	{
      		rt_kprintf("finsh: can not find device: %s\n", device_name);
      		return;
      	}
      
      	/* check whether it's a same device */
      	if (dev == shell->device) return;
      	/* open this device and set the new device in finsh shell */
      	if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR |  
      //					RT_DEVICE_FLAG_INT_RX |		
      					RT_DEVICE_FLAG_STREAM) == RT_EOK)
      	{
      		if (shell->device != RT_NULL)
      		{
      			/* close old finsh device */
      			rt_device_close(shell->device);
      			rt_device_set_rx_indicate(shell->device, RT_NULL);
      		}
      
      		/* clear line buffer before switch to new device */
      		memset(shell->line, 0, sizeof(shell->line));
      		shell->line_curpos = shell->line_position = 0;
      
      		shell->device = dev;
      		rt_device_set_rx_indicate(dev, finsh_rx_ind);
      	}
      }
      
      
      折叠

      ** 注意:** 改为流模式后,会发现一个奇怪的现象,就是当你使用调试模式时,可以正常接收指令,但是正常运行时,就无任何响应,遇到这样的现象不要慌,接着往下看。

    三、FinSH 卡死问题

    • 现象:FinSH线程卡死,明显的发现就是,使用调试模式时,可以正常接收指令,但是正常运行时,就无任何响应。

    • 原因:深入分析后,会在 shell.c 文件中的 finsh_getchar 函数中看到信号量的使用,如下图所示:

      现在原因找到了,那么我们只需要在适当的时候释放信号量即可,那我们在找找看看有么有信号释放的函数,接下来我们会发现在 shell.c 文件中 finsh_rx_ind 函数就是释放信号量的,如图所示:

      那么新的问题又来了,怎么调用这个函数了,因为在shell.h文件中也没有这个函数的定义, 不要怕我们接着找,最后在shell.c 文件中的 finsh_set_device 函数中,会将释放信号量的函数指针放入 rt_device 结构体中,如下图所示:

      那么问题就变得简单了,解决办法如下

    • 解决办法:我们已经知道怎么释放信号量了,所以只需要在 数据接收函数(stm32_getc)中,完成数据接收后,释放信号量即可,如下所示:

      /**
       * 接收一个字符数据 
       */
      static int stm32_getc(struct rt_serial_device *serial)
      {
      	int ch;
      	struct stm32_uart *uart;
          RT_ASSERT(serial != RT_NULL);
          uart = rt_container_of(serial, struct stm32_uart, serial);
      
      	ch = -1;
          if (USART_GetFlagStatus(uart->handle.Instance, USART_FLAG_RXNE) != RESET)
      {
      #if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \
          || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) \
          || defined(SOC_SERIES_STM32G4)
              ch = uart->handle.Instance->RDR & 0xff;
      #else		
      //		ch = (uint16_t)uart->handle.Instance->DR & (uint16_t)0x00ff;
      		ch = (char)USART_ReceiveData(uart->handle.Instance);
      #endif
      			
      	}
      	
      	/* 调用设备接收数据回调,释放信号量  */
      	uart->serial.parent.rx_indicate(&serial->parent, 0);
      	return ch;
      }
      
      
      折叠

    四、测试

    解决完问题后,在控制台输入回车有相应的回应就没问题,如下图所示:

  • 相关阅读:
    单调栈详解
    Hyper-V 安装windows10 虚拟机,且能调试窗口大小、与主机之间复制文件
    大数据技术之Shell学习笔记(常用)
    安装项目运行环境(python依赖包+allure)
    手写简易版flexible.js以及源码分析
    java学生管理系统
    谷歌沉浸式内容负责人:更看好开放的Web AR技术
    JavaScript系列之标识符、关键字、保留字
    STM32物联网项目-高级定时器功能介绍
    SpringCloud微服务治理技术入门(SCN)
  • 原文地址:https://www.cnblogs.com/jzcn/p/16450021.html