rt-thread 怎样查看回调函数例子

4747人阅读
rt-thread(22)
rt-thread可以采用软件定时器或硬件定时器来实现定时器管理的,所谓软件定时器是指由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。而硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。软件定时器的精度取决于它使用的硬件定时器精度。而rt-thread操作系统在默认情况下是采用的硬件定时器的方式,用户可以通过修改宏定义#ifdef
RT_USING_TIMER_SOFT来修改采用哪种。
2 rt-thread的定时器的基本工作原理
在RT-Thread定时器模块维护两个重要的全局变量,一个是当前系统的时间rt_tick(当硬件定时器中断来临时,它将加1),另一个是定时器链表rt_timer_list,系统中新创建的定时期都会被以排序的方式插入到rt_timer_list(硬件定时器模式下使用)链表中,rt_timer_list的每个节点保留了一个定时器的信息,并且在这个节点加入链表时就计算好了产生时间到达时的时间点,即tick,在rt-thread系统中如果采用软件定时器模式,则存在一定时器线程rt_thread_timer_entry,不断获取当前TICK值并与定时器链表rt_timer_list上的定时器对比判断是否时间已到,一旦发现就调用对应的回调函数,即事件处理函数进行处理,而如果采用硬件定时器管理模式的话,则该检查过程放到系统时钟中断例程中进行处理,此时,是不存在定时器线程的。如下图:注:如果采用软件定时器软件定时器,则该定时器链表为rt_soft_timer_list。
3 源码分析
3.1 数据定义
* timer structure
struct rt_timer
struct rt_ //内核对象
//链表节点
void (*timeout_func)(void *parameter);
//定时器超时例程
//定时器例程的传入参数
//定时器的超时时间,即总共多长时间将产生超时事件
//定时器超时的时间点,即产生超时事件时那一该的时间点
typedef struct rt_timer *rt_timer_t;
3.2 rt-thread的软件定时器模式
软件定时器线程初始化及启动:
* @ingroup SystemInit
* This function will initialize system timer thread
void rt_system_timer_thread_init(void)
#ifdef RT_USING_TIMER_SOFT//如果采用软件定时器管理模式,则启动定时器线程
rt_list_init(&rt_soft_timer_list);//初始化软件定时器链表
/* start software timer thread */
rt_thread_init(&timer_thread,//初始化软件定时器线程,并启动
rt_thread_timer_entry,
&timer_thread_stack[0],
sizeof(timer_thread_stack),
RT_TIMER_THREAD_PRIO,
/* startup */
rt_thread_startup(&timer_thread);
软件定时器线程如下:/* system timer thread entry */
static void rt_thread_timer_entry(void *parameter)
rt_tick_t next_
/* get the next timeout tick */
next_timeout = rt_timer_list_next_timeout(&rt_soft_timer_list);//得到软件定时器链表上的下一个定时器的超时时间点
if (next_timeout == RT_TICK_MAX)//如果超过范围,则挂起当前线程,继续线程调度
/* no software timer exist, suspend self. */
rt_thread_suspend(rt_thread_self());
rt_schedule();
rt_tick_t current_
/* get current tick */
current_tick = rt_tick_get();//获取当前时间点
if ((next_timeout - current_tick) & RT_TICK_MAX/2)//离下个中断时间点还差些时候
/* get the delta timeout tick */
next_timeout = next_timeout - current_//计算还差多长时间
rt_thread_delay(next_timeout);//休眠一段时间
/* lock scheduler */
rt_enter_critical();//时间到,进入临界区
/* check software timer */
rt_soft_timer_check();//检查是否该产生超时事件
/* unlock scheduler */
rt_exit_critical();//退出临界区
检查是否产生中断函数rt_soft_timer_check函数如下定义:/**
* This function will check timer list, if a timeout event happens, the
* corresponding timeout function will be invoked.
void rt_soft_timer_check(void)
rt_tick_t current_
rt_list_t *n;
struct rt_timer *t;
RT_DEBUG_LOG(RT_DEBUG_TIMER, (&software timer check enter\n&));
current_tick = rt_tick_get();//得到当前时间点
for (n = rt_soft_timer_list. n != &(rt_soft_timer_list);)//得到下一定时器节点
t = rt_list_entry(n, struct rt_timer, list);//t指向rt_timer定时器
* It supposes that the new tick shall less than the half duration of
* tick max.
if ((current_tick - t-&timeout_tick) & RT_TICK_MAX / 2)//如果当前的时间点超过定时器的超时时间点
RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));//使用钩子函数
/* move node to the next */
n = n-&//指向下一定时器
/* remove timer from timer list firstly */
rt_list_remove(&(t-&list));//移除当前定时器
/* call timeout function */
t-&timeout_func(t-&parameter);//产生定时器超时事件,调用对应处理函数
/* re-get tick */
current_tick = rt_tick_get();//再次获取当前时间点
RT_DEBUG_LOG(RT_DEBUG_TIMER, (&current tick: %d\n&, current_tick));
if ((t-&parent.flag & RT_TIMER_FLAG_PERIODIC) &&//如果当前定时器是周期性定时器,则将其再次按序放入软件定时器链表
(t-&parent.flag & RT_TIMER_FLAG_ACTIVATED))
/* start it */
t-&parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;//置标志为非激活状态
rt_timer_start(t);//再次将定时器t放入软件定时器链表末尾
/* stop timer */
t-&parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;//置标志为非激活状态
/* not check anymore */
RT_DEBUG_LOG(RT_DEBUG_TIMER, (&software timer check leave\n&));
上面代码中,为什么定时器里判断超时的条件是((current_tick - t→timeout_tick) & RT_TICK_MAX/2)?
因为系统时钟溢出后会自动回绕。取定时器比较最大值是定时器最大值的一半,即RT_TICK_MAX/2(在比较两个定时器值时,值是32位无符号数,相减运算将会自动回绕)。系统支持的定时器最大长度就是RT_TICK_MAX的一半:即248天(10ms/tick),124天(5ms/tick),24.5天(1ms/tick),以下内容相同道理。
其上rt_timer_start函数如下定义:
* This function will start the timer
* @param timer the timer to be started
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
rt_err_t rt_timer_start(rt_timer_t timer)
struct rt_timer *t;
register rt_base_
rt_list_t *n, *timer_
/* timer check */
RT_ASSERT(timer != RT_NULL);
if (timer-&parent.flag & RT_TIMER_FLAG_ACTIVATED)//如果传入的定时器已经激活,则直接返回错误
return -RT_ERROR;
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer-&parent)));//使用钩子函数
* get timeout tick,
* the max timeout tick shall not great than RT_TICK_MAX/2
RT_ASSERT(timer-&init_tick & RT_TICK_MAX / 2);
timer-&timeout_tick = rt_tick_get() + timer-&init_//得到定时器超时的时间点
/* disable interrupt */
level = rt_hw_interrupt_disable();//关中断
#ifdef RT_USING_TIMER_SOFT//如果采用的是软件定时器管理模式,则将定时器加入到rt_soft_timer_list中
if (timer-&parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
/* insert timer to soft timer list */
timer_list = &rt_soft_timer_
/* insert timer to system timer list */
timer_list = &rt_timer_
for (n = timer_list-& n != timer_ n = n-&next)//将定时器按序加入到定时器链表中
t = rt_list_entry(n, struct rt_timer, list);
* It supposes that the new tick shall less than the half duration of
* tick max.
if ((t-&timeout_tick - timer-&timeout_tick) & RT_TICK_MAX / 2)
rt_list_insert_before(n, &(timer-&list));//将定时器timer插入到t之前
/* no found suitable position in timer list */
if (n == timer_list)//没有找到合适的位置,则放到链表头
rt_list_insert_before(n, &(timer-&list));
timer-&parent.flag |= RT_TIMER_FLAG_ACTIVATED;//置定时器为激活状态
/* enable interrupt */
rt_hw_interrupt_enable(level);
#ifdef RT_USING_TIMER_SOFT
if (timer-&parent.flag & RT_TIMER_FLAG_SOFT_TIMER)//如果系统采用的是软件定时器管理模式,且软件定时器线程处理ready状态,则恢复此线程
/* check whether timer thread is ready */
if (timer_thread.stat != RT_THREAD_READY)
/* resume timer thread to check soft timer */
rt_thread_resume(&timer_thread);//恢复定时器线程
rt_schedule();//开始线程调度
return -RT_EOK;
软件定时器管理模式的源码分析完了,接下来介绍RTT的硬件定时器管理模式。
3.3 RTT的硬件定时器管理模式
硬件定时器管理模式顾名思义,就是说与硬件相关,因此,不用的MCU,其部分源码是不一样的,因为其要采用MCU的系统时钟中断例程来实现。
以STM32F2XX为例,先找到其启动汇编,位置在:RTT/bsp/stm32f2xx/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F2xx/startup/arm/startup_stm32f2xx.s
找到中断向量:
SysTick_H SysTick Handler这是系统时钟中断向量,再找到其中断例程实现:
在bsp/stm32f2xx/drivers/board.c文件中:
* This is the timer interrupt service routine.
void SysTick_Handler(void)//系统时钟中断例程
/* enter interrupt */
rt_interrupt_enter();
rt_tick_increase();
/* leave interrupt */
rt_interrupt_leave();
其中rt_tick_increase函数在RTT/src/clock.c文件中的实现如下:/**
* This function will notify kernel there is one tick passed. Normally,
* this function is invoked by clock ISR.
void rt_tick_increase(void)
struct rt_thread *
/* increase the global tick */
++ rt_//全局rt_tick加1
/* check time slice */
thread = rt_thread_self();//得到当前正在运行的线程
-- thread-&remaining_//纯种剩下时间减1
if (thread-&remaining_tick == 0)//如果线程剩余时间为0,即调度时间已到
/* change to initialized tick */
thread-&remaining_tick = thread-&init_//将线程剩余时间重新设置初始化值
/* yield */
rt_thread_yield();//调度时间到,切换到其它线程
/* check timer */
rt_timer_check();//检查硬件定时器链表是否有定时器产生超时事件
其中rt_timer_check函数在RTT/src/timer.c文件中如下定义:/**
* This function will check timer list, if a timeout event happens, the
* corresponding timeout function will be invoked.
* @note this function shall be invoked in operating system timer interrupt.
void rt_timer_check(void)
struct rt_timer *t;
rt_tick_t current_
register rt_base_
RT_DEBUG_LOG(RT_DEBUG_TIMER, (&timer check enter\n&));
current_tick = rt_tick_get();
/* disable interrupt */
level = rt_hw_interrupt_disable();
while (!rt_list_isempty(&rt_timer_list))
t = rt_list_entry(rt_timer_list.next, struct rt_timer, list);
* It supposes that the new tick shall less than the half duration of
* tick max.
if ((current_tick - t-&timeout_tick) & RT_TICK_MAX/2)
RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));
/* remove timer from timer list firstly */
rt_list_remove(&(t-&list));
/* call timeout function */
t-&timeout_func(t-&parameter);
/* re-get tick */
current_tick = rt_tick_get();
RT_DEBUG_LOG(RT_DEBUG_TIMER, (&current tick: %d\n&, current_tick));
if ((t-&parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t-&parent.flag & RT_TIMER_FLAG_ACTIVATED))
/* start it */
t-&parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
/* stop timer */
t-&parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
/* enable interrupt */
rt_hw_interrupt_enable(level);
RT_DEBUG_LOG(RT_DEBUG_TIMER, (&timer check leave\n&));
此函数与rt_soft_timer_check基本大致相同,只不过一个是查找硬件定时器链表rt_timer_list,一个是查找rt_soft_timer_list.
在此,硬件定时器管理模式基本上介绍完毕,接下来介绍一些定时器接口.
4 定时器接口
4.1 定时器初始化
静态初始化定义器
* This function will initialize a timer, normally this function is used to
* initialize a static timer object.
* @param timer the static timer object
* @param name the name of timer
* @param timeout the timeout function
* @param parameter the parameter of timeout function
* @param time the tick of timer
* @param flag the flag of timer
void rt_timer_init(rt_timer_t
const char *name,
void (*timeout)(void *parameter),
*parameter,
rt_uint8_t
/* timer check */
RT_ASSERT(timer != RT_NULL);
/* timer object initialization */
rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);//初始化内核对象
_rt_timer_init(timer, timeout, parameter, time, flag);
_rt_timer_init函数如下定义:static void _rt_timer_init(rt_timer_t timer,
void (*timeout)(void *parameter),
*parameter,
rt_uint8_t flag)
/* set flag */
timer-&parent.flag
/* set deactivated */
timer-&parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;//初始化时,设置为非激活状态
timer-&timeout_func =//设置超时事件处理函数
timer-&parameter
=//超时事件处理函数的传入参数
timer-&timeout_tick = 0;//定时器的超时时间点初始化时为0
timer-&init_tick
=//置超时时间
/* initialize timer list */
rt_list_init(&(timer-&list));//初始化本身节点
动态创建定时器
* This function will create a timer
* @param name the name of timer
* @param timeout the timeout function
* @param parameter the parameter of timeout function
* @param time the tick of timer
* @param flag the flag of timer
* @return the created timer object
rt_timer_t rt_timer_create(const char *name,
void (*timeout)(void *parameter),
*parameter,
rt_uint8_t
struct rt_timer *
/* allocate a object */
timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);//动态分配定时器内核对象
if (timer == RT_NULL)
return RT_NULL;
_rt_timer_init(timer, timeout, parameter, time, flag);//调用上述的初始化接口
4.2 脱离和删除
* This function will detach a timer from timer management.
* @param timer the static timer object
* @return the operation status, RT_EOK on OK; RT_ERROR on error
rt_err_t rt_timer_detach(rt_timer_t timer)
register rt_base_
/* timer check */
RT_ASSERT(timer != RT_NULL);
/* disable interrupt */
level = rt_hw_interrupt_disable();//关中断
/* remove it from timer list */
rt_list_remove(&(timer-&list));//从定时器链表中移除
/* enable interrupt */
rt_hw_interrupt_enable(level);//开中断
rt_object_detach((rt_object_t)timer);//脱离内核对象
return -RT_EOK;
删除动态创建的定时器
* This function will delete a timer and release timer memory
* @param timer the timer to be deleted
* @return the operation status, RT_EOK on OK; RT_ERROR on error
rt_err_t rt_timer_delete(rt_timer_t timer)
register rt_base_
/* timer check */
RT_ASSERT(timer != RT_NULL);
/* disable interrupt */
level = rt_hw_interrupt_disable();//关中断
/* remove it from timer list */
rt_list_remove(&(timer-&list));//从定时器链表中移除
/* enable interrupt */
rt_hw_interrupt_enable(level);//开中断
rt_object_delete((rt_object_t)timer);//删除动态创建的定时器内核对象
return -RT_EOK;
4.3 启动定时器
* This function will start the timer
* @param timer the timer to be started
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
rt_err_t rt_timer_start(rt_timer_t timer)此接口已在上面介绍软件定时器模式时已有分析,这里就不再重复了。
4.4 停止定时器
* This function will stop the timer
* @param timer the timer to be stopped
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
rt_err_t rt_timer_stop(rt_timer_t timer)
register rt_base_
/* timer check */
RT_ASSERT(timer != RT_NULL);
if (!(timer-&parent.flag & RT_TIMER_FLAG_ACTIVATED))//如果定时器已经为非激活状态
return -RT_ERROR;
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer-&parent)));//使用钩子函数
/* disable interrupt */
level = rt_hw_interrupt_disable();//关中断
/* remove it from timer list */
rt_list_remove(&(timer-&list));//从定时器链表中移除
/* enable interrupt */
rt_hw_interrupt_enable(level);//开中断
/* change stat */
timer-&parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;//置非激活状态
return RT_EOK;
此接口是用来修改一个定时器的参数,如下代码:
* This function will get or set some options of the timer
* @param timer the timer to be get or set
* @param cmd the control command
* @param arg the argument
* @return RT_EOK
rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void *arg)
/* timer check */
RT_ASSERT(timer != RT_NULL);
switch (cmd)
case RT_TIMER_CTRL_GET_TIME://获取时间参数
*(rt_tick_t *)arg = timer-&init_
case RT_TIMER_CTRL_SET_TIME://修改时间参数
timer-&init_tick = *(rt_tick_t *)
case RT_TIMER_CTRL_SET_ONESHOT://修改定时器模式为单次触发定时器
timer-&parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
case RT_TIMER_CTRL_SET_PERIODIC://修改定时器为周期触发定时器
timer-&parent.flag |= RT_TIMER_FLAG_PERIODIC;
return RT_EOK;
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:786937次
积分:8783
积分:8783
排名:第1445名
原创:115篇
转载:11篇
评论:369条
阅读:36387
文章:19篇
阅读:186191
文章:26篇
阅读:99495
(2)(1)(2)(2)(5)(5)(1)(5)(4)(17)(2)(1)(15)(5)(9)(11)(2)(8)(10)(3)(12)(10)(1)finsh是RT-Thread的命令行外壳(shell),提供一套供用户在命令行的操作接口,主要用于调试、查看系统信息。在大部分嵌入式系统中,一般开发调试都使用硬件调试器和printf日志打印,在有些情况下,这两种方式并不是那么好用。比如对于RT-Thread这个多线程系统,我们想知道某个时刻系统中的线程运行状态、手动控制系统状态。如果有一个shell,就可以输入命令,直接相应的函数执行获得需要的信息,或者控制程序的行为。这无疑会十分方便。
finsh支持两种模式:
1. C语言解释器模式, 为行文方便称之为c-style;
2. 传统命令行模式,此模式又称为msh(module shell)。C语言表达式解释模式下, finsh能够解析执行大部分C语言的表达式,并使用类似C语言的函数调用方式访问系统中的函数及全局变量,此外它也能够通过命令行方式创建变量。在msh模式下,finsh运行方式类似于dos/bash等传统shell。
大致工作流程
一、finsh组件初始化函数finsh_system_init(),并且添加了INIT_COMPONENT_EXPORT(finsh_system_init),支持组件初始化;
这个函数会初始化finsh组件,包括一些finsh变量以及相关数据结构。
然后它会创建一个线程,代码如下:
result = rt_thread_init(&finsh_thread,
finsh_thread_entry, RT_NULL,
&finsh_thread_stack[0], sizeof(finsh_thread_stack),
FINSH_THREAD_PRIORITY, 10);
if (result == RT_EOK)
rt_thread_startup(&finsh_thread);
可以看到,线程函数是finsh_thread_entry,在下一节中我们将分析它具体工作流程。
二、void finsh_set_device(const char* device_name)函数为finsh设置终端设备,在stm32中主要设置串口设备为终端。该函数一般放在组件初始化函数rt_component_init()后面,因为要先完成finsh组件初始化才能设置终端设备。
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);
/* 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);
shell-&device =
rt_device_set_rx_indicate(dev, finsh_rx_ind);
这个函数为finsh组件设置使用的串口,从这个函数中我们可以总结出,如何使用串口设备。
调用rt_device_find使用设备的字符串名字查找设备,得到设备数据结构指针
调用rt_devcie_open打开设备,open_flag为RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX |RT_DEVICE_FLAG_STREAM
* 对finsh来说,还使用了rt_device_set_rx_indicate函数设置了一个回调函数finsh_rx_ind,它的作用我们后面会讨论
到这里设备就被打开了。
在serial.c中rt_hw_serial_isr()中有:
/* invoke callback */
if (serial-&parent.rx_indicate != RT_NULL)
rt_size_t rx_
/* 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);
上面计算得到rx_length,然后触发回调函数,也就是前面的finsh_rx_ind函数,即实际执行的是fins_rx_ind(device, rx_length)。
在shell.c中fins_rx_ind源码为:
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;
这个函数里只是简单的释放信号量。也就是说,当串口硬件上接收到一个字节,就会调用finsh_rx_ind函数来释放一个信号量。
三、finsh线程函数的工作流程概述
void finsh_thread_entry(void* parameter)
/* normal is echo mode */
shell-&echo_mode = 1;
#ifndef FINSH_USING_MSH_ONLY
finsh_init(&shell-&parser);
rt_kprintf(FINSH_PROMPT);
/* set console device as shell device */
if (shell-&device == RT_NULL)
#ifdef RT_USING_CONSOLE
shell-&device = rt_console_get_device();
RT_ASSERT(shell-&device);
rt_device_set_rx_indicate(shell-&device, finsh_rx_ind);
rt_device_open(shell-&device, (RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM | RT_DEVICE_FLAG_INT_RX));
RT_ASSERT(shell-&device);
/* wait receive */
if (rt_sem_take(&shell-&rx_sem, RT_WAITING_FOREVER) != RT_EOK) continue;
/* read one character from device */
while (rt_device_read(shell-&device, 0, &ch, 1) == 1)
* handle control 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;
else if (shell-&stat == WAIT_SPEC_KEY)
if (ch == 0x5b)
shell-&stat = WAIT_FUNC_KEY;
shell-&stat = WAIT_NORMAL;
else if (shell-&stat == WAIT_FUNC_KEY)
shell-&stat = WAIT_NORMAL;
if (ch == 0x41) /* up key */
#ifdef FINSH_USING_HISTORY
/* prev history */
if (shell-&current_history & 0)
shell-&current_history --;
shell-&current_history = 0;
/* copy the history command */
memcpy(shell-&line, &shell-&cmd_history[shell-&current_history][0],
FINSH_CMD_SIZE);
shell-&line_curpos = shell-&line_position = strlen(shell-&line);
shell_handle_history(shell);
else if (ch == 0x42) /* down key */
#ifdef FINSH_USING_HISTORY
/* next history */
if (shell-&current_history & shell-&history_count - 1)
shell-&current_history ++;
/* set to the end of history */
if (shell-&history_count != 0)
shell-&current_history = shell-&history_count - 1;
memcpy(shell-&line, &shell-&cmd_history[shell-&current_history][0],
FINSH_CMD_SIZE);
shell-&line_curpos = shell-&line_position = strlen(shell-&line);
shell_handle_history(shell);
else if (ch == 0x44) /* left key */
if (shell-&line_curpos)
rt_kprintf("\b");
shell-&line_curpos --;
else if (ch == 0x43) /* right key */
if (shell-&line_curpos & shell-&line_position)
rt_kprintf("%c", shell-&line[shell-&line_curpos]);
shell-&line_curpos ++;
/* handle CR key */
if (ch == '\r')
if (rt_device_read(shell-&device, 0, &next, 1) == 1)
else ch = '\r';
/* handle tab key */
else if (ch == '\t')
/* move the cursor to the beginning of line */
for (i = 0; i & shell-&line_ i++)
rt_kprintf("\b");
/* auto complete */
shell_auto_complete(&shell-&line[0]);
/* re-calculate position */
shell-&line_curpos = shell-&line_position = strlen(shell-&line);
/* handle backspace key */
else if (ch == 0x7f || ch == 0x08)
/* note that shell-&line_curpos &= 0 */
if (shell-&line_curpos == 0)
shell-&line_position--;
shell-&line_curpos--;
if (shell-&line_position & shell-&line_curpos)
rt_memmove(&shell-&line[shell-&line_curpos],
&shell-&line[shell-&line_curpos + 1],
shell-&line_position - shell-&line_curpos);
shell-&line[shell-&line_position] = 0;
rt_kprintf("\b%s
\b", &shell-&line[shell-&line_curpos]);
/* move the cursor to the origin position */
for (i = shell-&line_ i &= shell-&line_ i++)
rt_kprintf("\b");
rt_kprintf("\b \b");
shell-&line[shell-&line_position] = 0;
/* handle end of line, break */
if (ch == '\r' || ch == '\n')
#ifdef FINSH_USING_HISTORY
shell_push_history(shell);
#ifdef FINSH_USING_MSH
if (msh_is_used() == RT_TRUE)
rt_kprintf("\n");
msh_exec(shell-&line, shell-&line_position);
#ifndef FINSH_USING_MSH_ONLY
/* add ';' and run the command line */
shell-&line[shell-&line_position] = ';';
if (shell-&line_position != 0) finsh_run_line(&shell-&parser, shell-&line);
else rt_kprintf("\n");
rt_kprintf(FINSH_PROMPT);
memset(shell-&line, 0, sizeof(shell-&line));
shell-&line_curpos = shell-&line_position = 0;
/* it's a large line, discard it */
if (shell-&line_position &= FINSH_CMD_SIZE)
shell-&line_position = 0;
/* normal character */
if (shell-&line_curpos & shell-&line_position)
rt_memmove(&shell-&line[shell-&line_curpos + 1],
&shell-&line[shell-&line_curpos],
shell-&line_position - shell-&line_curpos);
shell-&line[shell-&line_curpos] =
if (shell-&echo_mode)
rt_kprintf("%s", &shell-&line[shell-&line_curpos]);
/* move the cursor to new position */
for (i = shell-&line_ i & shell-&line_ i++)
rt_kprintf("\b");
shell-&line[shell-&line_position] =
if (shell-&echo_mode)
rt_kprintf("%c", ch);
shell-&line_position ++;
shell-&line_curpos++;
if (shell-&line_position &= 80)
/* clear command line */
shell-&line_position = 0;
shell-&line_curpos = 0;
} /* end of device read */
函数主体依然是一个while(1)循环,这是显然的,因为finsh要不停的监听终端上输入。
if (rt_sem_take(&shell-&rx_sem, RT_WAITING_FOREVER) != RT_EOK)
即,如果串口上没有收到任何数据,并且串口缓冲区中也无数据,即shell&rx_sem信号量的值为0,那么这个函数会使finsh线程休眠,RTT内核会执行其他线程。
当串口收到数据,串口终端调用回调函数finsh_rx_ind函数来释放信号量,这会唤醒finsh线程,rt_sem_take函数会执行完毕,继续执行接下来的代码。
接下来的代码调用rt_device_read函数从串口数据缓冲池中读取一个字节。
然后判断所读取的到这个字节(判断上下左右四个按键所代表的字节)。
(1) 如果是'\r',即表示用户按下了回车键,再调用rt_device_read函数来读取一个字节,如果读到,则这将更新读到的字节,一般情况下,这个函数会返回0,即没有读到新的字节。
(2) 如果是'\t',即表示用户按下了TAB键,则调用finsh_auto_complete函数,这个函数做自动补全操作,也就是根据当前已输入的字符串,从finsh内部已注册的函数/变量中查找匹配字符串,如果找到则会在终端上自动补全。
(3) 如果是0x7f或者0x08 说明:查ascii码表可知,0x08 表示按下了backspace键,【0x7f表示按下了DEL键,这个不对劲,如何知道当我们按下了键盘按键时,串口都收到了什么数据呢?】 这表示用户期望删除已经输入的字符串,根据测试结果,发送&\0x08 \0x08&,可以实现退格。
(4) 如果收到了'\r'或者'\n',则表示用户按下了回车,希望处理这个命令,那么finsh_run_line函数被执行,这个函数会从从finsh已注册的函数/变量中匹配当前从终端里获取的字符串,如果匹配到,则执行对应的函数(若字符串为函数名)或者打印变量的值(若字符串为已变量)。
(5) 回显字符,也就是将刚才从串口接收到终端发送的字符发送到终端软件上显示出来。这就是说,我们在终端软件上输入字符,并且可以看到我们输入的字符,实际上是板子上的串口重新发回来显示的。在上面finsh的线程代码中,rt_device_write函数是在rt_kprintf中调用的。
然后回到(1),重复这个过程。
阅读(...) 评论()}

我要回帖

更多关于 js 回调函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信