标签归档:ucos-ii

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中断函数名称对应起来。

STM32+UCOS-II实现临界区不关闭重要中断

在ARM7内核芯片中,在临界区不关闭快速中断,可以避免丢失重要的中断信号。STM32要实现类似功能,需要用到BASEPRI寄存器。

比如一个产品需要外接维根读头,STM32利用外部中断解码。如果在临界区关闭所有中断,那么在刷卡时,有可能丢掉部分外部中断信号,导致解码错误。

系统简化中断设置,只设置抢占式优先级,不要子优先级。抢占式优先级(0-15),子优先级都为0.

UCOS的SYSTICK中断及PENDSV中断设定在最低优先级

#define ConfigKERNEL_INTERRUPT_PRIORITY_PENSV (15)

#define ConfigKERNEL_INTERRUPT_PRIORITY_SYSTICK (14)

Pendsvde 的优先级在OS_CPU_A.S的OSStartHighRdy中设置,优先级最低为15.

Systick在core_cm3.h的systick_config()中设置,pendsv的优先级寄存器0Xe000ed22的值为0Xf0,抢占式优先级为15,亚优先级为0。Systick的优先级寄存器0XE000ED23的值为0Xe0,抢占式优先级为14,亚优先级为0.

维根读头的外部中断设置为0号抢占式优先级。

如果OS_ENTER_CRITIAL()函数,即OS_CPU_A.S中的OSCPUSAVESR如下

MRS R0,PRIMASK

CPSID I

BX LR

那么程序进入临界区后,所有外部中断都会关闭,包括维根读头使用的外部中断0,导致接收维根数据出错。这时应该使用BASEPRI寄存器。

BASEPRI寄存器最多有9位(由表达优先级的位数决定,STM32F103中,高4位有效),它定义了被屏蔽优先级的阈值。当它设定成某个值后,所有优先级大于等于此值的中断都被关(优先级号越大,优先级越低)。小于此值的中断不会被关闭。若设成0,则不关闭任何中断,0是缺省值。

此处要保证维根读头使用的中断优先级0不被关闭,所以要设置BASEPRI高4位为1.

MASK_INTERRUPT_PRIORITY EQU 0X01

OSCPUSAVESR

MRS R0,BASEPRI

MOVS R1,#(MASK_INTERRUPT_PRIORITY<< 4)

MSR BASEPRI,R1

BX LR

OSCPURESTORESR

MSR BASEPRI,R0

BX LR

实际测试,这样改写后,接收的维根读头数据每次都正确。

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文件夹放到项目文件中,项目文件夹入下图:

clip_image002

Lwip包含的功能:

1、 ARP协议,以太网地址解析协议

2、 IP协议,包括IPv4和IPv6,支持IP分片与重装,支持多网络接口下数据包转发

3、 IGMP协议,用于网络组管理,可以实现多播数据的接收

4、 UDP协议,用户数据报协议

5、 TCP协议,支持TCP拥塞控制,RTT估计,快速恢复与重传等

6、 提供三种用户编程接口方式:raw/callback API,sequential API,BSD-style socket API

7、 DNS,域名解析

8、 SNMP,简单网络管理协议

9、 DHCP,动态主机配置协议

10、 AUTOIP,IP地址自动配置

11、 PPP,点对点协议,支持PPPoE

下载的LWIP源代码包括3个文件夹,doc,src和test。doc 文件夹包含了几个与协议栈使用相关的文档。Test文件夹下是lwip提供的一些协议栈内核测试程序,移植不会用到。Src文件夹里就是lwip的所有源代码,需要放到项目文件中,上图中的lwip文件夹下就是src文件夹里的内容。

Api文件包含了lwip的sequential API和socket API两类接口函数及实现相关的源代码,要使用这两种类型的API,需要底层操作系统的支持。Core文件夹是lwip的内核源代码,内核源代码可以单独运行,且不需要操作系统的支持。Include主要包含整个协议栈使用的头文件。Netif主要包含了与底层网络接口相关的文件。

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 OSStartHighRdy函数移植

UCOS-II启动多任务环境的函数是OSStart();用户在调用OSStart()前,必须已经建立了一个或多个任务。OSStart()最终调用OSStartHighRdy()运行多任务启动前优先级最高的任务。OSStartHighRdy()代码是用汇编写的,在文件OS_CPU_A.S中。

函数如下:

clip_image002

代码分析如下:

1:进入系统模式,并关闭IRQ,FIQ中断

2:调用OSTaskSwHook(),这是钩子函数,一般为空,用户可以视需要自己添加内容。也可以不调用。

3、LDR R4, =OSRunning的LDR是伪指令,‘=’是LDR作为伪指令和标准指令的区别。

这条指令的作用是把变量OSRunning的地址送给R4。OSRunning是UCOS系统己经启动的一个标识,它在调用UCOS中的OSStart时被置为1。

4、OS_EXT OS_TCB *OSTCBHighRdy;

OSTCBHighRdy是一个指针,指向最高优先级任务的任务控制块,任务控制块的第一个内容是任务的堆栈指针,所以OSTCBHighRdy地址存的值也就是任务控制块的堆栈指针。

LDR R4, =OSTCBHighRdy

LDR R4, [R4]

LDR SP, [R4]

这3条指令是把最高优先级任务的堆栈指针送到ARM的SP寄存器中

5、LDMFD SP!, {R4}

从任务堆栈中取出最后压入堆栈的CPSR的值,送到R4,!表示SP自动加4

MSR CPSR_cxsf, R4

c - control field mask byte (PSR[7:0])
x - extension field mask byte (PSR[15:8])
s - status field mask byte (PSR[23:16)
f - flags field mask byte (PSR[31:24]).

把R4的值保存到CPSR中。

6、LDMFD SP!, {R0-R12, LR, PC}

把堆栈的数据送到R0-R12,LR,PC,系统开始执行最高优先级任务。

第 1 页,共 2 页12