Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
FinSH
在RT_USING_DFS条件下使用FINSH组件的方法探索
发布于 2018-03-18 14:37:03 浏览:4204
订阅该版
[tocm] * 本帖最后由 Spunky 于 2018-3-18 14:37 编辑 * 从去年12月在MDK5环境下使用RT-Thread 3.0版本,但在使用DFS组件时,出现程序不能正常运行。经过调试跟踪,发现FINSH组件的有些函数在RT_USING_DFS下被禁用,finsh_rx_ind(), finsh_set_device()和finsh_get_device()。同时struct finsh_struct中的rt_device_t 成员也被屏蔽掉。 ``` struct finsh_shell { struct rt_semaphore rx_sem; enum input_stat stat; rt_uint8_t echo_mode:1; #ifdef FINSH_USING_HISTORY rt_uint16_t current_history; rt_uint16_t history_count; char cmd_history[FINSH_HISTORY_LINES][FINSH_CMD_SIZE]; #endif #ifndef FINSH_USING_MSH_ONLY struct finsh_parser parser; #endif char line[FINSH_CMD_SIZE]; rt_uint8_t line_position; rt_uint8_t line_curpos; #ifndef RT_USING_DFS rt_device_t device; #endif #ifdef FINSH_USING_AUTH char password[FINSH_PASSWORD_MAX]; #endif }; #ifndef RT_USING_DFS static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size) { RT_ASSERT(shell != RT_NULL); /* release semaphore to let finsh thread rx data */ rt_sem_release(&shell->rx_sem); return RT_EOK; } /** * @ingroup finsh * * This function sets the input device of finsh shell. * * @param device_name the name of new input device. */ 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
", 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); } } /** * @ingroup finsh * * This function returns current finsh shell input device. * * @return the finsh shell input device name is returned. */ const char *finsh_get_device() { RT_ASSERT(shell != RT_NULL); return shell->device->parent.name; } #endif```而程序不能运行的最主要原因是finsh_getchar()中,在RT_USING_DFS条件下使用了标准函数getchar(),而getchar()函数需要重写才能够满足。由于当时假期临近,希望尽快完成工作后出去自驾游,于是打了个懒主意,直接修改finsh_getchar()函数,让它不使用getchar(),直接使用console设备。 ```static char finsh_getchar(void) { // RT_ASSERT(shell != RT_NULL); #ifdef RT_USING_DFS // return getchar(); char ch; rt_device_t console_device; console_device = rt_console_get_device(); if (console_device != RT_NULL) { while (rt_device_read(console_device, 0, &ch, 1) <= 0) { /* console的串口速度慢,周期性50ms读取没有问题 */ rt_thread_delay(50); } } return ch; #else char ch; while (rt_device_read(shell->device, -1, &ch, 1) != 1) rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER); return ch; #endif }```以上方法简单,另外就是要保证usart设备成员rx_indicate为空指针即可,因为我使用的周期性读取serial设备对象的rx_fifo,没有设置中断回调函数。但RT-Thread操作系统官方自带的串口驱动serial.c中已经完成,倒是不用担心。 ```/* * serial register */ rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, const char *name, rt_uint32_t flag, void *data) { struct rt_device *device; RT_ASSERT(serial != RT_NULL); device = &(serial->parent); device->type = RT_Device_Class_Char; device->rx_indicate = RT_NULL; device->tx_complete = RT_NULL; device->init = rt_serial_init; device->open = rt_serial_open; device->close = rt_serial_close; device->read = rt_serial_read; device->write = rt_serial_write; device->control = rt_serial_control; device->user_data = data; /* register a character device */ return rt_device_register(device, name, flag); }```以上修改即可保证正常在RT_USING_DFS条件下使用finsh组件: [align=center]![console1.jpg](/uploads/201803/18/130921c61nb2xw3mnwmmxv.jpg) 今年开年上班后,工作的事情不是很多。于是我打算还是重写getchar()来解决。查找了许多关于RT-Thread的帖子,没有找到现成果子吃。但想起来这个也比较简单,于是开始自己编写。 重写getchar()还是很简单的,参照原子开发板的办法,重定向编写int fgetc(FILE *p)即可。我重新建立1个新的文件redirection.c,它包含几个方面内容: 1. 建立1个redirection对象,里面包含关联的终端设备指针和1个数据接收信号量。 2. 编写xxx_rx_ind()函数,作为redirection关联终端设备通知getchar()读取数据。 3. 接收信号量rx_sem作为线程同步信号,它在xxx_rx_ind()中被release。 4. 重写的getchar()中让rx_sem进行阻塞接收,直至收到xxx_rx_ind()中release后退出,然后读取当前redirection关联的终端设备数据,并且返回。 5. redirection关联的终端设备驱动建议都应该带有接收FIFO。 具体redirection.c的实现代码如下: ```#include
#include
#include
#include
#ifdef RT_DEBUG # define REDIRECTION_DEBUG rt_kprintf #else # define REDIRECTION_DEBUG(...) #endif #ifdef RT_USING_DFS struct rt_redirection_device { rt_device_t device; /* the actual device */ rt_sem_t rx_sem; }; struct rt_redirection_device _redirection; #pragma import(__use_no_semihosting) //标准库需要的支持函数 struct __FILE { int handle; }; FILE __stdin; //定义_sys_exit()以避免使用半主机模式 _sys_exit(int x) { x = x; } //重定义fgetc函数 int fgetc(FILE *f) { char ch; RT_ASSERT(_redirection.rx_sem != RT_NULL); while (rt_device_read(_redirection.device, 0, &ch, 1) != 1) rt_sem_take(_redirection.rx_sem, RT_WAITING_FOREVER); return (int)ch; } static rt_err_t redirection_rx_ind(rt_device_t dev, rt_size_t size) { RT_ASSERT(_redirection.rx_sem != RT_NULL); /* release semaphore to let fgetc to rx data */ rt_sem_release(_redirection.rx_sem); return RT_EOK; } void rt_redirection_set_device(const char *device_name) { rt_device_t dev = RT_NULL; dev = rt_device_find(device_name); if (dev == RT_NULL) { rt_kprintf("redirection: can not find device: %s
", device_name); return; } /* check whether it's a same device */ if (dev == _redirection.device) return; /* open this device and set the new device in redirection */ if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM) == RT_EOK) { if (_redirection.device != RT_NULL) { /* close old finsh device */ rt_device_close(_redirection.device); rt_device_set_rx_indicate(_redirection.device, RT_NULL); } /* clear line buffer before switch to new device */ _redirection.device = dev; rt_device_set_rx_indicate(dev, redirection_rx_ind); } } int rt_redirection_init(void) { _redirection.rx_sem = rt_sem_create("rdsem", 0, RT_IPC_FLAG_FIFO); RT_ASSERT(_redirection != RT_NULL); return 0; } INIT_COMPONENT_EXPORT(rt_redirection_init); #endif```将redirection.c加入到工程中编译即可。 在实际使用中,rt_redirection_set_device()来关联终端设备,它的用法与finsh_set_device()一样。为了规范代码和初始化默认使用Console设备作为Finsh的终端设备,在shell.c中的Finsh的主线程finsh_thread_entry()进行了修改: ```#ifdef RT_USING_DFS rt_device_t console; extern void rt_redirection_set_device(const char *device_name); #endif #ifndef RT_USING_DFS /* set console device as shell device */ if (shell->device == RT_NULL) { rt_device_t console = rt_console_get_device(); if (console) { finsh_set_device(console->parent.name); } } #else console = rt_console_get_device(); if (console) { rt_redirection_set_device(console->parent.name); } #endif```在串口、telnet或者其他终端设备都是方法都是一样的: 1 .使用rt_redirection_set_device()进行终端设备关联,它的作用实际上就是将关联设备对象rt_device下的rx_indicate指向redirection_rx_ind()。 2. 当终端设备接收到数据,需要通知Finsh组件的getchar()读取数据。调用关联终端设备对象rt_device下的rx_indicate()即可。 在telnet应用中,process_rx()调用关联的redirection设备对象rt_device的rx_indicate(),即通知Finsh组件读取数据。 ```/* indicate there are reception data */ if ((rx_length > 0) && (telnet->device.rx_indicate != RT_NULL)) telnet->device.rx_indicate(&telnet->device, rx_length);```在官方发布的串口驱动文件serial.c中也是一样实现的: ```/* invoke callback */ if (serial->parent.rx_indicate != RT_NULL) { rt_size_t rx_length; /* get rx length */ level = rt_hw_interrupt_disable(); rx_length = (rx_fifo->put_index >= rx_fifo->get_index)? (rx_fifo->put_index - rx_fifo->get_index): (serial->config.bufsz - (rx_fifo->get_index - rx_fifo->put_index)); rt_hw_interrupt_enable(level); serial->parent.rx_indicate(&serial->parent, rx_length); }``` 以上方法感觉还是野路子,不像官方版本那么正统。也许RT-Thread官方也许包含这方面的功能,只是我还没有发现,还请高手能够赐教!!!!
查看更多
1
个回答
默认排序
按发布时间排序
panda
2018-07-22
这家伙很懒,什么也没写!
finsh shell线程: 每次的命令执行都是在finsh shell线程的上下文中完成的。当定义RT_USING_FINSH宏时,就可以在初始化线程中调用finsh_system_init()初始化finsh shell线程。RT-Thread 1.2.0之后的版本中可以不使用 finsh_set_device(const char* device_name) 函数去显式指定使用的设备,而是会自动调用 rt_console_get_device() 函数去使用console设备(RT-Thread 1.1.x及以下版本中必须使用 finsh_set_device(const char* device_name) 指定finsh shell使用的设备)。finsh shell线程在函数 finsh_system_init() 函数中被创建,它将一直等待rx_sem信号量。 finsh的输出: finsh的输出依赖于系统的输出,在RT-Thread中依赖rt_kprintf输出。在启动函数 rt_hw_board_init() 中, rt_console_set_device(const char* name) 函数设置了finsh的打印输出设备。 finsh的输入: finsh shell线程在获得了rx_sem信号量后,调用 rt_device_read() 函数从设备(选用串口设备)中获得一个字符然后处理。所以finsh的移植需要 rt_device_read() 函数的实现。而rx_sem信号量的释放通过调用 rx_indicate() 函数以完成对finsh shell线程的输入通知。通常的过程是,当串口接收中断发生是(即串口有输入),接受中断服务例程调用 rx_indicate() 函数通知finsh shelli线程有输入:而后finsh shell线程获取串口输入最后做相应的命令处理。 文档里说得有了吧,我也遇了这种情况,查出来是我有I2C初始化卡死了,造成后面的调度器没有得到初始化。
撰写答案
登录
注册新账号
关注者
0
被浏览
4.2k
关于作者
Spunky
这家伙很懒,什么也没写!
提问
19
回答
98
被采纳
0
关注TA
发私信
相关问题
1
RT-THREAD shell无反应呢?
2
RT-thread2.0beta下用类似linux风格MSH,参数如何输入和导出
3
rt-thread finsh windows下的那个终端软件叫什么来着
4
板子上只有485接口,能把FINSH改造成485的么?
5
finsh最大字符问题
6
finsh命令个数是不是有限制啊
7
finsh支持转义字符吗
8
不用finsh如何知道堆栈使用量
9
强烈建议 RT-Thread下finsh原理深入分析
10
finsh输入命令全部返回null node
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
CherryUSB的bootuf2配置
2
在用clangd开发RTT吗,快来试试如何简单获得清晰干净的工作区
3
GD32F450 片内 flash驱动适配
4
STM32H7R7运行CherryUSB
5
RT-Smart首次线下培训,锁定2024 RT-Thread开发者大会!
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
YZRD
2
篇文章
5
次点赞
lizimu
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部