中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

dede網(wǎng)站地圖文章變量合肥網(wǎng)站seo公司

dede網(wǎng)站地圖文章變量,合肥網(wǎng)站seo公司,直播開發(fā)平臺,推薦好的網(wǎng)站或網(wǎng)頁本教程基于韋東山百問網(wǎng)出的 DShanMCU-RA6M5開發(fā)板 進行編寫,需要的同學可以在這里獲取: https://item.taobao.com/item.htm?id728461040949 配套資料獲取:https://renesas-docs.100ask.net 瑞薩MCU零基礎(chǔ)入門系列教程匯總: ht…

本教程基于韋東山百問網(wǎng)出的 DShanMCU-RA6M5開發(fā)板 進行編寫,需要的同學可以在這里獲取: https://item.taobao.com/item.htm?id=728461040949

配套資料獲取:https://renesas-docs.100ask.net

瑞薩MCU零基礎(chǔ)入門系列教程匯總: https://blog.csdn.net/qq_35181236/article/details/132779862


第1章 單片機程序的設(shè)計模式

本章目標

  • 理解裸機程序設(shè)計模式
  • 了解多任務(wù)系統(tǒng)中程序設(shè)計的不同

1.1 裸機程序設(shè)計模式

裸機程序的設(shè)計模式可以分為:輪詢、前后臺、定時器驅(qū)動、基于狀態(tài)機。前面三種方法都無法解決一個問題:假設(shè)有 A、B 兩個都很耗時的函數(shù),無法降低它們相互之間的影響。第 4 種方法可以解決這個問題,但是實踐起來有難度。

假設(shè)一位職場媽媽需要同時解決 2 個問題:給小孩喂飯、回復工作信息,場景如圖所示,

后面將會演示各類模式下如何寫程序:

image1

1.1.1 輪詢模式

示例代碼如下:

 // 經(jīng)典單片機程序: 輪詢void main(){while (1){喂一口飯();回一個信息();}}

在 main 函數(shù)中是一個 while 循環(huán),里面依次調(diào)用 2 個函數(shù),這兩個函數(shù)相互之間有影響:如果“喂一口飯”太花時間,就會導致遲遲無法“回一個信息”;如果“回一個信息”太花時間,就會導致遲遲無法“喂下一口飯”。

使用輪詢模式編寫程序看起來很簡單,但是要求 while 循環(huán)里調(diào)用到的函數(shù)要執(zhí)行得非常快,在復雜場景里反而增加了編程難度。

1.1.2 前后臺

所謂“前后臺”就是使用中斷程序。假設(shè)收到同事發(fā)來的信息時,電腦會發(fā)出“滴”的一聲,這時候媽媽才需要去回復信息。示例程序如下:

 // 前后臺程序void main(){while (1){// 后臺程序喂一口飯();}}// 前臺程序
void 滴_中斷()
{回一個信息();
}
  • main 函數(shù)里 while 循環(huán)里的代碼是后臺程序,平時都是 while 循環(huán)在運行;
  • 當同事發(fā)來信息,電腦發(fā)出“滴”的一聲,觸發(fā)了中斷。媽媽暫停喂飯,去執(zhí)行“滴_中斷”給同事回復信息;

在這個場景里,給同事回復信息非常及時:即使正在喂飯也會暫停下來去回復信息。“喂一口飯”無法影響到“回一個信息”。但是,如果“回一個信息”太花時間,就會導致 “喂一口飯”遲遲無法執(zhí)行。

繼續(xù)改進,假設(shè)小孩吞下飯菜后會發(fā)出“啊”的一聲,媽媽聽到后才會喂下一口飯。喂飯、回復信息都是使用中斷函數(shù)來處理。示例程序如下:


// 前后臺程序
void main()
{while (1){// 后臺程序}
}// 前臺程序
void 滴_中斷()
{回一個信息();
}// 前臺程序
void 啊_中斷()
{喂一口飯();
}

main 函數(shù)中的 while 循環(huán)是空的,程序的運行靠中斷來驅(qū)使。如果電腦聲音“滴”、小孩聲音“啊”不會同時、相近發(fā)出,那么“回一個信息”、“喂一口飯”相互之間沒有影響。在不能滿足這個前提的情況下,比如“滴”、“啊”同時響起,先“回一個信息”時就會耽誤“喂一口飯”,這種場景下程序遭遇到了輪詢模式的缺點:函數(shù)相互之間有影響。

1.1.3 定時器驅(qū)動

定時器驅(qū)動模式,是前后臺模式的一種,可以按照不用的頻率執(zhí)行各種函數(shù)。比如需要每 2 分鐘給小孩喂一口飯,需要每 5 分鐘給同事回復信息。那么就可以啟動一個定時器,讓它每 1 分鐘產(chǎn)生一次中斷,讓中斷函數(shù)在合適的時間調(diào)用對應(yīng)函數(shù)。示例代碼如下:

// 前后臺程序: 定時器驅(qū)動
void main()
{while (1){// 后臺程序}
}// 前臺程序: 每 1 分鐘觸發(fā)一次中斷
void 定時器_中斷()
{static int cnt = 0;cnt++;if (cnt % 2 == 0){喂一口飯();}else if (cnt % 5 == 0){回一個信息();}
}

main 函數(shù)中的 while 循環(huán)是空的,程序的運行靠定時器中斷來驅(qū)使。

  • 定時器中斷每 1 分鐘發(fā)生一次,在中斷函數(shù)里讓 cnt 變量累加(代碼第 14 行)。
  • 第 15 行:進行求模運算,如果對 2 取模為 0,就“喂一口飯”。這相當于每發(fā)生 2 次中斷就“喂一口飯”。
  • 第 19 行:進行求模運算,如果對 5 取模為 0,就“回一個信息”。這相當于每發(fā)生 5 次

中斷就“回一個信息”。

這種模式適合調(diào)用周期性的函數(shù),并且每一個函數(shù)執(zhí)行的時間不能超過一個定時器周期。如果“喂一口飯”很花時間,比如長達 10 分鐘,那么就會耽誤“回一個信息”;反過來也是一樣的,如果“回一個信息”很花時間也會影響到“喂一口飯”;這種場景下程序遭遇到了輪詢模式的缺點:函數(shù)相互之間有影響。

1.1.4 基于狀態(tài)機

當“喂一口飯”、“回一個信息”都需要花很長的時間,無論使用前面的哪種設(shè)計模式,都會退化到輪詢模式的缺點:函數(shù)相互之間有影響??梢允褂脿顟B(tài)機來解決這個缺點,示例代碼如下:

// 狀態(tài)機
void main()
{while (1){喂一口飯();回一個信息();}
}

在 main 函數(shù)里,還是使用輪詢模式依次調(diào)用 2 個函數(shù)。

關(guān)鍵在于這 2 個函數(shù)的內(nèi)部實現(xiàn):使用狀態(tài)機,每次只執(zhí)行一個狀態(tài)的代碼,減少每次執(zhí)行的時間,代碼如下:

void 喂一口飯(void)
{static int state = 0;switch (state){case 0:{/* 舀飯 *//* 進入下一個狀態(tài) */state++;break;}case 1:{/* 喂飯 *//* 進入下一個狀態(tài) */state++;break;}case 2:{/* 舀菜 *//* 進入下一個狀態(tài) */state++;break;}case 2:{/* 喂菜 *//* 恢復到初始狀態(tài) */state = 0;break;}}
}void 回一個信息(void)
{static int state = 0;switch (state){case 0:{/* 查看信息 *//* 進入下一個狀態(tài) */state++;break;}case 1:{/* 打字 *//* 進入下一個狀態(tài) */state++;break;}case 2:{/* 發(fā)送 *//* 恢復到初始狀態(tài) */state = 0;break;}}
}

以“喂一口飯”為例,函數(shù)內(nèi)部拆分為 4 個狀態(tài):舀飯、喂飯、舀菜、喂菜。每次執(zhí)行“喂一口飯”函數(shù)時,都只會執(zhí)行其中的某一狀態(tài)對應(yīng)的代碼。以前執(zhí)行一次“喂一口飯”函數(shù)可能需要 4 秒鐘,現(xiàn)在可能只需要 1 秒鐘,就降低了對后面“回一個信息”的影響。

同樣的,“回一個信息”函數(shù)內(nèi)部也被拆分為 3 個狀態(tài):查看信息、打字、發(fā)送。每次執(zhí)行這個函數(shù)時,都只是執(zhí)行其中一小部分代碼,降低了對“喂一口飯”的影響。

使用狀態(tài)機模式,可以解決裸機程序的難題:假設(shè)有 A、B 兩個都很耗時的函數(shù),怎樣降低它們相互之間的影響。但是很多場景里,函數(shù) A、B 并不容易拆分為多個狀態(tài),并且這些狀態(tài)執(zhí)行的時間并不好控制。所以這并不是最優(yōu)的解決方法,需要使用多任務(wù)系統(tǒng)。

1.2 多任務(wù)系統(tǒng)

1.2.1 多任務(wù)模式

對于裸機程序,無論使用哪種模式進行精心的設(shè)計,在最差的情況下都無法解決這個問題:假設(shè)有 A、B 兩個都很耗時的函數(shù),無法降低它們相互之間的影響。使用狀態(tài)機模式時,如果函數(shù)拆分得不好,也會導致這個問題。本質(zhì)原因是:函數(shù)是輪流執(zhí)行的。假設(shè)“喂一口飯”需要 t1~t5 這 5 段時間,“回一個信息需要”ta~te 這 5 段時間,輪流執(zhí)行時:先執(zhí)行完 t1~t5,再執(zhí)行 ta~te,如下圖所示:

image2

對于職場媽媽,她怎么解決這個問題呢?她是一個眼明手快的人,可以一心多用,她這

樣做:

  • 左手拿勺子,給小孩喂飯
  • 右手敲鍵盤,回復同事
  • 兩不耽誤,小孩“以為”媽媽在專心喂飯,同事“以為”她在專心聊天
  • 但是腦子只有一個啊,雖然說“一心多用”,但是誰能同時思考兩件事?
  • 只是她反應(yīng)快,上一秒鐘在考慮夾哪個菜給小孩,下一秒鐘考慮給同事回復什么信息
  • 本質(zhì)是:交叉執(zhí)行,t1~t5 和 ta~te 交叉執(zhí)行,如下圖所示:

image3

基于多任務(wù)系統(tǒng)編寫程序時,示例代碼如下:

// RTOS 程序
喂飯任務(wù)()
{while (1){喂一口飯();}
}回信息任務(wù)()
{while (1){回一個信息();}
}void main()
{// 創(chuàng)建 2 個任務(wù)create_task(喂飯任務(wù));create_task(回信息任務(wù));// 啟動調(diào)度器start_scheduler();
}
  • 第 21、22 行,創(chuàng)建 2 個任務(wù);
  • 第 25 行,啟動調(diào)度器;
  • 之后,這 2 個任務(wù)就會交叉執(zhí)行了;

基于多任務(wù)系統(tǒng)編寫程序時,反而更簡單了:

  1. 上面第 2~8 行是“喂飯任務(wù)”的代碼;
  2. 第 10~16 行是“回信息任務(wù)”的代碼,編寫它們時甚至都不需要考慮它和其他函數(shù)的相互影響。就好像有 2 個單板:一個只運行“喂飯任務(wù)”這個函數(shù)、另一個只運行“回信息任務(wù)”這個函數(shù)。

多任務(wù)系統(tǒng)會依次給這些任務(wù)分配時間:你執(zhí)行一會,我執(zhí)行一會,如此循環(huán)。只要切換的間隔足夠短,用戶會“感覺這些任務(wù)在同時運行”。如下圖所示:

image4

1.2.2 互斥操作

多任務(wù)系統(tǒng)中,多個任務(wù)可能會“同時”訪問某些資源,需要增加保護措施以防止混亂。比如任務(wù) A、B 都要使用串口,能否使用一個全局變量讓它們獨占地、互斥地使用串口?示例代碼如下:

// RTOS 程序
int g_canuse = 1;
void uart_print(char *str)
{if (g_canuse){g_canuse = 0;printf(str);g_canuse = 1;}
}task_A()
{while (1){uart_print("0123456789\n");}}task_B()
{while (1){uart_print("abcdefghij");}
}void main()
{// 創(chuàng)建 2 個任務(wù)create_task(task_A);create_task(task_B);// 啟動調(diào)度器start_scheduler();
}

程序的意圖是:task_A 打印“0123456789”,task_B 打印“abcdefghij”。在 task_A 或task_B 打印的過程中,另一個任務(wù)不能打印,以避免數(shù)字、字母混雜在一起,比如避免打印這樣的字符:“012abc”。

第 6 行使用全局變量 g_canuse 實現(xiàn)互斥打印,它等于 1 時表示“可以打印”。在進行實際打印之前,先把 g_canuse 設(shè)置為 0,目的是防止別的任務(wù)也來打印。

這個程序大部分時間是沒問題的,但是只要它運行的時間足夠長,就會出現(xiàn)數(shù)字、字母混雜的情況。下圖把 uart_print 函數(shù)標記為①~④個步驟:

void uart_print(char *str)
{if (g_canuse){g_canuse = 0;printf(str);  ③g_canuse = 1;}
}

如果 task_A 執(zhí)行完①,進入 if 語句里面執(zhí)行②之前被切換為 task_B:在這一瞬間,g_canuse 還是 1。

task_B 執(zhí)行①時也會成功進入 if 語句,假設(shè)它執(zhí)行到③,在 printf 打印完部分字符比如“abc”后又再次被切換為 task_A。

task_A 繼續(xù)從上次被暫停的地方繼續(xù)執(zhí)行,即從②那里繼續(xù)執(zhí)行,成功打印出“0123456789”。這時在串口上可以看到打印的結(jié)果為:“abc0123456789”。

是不是“①判斷”、“②清零”間隔太遠了,uart_print 函數(shù)改進成如下的代碼呢?

void uart_print(char *str)
{g_canuse--;        ① 減一 if (g_canuse == 0) ② 判斷{printf(str);   ③ 打印}g_canuse++;        ④ 加一
}

即使改進為上述代碼,仍然可能產(chǎn)生兩個任務(wù)同時使用串口的情況。因為“①減一”這個操作會分為 3 個步驟:a.從內(nèi)存讀取變量的值放入寄存器里,b.修改寄存器的值讓它減一,c.把寄存器的值寫到內(nèi)存上的變量上去。

如果task_A執(zhí)行完步驟a、b,還沒來得及把新值寫到內(nèi)存的變量里,就被切換為task_B:在這一瞬間,g_canuse 還是 1。

task_B 執(zhí)行①②時也會成功進入 if 語句,假設(shè)它執(zhí)行到③,在 printf 打印完部分字符比如“abc”后又再次被切換為 task_A。

task_A 繼續(xù)從上次被暫停的地方繼續(xù)執(zhí)行,即從步驟 c 那里繼續(xù)執(zhí)行,成功打印出“0123456789”。這時在串口上可以看到打印的結(jié)果為:“abc0123456789”。

從上面的例子可以看到,基于多任務(wù)系統(tǒng)編寫程序時,訪問公用的資源的時候要考慮“互斥操作”。任何一種多任務(wù)系統(tǒng)都會提供相應(yīng)的函數(shù)。

1.2.3 同步操作

如果任務(wù)之間有依賴關(guān)系,比如任務(wù) A 執(zhí)行了某個操作之后,需要任務(wù) B 進行后續(xù)的處理。如果代碼如下編寫的話,任務(wù) B 大部分時間做的都是無用功。

// RTOS 程序
int flag = 0;void task_A()
{while (1){// 做某些復雜的事情// 完成后把 flag 設(shè)置為 1flag = 1;}
}void task_B()
{while (1){if (flag){// 做后續(xù)的操作}}
}void main()
{// 創(chuàng)建 2 個任務(wù)create_task(task_A);create_task(task_B);// 啟動調(diào)度器start_scheduler();
}

上述代碼中,在任務(wù) A 沒有設(shè)置 flag 為 1 之前,任務(wù) B 的代碼都只是去判斷 flag。而任務(wù) A、B 的函數(shù)是依次輪流運行的,假設(shè)系統(tǒng)運行了 100 秒,其中任務(wù) A 總共運行了 50秒,任務(wù) B 總共運行了 50 秒,任務(wù) A 在努力處理復雜的運算,任務(wù) B 僅僅是浪費 CPU 資源。

如果可以讓任務(wù) B 阻塞,即讓任務(wù) B 不參與調(diào)度,那么任務(wù) A 就可以獨占 CPU 資源加快處理復雜的事情。當任務(wù) A 處理完事情后,再喚醒任務(wù) B。示例代碼如下:

//RTOS 程序
void task_A()
{while (1){// 做某些復雜的事情// 釋放信號量,會喚醒任務(wù) B;}
}void task_B()
{while (1){// 等待信號量, 會讓任務(wù) B 阻塞// 做后續(xù)的操作}
}void main()
{// 創(chuàng)建 2 個任務(wù)create_task(task_A);create_task(task_B);// 啟動調(diào)度器start_scheduler();
}
  • 第 15 行:任務(wù) B 運行時,等待信號量,不成功時就會阻塞,不在參與任務(wù)調(diào)度。
  • 第 7 行: 任務(wù) A 處理完復雜的事情后,釋放信號量會喚醒任務(wù) B。
  • 第 16 行:任務(wù) B 被喚醒后,從這里繼續(xù)運行。

在這個過程中,任務(wù) A 處理復雜事情的時候可以獨占 CPU 資源,加快處理速度。


本章完
http://m.risenshineclean.com/news/48598.html

相關(guān)文章:

  • 閬中網(wǎng)站建設(shè)優(yōu)化網(wǎng)站關(guān)鍵詞優(yōu)化
  • 網(wǎng)上最好的網(wǎng)站模塊免費域名注冊網(wǎng)站
  • 專門做三國戰(zhàn)紀的網(wǎng)站叫什么旅游企業(yè)seo官網(wǎng)分析報告
  • 帶做騎傳奇私服網(wǎng)站泉州seo按天計費
  • 幻燈片在什么網(wǎng)站做企業(yè)品牌推廣營銷方案
  • 3g網(wǎng)站開發(fā)站長工具seo綜合查詢騰訊
  • 無錫專業(yè)網(wǎng)站營銷南京百度快照優(yōu)化排名
  • 東至縣住房和城鄉(xiāng)建設(shè)網(wǎng)站推廣關(guān)鍵詞
  • php網(wǎng)站數(shù)據(jù)遷移seo網(wǎng)絡(luò)搜索引擎優(yōu)化
  • 電商批發(fā)平臺網(wǎng)站會計培訓機構(gòu)排名前十
  • 現(xiàn)代感網(wǎng)站線下推廣怎么做
  • 自己制作網(wǎng)頁鏈接的軟件企業(yè)網(wǎng)站優(yōu)化公司
  • 網(wǎng)站做百度推廣為什么沒人咨詢做網(wǎng)站推廣的公司
  • 博物館網(wǎng)站模版搜索引擎營銷分析
  • css3做的牛逼網(wǎng)站免費觀看行情軟件網(wǎng)站進入
  • 廣州 深圳 外貿(mào)網(wǎng)站建設(shè)搜索引擎在線
  • 網(wǎng)站開發(fā)培訓機構(gòu)百度公司總部地址
  • 微網(wǎng)站模板源代碼sem競價推廣怎么做
  • 阿里云建站和華為云建站哪個好互聯(lián)網(wǎng)營銷師培訓教材
  • ip做網(wǎng)站地址北京昨天出啥大事了
  • 做同城購物網(wǎng)站賺錢嗎創(chuàng)意營銷點子
  • 如何利用某個軟件做一個網(wǎng)站長沙百度搜索排名優(yōu)化
  • 長春網(wǎng)站開發(fā)推薦網(wǎng)絡(luò)營銷案例100例
  • centos lamp wordpress百度seo搜索
  • 內(nèi)江網(wǎng)站建設(shè)0832hdsj長沙疫情最新消息今天封城了
  • 河南做網(wǎng)站哪個公司好蘋果cms永久免費全能建站程序
  • 做網(wǎng)站生意越來越差阜陽seo
  • 重慶專業(yè)的網(wǎng)站建設(shè)網(wǎng)站百度權(quán)重查詢
  • 14版哥斯拉的官方做的宣傳網(wǎng)站營銷策略有哪幾種
  • 網(wǎng)站怎樣做權(quán)重百度賬號免費注冊