由于工作的原因,涉及到了threadx操作系统,其实我个人觉得,threadx和ucosII十分的相识,只是threadx用于商业性质,而不像ucosII那样提供了源代码。
在消息队列,消息邮箱,事件标志组,信号量与ucosII有相似之处。我个人ucosII源代码自己研究过,对ucos的使用知道些。但是在设计多thread的能力还是欠缺,多个thread之间高效,简单的数据通信方面还有待提高。
Threadx是啥东西,是OS,是OS了事情就好办了,OS主要是用来做使用的?主要是用来在单CPU上面模拟多CPU,从而达到对多个task实时调度。同时提供多任务之间资源的同步与互斥使用及任务之间数据的传递。
其实例代码如下:main.c
#include "tx_api.h"
unsigned long my_thread_counter = 0;
TX_THREAD my_thread;
main( )
{
/* Enter the ThreadX kernel. */
tx_kernel_enter( ); //进入了内核,即启动了threadx操作系统,对threadx进行了调度。必须要mian函数的最后一个调用函数。
}
void tx_application_define(void *first_unused_memory) //用户应用程序定义启动
{
/* Create my_thread! */
tx_thread_create(&my_thread, "My Thread",
my_thread_entry, 0x1234, first_unused_memory, 1024,3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);
}
void my_thread_entry(ULONG thread_input)
{
/* Enter into a forever loop. */
while(1)
{
/* Increment thread counter. */
my_thread_counter++;
/* Sleep for 1 tick. */
tx_thread_sleep(1);
}
}
Task创建函数:
UINT tx_thread_create(
TX_THREAD *thread_ptr,
CHAR *name_ptr, VOID (*entry_function)(ULONG),
ULONG entry_input,
VOID *stack_start,
ULONG stack_size,
UINT priority,
UINT preempt_threshold,
ULONG time_slice,
UINT auto_start)
其中VOID (*entry_function)(ULONG)为任务处理函数。其task的创建各个OS之间基本类似。没有什么好说的,对于用户来说主要是:编写VOID (*entry_function)(ULONG)任务处理函数。其中threadx任务调度方式为:基于时间片。在创建任务时可以将多个任务的优先级priority设置为相同值不会影响threadx对task的调度。
但是由于基于时间片的调度方式,因此每一个task必须在必要的时候释放对cpu的独占使用。其方式有:tx_thread_sleep()和tx_thread_relinquish()
个人工作中使用到的API函数包括如下:互斥信号量,计数信号量,消息队列,定时器.事件组。其中Event Flags Group Services最为复杂,主要用于多任务之间的同步使用。
在arm的裸机代码中我们,经常会看到如下类似的代码:
B armExceptionHandlerUndefinedAsm ; Undefined handler
B armExceptionHandlerSwiAsm ; Software interrupt handler
B armExceptionHandlerPrefetchAsm ; Prefetch exception handler
B armExceptionHandlerAbortAsm ; Abort exception handler
B armExceptionHandlerReservedAsm ; Reserved exception handler
B _irq_handler ; IRQ interrupt handler
B _fiq_handler ; FIQ interrupt handler
上面描述了arm中的7中类型的异常处理汇编代码,一般主要关注的是IRQ和FIQ这俩种中断。
下面看看irq的处理函数。
IMPORT _tx_thread_context_save
IMPORT _tx_thread_context_restore
IMPORT _tx_thread_fiq_context_save
IMPORT _tx_thread_fiq_context_restore
IF :DEF:TX_ENABLE_IRQ_NESTING
IMPORT _tx_thread_irq_nesting_start
IMPORT _tx_thread_irq_nesting_end
ENDIF
IF :DEF:TX_ENABLE_FIQ_NESTING
IMPORT _tx_thread_fiq_nesting_start
IMPORT _tx_thread_fiq_nesting_end
ENDIF
/*
上面以 _tx_thread_*开头的函数,是由theadx OS提供的,主要用在进行中断处理时,保存中断的上下文,便于中断返回时
能够继续执行.以下内容来自ThreadXUserGuide.pdf
Like other ThreadX interrupt service routines, the low-level handler must
call _tx_thread_context_save before calling the simple driver ISR. After the driver ISR returns, the
low-level handler must call _tx_thread_context_restore.
The following small code segment is typical of most ThreadX managed ISRs. In most cases, this processing is in assembly language.
_application_ISR_vector_entry:
; Save context and prepare for
; ThreadX use by calling the ISR
; entry function.
CALL __tx_thread_context_save
; The ISR can now call ThreadX
; services and its own C functions
; When the ISR is finished, context
; is restored (or thread preemption)
; by calling the context restore
; function. Control does not return!
JUMP __tx_thread_context_restore
上面的英文描述,与实例代码格式大致相同
*/
IMPORT myIrqHander
;
;
AREA ||.text||, CODE, READONLY
;
;
EXPORT _irq_handler
EXPORT __tx_irq_processing_return
_irq_handler
;
; /* Jump to context save to save system context. */
B _tx_thread_context_save
__tx_irq_processing_return
;
; /* At this point execution is still in the IRQ mode. The CPSR, point of
; interrupt, and all C scratch registers are available for use. In
; addition, IRQ interrupts may be re-enabled - with certain restrictions -
; if nested IRQ interrupts are desired. Interrupts may be re-enabled over
; small code sequences where lr is saved before enabling interrupts and
; restored after interrupts are again disabled. */
;
;
; The myIrqHander is written without support for nested interrupts, so it
; is called before the nested interrupt support code.
;
BLX myIrqHander
;
; /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start
; from IRQ mode with interrupts disabled. This routine switches to the
; system mode and returns with IRQ interrupts enabled.
;
; NOTE: It is very important to ensure all IRQ interrupts are cleared
; prior to enabling nested IRQ interrupts. */
IF :DEF:TX_ENABLE_IRQ_NESTING
BL _tx_thread_irq_nesting_start
ENDIF
;
;
; /* Application IRQ handlers can be called here! */
;
; /* If interrupt nesting was started earlier, the end of interrupt nesting
; service must be called before returning to _tx_thread_context_restore.
; This routine returns in processing in IRQ mode with interrupts disabled. */
IF :DEF:TX_ENABLE_IRQ_NESTING
BL _tx_thread_irq_nesting_end
ENDIF
;
; /* Jump to context restore to restore system context. */
B _tx_thread_context_restore
; end of _irq_handler
其中myIrqHander为C代码的入口函数,在IRQ中断发生时执行上面的汇编代码,保存上下文,
最后跳转到 BLX myIrqHander,开始myIrqHander()函数的执行。
/**
*
* @Name: myIrqHander()
*
* @Description: This is the 'C' entry for the IRQ interrupt handler. This is
* called from assembly to process all ARM IRQ interrupts and all
* interrupts should be cleared before exiting this function.
*
* @note The myIrqHander() should reside in RAM.
*
*****************************************************************************/
typedef struct _interuptStatus
{
union _ARM_INTERRUPT_PINS_U
{
struct _ARM_INTERRUPT_PINS_BITS
{
U32 armInterrupt0 : 1;
U32 armInterrupt1 : 1;
U32 armInterrupt2 : 1;
U32 armInterrupt3 : 1;
U32 armInterrupt4 : 1;
U32 armInterrupt5 : 1;
U32 armInterrupt6 : 1;
U32 armInterrupt7 : 1;
} Bits;
U32 Dword;
}u;
}INTERUPT_STATUS;
INTERUPT_STATUS gInterruptStatus; //定义全局的中断状态,此struct状态根据具体的CPU来定义
INTERUPT_STATUS getIrqStatus()
{
return gInterruptStatus;
}
void myIrqHander() //中断处理函数
{
INTERUPT_STATUS irqStatus = getIrqStatus();
if(irqStatus.u.armInterrupt0)
{
/*
表示armInterrupt0 发生,假设为uart接收中断发生。表明有数据需要接收。
但对数据的消费处理由其他任务来完成。
这种情况下就需要一种机制来完成中断中对接收的数据和数据消费任务之间的通信。
比如现在从uart上面收到的数据保存在PtrSendBuffer[len],len为收到的数据长度。
在threadx或者ucosII中如果需要在中断或者任务之间传递数据,可以采用消息队列,消息邮箱机制。
消息队列可以用来传递大量数据,
*/
return (HALI_OS_STATUS) tx_queue_send((TX_QUEUE*)MyQueue,(VOID*)PtrSendBuffer,(ULONG)Timeout);
}
if(irqStatus.u.armInterrupt1)
{
//表示armInterrupt1 发生
}
if(irqStatus.u.armInterrupt2)
{
//表示armInterrupt2 发生
}
........................
}