娇小w搡bbbb搡bbb,《第一次の人妻》,中国成熟妇女毛茸茸,边啃奶头边躁狠狠躁视频免费观看

手動實現51單片機函數切換

發布者:SereneSpirit最新更新時間:2024-08-22 來源: cnblogs關鍵字:51單片機 手機看文章 掃描二維碼
隨時隨地手機看文章

一、前言

為什么要研究單片機函數切換的過程?實際上是我在20年暑假時給51單片機寫了一個簡單的實時操作系統,具有簡單的搶占式內核調度功能,雖然很簡單,但我還是想把實現的過程分享出來,這篇文章是其中的內容之一,有興趣的同學可以先了解一下,點個關注收藏,后面持續更新!

二、函數切換原理

在使用C語言編寫51單片機的程序時,如果我們在函數一中調用另外一個函數,只需要添加一行 函數名+括號及參數 就可以執行另外一個函數,就就像下面的例子:

int main(void){
	int a=0;
	Fun1(a);
	Fun2(a);		
    return 0;}

在main函數中直接調用Fun1,Fun2函數,然后程序就會跳轉。但是問題來了,函數是怎么跳轉的呢?在函數跳轉的過程中51單片機的寄存器是如何變換的呢?

實際上,函數的切換過程其實就是將當前函數的運行狀態和數據以及返回地址等保存到堆棧,然后讀取新函數的運行狀態和數據,PC(程序計數器)再跳轉到調用函數的地址執行對應的函數,這些操作其實都是在對51單片機的寄存器進行操作,具體用到的幾個寄存器如下:

寄存器功能
R0-R7工作寄存器R0~R7:存儲當前程序的 “環境“
DPH數據地址指針(高8位):DPH和DPL組合在一起使用,用它來訪問外部數據存儲器中的任一單元,也可以作為通用寄存器來用
DPL數據地址指針(低8位):DPH和DPL組合在一起使用,用它來訪問外部數據存儲器中的任一單元,也可以作為通用寄存器來用
PSW程序狀態字:里面放了CPU工作時的很多狀態,可以了解CPU的當前狀態
BB寄存器:在做乘、除法時放乘數或除數
ACC累加器:運算寄存器
SP堆棧指針:指向堆棧操作的棧頂地址,是8位計數器
PC程序計數器:指向下一條待執行的指令

下面我們來用匯編手動編寫一個函數切換函數,然后在定時器中斷中調用,不停的切換兩個函數,編寫前先了解一下切換框架和使用到的匯編代碼

  • POP出棧指令

    彈出堆棧數據到data,然后SP指針減一

    POP data
  • PUSH壓棧指令

    先把SP指針加一,然后將data數據壓入堆棧

    PUSH data
  • RET返回指令

    把彈出堆棧兩個字節的數據到PC,指向下一個程序的執行地址

三、函數切換代碼實現

函數代碼我們使用51單片機作為運行平臺,在主函數中通過切換函數1切換到函數1,函數1是一個死循環,之后我們在函數1里面調用函數切換2切換到函數2運行,函數2延時一段時間后再切換回1,一直循環下去;代碼如下:

定義用到的函數:

void task1(void); //函數1

void task2(void); //函數2

void delay(unsigned short time);//延時函數


定義用到的變量和類型

unsigned char a; //函數一運行的標志

unsigned char b;  //函數二運行的標志

unsigned char task1_stack[20];    //函數堆棧

unsigned char task2_stack[20];    //函數堆棧

//聲明函數控制塊結構體

typedef struct

{         

unsigned char Task_SP;      //函數堆棧指針

}TASK_TCB;

//定義TCB

TASK_TCB task1_tcb;

TASK_TCB task2_tcb;


編寫main函數主體初始化,此處定義兩個函數控制塊tcb,用來存放函數的堆棧指針(函數的堆棧其實就是一個數組,用來保存函數的運行數據),然后我們在將函數的入口地址保存在堆棧的最低兩位,接著將SP指針向上偏移14位,因為我們要保存的寄存器加起來有13位,同時在一開始要把函數入口保存在堆棧所以是14位

而切換到函數的時候是要先從函數堆棧出棧,所以預先偏移14位地址,main函數代碼如下:

void main(void){
    //保存堆棧指針和函數入口
	task1_tcb.Task_SP = task1_stack;
	task1_stack[0]= (unsigned char)task1;
	task1_stack[0]= (unsigned char)task1>>8;
    //偏移堆棧
	task1_tcb.Task_SP += 14;
    //保存堆棧指針和函數入口
	task2_tcb.Task_SP = task2_stack;
	task2_stack[0]= (unsigned char)task2;
	task2_stack[0]= (unsigned char)task2>>8;
    //偏移堆棧
	task2_tcb.Task_SP += 14;
    //切換到函數1
	Task_Sched_1();
	while(1);}

編寫函數1和函數2實體

void task1(void) {
	while(1)
	{
		a=1;
		b=0;
		delay(100);		//延時
		Task_Sched_2();//切換到函數2
	}}void task2(void){
	while(1)
	{
		a=0;
		b=1;
		delay(100);//延時
		Task_Sched_1();//切換到函數1
	}}

編寫函數切換函數

切換到函數1

void Task_Sched_1(void){
		__asm PUSH ACC       //保護當前寄存器,壓棧
		__asm PUSH B
		__asm PUSH PSW
		__asm PUSH DPL
		__asm PUSH DPH
		__asm PUSH 0         //0-7為工作寄存器
		__asm PUSH 1
		__asm PUSH 2
		__asm PUSH 3
		__asm PUSH 4
		__asm PUSH 5
		__asm PUSH 6
		__asm PUSH 7
		SP = (task1_tcb.Task_SP);
		__asm POP 7         //恢復目標函數寄存器
		__asm POP 6
		__asm POP 5
		__asm POP 4
		__asm POP 3
		__asm POP 2
		__asm POP 1
		__asm POP 0
		__asm POP DPH
		__asm POP DPL
		__asm POP PSW
		__asm POP B
		__asm POP ACC}

切換到函數2

void Task_Sched_2(void){
		__asm PUSH ACC       //保護當前寄存器,壓棧
		__asm PUSH B
		__asm PUSH PSW
		__asm PUSH DPL
		__asm PUSH DPH
		__asm PUSH 0         //0-7為工作寄存器
		__asm PUSH 1
		__asm PUSH 2
		__asm PUSH 3
		__asm PUSH 4
		__asm PUSH 5
		__asm PUSH 6
		__asm PUSH 7
		SP = (task2_tcb.Task_SP);
		__asm POP 7         //恢復目標函數寄存器
		__asm POP 6
		__asm POP 5
		__asm POP 4
		__asm POP 3
		__asm POP 2
		__asm POP 1
		__asm POP 0
		__asm POP DPH
		__asm POP DPL
		__asm POP PSW
		__asm POP B
		__asm POP ACC}

注意此處的切換函數使用匯編編譯,主要內容就是保存當前函數的運行環境到函數堆棧,然后從下一個函數的堆棧讀取其運行環境,切換代碼我寫在一個os.c文件里面,編譯前需要匯編編譯,步驟如下:

右擊文件->options

20210808163102

開啟嵌入匯編程序,使C語言中可以編譯匯編代碼,加__asm聲明一下是匯編就行

20210808163124

四、實驗現象

函數1中把a取1,b取0,而函數2相反,當這兩個函數交叉運行時a和b的波形應該相反,所以仿真后結果如下,手動切換函數完成

20210808161548


關鍵字:51單片機 引用地址:手動實現51單片機函數切換

上一篇:51單片機定時器、串口、中斷
下一篇:51單片機串口應用實例(匯編)

推薦閱讀最新更新時間:2025-06-07 23:41

89c51單片機和89s51單片機的區別,89s51單片機新增功能匯總
  AT89S51 是一個低功耗,高性能CMOS 8位 單片機 ,片內含8k Bytes ISP(In-system programmable)的可反復擦寫1000次的Flash只讀程序存儲器,器件采用ATMEL公司的高密度、非易失性存儲技術制造,兼容標準MCS-51指令系統及80C51引腳結構,芯片內集成了通用8位中央處理器和ISP Flash存儲單元,功能強大的微型計算機的AT89S51可為許多嵌入式控制應用系統供給高性價比的解決方案。   AT89S51具有如下特點:40個引腳,8k Bytes Flash片內程序存儲器,128 bytes的隨機存取數據存儲器(RAM),32個外部雙向輸入/輸出(I/O)口,5個中斷優先
[單片機]
《8051單片機C語言創新教程》筆記 03
GPIO 定時器、計數器與中斷 定時器的定義與配置: 計數寄存器由TH 高8位和TL低8位構成 -----------》T/C0 為TH0和TL0--------------》T/C1 為TH1和TL1 M1 M0 工作方式 功能說明 0 0 方式0 13位定時器/計數器 0 1 方式1 16位定時器/計數器 1 0 方式2 自動重載8位定時器/計數器 1 1 方式3 T0分為2個8位獨立計數器,T1無方式3 定時器計算初值: 串口:
[單片機]
《80<font color='red'>51單片機</font>C語言創新教程》筆記 03
基于8051單片機DPTR擴展設計介紹
單片機的出現是計算機技術發展史上的一個里程碑,它使計算機從海量數值計算進入到控制領域。在單片機中,以8051系列最為經典,至今仍是最普及、廣泛使用的8位MCU架構。業界許多技術人員在其基礎上不斷進行性能擴展,使得8051系列芯片不斷完善,從而形成一個龐大的體系。在傳統的8051系列單片機中,設置了一組雙字節寄存器(數據指針DPTR),用于訪問外接的64 KB數據存儲器和I/O接口電路;但在現今的8051單片機應用中,特別是在嵌入式系統中,往往涉及大規模的數據轉移操作,而傳統8051的一組數據指針使用起來則顯得捉襟見肘,因此若在8051設計中將數據指針設計為兩組或多組,則在執行大規模數據轉移操作時會相當簡便、迅速。在這種背景下,本文
[單片機]
基于80<font color='red'>51單片機</font>DPTR擴展設計介紹
如何正確的使用C51單片機中的位域
定義這樣的結構: typedef struct { uchar DC0_ALA:1; //電源0告警 uchar DC1_ALA:1; //電源1告警 uchar AC_ALA:1; //停電告警 uchar UN_H_ALA:1; //同頻信道機失鎖告警 uchar UN_L_ALA:1; //異頻信道機失鎖告警 uchar FAR_ALA:1; //遠端通訊故障告警 uchar OPEN_ALA:1; //門襟告警 uchar x:1; }ALARM;//系統告警結構定義 定義變量并初始化: idata ALARM old_alarm={0,0,0,0,0,0,0,0}; 在main()函數中這樣應用位域: if(ol
[單片機]
51單片機兩種減法指令的用法介紹
1.帶借位減法指令 SUBBA,Rn;(A)(A)-(Rn)-(C),以下類同。 SUBBA,direct SUBBA,@Ri SUBBA,#data 注意:減法之前先清零C,減法指令無不帶借位減法指令。 2.減1指令 DECA;(A)(A)-1,以下類同。 DECRn DEC@Ri DECdirect 例、設(R0)=7FH,在內RAM中,(7EH)=00H,(7FH)=40H 執行:DEC@R0 DECR0 DEC@R0 結果為: (R0)= 7EH,(7EH)=0FFH,(7FH)=3FH。
[單片機]
基于51單片機的OLED顯示圖片文字
一、前言 從性能來說OLED屏幕分辨率相對較高,引腳也少很多,占用的I/O口會少很多。從功耗來說首先oled顯示屏不需要背光燈,當有電流通過時,這些有機材料就可以發光,而且3.3V~5V之間都可以使用。從外觀來說體型更小,重量更輕更薄,可用來制作許多小型顯示設備。這次我使用的是四個引腳的0.95寸oled顯示屏,進行文字圖片的循環顯示。 二、OLED模塊介紹 1、液晶顯示屏 液晶顯示器,為平面超薄的顯示設備,它由一定數量的彩色或黑白像素組成,放置于光源或者反射面前方。液晶顯示器功耗很低,因此倍受工程師青睞,適用于使用電池的電子設備。它的主要原理是以電流刺激液晶分子產生點、線、面配合背部燈管構成畫面。 液晶顯示器的工作原理:液晶
[單片機]
基于<font color='red'>51單片機</font>的OLED顯示圖片文字
51單片機學習:步進電機實驗
實驗名稱:步進電機實驗 接線說明: 實驗現象:下載程序后,當按下KEY1鍵可調節電機旋轉方向;當按下KEY2鍵,電機加速; 當按下KEY3鍵,電機減速 注意事項:將步進電機紅色線對接到“步進電機模塊”輸出端子J47的5V上,其它相序依次接入。 ***************************************************************************************/ #include reg52.h typedef unsigned int u16; //對系統默認數據類型進行重定義 typedef unsigned char u8; //定義ULN2003控制步進電機管腳 s
[單片機]
51單片機可以做什么實用的產品?
我用51的單片機做過不少于10款產品了。 我看到很多文章,說51已經過時了,新手沒必要學習51單片機,可以直接學STM32。 我個人認為這種說法存在一定的誤導, 51還是有很大的市場 。 很多人想從事嵌入式單片機開發,覺得這個行業會隨著物聯網和5G等技術的發展,前景越來越好。 但是又不知道從哪里入手,該學習哪個單片機,別人都說現在主流是用STM32,你就隨波逐流去學習STM32。 但是卻 忽略了一個很重要的前提,就是你的基礎 。 每個人的基礎都不一樣,很多人沒什么學歷,也是從別的行業轉過來的,很多連c語言基本語法和電路基礎都不懂。 這個時候去學習stm32屬于跨級打怪了,能學會學好才怪。 這就是為什么我們無際單片機編程對每個新加入
[單片機]
小廣播
設計資源 培訓 開發板 精華推薦

最新單片機文章
何立民專欄 單片機及嵌入式寶典

北京航空航天大學教授,20余年來致力于單片機與嵌入式系統推廣工作。

 
EEWorld訂閱號

 
EEWorld服務號

 
汽車開發圈

 
機器人開發圈

電子工程世界版權所有 京ICP證060456號 京ICP備10001474號-1 電信業務審批[2006]字第258號函 京公網安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
主站蜘蛛池模板: 友谊县| 民丰县| 太康县| 原平市| 即墨市| 肇东市| 水富县| 海兴县| 吴堡县| 翼城县| 濮阳县| 西充县| 苗栗县| 苏尼特右旗| 博客| 五大连池市| 泸定县| 喀喇| 延寿县| 中牟县| 临沧市| 翁源县| 和硕县| 甘孜| 平顺县| 双辽市| 东平县| 宁阳县| 邳州市| 呈贡县| 金川县| 沙坪坝区| 宣威市| 长宁区| 达日县| 巴楚县| 乐平市| 镇江市| 锦屏县| 洪江市| 嵊州市|