一個極簡單的RTOS代碼 STM32 借鑑UCOSII 彙編部分有詳細註釋

;/*********************** (C) COPYRIGHT 2010 Libraworks *************************
;* File Name	: os_cpu_a.asm 
;* Author		: Librae 
;* Version		: V1.0
;* Date			: 06/10/2010
;* Description	: μCOS-II asm port	for STM32
;*******************************************************************************/

		EXTERN CPU_ExceptStkBase
		EXTERN p_TCB_Cur
		EXTERN p_TCBHighRdy
        ;IMPORT  OSPrioCur
        ;IMPORT  OSPrioHighRdy
        ;IMPORT  OSTCBCur
        ;IMPORT  OSTCBHighRdy
      
           
        EXPORT  OSStartHighRdy               
        EXPORT  OSCtxSw
        EXPORT  OSIntCtxSw
		EXPORT  OS_CPU_SR_Save                                      ; Functions declared in this file
    	EXPORT  OS_CPU_SR_Restore       
        EXPORT  PendSV_Handler
        	
     
NVIC_INT_CTRL   	EQU     0xE000ED04  ; 中斷控制寄存器
NVIC_SYSPRI14    	EQU     0xE000ED22  ; 系統優先級寄存器(2)
NVIC_PENDSV_PRI 	EQU     0xFF  ; PendSV中斷和系統節拍中斷
                                        ; (都爲最低,0xff).
NVIC_PENDSVSET  	EQU     0x10000000  ; 觸發軟件中斷的值.


		PRESERVE8 
		
		AREA    |.text|, CODE, READONLY
        THUMB 
    
           

;********************************************************************************************************
;                                   CRITICAL SECTION METHOD 3 FUNCTIONS
;
; Description: Disable/Enable interrupts by preserving the state of interrupts.  Generally speaking you
;              would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
;              disable interrupts.  'cpu_sr' is allocated in all of uC/OS-II's functions that need to
;              disable interrupts.  You would restore the interrupt disable state by copying back 'cpu_sr'
;              into the CPU's status register.
;
; Prototypes :     OS_CPU_SR  OS_CPU_SR_Save(void);
;                  void       OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
;
;
; Note(s)    : 1) These functions are used in general like this:
;
;                 void Task (void *p_arg)
;                 {
;                 #if OS_CRITICAL_METHOD == 3          /* Allocate storage for CPU status register */
;                     OS_CPU_SR  cpu_sr;
;                 #endif
;
;                          :
;                          :
;                     OS_ENTER_CRITICAL();             /* cpu_sr = OS_CPU_SaveSR();                */
;                          :
;                          :
;                     OS_EXIT_CRITICAL();              /* OS_CPU_RestoreSR(cpu_sr);                */
;                          :
;                          :
;                 }
;********************************************************************************************************

;CPSID  I  ;PRIMASK=1,  ;關中斷
;CPSIE  I  ;PRIMASK=0,  ;開中斷
;CPSID  F  ;FAULTMASK=1,  ;關異常
;CPSIE  F  ;FAULTMASK=0  ;開異常

OS_CPU_SR_Save
    MRS     R0, PRIMASK  	;讀取 常規異常屏蔽寄存器(PRIMASK) 到R0,R0爲返回值 
    CPSID   I				;PRIMASK=1,關中斷(NMI和硬件FAULT可以響應)
    BX      LR			    ;返回

OS_CPU_SR_Restore
    MSR     PRIMASK, R0	   	;讀取R0到PRIMASK中,R0爲參數
    BX      LR				;返回


;/**************************************************************************************
;* 函數名稱: OSStartHighRdy
;*
;* 功能描述: 使用調度器運行第一個任務
;* 
;* 參    數: None
;*
;* 返 回 值: None
;**************************************************************************************/  

OSStartHighRdy
        LDR     R4, =NVIC_SYSPRI14      ; set the PendSV exception priority
        LDR     R5, =NVIC_PENDSV_PRI
        STR     R5, [R4]

        MOV     R4, #0                 ; set the PSP to 0 for initial context switch call
        MSR     PSP, R4

		LDR     R4, =CPU_ExceptStkBase         ; 中斷嵌套時有用   主堆棧
        LDR     R5, [R4]
        MSR     MSP, R5 			;讀取主堆棧指針到MSP,就是設置MSP,初始化的意思

                                       ;切換到最高優先級的任務
        LDR     R4, =NVIC_INT_CTRL     ;rigger the PendSV exception (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]

        CPSIE   I                      ;enable interrupts at processor level 中斷可以運行了 ,然後就進入pendsv中斷要了
OSStartHang
        B       OSStartHang            ;should never get here

;/**************************************************************************************
;* 函數名稱: OSCtxSw
;*
;* 功能描述: 任務級上下文切換         
;*
;* 參    數: None
;*
;* 返 回 值: None
;***************************************************************************************/
  
OSCtxSw
		PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL  	;觸發PendSV異常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]				;觸發了pendsv異常,切換的事情在pendsv異常裏面處理
		POP     {R4, R5}
        BX      LR

;/**************************************************************************************
;* 函數名稱: OSIntCtxSw
;*
;* 功能描述: 中斷級任務切換
;*
;* 參    數: None
;*
;* 返 回 值: None
;***************************************************************************************/

OSIntCtxSw
		PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL      ;觸發PendSV異常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]
		POP     {R4, R5}				;同上一個函數一樣,不過,是從中斷裏面切出來的,應該是這樣,,,
        BX      LR
        NOP

;/**************************************************************************************
;* 函數名稱: OSPendSV
;*
;* 功能描述: OSPendSV is used to cause a context switch.
;*
;* 參    數: None
;*
;* 返 回 值: None
;***************************************************************************************/
;真正的上下文切換是在這裏面是下面的彙編函數實現的。
;
PendSV_Handler
    CPSID   I                                                   ; Prevent interruption during context switch   硬中斷沒關,意思是除了嚴重的問題,還是可以有中斷來處理的
    MRS     R0, PSP                                             ; PSP is process stack pointer 如果在用PSP堆棧,則可以忽略保存寄存器,參考CM3權威中的雙堆棧-白菜注
    CBZ     R0, PendSV_Handler_Nosave		                    ; Skip register save the first time
	;CBZ :	比較,如果結果爲 0 就轉移到 PendSV_Handler_Nosave函數  (只能跳到後面的指令——譯註)
    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
	;先減去八個寄存器的共32個字節,然後把8個寄存器的值裝進去。
    STM     R0, {R4-R11}
	
    LDR     R1, =p_TCB_Cur                                      ; OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out

; At this point, entire context of process has been saved
;注意,,,這裏並沒有跳轉指令,這是一個函數啊,接着往下運行,只是第一次運行這個函數時,直
;接跳到了PendSV_Handler_Nosave,後面就不會跳了,而是一直往下運行
PendSV_Handler_Nosave

    LDR     R0, =p_TCB_Cur                                      ; OSPrioCur = OSPrioHighRdy;
    LDR     R1, =p_TCBHighRdy
    LDR     R2, [R1] ;R2裏面是最高優先級任務控制塊的地址
    STR     R2, [R0] ;R2裏面是最高優先級任務控制塊的地址給到R0,就是說,R0裏面裝的就是最高優先級任務的地址

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
	;上面這一步就是說取出最高優先級任務地址裏面裝的值,由於任務控
	;制塊是一個結構體,裏面第一個地址裝的值就是該任務的堆棧指
	;針,所以說堆棧指針要放在最前面,就是取地址的地址,R0裏面放的就是最高優先級任務的堆棧指針
	LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack
	;LDM 是多個數據的加載指令 ,有幾個寄存器不用指定它加載,因爲處理器自己完成這個事情,不用你管
	;上面這條指令就是加載新的任務上下文所做的事情,
	;注意,這裏並沒有先保存上一個任務的上下文,原因是,這是第一次運行,並沒有上一個任務,而
	;且PendSV_Handler_Nosave只是PendSV_Handler函數的一部分,別以爲是兩個函數,,,
    ADDS    R0, R0, #0x20
	;上面的0x20等於32,就是加載的8個寄存器的值
    MSR     PSP, R0                                             ; Load PSP with new process SP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context

 end  

 

#include "timer.h"
#include "led.h"
#include "OS_Need.h"

//通用定時器3中斷初始化
//這裏時鐘選擇爲APB1的2倍,而APB1爲36M
//arr:自動重裝值。
//psc:時鐘預分頻數
//這裏使用的是定時器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //時鐘使能
	
	//定時器TIM3初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器週期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作爲TIMx時鐘頻率除數的預分頻值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設置時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據指定的參數初始化TIMx的時間基數單位
 
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中斷,允許更新中斷

	//中斷優先級NVIC設置
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中斷
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先佔優先級0級
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //從優先級3級
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器


	TIM_Cmd(TIM3, ENABLE);  //使能TIMx					 
}
//定時器3中斷服務程序
void TIM3_IRQHandler(void)   //TIM3中斷
{
	char i=0;
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //檢查TIM3更新中斷髮生與否
	{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中斷標誌 
		for(i=0;i<TCB_Task_NUM;i++)
		{
			if(TCB_Task[i].Dly > 0)
			{
				TCB_Task[i].Dly--;
				if(TCB_Task[i].Dly == 0)
				{
					OSSetPrioRly(i);
				}
			}
		}
	}
}
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
 #include "timer.h"
 
uint32_t OSRdyTbl;//此係統有32個優先級,同時只能有32個任務,每個任務和優先級對應
uint32_t OSPrioHighRdy;
uint32_t OSPrioCur;
struct os_tcb{ //任務控制塊結構體
	uint32_t *StkPtr;
	uint32_t Dly;
	uint32_t Prio;//沒有用到
};
typedef struct os_tcb TCB;

TCB *p_TCB_Cur;
TCB *p_TCBHighRdy;

#define OS_EXCEPT_STK_SIZE (1024*10)
#define TASK_1_STK_SIZE      1024
#define TASK_2_STK_SIZE      1024
#define OS_IdleTask_STK_SIZE 1024
uint32_t TASK_1_STK[TASK_1_STK_SIZE];
uint32_t TASK_2_STK[TASK_2_STK_SIZE];
uint32_t OS_IdleTask_Stk[OS_IdleTask_STK_SIZE];

TCB TCB_Task1,TCB_Task2;
#define TCB_Task_NUM 32
TCB TCB_Task[TCB_Task_NUM];//最對創建TCB_Task_NUM個任務
uint32_t TCB_Task_NUM_Counts=0;//記錄現在有幾個任務

uint32_t cpu_sr;
#define  OS_ENTER_CRITICAL()  {cpu_sr = OS_CPU_SR_Save();}
#define  OS_EXIT_CRITICAL()   {OS_CPU_SR_Restore(cpu_sr);}
uint32_t   OS_CPU_SR_Save(void);
void       OS_CPU_SR_Restore(uint32_t	cpu_sr);

void       OSCtxSw(void);
void       OSIntCtxSw(void);
void       OSStartHighRdy(void);

void       OSPendSV(void);
void OSTimeDly(uint32_t ticks);
void OSSched(void);
void OS_TASK_Init(void);
void task_1(void);
void task_2(void);
void OS_IdleTask(void);



void OSSetPrioRly(uint16_t prio)
{
	OSRdyTbl |= 0x01<<prio;
}

void OSDelPrioRly(uint16_t prio)
{
	OSRdyTbl &= ~(0x01<<prio);
}
 
void OSGetHighRdy(void)
{//此函數在優先級表OSRdyTbl中選出最高的優先級
	uint32_t OSNextTaskPrio;
	for(OSNextTaskPrio = 0;(OSNextTaskPrio<32) && !(OSRdyTbl&(0x01<<OSNextTaskPrio));OSNextTaskPrio++)
		;
	OSPrioHighRdy = OSNextTaskPrio;
}


//以下是任務控制塊

//以下是堆棧
uint32_t CPU_ExceptStk[OS_EXCEPT_STK_SIZE];
uint32_t *CPU_ExceptStkBase;

//建立一個任務
void Task_Create (void (*task)(void ), uint32_t *p_stk ,uint16_t prio)
{
    uint32_t *stk;
    stk       = p_stk;                            /* Load stack pointer                                 */
                                                 /* Registers stacked as if auto-saved on exception    */
    *(stk)    = (uint32_t)0x01000000L;             /* xPSR                                               */
    *(--stk)  = (uint32_t)task;                    /* Entry Point                                        */
    *(--stk)  = (uint32_t)0xFFFFFFFEL;             /* R14 (LR) (init value will cause fault if ever used)*/
    *(--stk)  = (uint32_t)0x12121212L;             /* R12                                                */
    *(--stk)  = (uint32_t)0x03030303L;             /* R3                                                 */
    *(--stk)  = (uint32_t)0x02020202L;             /* R2                                                 */
    *(--stk)  = (uint32_t)0x01010101L;             /* R1                                                 */
    *(--stk)  = (uint32_t)0x01010101L;                   /* R0 : argument                                      */

                                                 /* Remaining registers saved on process stack         */
    *(--stk)  = (uint32_t)0x11111111L;             /* R11                                                */
    *(--stk)  = (uint32_t)0x10101010L;             /* R10                                                */
    *(--stk)  = (uint32_t)0x09090909L;             /* R9                                                 */
    *(--stk)  = (uint32_t)0x08080808L;             /* R8                                                 */
    *(--stk)  = (uint32_t)0x07070707L;             /* R7                                                 */
    *(--stk)  = (uint32_t)0x06060606L;             /* R6                                                 */
    *(--stk)  = (uint32_t)0x05050505L;             /* R5                                                 */
    *(--stk)  = (uint32_t)0x04040404L;             /* R4                                                 */
		
    TCB_Task[prio].StkPtr = (stk);
		OSSetPrioRly(prio);
		//TCB_Task[TCB_Task_NUM_Counts].Prio =  prio;
		//TCB_Task_NUM_Counts++;//任務數量計數器加1 , TCB_Task_NUM_Counts 必須不能大於TCB_Task_NUM
}




int main(void)
{	
	delay_init();	    //延時函數初始化	 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶佔優先級,2位響應優先級	
	LED_Init();		  	//初始化與LED連接的硬件接口
	uart_init(9600);
	TIM3_Int_Init(49,7199);//10Khz的計數頻率,計數到50爲5ms  
	
	printf("this is the begin of the task ...\ntask is running ...\n");
	CPU_ExceptStkBase = CPU_ExceptStk + OS_EXCEPT_STK_SIZE - 1;
	OS_ENTER_CRITICAL();
	Task_Create(   task_1 , &TASK_1_STK[TASK_1_STK_SIZE-1] ,7);
	Task_Create(   task_2 , &TASK_2_STK[TASK_2_STK_SIZE-1] ,6);
	OS_EXIT_CRITICAL();
	//p_TCBHighRdy = &TCB_Task1;
	//OSStartHighRdy();
	OS_TASK_Init();
	while(1);
}
/*
void Task_Switch(void)
{
	if(p_TCB_Cur == &TCB_Task1)
		p_TCBHighRdy = &TCB_Task2;
	else
		p_TCBHighRdy = &TCB_Task1;
	OSCtxSw();
}*/

void OSTimeDly(uint32_t ticks)
{
	if(ticks > 0)
	{
		OS_ENTER_CRITICAL();
		OSDelPrioRly(OSPrioCur);
		TCB_Task[OSPrioCur].Dly = ticks;
		OS_EXIT_CRITICAL();
		OSSched();
	}
}
void OS_TASK_Init(void)
{
	OS_ENTER_CRITICAL();
	Task_Create(OS_IdleTask , &OS_IdleTask_Stk[OS_IdleTask_STK_SIZE-1],TCB_Task_NUM-1);//將空閒任務設置爲最低優先級
	OS_EXIT_CRITICAL();
	OSGetHighRdy();
	OSPrioCur = OSPrioHighRdy;
	p_TCBHighRdy = &TCB_Task[OSPrioHighRdy];
	OSStartHighRdy();
}
void OSSched(void)
{
	OSGetHighRdy();
	if(OSPrioHighRdy != OSPrioCur)
	{
		p_TCBHighRdy = &TCB_Task[OSPrioHighRdy];
		OSPrioCur = OSPrioHighRdy;
		OSCtxSw();
	}
}
void task_1(void){
	while(1)
	{
		printf("task 1\n");
		//Task_Switch();
		OSTimeDly(20);
	}
}
void task_2(void){
	while(1)
	{
		printf("task 2\n");
		//Task_Switch();
		OSTimeDly(20);
	}
}

void OS_IdleTask(void)
{

	while(1){
	
		OSSched();
	}
}

 

 

https://www.cnblogs.com/WeyneChen/p/4891885.html    這個也是一種極簡功能RTOS的實現方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章