;/*********************** (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的實現方式。