分类目录归档:UCOS-II

UCOS中PENDSV和SYSTICK中断优先级

PENDSV和SYSTICK属于系统异常,定时器中断,串口中断这些属于外部中断。PENDSV和SYSTICK的中断优先级可以编程,一般要把PENDSV的优先级设置成最低。在很多CM3内核芯片的UCOS-II移植例子中,PENDSV和SYSTICK的优先级都设置成OxFF.都是最低优先级,因为PENDSV在中断向量表中排在SYSTICK前面,所以如果PENDSV,SYSTICK同时产生中断,PENDSV优先中断。

在UCOS中,如果产生中断嵌套,在退出最后一个中断时,OSIntExit()函数调用OSIntCtxSw()函数执行任务切换。在OSIntCtxSw()函数中,悬起PENDSV,然后在PENDSV中断函数中进行任务切换。

在PENDSV中断中,首先会关闭总中断,执行完任务切换后才开启总中断,避免PENDSV中断被其它中断打断,产生不可预料的结果。

SYSTICK的中断处理函数主要是调用函数OSTimeTick ()来完成系统在每个时钟节拍需要完成的工作,调用OSTimeTick()做了两件事情:一是把用来记录时间进程的计数器OSTime加1;二是遍历任务控制块链表中的所有任务控制块,把各个任务控制块中用来存放任务延时时限的OSTCBDly变量减1,并使该项为0,同时又不是被挂起的任务(即延时时间已到的任务)进入就绪状态。SYSTICK的中断优先级比PENDSV高还是低并没有什么影响。

UCOS中函数局部变量的地址分配

一个程序有4个任务,堆栈分配如下:

image

有个函数被多个任务调用

void Led_Disp()

{

uint16_t checksum = 0;

uint16_t i,length;

uint8_t temp[200];

…….

}

TaskStart 任务调用Led_Disp()函数时,在KEIL MDK仿真调试可以看到Led_Disp()内的局部变量temp[200]分配的起始地址是0X1000 1E64,在TaskStart任务栈内。

TaskEthernet任务调用Led_Disp()函数时,在KEIL MDK仿真调试可以看到Led_Disp()内的局部变量temp[200]分配的起始地址是0X1000 2ED4,在TaskEthernet任务栈内。

Led_Disp()内的一部分局部变量存在CPU寄存器中。在任务切换时,会保存CPU寄存器的值,因为CPU寄存器的值会被其它任务使用。Led_Disp()函数局部变量temp[200]使用的内存区域不用保存,因为其它的任务不会使用这部分区域。其它任务调用Led_Disp()需要给temp[200]分配内存时,会在自己的任务堆栈内重新分配。

在执行每个任务时,堆栈寄存器SP指向当前任务堆栈,当任务调用函数时,函数的局部变量在SP当前指向的内存区域分配。

Lpc1768移植UCOS-II(三)

三、移植OS_CPU_A.ASM

1、移植OS_CPU_SR_Save() OS_CPU_SR_Restore()

clip_image002

OS_CPU_SR_Save()保存当前的开关中断状态到R0,使用R0是因为编译器一般用R0传递参数。然后关中断。

Bx LR

从被OS_CPU_SR_Save()函数中断的程序处继续执行。进入临界段。

OS_CPU_SR_Restore()恢复R0中保存的开关状态,

Bx LR

从被OS_CPU_SR_Restore()函数中断的程序处继续执行。退出临界段。

这里使用的是PRIMASK,PRIMASK用于除能在 NMI和硬 fault之外的所有异常。

FAULTMASK能关闭所有异常和硬fault。

这里使用PRIMASK而不是FAULTMASK,是因为出现硬fault,必须要去处理,硬fault不能被关掉。

继续阅读

Lpc1768移植UCOS-II(二)

二、移植OS_CPU_C.C

1、头文件包含,

clip_image002

ucos_ii.h中包含了头文件os_cpu.h,os_cpu.h中有一段代码如下:

clip_image004

在OS_CPU_C.C中定义宏OS_CPU_GLOBALS,表示在编译OS_CPU_C.C时,定义os_cpu.h中的全局变量,其它C文件在引用os_cpu.h时不能定义OS_CPU_GLOBALS,编译时只是声明全局变量。

2、定义SYSTICK相关寄存器

clip_image006

3、钩子函数定义

void OSInitHookBegin (void)

void OSInitHookEnd (void)

void OSTaskCreateHook (OS_TCB *ptcb)

void OSTaskDelHook (OS_TCB *ptcb)

void OSTaskIdleHook (void)

void OSTaskReturnHook (OS_TCB *ptcb)

void OSTaskStatHook (void)

void OSTaskSwHook (void)

void OSTCBInitHook (OS_TCB *ptcb)

void OSTimeTickHook (void)

这些钩子函数必须定义,但可以是空函数。

clip_image008

在OSInitHookBegin()中把定义的CPU异常堆栈清零,并把栈顶赋给OS_CPU_ExceptStkBase。

继续阅读

Lpc1768移植UCOS-II(一)

UCOS-II在不同芯片上的移植,和处理器相关的文件有

OS_CPU.H

OS_CPU_A.ASM

OS_CPU_C.C

这几个文件的一些函数或代码要根据芯片和编译器的特效来修改。

UCOS_II的配置文件

OS_CFG.H

这个文件的内容要根据实际的使用情况来修改。

一, 移植OS_CPU.H

1、数据类型移植:

clip_image002

2、定义临界段模式为3

clip_image004

3、定义堆栈为递减模式

clip_image006

4、定义一个宏

clip_image008

宏OS_TASK_SW()用来实现从低优先级任务切换到高优先级任务。在UCOS-ii核心代码中调用。汇编函数OSCtxSw()在OS_CPU_A.ASM中定义。

5、MSP主堆栈定义

clip_image010

clip_image012

操作系统启用后,MSP变为OS_CPU_ExceptStkBase的值,不是复位时取的堆栈值,这个堆栈的初始化是在OSInit()中的OSInitHookBegin()进行的。

6、函数声明:

clip_image014

这些函数分别在OS_CPU_A.ASM和OS_CPU_C.C中定义。注意SysTick_Handler()就是SYSTICK的中断处理函数,要和startup_LPC17xx.s中的中断向量表定义的SYSTICK中断函数名称对应起来。

Lwip在UCOS-II上的移植(三)

移植网络驱动程序。在netif文件夹下有ethernetif.c,就是网络芯片的驱动程序,需要对照实际使用的网络芯片修改。下面是DM9000A的在lwip下的驱动的程序。

/* Define those to better describe your network interface. */

#define IFNAME0 ‘d’

#define IFNAME1 ‘m’

#define NIC_BASE 0x82000000

#define NIC_REG_ADDR (*(volatile u8_t *) 0x82000000)

/* if cmd link to system bus A2 */

#define NIC_REG_DATA_byte (*(volatile u8_t *) 0x82000004)

#define NIC_REG_DATA_word (*(volatile u16_t *) 0x82000004)

/* for dm9000a function setting */

#define Fix_Note_Address

#define DM9000A_FLOW_CONTROL

#define Rx_Int_enable

static void low_level_init(struct netif * netif);

static err_t low_level_output(struct netif * netif,struct pbuf *p);

static void ethernetif_input(struct netif *netif);

struct pbuf * low_level_input(struct netif *netif);

u16_t DM9000A_Phy_Read (u8_t offset);

void udelay(u32_t time)

{

//wait_1us(time);

while (–time > 0);

}

u8_t ior(u8_t reg_addr)

{

NIC_REG_ADDR = reg_addr;

return (NIC_REG_DATA_byte);

}

继续阅读

Lwip在UCOS-II上的移植(一)

LwIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。LwIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用,它只需十几KB的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的嵌入式系统中使用。

Lwip的官方下载地址:http://savannah.nongnu.org/projects/lwip/

ARM和网络芯片(例如DM9000A)通讯,DM9000A接收到网络传送的数据包后,产生中断,ARM 检测到中断后从DM9000A接收缓冲区读取数据包,利用Lwip的内部数据包管理函数,把接收的数据包放在一个pbuf结构中。Lwip判断当前的数据包是ARP包还是IP包,调用相应的函数进行处理。

Lwip实现有几种方式:1、raw\callback API 2、sequential API 3、socket API

采用第1种方式时,Lwip做为UCOS中的一个单独的任务。用户程序和协议栈之间的通讯通过回调函数实现。所有网络相关的处理都在同一个任务里完成,避免了任务的切换。在嵌入式操作系统中使用Lwip时,最好将Lwip任务设为系统的最高优先级,以提高协议栈的实时性。

另外两种方式没有使用过,这两种方式需要使用操作系统的信号量和邮箱机制。对一些小的嵌入式系统和工作频率不高的ARM芯片来说,频繁的切换任务时没有必要的。使用第一种回调方式实现,我觉得是比较合适的。

UCOS-II OS_CPU_IRQ_ISR函数移植

在有中断发生时,程序跳到OS_CPU_IRQ_ISR处执行。OS_CPU_IRQ_ISR函数在OS_CPU_A.S中。

clip_image002

在分析这个移植代码前,先了解下发生IRQ中断时,ARM的处理流程:

1、 会把当前的CPSR的值拷贝到SPSR_irq

2、 把PC的值拷贝到LR_irq

3、 强制进入IRQ异常模式

4、 强制进入ARM状态

5、 禁止IRQ中断

6、 PC=0X18,跳转到OS_CPU_IRQ_ISR

上面这些都是硬件自动完成的。

继续阅读

UCOS-II OSIntCtxSw函数移植

clip_image002

OSIntCtxSw()的代码的OSCtxSw()下半部分的代码基本上一样。因为OSIntCtxSw()是在OSINTEXIT()中调用的。也就是在中断服务程序中调用的。在进入中断服务时,寄存器值已经被保存到了被中断任务的堆栈里。所以OSIntCtxSw()里不需要再保存。

OSIntCtxSw()得到待运行任务的任务控制块地址,根据控制块地址得到待运行任务的堆栈指针。然后把待运行任务的堆栈内容放到寄存器中,实现任务切换功能。

UCOS-II的OSCtxSw函数移植

OSCtxSw()函数把被中止任务的断点指针和CPU的寄存器值保存到该任务的堆栈中,并从待运行任务的堆栈中得到其堆栈指针,把待运行任务堆栈中的存储的CPU通用寄存器的内容恢复到CPU的通用寄存器中,最后使CPU获得待运行任务的断点指针。

任务切换函数OSCtxSw()一般有2种方法实现。一种是软中断,周立功的LPC资料上都是这么用的。一种是用子程序调用,我现在做的这个项目就是这么实现的。

在OS_CPU.H中定义

#define OS_TASK_SW() OSCtxSw()

OSCtxSw()函数在OS_CPU_A.S中:

OSCtxSw

STMFD SP!, {LR} ;PC (1)

STMFD SP!, {R0-R12, LR} ;R0-R12 LR (2)

MRS R4, CPSR ;Push CPSR (3)

STMFD SP!, {R4}

LDR R4, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP; (4)

LDR R5, [R4] ;(5)

STR SP, [R5] ;(6)

;BL OSTaskSwHook ; OSTaskSwHook(); (7)

LDR R4, =OSPrioCur ; OSPrioCur = OSPrioHighRdy (8)

LDR R5, =OSPrioHighRdy ;(9)

LDRB R6, [R5] ;(10)

STRB R6, [R4] ;(11)

LDR R4, =OSTCBCur ; OSTCBCur = OSTCBHighRdy; ;(12)

LDR R6, =OSTCBHighRdy ;(13)

LDR R6, [R6] ;(14)

STR R6, [R4] ;(15)

LDR SP, [R6] ; SP = OSTCBHighRdy->OSTCBStkPtr; ;(16)

LDMFD SP!, {R4} ;POP CPSR ;(17)

MSR CPSR_cxsf, R4 ;(18)

LDMFD SP!, {R0-R12, LR, PC} ;(19)

OS_Sched (void)函数在OS_CORE.C中:

void OS_Sched (void)

{

#if OS_CRITICAL_METHOD == 3

OS_CPU_SR cpu_sr = 0;

#endif

OS_ENTER_CRITICAL();

if (OSIntNesting == 0) {

if (OSLockNesting == 0) {

OS_SchedNew();

if(OSPrioHighRdy!=OSPrioCur) {

OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

#if OS_TASK_PROFILE_EN > 0

OSTCBHighRdy->OSTCBCtxSwCtr++;

#endif

OSCtxSwCtr++; OS_TASK_SW();

}

}

}

OS_EXIT_CRITICAL();

}

在ADS中通过汇编语言可以看到,OS_TASK_SW();语句对应的汇编语句是 BL OSCtxSW

继续阅读

第 1 页,共 2 页12