久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 5091|回復: 4
收起左側

屏棄Delay(),高效率單片機

[復制鏈接]
ID:103513 發表于 2016-1-16 22:36 | 顯示全部樓層 |閱讀模式

//2015.09.06  于廈門軟二. 程序思想轉變
    單片機已經學了4、5年頭了,一直用慣了delay()函數,意識到電子工程師們的硬件編程思想與PC機底層編程思想上的很多不同,引發了一些思考。

學單片機時,老師都教我們用Delay()函數。

絕大多數8位的單片機程序,它們的單片機99.9%的工作時間都在打空轉,(是否嚇了一跳,竟然讓單片機空轉以實現和外界同步,這怎么可能?試想,如果PC機CPU空轉一秒,那么音樂會斷一秒、畫面會停頓一秒、下載文件會斷一秒,這怎么可行?)這是一種教條的思想,把書讀死了

99.9%大家可能感到有些危言聳聽,那就讓我們算一算:

已內部8M頻的AVR單片機來說,單指令周期僅為1/8 = 0.125us,那一毫秒可以執行多少個單周期指令? 1%0.125*1000 = 8000個而我看到論壇里下到的絕大多數程序,兩個延時函數之間代碼的執行時間要遠遠小于8000個指令周期。

說實話,很多16K以上的程序,把所有延時函數去掉,總體能執行幾毫秒就不錯了。換句話說,我說單片機的利用率小于0.01%還是口下留情了。

要說怎么解決問題,就要先找到問題,我問問大家,程序中,我們為什么延時?原因很多,可能是外設速度太慢,也可能是為了躲過人眼視覺停留時間,等等。總之就是與外界不同步,而我們想要同步。

所以說這些延時應該是很有道理的,我不否定這一點,但問題的關鍵這些延時空轉,我們為什么不能把這些時間回收起來做一些別的事呢?

試想,如果把這99.9%的時間回收,那可以一筆相當巨大的資源。

有很多人有些特殊方法回收過這些空轉時間,比如說在延時函數中做點事。

但這些往往都不通用,下面我說一些我的兩種方法:

 

 1.從點亮LED(發光二極管)開始

在市面上眾多的單片機學習資料中,最基礎的實驗無疑于點亮LED了,即控制單片機的I/O的電平的變化。

如同如下實例代碼一般

void main(void)

{

    LedInit() ;

    While(1) 

    { 

        LED = ON ;

        DelayMs(500) ;

        LED = OFF ;

        DelayMs(500) ;

        }

}

程序很簡單,從它的結構可以看出,LED先點亮500MS,然后熄滅500MS,如此循環下去,形成的效果就是LED以1HZ的頻率進行閃爍。下面讓我們分析上面的程序有沒有什么問題。

看來看出,好像很正常的啊,能有什么問題呢?這個時候我們應該換一個思路去想了。試想,整個程序除了控制LED = ON ; LED = OFF; 這兩條語句外,其余的時間,全消耗在了DelayMs(500)這兩個函數上。而在實際應用系統中是沒有哪個系統只閃爍一只LED就其它什么事情都不做了的。因此,在這里我們要想辦法,把CPU解放出來,讓它不要白白浪費500MS的延時等待時間。寧可讓它一遍又一遍的掃描看有哪些任務需要執行,也不要讓它停留在某個地方空轉消耗CPU時間。

 

從上面我們可以總結出

(1)    無論什么時候我們都要以實際應用的角度去考慮程序的編寫。

(2)    無論什么時候都不要讓CPU白白浪費等待,尤其是延時(超過1MS)這樣的地方。

 

下面讓我們從另外一個角度來考慮如何點亮一顆LED。 先看看我們的硬件結構是什么樣子的。

紅色的壓降為1.82-1.88V,電流5-8mA,

綠色的壓降為1.75-1.82V,電流3-5mA,

橙色的壓降為1.7-1.8V,電流3-5mA

蘭色的壓降為3.1-3.3V,電流8-10mA,

白色的壓降為3-3.2V,電流10-15mA,

(供電電壓5V,LED直徑為5mm)

 

74HC573真值表如下:

一般的LED的正常發光電流為10~20MA而低電流LED的工作電流在2mA以下(亮度與普通發光管相同)。在上圖中我們可知,當Q1~Q8引腳上面的電平為低電平時,LED發光。通過LED的電流約為(VCC - Vd)/ RA2 。其中Vd為LED導通后的壓降,約為1.7V左右。這個導通壓降根據LED顏色的不同,以及工作電流的大小的不同,會有一定的差別。下面一些參數是網上有人測出來的,供大家參考。

需要注意的是,通過74HC573的最大電流是有限制的,否則可能會燒壞74HC573這個芯片。 上面這個圖是從74HC573的DATASHEET中截取出來的,從上可以看出,每個引腳允許通過的最大電流為35mA 整個芯片允許通過的最大電流為75mA。在我們設計相應的驅動電路時候,這些參數是相當重要的,而且是最容易被初學者所忽略的地方。同時在設計的時候,要留出一定量的余量出來,不能說單個引腳允許通過的電流為35mA,你就設計為35mA,這個時候你應該把設計的上限值定在20mA左右才能保證能夠穩定的工作。

(設計相應驅動電路時候,應該仔細閱讀芯片的數據手冊,了解每個引腳的驅動能力,以及整個芯片的驅動能力)

 

了解了相應的硬件后,我們再來編寫驅動程序。

#include<reg52.h>

sbit LED_SEG  = P1^4;       //數碼管段選

sbit LED_DIG  = P1^5;       //數碼管位選

sbit LED_CS11 = P1^6;       //led控制位

sbit ir=P1^7;

//首先定義LED的接口然后為亮滅常數定義一個宏

#define LED P0                     //定義LED接口

bit  g_bSystemTime1Ms = 0 ;                            // 1MS系統時標

//下面到了重點了,究竟該如何釋放CPU,避免其做延時空等待這樣的事情呢。

很簡單,我們為系統產生一個1MS的時標。假定LED需要亮500MS,熄滅500MS,那么我們可以對這個1MS的時標進行計數,當這個計數值達到500時候,清零該計數值,同時把LED的狀態改變。

unsigned int  g_u16LedTimeCount = 0 ;       //LED計數器

unsigned char g_u8LedState = 0 ;              //LED狀態標志, 0表示亮,1表示熄滅

 

#define LED_ON()       LED = 0x00 ;              //所有LED亮

#define LED_OFF()       LED = 0xff ;              //所有LED熄滅

 

void Timer0Init(void)              //定時器初始化

{

    TMOD &= 0xf0 ;

    TMOD |= 0x01 ;                     //定時器0工作方式1

    TH0  =  0xfc ;                     //定時器初始值

    TL0  =  0x66 ;

    TR0  = 1 ;

    ET0  = 1 ;

}

void LedProcess(void)              //計時到_執行LED動作

{

    if(0 == g_u8LedState)       //如果LED的狀態為亮,則點亮LED

    { 

        LED_ON() ;

    } 

    else                                   //否則熄滅LED 

    { 

        LED_OFF() ;

    } 

}

 

void LedStateChange(void)        //1ms計時到_執行動作

{

       if(g_bSystemTime1Ms)                                   //系統1MS時標到

       {

              g_bSystemTime1Ms = 0 ;

              //在我們的定時器中斷函數中對其置位,其它函數使用該變量后,應該對其復位(清0)

              g_u16LedTimeCount++ ;                            //LED計數器加一

              if(g_u16LedTimeCount >= 500)              //計數達到500,即500MS到了,改變LED的狀態。

              {

                     g_u16LedTimeCount = 0 ;               //LED計數器_清0

                     g_u8LedState  = ! g_u8LedState;       //LED狀態標志, 0表示亮,1表示熄滅

              }

    }

}

//因為LED的亮或者滅依賴于LED狀態變量(g_u8LedState)的改變,而狀態變量的改變,又依賴于LED計數器的計數值(g_u16LedTimeCount ,只有計數值達到一定后,狀態變量才改變)所以,兩個函數都沒有堵塞CPU的地方。

void main(void)

{

    Timer0Init() ;

    EA = 1 ;

       LED_CS11 = 1 ; //74HC595輸出允許

    LED_SEG = 0 ;  //數碼管段選和位選禁止(因為它們和LED共用P0口)

    LED_DIG = 0 ;

    while(1) 

    { 

        LedProcess() ;              //

        LedStateChange() ;       //計時到_執行動作

    } 

} 

 

void Time0Isr(void) interrupt 1

{

       TH0=0xfc;                            //定時器重新賦初值

    TL0=0x66;                            //每1ms溢出1次

    g_bSystemTime1Ms=1;              //1ms時標“標志位”置位 

}

因為LED的亮或者滅依賴于LED狀態變量(g_u8LedState)的改變,而狀態變量的改變,又依賴于LED計數器的計數值(g_u16LedTimeCount ,只有計數值達到一定后,狀態變量才改變)所以,兩個函數都沒有堵塞CPU的地方。讓我們來從頭到尾分析一遍整個程序的流程。

 

程序首先執行LedProcess() ;函數因為g_u8LedState 的初始值為0 (見定義,對于全局變量,在定義的時候最好給其一個確定的值)所以LED被點亮,然后退出LedStateChange()函數,執行下一個函數LedStateChange()

在函數LedStateChange()內部首先判斷1MS的系統時標是否到了,如果沒有到就直接退出函數,如果到了,就把時標清0以便下一個時標消息的到來,同時對LED計數器加一,然后再判斷LED計數器是否到達我們預先想要的值500,如果沒有,則退出函數,如果有,對計數器清0,以便下次重新計數,同時把LED狀態變量取反,然后退出函數。

由上面整個流程可以知道,CPU所做的事情,就是對一些計數器加一,然后根據條件改變狀態,再根據這個狀態來決定是否點亮LED。這些函數執行所花的時間都是相當短的,如果主程序中還有其它函數,則CPU會順次往下執行下去。對于其它的函數(如果有的話)也要采取同樣的措施,保證其不堵塞CPU,如果全部基于這種方法設計,那么對于不是非常龐大的系統,我們的系統依舊可以保證多個任務(多個函數)同時執行。系統的實時性得到了一定的保證,從宏觀上看來,就是多個任務并發執行。

 

好了,這一章就到此為止,讓我們總結一下,究竟有哪些需要注意的吧。

(1)    無論什么時候我們都要以實際應用的角度去考慮程序的編寫。

(2)    無論什么時候都不要讓CPU白白浪費等待,尤其是延時(超過1MS)這樣的地方。

(3)    設計相應驅動電路時候,應該仔細閱讀芯片的數據手冊,了解每個引腳的驅動能力,以及整個芯片的驅動能力

(4)    最重要的是,如何去釋放CPU(參考本章的例子),這是寫出合格程序的基礎。

 

 

回復

使用道具 舉報

ID:149451 發表于 2019-5-25 13:18 | 顯示全部樓層
LZ軟硬兼修,佩服佩服...................
回復

使用道具 舉報

ID:522695 發表于 2019-6-1 12:00 | 顯示全部樓層
總感覺就這樣用去了一個定時器。。。。有點浪費或許會有更好解決cpu堵塞的方法
回復

使用道具 舉報

ID:569384 發表于 2020-6-23 10:58 | 顯示全部樓層
一直都是用計數的方法來延時,如果用while或者for循環的話程序就會卡在循環里等待,如果我的產品是有按鍵的話就很影響使用,所以一定要用計數的方法來延時。
回復

使用道具 舉報

ID:569384 發表于 2020-6-23 11:02 | 顯示全部樓層
鉆研旋律 發表于 2019-6-1 12:00
總感覺就這樣用去了一個定時器。。。。有點浪費或許會有更好解決cpu堵塞的方法

樓主說的應該是這樣一個概念,肯定有其他的方法。比如我寫程序的時候如果延時時間不需要太精準,我就會直接在主函數或子函數中讓變量++,到了一個值后(比如500)再清零,讓后執行命令。這樣也能做到延時效果。如果要時間要精準就一定要用定時器來實現了。
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 日韩欧美三级电影在线观看 | 国产激情毛片 | 精品自拍视频在线观看 | 综合自拍| 久久综合久久综合久久 | 国产麻豆一区二区三区 | 成人精品一区二区三区四区 | 中文字幕高清av | 久久久精品一区 | 欧洲一级黄| 亚洲中午字幕 | 国产福利在线 | 午夜欧美 | 亚洲精品视频免费观看 | 精品啪啪 | 久久精品国产一区 | 色噜噜亚洲男人的天堂 | 精品国产乱码一区二区三区a | 日韩国产高清在线观看 | 18gay男同69亚洲网站 | 欧美伊人影院 | 人人人干 | 色综合久久伊人 | 欧美综合自拍 | 中文字幕一区二区三区日韩精品 | 精品视频在线播放 | av激情影院 | 亚洲色片网站 | 天啪| 福利视频网址 | 精品无码三级在线观看视频 | 国产在线视频一区二区 | 午夜视频免费在线观看 | 黄a免费看 | 亚洲午夜久久久 | 在线观看国产视频 | 免费在线一区二区 | 男女免费在线观看视频 | 国产一区二区三区在线看 | 欧美日韩一区精品 | 91精品国模一区二区三区 |