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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2495|回復: 2
打印 上一主題 下一主題
收起左側

分享一篇新手入門資料

[復制鏈接]
跳轉到指定樓層
樓主
ID:886808 發表于 2021-4-1 10:12 來自手機 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
前言        2
基礎知識:單片機編程基礎        2
第一節:單數碼管按鍵顯示        4
第二節:雙數碼管可調秒表        6
第三節:十字路口交通燈        6
第四節:數碼管驅動        7
第五節:鍵盤驅動        8
第六節:低頻頻率計        14
第七節:電子表        17
第八節:串行口應用        17

基礎知識:單片機編程基礎
單片機的外部結構:
DIP40雙列直插;
P0,P1,P2,P3四個8位準雙向I/O引腳;(作為I/O輸入時,要先輸出高電平)
電源VCC(PIN40)和地線GND(PIN20);
高電平復位RESET(PIN9);(10uF電容接VCC與RESET,即可實現上電復位)
內置振蕩電路,外部只要接晶體至X1(PIN18)和X0(PIN19);(頻率為主頻的12倍)
程序配置EA(PIN31)接高電平VCC;(運行單片機內部ROM中的程序)
P3支持第二功能:RXD、TXD、INT0、INT1、T0、T1

單片機內部I/O部件:(所為學習單片機,實際上就是編程控制以下I/O部件,完成指定任務)
四個8位通用I/O端口,對應引腳P0、P1、P2和P3;
兩個16位定時計數器;(TMOD,TCON,TL0,TH0,TL1,TH1)
一個串行通信接口;(SCON,SBUF)
一個中斷控制器;(IE,IP)

針對AT89C52單片機,頭文件AT89x52.h給出了SFR特殊功能寄存器所有端口的定義。教科書的160頁給出了針對MCS51系列單片機的C語言擴展變量類型。

C語言編程基礎:
十六進制表示字節0x5a:二進制為01011010B;0x6E為01101110。
如果將一個16位二進數賦給一個8位的字節變量,則自動截斷為低8位,而丟掉高8位。
++var表示對變量var先增一;var—表示對變量后減一。
x |= 0x0f;表示為 x = x | 0x0f;
TMOD = ( TMOD & 0xf0 ) | 0x05;表示給變量TMOD的低四位賦值0x5,而不改變TMOD的高四位。
While( 1 ); 表示無限執行該語句,即死循環。語句后的分號表示空循環體,也就是{;}

在某引腳輸出高電平的編程方法:(比如P1.3(PIN4)引腳)
#include <AT89x52.h>        //該頭文檔中有單片機內部資源的符號化定義,其中包含P1.3
void main( void )                //void 表示沒有輸入參數,也沒有函數返值,這入單片機運行的復位入口
{
        P1_3 = 1;                        //給P1_3賦值1,引腳P1.3就能輸出高電平VCC
        While( 1 );                //死循環,相當 LOOP: goto LOOP;
}
注意:P0的每個引腳要輸出高電平時,必須外接上拉電阻(如4K7)至VCC電源。

在某引腳輸出低電平的編程方法:(比如P2.7引腳)
#include <AT89x52.h>        //該頭文檔中有單片機內部資源的符號化定義,其中包含P2.7
void main( void )                //void 表示沒有輸入參數,也沒有函數返值,這入單片機運行的復位入口
{
        P2_7 = 0;                        //給P2_7賦值0,引腳P2.7就能輸出低電平GND
        While( 1 );                //死循環,相當 LOOP: goto LOOP;
}

在某引腳輸出方波編程方法:(比如P3.1引腳)
#include <AT89x52.h>        //該頭文檔中有單片機內部資源的符號化定義,其中包含P3.1
void main( void )                //void 表示沒有輸入參數,也沒有函數返值,這入單片機運行的復位入口
{
        While( 1 )                //非零表示真,如果為真則執行下面循環體的語句
        {
P3_1 = 1;                //給P3_1賦值1,引腳P3.1就能輸出高電平VCC
                P3_1 = 0;                //給P3_1賦值0,引腳P3.1就能輸出低電平GND
        }                                //由于一直為真,所以不斷輸出高、低、高、低……,從而形成方波
}

將某引腳的輸入電平取反后,從另一個引腳輸出:( 比如 P0.4 = NOT( P1.1) )
#include <AT89x52.h>        //該頭文檔中有單片機內部資源的符號化定義,其中包含P0.4和P1.1
void main( void )                //void 表示沒有輸入參數,也沒有函數返值,這入單片機運行的復位入口
{
        P1_1 = 1;                        //初始化。P1.1作為輸入,必須輸出高電平
While( 1 )                //非零表示真,如果為真則執行下面循環體的語句
        {
if( P1_1 == 1 )                //讀取P1.1,就是認為P1.1為輸入,如果P1.1輸入高電平VCC
                {        P0_4 = 0;                }        //給P0_4賦值0,引腳P0.4就能輸出低電平GND
                else                                        //否則P1.1輸入為低電平GND
                //{        P0_4 = 0;                }        //給P0_4賦值0,引腳P0.4就能輸出低電平GND
                {        P0_4 = 1;                }        //給P0_4賦值1,引腳P0.4就能輸出高電平VCC
        }                                //由于一直為真,所以不斷根據P1.1的輸入情況,改變P0.4的輸出電平
}

將某端口8個引腳輸入電平,低四位取反后,從另一個端口8個引腳輸出:( 比如 P2 = NOT( P3 ) )
#include <AT89x52.h>        //該頭文檔中有單片機內部資源的符號化定義,其中包含P2和P3
void main( void )                //void 表示沒有輸入參數,也沒有函數返值,這入單片機運行的復位入口
{
        P3 = 0xff;                //初始化。P3作為輸入,必須輸出高電平,同時給P3口的8個引腳輸出高電平
While( 1 )                //非零表示真,如果為真則執行下面循環體的語句
        {                                //取反的方法是異或1,而不取反的方法則是異或0
P2 = P3^0x0f        //讀取P3,就是認為P3為輸入,低四位異或者1,即取反,然后輸出
        }                                //由于一直為真,所以不斷將P3取反輸出到P2
}
注意:一個字節的8位D7、D6至D0,分別輸出到P3.7、P3.6至P3.0,比如P3=0x0f,則P3.7、P3.6、P3.5、P3.4四個引腳都輸出低電平,而P3.3、P3.2、P3.1、P3.0四個引腳都輸出高電平。同樣,輸入一個端口P2,即是將P2.7、P2.6至P2.0,讀入到一個字節的8位D7、D6至D0。


第一節:單數碼管按鍵顯示
單片機最小系統的硬件原理接線圖:
接電源:VCC(PIN40)、GND(PIN20)。加接退耦電容0.1uF
接晶體:X1(PIN18)、X2(PIN19)。注意標出晶體頻率(選用12MHz),還有輔助電容30pF
接復位:RES(PIN9)。接上電復位電路,以及手動復位電路,分析復位工作原理
接配置:EA(PIN31)。說明原因。

發光二極的控控制:單片機I/O輸出
將一發光二極管LED的正極(陽極)接P1.1,LED的負極(陰極)接地GND。只要P1.1輸出高電平VCC,LED就正向導通(導通時LED上的壓降大于1V),有電流流過LED,至發LED發亮。實際上由于P1.1高電平輸出電阻為10K,起到輸出限流的作用,所以流過LED的電流小于(5V-1V)/10K = 0.4mA。只要P1.1輸出低電平GND,實際小于0.3V,LED就不能導通,結果LED不亮。

開關雙鍵的輸入:輸入先輸出高
一個按鍵KEY_ON接在P1.6與GND之間,另一個按鍵KEY_OFF接P1.7與GND之間,按KEY_ON后LED亮,按KEY_OFF后LED滅。同時按下LED半亮,LED保持后松開鍵的狀態,即ON亮OFF滅。
#include <at89x52.h>
#define LED                P1^1                        //用符號LED代替P1_1
#define KEY_ON        P1^6                        //用符號KEY_ON代替P1_6
#define KEY_OFF        P1^7                        //用符號KEY_OFF代替P1_7
void main( void )                                //單片機復位后的執行入口,void表示空,無輸入參數,無返回值
{
        KEY_ON = 1;                //作為輸入,首先輸出高,接下KEY_ON,P1.6則接地為0,否則輸入為1
        KEY_OFF = 1;                //作為輸入,首先輸出高,接下KEY_OFF,P1.7則接地為0,否則輸入為1
        While( 1 )                //永遠為真,所以永遠循環執行如下括號內所有語句
        {
                if( KEY_ON==0 )        LED=1;        //是KEY_ON接下,所示P1.1輸出高,LED亮
                if( KEY_OFF==0 )        LED=0;        //是KEY_OFF接下,所示P1.1輸出低,LED滅
        }        //松開鍵后,都不給LED賦值,所以LED保持最后按鍵狀態。
//同時按下時,LED不斷亮滅,各占一半時間,交替頻率很快,由于人眼慣性,看上去為半亮態
}

數碼管的接法和驅動原理
一支七段數碼管實際由8個發光二極管構成,其中7個組形構成數字8的七段筆畫,所以稱為七段數碼管,而余下的1個發光二極管作為小數點。作為習慣,分別給8個發光二極管標上記號:a,b,c,d,e,f,g,h。對應8的頂上一畫,按順時針方向排,中間一畫為g,小數點為h。
我們通常又將各二極與一個字節的8位對應,a(D0),b(D1),c(D2),d(D3),e(D4),f(D5),g(D6),h(D7),相應8個發光二極管正好與單片機一個端口Pn的8個引腳連接,這樣單片機就可以通過引腳輸出高低電平控制8個發光二極的亮與滅,從而顯示各種數字和符號;對應字節,引腳接法為:a(Pn.0),b(Pn.1),c(Pn.2),d(Pn.3),e(Pn.4),f(Pn.5),g(Pn.6),h(Pn.7)。
如果將8個發光二極管的負極(陰極)內接在一起,作為數碼管的一個引腳,這種數碼管則被稱為共陰數碼管,共同的引腳則稱為共陰極,8個正極則為段極。否則,如果是將正極(陽極)內接在一起引出的,則稱為共陽數碼管,共同的引腳則稱為共陽極,8個負極則為段極。
以單支共陰數碼管為例,可將段極接到某端口Pn,共陰極接GND,則可編寫出對應十六進制碼的七段碼表字節數據如右圖:

16鍵碼顯示的程序
我們在P1端口接一支共陰數碼管SLED,在P2、P3端口接16個按鍵,分別編號為KEY_0、KEY_1到KEY_F,操作時只能按一個鍵,按鍵后SLED顯示對應鍵編號。
#include <at89x52.h>
#define SLED        P1
#define KEY_0        P2^0
#define KEY_1        P2^1
#define KEY_2        P2^2
#define KEY_3        P2^3
#define KEY_4        P2^4
#define KEY_5        P2^5
#define KEY_6        P2^6
#define KEY_7        P2^7
#define KEY_8        P3^0
#define KEY_9        P3^1
#define KEY_A        P3^2
#define KEY_B        P3^3
#define KEY_C        P3^4
#define KEY_D        P3^5
#define KEY_E        P3^6
#define KEY_F        P3^7
Code unsigned char Seg7Code[16]=        //用十六進數作為數組下標,可直接取得對應的七段編碼字節
// 0     1    2     3     4    5     6     7     8     9    A     b     C     d    E    F
{0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
void main( void )
{
        unsigned char i=0;        //作為數組下標
P2 = 0xff;        //P2作為輸入,初始化輸出高
        P3 = 0xff;        //P3作為輸入,初始化輸出高
        While( 1 )
        {
                if( KEY_0 == 0 ) i=0;                if( KEY_1 == 0 ) i=1;
                if( KEY_2 == 0 ) i=2;                if( KEY_3 == 0 ) i=3;
                if( KEY_4 == 0 ) i=4;                if( KEY_5 == 0 ) i=5;
                if( KEY_6 == 0 ) i=6;                if( KEY_7 == 0 ) i=7;
                if( KEY_8 == 0 ) i=8;                if( KEY_9 == 0 ) i=9;
                if( KEY_A == 0 ) i=0xA;                if( KEY_B == 0 ) i=0xB;
                if( KEY_C == 0 ) i=0xC;                if( KEY_D == 0 ) i=0xD;
                if( KEY_E == 0 ) i=0xE;                if( KEY_F == 0 ) i=0xF;
                SLED = Seg7Code[ i ];        //開始時顯示0,根據i取應七段編碼
}
}
第二節:雙數碼管可調秒表
解:只要滿足題目要求,方法越簡單越好。由于單片機I/O資源足夠,所以雙數碼管可接成靜態顯示方式,兩個共陰數碼管分別接在P1(秒十位)和P2(秒個位)口,它們的共陰極都接地,安排兩個按鍵接在P3.2(十位數調整)和P3.3(個位數調整)上,為了方便計時,選用12MHz的晶體。為了達到精確計時,選用定時器方式2,每計數250重載一次,即250us,定義一整數變量計數重載次數,這樣計數4000次即為一秒。定義兩個字節變量S10和S1分別計算秒十位和秒個位。編得如下程序:
#include <at89x52.h>
Code unsigned char Seg7Code[16]=        //用十六進數作為數組下標,可直接取得對應的七段編碼字節
// 0     1    2     3     4    5     6     7     8     9    A     b     C     d    E    F
{0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
void main( void )
{
        unsigned int us250 = 0;
        unsigned char s10 = 0;
        unsigned char s1 = 0;
        unsigned char key10 = 0;        //記憶按鍵狀態,為1按下
        unsigned char key1 = 0;                //記憶按鍵狀態,為1按下
        //初始化定時器 Timer0
        TMOD = (TMOD & 0xF0) | 0x02;
        TH1 = -250;        //對于8位二進數來說,-250=6,也就是加250次1時為256,即為0
        TR1 = 1;
        while(1){                                                                                        //----------循環1
                P1 = Seg7Code[ s10 ];        //顯示秒十位
                P2 = Seg7Code[ s1 ];        //顯示秒個位
                while( 1 ){                                                                        //----------循環2
                        //計時處理
if( TF0 == 1 ){
                                TF0 = 0;
                                if( ++us250 >= 4000 ){
                                        us250 = 0;
                                        if( ++s1 >= 10 ){
                                                s1 = 0;
                                                if( ++s10 >= 6 ) s10 = 0;
                                        }
                                        break;        //結束“循環2”,修改顯示
                                }
                        }
                        //按十位鍵處理
                        P3.2 = 1;                //P3.2作為輸入,先要輸出高電平
                        if( key10 == 1 ){        //等松鍵
        if( P3.2 == 1 )        key10=0;
                        }
else{                        //未按鍵
                                if( P3.2 == 0 ){
                                        key10 = 1;
        if( ++s10 >= 6 ) s10 = 0;
                                        break; //結束“循環2”,修改顯示
                                }
                        }
                        //按個位鍵處理
                        P3.3 = 1;                //P3.3作為輸入,先要輸出高電平
                        if( key1 == 1 )        //等松鍵
{        if( P3.3 == 1 )        key1=0;        }
                        else        {                        //未按鍵
                                if( P3.3 == 0 ){        key1 = 1;
        if( ++s1 >= 10 ) s1 = 0;
                                        break; //結束“循環2”,修改顯示
                                }
                        }
                } //循環2’end
        }//循環1’end
}//main’end
                               
第三節:十字路口交通燈
如果一個單位時間為1秒,這里設定的十字路口交通燈按如下方式四個步驟循環工作:
60個單位時間,南北紅,東西綠;
10個單位時間,南北紅,東西黃;
60個單位時間,南北綠,東西紅;
10個單位時間,南北黃,東西紅;
解:用P1端口的6個引腳控制交通燈,高電平燈亮,低電平燈滅。
#include <at89x52.h>
//sbit用來定義一個符號位地址,方便編程,提高可讀性,和可移植性
sbit SNRed        =P1^0;                //南北方向紅燈
sbit SNYellow        =P1^1;                //南北方向黃燈
sbit SNGreen        =P1^2;                //南北方向綠燈
sbit EWRed        =P1^3;                //東西方向紅燈
sbit EWYellow        =P1^4;                //東西方向黃燈
sbit EWGreen        =P1^5;                //東西方向綠燈
/* 用軟件產生延時一個單位時間 */
void Delay1Unit( void )                       
{
        unsigned int i, j;
        for( i=0; i<1000; i++ )
                for( j<0; j<1000; j++ );        //通過實測,調整j循環次數,產生1ms延時
//還可以通過生成匯編程序來計算指令周期數,結合晶體頻率來調整j循環次數,接近1ms
}
/* 延時n個單位時間 */
void Delay( unsigned int n ){ for( ; n!=0; n-- ) Delay1Unit(); }
void main( void )
{
        while( 1 )
        {
                SNRed=0; SNYellow=0; SNGreen=1; EWRed=1; EWYellow=0; EWGreen=0; Delay( 60 );
                SNRed=0; SNYellow=1; SNGreen=0; EWRed=1; EWYellow=0; EWGreen=0; Delay( 10 );
                SNRed=1; SNYellow=0; SNGreen=0; EWRed=0; EWYellow=0; EWGreen=1; Delay( 60 );
                SNRed=1; SNYellow=0; SNGreen=0; EWRed=0; EWYellow=1; EWGreen=0; Delay( 10 );
        }
}

第四節:數碼管驅動
顯示“12345678”
P1端口接8聯共陰數碼管SLED8的段極:P1.7接段h,…,P1.0接段a
P2端口接8聯共陰數碼管SLED8的段極:P2.7接左邊的共陰極,…,P2.0接右邊的共陰極
方案說明:晶振頻率fosc=12MHz,數碼管采用動態刷新方式顯示,在1ms定時斷服務程序中實現
#include <at89x92.h>
unsigned char DisBuf[8];         //全局顯示緩沖區,DisBuf[0]對應右SLED,DisBuf[7]對應左SLED,
void DisplayBrush( void )
{        code unsigned char cathode[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};        //陰極控制碼
Code unsigned char Seg7Code[16]=        //用十六進數作為數組下標,可直接取得對應的七段編碼字節
{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
static unsigned char i=0; // (0≤i≤7) 循環刷新顯示,由于是靜態變量,此賦值只做一次。
        P2 = 0xff;                //顯示消隱,以免下一段碼值顯示在前一支SLED
        P1 = Seg7Code[ DisBuf[i] ];        //從顯示緩沖區取出原始數據,查表變為七段碼后送出顯示
P2 = cathode[ i ];                        //將對應陰極置低,顯示
if( ++i >= 8 ) i=0;                //指向下一個數碼管和相應數據
}
void Timer0IntRoute( void ) interrupt 1
{
        TL0 = -1000;                //由于TL0只有8bits,所以將(-1000)低8位賦給TL0
        TH0 = (-1000)>>8;        //取(-1000)的高8位賦給TH0,重新定時1ms
        DisplayBrush();
}
void Timer0Init( void )
{        TMOD=(TMOD & 0xf0) | 0x01;        //初始化,定時器T0,工作方式1
        TL0 = -1000;        //定時1ms
        TH0 = (-1000)>>8;
        TR0 = 1;                        //允許T0開始計數
        ET0 = 1;                        //允許T0計數溢出時產生中斷請求
}
void Display( unsigned char index, unsigned char dataValue ){ DisBuf[ index ] = dataValue; }
void main( void )
{
unsigned char i;
for( i=0; i<8; i++ ){ Display(i, 8-i);區qhkode[ DisBuf[i] ];        //; f7,0xfd,0xfb,0xfe};
} //DisBuf[0]為右,DisBuf[7]為左
Timer0Init();
EA = 1;                        //允許CPU響應中斷請求
While(1);
}
第五節:鍵盤驅動
指提供一些函數給任務調用,獲取按鍵信息,或讀取按鍵值。
定義一個頭文檔 <KEY.H>,描述可用函數,如下:
#ifndef _KEY_H_                //防止重復引用該文檔,如果沒有定義過符號 _KEY_H_,則編譯下面語句
#define _KEY_H_                //只要引用過一次,即 #include <key.h>,則定義符號 _KEY_H_
unsigned char keyHit( void );        //如果按鍵,則返回非0,否則返回0
unsigned char keyGet( void );        //讀取按鍵值,如果沒有按鍵則等待到按鍵為止
void keyPut( unsigned char ucKeyVal );        //保存按鍵值ucKeyVal到按鍵緩沖隊列末
void keyBack( unsigned char ucKeyVal );        //退回鍵值ucKeyVal到按鍵緩沖隊列首
#endif

定義函數體文檔 KEY.C,如下:
#include “key.h”
#define KeyBufSize        16        //定義按鍵緩沖隊列字節數
unsigned char KeyBuf[ KeyBufSize ];        //定義一個無符號字符數組作為按鍵緩沖隊列。該隊列為先進
                                                                //先出,循環存取,下標從0到 KeyBufSize-1
unsigned char KeyBufWp=0;        //作為數組下標變量,記錄存入位置
unsigned char KeyBufRp=0;        //作為數組下標變量,記錄讀出位置
//如果存入位置與讀出位置相同,則表明隊列中無按鍵數據
unsigned char keyHit( void )
{        if( KeyBufWp == KeyBufRp ) return( 0 ); else return( 1 ); }

unsigned char keyGet( void )
{        unsigned char retVal;        //暫存讀出鍵值
while( keyHit()==0 );        //等待按鍵,因為函數keyHit()的返回值為 0 表示無按鍵
retVal = KeyBuf[ KeyBufRp ];        //從數組中讀出鍵值
if( ++KeyBufRp >= KeyBufSize ) KeyBufRp=0;        //讀位置加1,超出隊列則循環回初始位置
        return( retVal );
}

void keyPut( unsigned char ucKeyVal )
{        KeyBuf[ KeyBufWp ] = ucKeyVal;        //鍵值存入數組
        if( ++KeyBufWp >= KeyBufSize ) KeyBufWp=0;         //存入位置加1,超出隊列則循環回初始位置
}
/*****************************************************************************************
由于某種原因,讀出的按鍵,沒有用,但其它任務要用該按鍵,但傳送又不方便。此時可以退回按鍵隊列。就如取錯了信件,有必要退回一樣
******************************************************************************************/
void keyBack( unsigned char ucKeyVal )
{
/*
如果KeyBufRp=0; 減1后則為FFH,大于KeyBufSize,即從數組頭退回到數組尾。或者由于干擾使得KeyBufRp超出隊列位置,也要調整回到正常位置,
*/
        if( --KeyBufRp >= KeyBufSize ) KeyBufRp=KeyBufSize-1;
KeyBuf[ KeyBufRp ] = ucKeyVal;        //回存鍵值
}
下面漸進講解鍵盤物理層的驅動。
電路共同點:P2端口接一共陰數碼管,共陰極接GND,P2.0接a段、P2.1接b段、…、P2.7接h段。
軟件共同點:code unsigned char Seg7Code[10] 是七段數碼管共陰編碼表。
Code unsigned char Seg7Code[16]=
// 0     1    2     3     4    5     6     7     8     9    A     b     C     d    E    F
{0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};

例一:P1.0接一按鍵到GND,鍵編號為‘6’,顯示按鍵。
#include <at89x52.h>
#include “KEY.H”
void main( void )
{        P1_0 = 1;                //作為輸入引腳,必須先輸出高電平
while( 1 )        //永遠為真,即死循環
        {        if( P1_0 == 0 )        //如果按鍵,則為低電平
{        keyPut( 6 );        //保存按鍵編號值為按鍵隊列
while( P1_0 == 0 );        //如果一直按著鍵,則不停地執行該循環,實際是等待松鍵
                }
if( keyHit() != 0 )        //如果隊列中有按鍵
P2=Seg7Code[ keyGet() ];        //從隊列中取出按鍵值,并顯示在數碼管上
        }
}
例二:在例一中考慮按鍵20ms抖動問題。
#include <at89x52.h>
#include “KEY.H”
void main( void )
{        P1_0 = 1;                //作為輸入引腳,必須先輸出高電平
while( 1 )        //永遠為真,即死循環
        {        if( P1_0 == 0 )        //如果按鍵,則為低電平
{        delay20ms();        //延時20ms,跳過接下抖動
keyPut( 6 );        //保存按鍵編號值為按鍵隊列
        while( P1_0 == 0 );        //如果一直按著鍵,則不停地執行該循環,實際是等待松鍵
delay20ms();        //延時20ms,跳過松開抖動
                }
if( keyHit() != 0 )        //如果隊列中有按鍵
P2=Seg7Code[ keyGet() ];        //從隊列中取出按鍵值,并顯示在數碼管上
        }
}
例三:在例二中考慮干擾問題。即小于20ms的負脈沖干擾。
#include <at89x52.h>
#include “KEY.H”
void main( void )
{        P1_0 = 1;                //作為輸入引腳,必須先輸出高電平
while( 1 )        //永遠為真,即死循環
        {        if( P1_0 == 0 )        //如果按鍵,則為低電平
{        delay20ms();        //延時20ms,跳過接下抖動
        if( P1_0 == 1 ) continue;        //假按鍵
keyPut( 6 );        //保存按鍵編號值為按鍵隊列
        while( P1_0 == 0 );        //如果一直按著鍵,則不停地執行該循環,實際是等待松鍵
delay20ms();        //延時20ms,跳過松開抖動
                }
if( keyHit() != 0 )        //如果隊列中有按鍵
P2=Seg7Code[ keyGet() ];        //從隊列中取出按鍵值,并顯示在數碼管上
        }
}
例四:狀態圖編程法。通過20ms周期中斷,掃描按鍵。
/****************************************************************************************
采用晶體為12KHz時,指令周期為1ms(即主頻為1KHz),這樣T0工作在定時器方式2,8位自動重載。計數值為20,即可產生20ms的周期性中斷,在中斷服務程序中實現按鍵掃描
*****************************************************************************************/
#include <at89x52.h>
#include “KEY.H”
void main( void )
{       
TMOD = (TMOD & 0xf0 ) | 0x02;        //不改變T1的工作方式,T0為定時器方式2
TH0 = -20;                                        //計數周期為20個主頻脈,即20ms
TL0=TH0;                                                //先軟加載一次計數值
TR0=1;                                                //允許T0開始計數
ET0=1;                                                //允許T0計數溢出時產生中斷請求
EA=1;                                                //允許CPU響應中斷請求
while( 1 )        //永遠為真,即死循環
        {
if( keyHit() != 0 )        //如果隊列中有按鍵
P2=Seg7Code[ keyGet() ];        //從隊列中取出按鍵值,并顯示在數碼管上
        }
}
void timer0int( void ) interrupt 1        //20ms;T0的中斷號為1
{        static unsigned char sts=0;
        P1_0 = 1;                //作為輸入引腳,必須先輸出高電平
switch( sts )
        {
                case 0: if( P1_0==0 ) sts=1; break;        //按鍵則轉入狀態1
                case 1:
if( P1_0==1 ) sts=0;                //假按錯,或干擾,回狀態0
else{ sts=2; keyPut( 6 ); }        //確實按鍵,鍵值入隊列,并轉狀態2
break;
                case 2: if( P1_0==1 ) sts=3; break;        //如果松鍵,則轉狀態3
                case 3:
                        if( P1_0==0 ) sts=2;                //假松鍵,回狀態2
                        else sts=0;                                //真松鍵,回狀態0,等待下一次按鍵過程       
        }
}       
例五:狀態圖編程法。
/****************************************************************************************
如果采用晶體為12MHz時,指令周期為1us(即主頻為1MHz),要產生20ms左右的計時,則計數值達到20000,T0工作必須為定時器方式1,16位非自動重載,即可產生20ms的周期性中斷,在中斷服務程序中實現按鍵掃描
*****************************************************************************************/
#include <at89x52.h>
#include “KEY.H”
void main( void )
{       
TMOD = (TMOD & 0xf0 ) | 0x01;        //不改變T1的工作方式,T0為定時器方式1
TL0 = -20000;                                        //計數周期為20000個主頻脈,自動取低8位
TH0 = (-20000)>>8;                                //右移8位,實際上是取高8位
TR0=1;                                                //允許T0開始計數
ET0=1;                                                //允許T0計數溢出時產生中斷請求
EA=1;                                                //允許CPU響應中斷請求
while( 1 )        //永遠為真,即死循環
        {
if( keyHit() != 0 )        //如果隊列中有按鍵
P2=Seg7Code[ keyGet() ];        //從隊列中取出按鍵值,并顯示在數碼管上
        }
}
void timer0int( void ) interrupt 1        //20ms;T0的中斷號為1
{        static unsigned char sts=0;
TL0 = -20000;                                        //方式1為軟件重載
TH0 = (-20000)>>8;                                //右移8位,實際上是取高8位
        P1_0 = 1;                //作為輸入引腳,必須先輸出高電平
switch( sts )
        {
                case 0: if( P1_0==0 ) sts=1; break;        //按鍵則轉入狀態1
                case 1:
if( P1_0==1 ) sts=0;                //假按錯,或干擾,回狀態0
else{ sts=2; keyPut( 6 ); }        //確實按鍵,鍵值入隊列,并轉狀態2
break;
                case 2: if( P1_0==1 ) sts=3; break;        //如果松鍵,則轉狀態3
                case 3:
                        if( P1_0==0 ) sts=2;                //假松鍵,回狀態2
                        else sts=0;                                //真松鍵,回狀態0,等待下一次按鍵過程       
        }
}       
例六:4X4按鍵。
/****************************************************************************************
由P1端口的高4位和低4位構成4X4的矩陣鍵盤,本程序只認為單鍵操作為合法,同時按多鍵時無效。
這樣下面的X,Y的合法值為0x7, 0xb, 0xd, 0xe, 0xf,通過表keyCode影射變換可得按鍵值
*****************************************************************************************/
#include <at89x52.h>
#include “KEY.H”
unsigned char keyScan( void )        //返回0表示無按鍵,或無效按鍵,其它值為按鍵編碼值
{        code unsigned char keyCode[16]=
        /0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF
{ 0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,   2,   0,   3,   4,   0 };
unsigned char x, y, retVal;
P1=0x0f;                                //低四位輸入,高四位輸出0
x=P1&0x0f;                        //P1輸入后,清高四位,作為X值
P1=0xf0;                                //高四位輸入,低四位輸出0
y=(P1 >> 4) & 0x0f;        //P1輸入后移位到低四位,并清高四位,作為Y值
retVal = keyCode[x]*4 + keyCode[y];        //根據本公式倒算按鍵編碼
if( retVal==0 ) return(0); else return( retVal-4 );               
}
//比如按鍵‘1’,得X=0x7,Y=0x7,算得retVal= 5,所以返回函數值1。
//雙如按鍵‘7’,得X=0xb,Y=0xd,算得retVal=11,所以返回函數值7。
void main( void )
{       
TMOD = (TMOD & 0xf0 ) | 0x01;        //不改變T1的工作方式,T0為定時器方式1
TL0 = -20000;                                        //計數周期為20000個主頻脈,自動取低8位
TH0 = (-20000)>>8;                                //右移8位,實際上是取高8位
TR0=1;                                                //允許T0開始計數
ET0=1;                                                //允許T0計數溢出時產生中斷請求
EA=1;                                                //允許CPU響應中斷請求
while( 1 )        //永遠為真,即死循環
        {
if( keyHit() != 0 )        //如果隊列中有按鍵
P2=Seg7Code[ keyGet() ];        //從隊列中取出按鍵值,并顯示在數碼管上
        }
}
void timer0int( void ) interrupt 1        //20ms;T0的中斷號為1
{        static unsigned char sts=0;
TL0 = -20000;                                        //方式1為軟件重載
TH0 = (-20000)>>8;                                //右移8位,實際上是取高8位
        P1_0 = 1;                //作為輸入引腳,必須先輸出高電平
switch( sts )
        {
                case 0: if( keyScan()!=0 ) sts=1; break;        //按鍵則轉入狀態1
                case 1:
if( keyScan()==0 ) sts=0;                //假按錯,或干擾,回狀態0
else{ sts=2; keyPut( keyScan() ); }        //確實按鍵,鍵值入隊列,并轉狀態2
break;
                case 2: if(keyScan()==0 ) sts=3; break;        //如果松鍵,則轉狀態3
                case 3:
                        if( keyScan()!=0 ) sts=2;                //假松鍵,回狀態2
                        else sts=0;                                //真松鍵,回狀態0,等待下一次按鍵過程       
        }
}       
第六節:低頻頻率計
實例目的:學時定時器、計數器、中斷應用
說明:選用24MHz的晶體,主頻可達2MHz。用T1產生100us的時標,T0作信號脈沖計數器。假設晶體頻率沒有誤差,而且穩定不變(實際上可達萬分之一);被測信號是周期性矩形波(正負脈沖寬度都不能小于0.5us),頻率小于1MHz,大于1Hz。要求測量時標1S,測量精度為0.1%。
解:從測量精度要求來看,當頻率超過1KHz時,可采用1S時標內計數信號脈沖個數來測量信號頻,而信號頻率低于1KHz時,可以通過測量信號的周期來求出信號頻率。兩種方法自動轉換。
對于低于1KHz的信號,信號周期最小為1ms,也就是說超過1000us,而我們用的定時器計時脈沖周期為0.5us,如果定時多計或少計一個脈沖,誤差為1us,所以相對誤差為1us/1000us=0.1%。信號周期越大,即信號頻率越低,相對誤差就越小。
從上面描述來看,當信號頻率超過1KHz后,信號周期就少于1000us,顯然采用上面的測量方法,不能達到測量精度要求,這時我們采用1S單位時間計數信號的脈沖個數,最少能計到1000個脈沖,由于信號頻率不超過1MHz,而我們定時脈沖為2MHz,最差多計或少計一個信號脈沖,這樣相對誤差為1/1000,可見信號頻率越高,相對誤差越小。
信號除輸入到T1(P3.5)外,還輸入到INT1(P3.3)。
unsigned int us100;                        //對100us時間間隔單位計數,即有多少個100us。
unsigned char Second;
unsigned int K64;                                //對64K單位計數,即有多少個64K               
unsigned char oldT0;
unsigned int oldus, oldK64, oldT1;
unsigned long fcy;                                //存放頻率值,單位為Hz
bit HighLow=1;                                //1:表示信號超過1KHz;0:表示信號低于1KHz。
void InitialHigh( void )
{
        IE=0; IP=0; HighLow=1;
TMOD = (TMOD & 0xf0) | 0x02; TH0=-200; TL0=TH0; PX0=1; T0=1;
        TMOD = (TMOD & 0x0f) | 0x50; TH1=0; TL1=0; T1=1; ET1=1;
        Us100=0; Second=0; K64=0;
        oldK64=0; oldT1=0;
        TCON |= 0x50;                //同時置 TR0=1; TR1=1;
        EA = 1;
}
void InitialLow( void )
{
        IE=0; IP=0; HighLow=0;
TMOD = (TMOD & 0xf0) | 0x02; TH0=-200; TL0=TH0; ET0=1; TR0=1;
        INT1 = 1; IT1=1; EX1=1;
        Us100=0; Second=0; K64=0;
        oldK64=0; oldT1=0;
        EA = 1;
}
void T0intr( void ) interrupt 1
{        if( HighLow==0 ) ++us100;
else
if( ++us100 >= 10000 )
{         unsigned int tmp1, tmp2;
TR1=0; tmp1=(TH1<<8) + (TL1); tmp2=K64; TR1=1;
fcy=((tmp2-oldK64)<<16) + (tmp1-oldT1);
oldK64=tmp1; oldT1=tmp2;
Second++;
us100=0;
}
}
void T1intr( void ) interrupt 3        {        ++K64;        }
void X1intr( void ) interrupt 2
{        static unsigned char sts=0;
switch( sts )       
{
case 0: sts = 1; break;
case 1: oldT0=TL0; oldus=us100; sts=2; break;
case 2:
{
        unsigned char tmp1, tmp2;
        TR0=0; tmp1=TL0; tmp2=us100; TR0=1;
        fcy = 1000000L/( (tmp2-oldus)*100L + (256-tmp1)/2 );
        Second ++;
}
        Sts = 0;
        break;
        }
}
void main( void )
{
if( HighLow==1) InitialHigh(); else InitialLow();
        While(1)
        {
if( Second != 0 )
{
Second = 0;
//display fcy  引用前面的數碼管驅動程序,注意下面對T0中斷服務程序的修改
{        unsigned char i;
        for( i=0; i<8; i++ ){ Display(i, fcy%10); fcy /= 10; }
}
if( HighLow==1 )
if( fcy<1000L ){ InitalLow();}  
                        else
                                if( fcy>1000L ){ InitalHigh();}
}
        }
}
//修改T0的中斷服務程序,讓它在完成時標的功能時,同時完成數碼管顯示刷新
void T0intr( void ) interrupt 1
{
        static unsigned char ms = 0;
        if( HighLow==0 ) ++us100;
else
if( ++us100 >= 10000 )
{         unsigned int tmp1, tmp2;
TR1=0; tmp1=(TH1<<8) + (TL1); tmp2=K64; TR1=1;
fcy=((tmp2-oldK64)<<16) + (tmp1-oldT1);
oldK64=tmp1; oldT1=tmp2;
Second++;
us100=0;
}
        if( ++ms >= 10 ){ ms=0; DisplayBrush(); }        //1ms數碼管刷新
}
       
第七節:電子表
單鍵可調電子表:主要學習編程方法。
外部中斷應用,中斷嵌
解:電子表分為工作狀態和調整狀態。平時為工作狀態,按鍵不足一秒,接鍵為換屏‘S’。按鍵超過一秒移位則進入調整狀態‘C’,而且調整光標在秒個位開始。調整狀態時,按鍵不足一秒為光標移動‘M’,超過一秒則為調整讀數,每0.5秒加一‘A’,直到松鍵;如果10秒無按鍵則自動回到工作狀態‘W’。
如果有年、月、日、時、分、秒。四聯數碼管可分三屏顯示,顯示格式為“年月.”、“日.時.”、“分.秒”,從小數點的位置來區分顯示內容。(月份的十位數也可以用“-”和“-1”表示)。
enum status = { Work, Change, Add, Move, Screen }        //狀態牧舉
//計時和調整都是對下面時間數組Time進行修改
unsigned char Time[12]={0,4, 0,6, 1,0, 0,8, 4,5, 3,2};        //04年06月10日08時45分32秒
unsigned char cursor = 12;        //指向秒個位,=0時無光標
unsigned char YmDhMs = 3;        //指向“分秒”顯示        ,=0時無屏顯
static unsigned char sts = Work;
/*
如果cursor不為0,裝入DisBuf的對應數位,按0.2秒周期閃爍,即設一個0.1秒計數器S01,S01為奇數時滅,S01為偶數時亮。
小數點顯示與YmDhMs變量相關。
*/
void DisScan( void )        //動態刷新顯示時調用。沒編完,針對共陰數碼管,只給出控控制算法
{
        //DisBuf每個顯示數據的高四位為標志,最高位D7為負號,D6為小數點,D5為閃爍
unsigned char tmp;
        tmp = Seg7Code[?x & 0x1f ];                //設?x為顯示數據,高3位為控制位,將低5位變為七段碼
        if( ?x & 0x40 ) tmp |= 0x80;        //添加小數點
        if( ?x & 0x20 ){ if( S01 & 0x01 ) tmp=0; } //閃爍,S01奇數時不亮
        //這里沒有處理負號位
        //將tmp送出顯示,并控制對應數碼管動作顯示
}
void Display( void )                //根據狀態進行顯示
{
        if( cursor != 0 ){ YmDhMs=(cursor+3)/4; }        //1..4=1; 5..8=2; 9..12=3
for( i=(YmDhMs-1)*4; i<(YmDhMs)*4; i++ )
{        unsigned char j = i%4;
        Disbuf[j] = Time[i];
        if( i == (cursor-1) ) Disbuf[j] |= 0x20;        //閃爍,cursor!=0時才閃爍
        if(        (i==9) ||                //小數點:分個位
                (i==7) ||                //小數點:時個位
                (i==5) ||                //小數點:日個位
                (i==3)                 //小數點:月個位
        )        Disbuf[j] |= 0x40;
        //if(i==2){ if(Time[2]==1) DisBuf[2]=“-1”; else DisBuf=“-”; }
}
//工作狀態:根據YmDhMs將屏數據裝入DisBuf
        //調整狀態:根據cursor將屏數據裝入DisBuf
}
void KeyScan( void )                //根據狀態掃描按鍵
void ProcessKey( void )                //根據狀態處理鍵信息
{
        keyVal = KeyGet();
        if( keyVal == 0 ) return;
        switch( sts )
        {
                case Work:
                        if( keyVal ==‘S’)
                        {
                                if( --YmDhMs == 0 ) YmDhMs = 3;        //換屏
                        }
                        if( keyVal == ‘C’)
                        {
                                sts = Change;
                                YmDhMs = 3;
                                Cursor = 12;
                        }
                        break;
                case Change:
                        if( keyVal == ‘W’ )
                        if( keyVal == ‘A’ )
                        if( keyVal == ‘M’ ) //根據cursor
                        break;
        }               
}
第八節:串行口應用
使用晶體頻率為22.1184MHz的AT89C52單片機,串行口應用工作方式1,以9600bps的波特率向外發送數據,數據為十個數字‘0’到‘9’,循環不斷地發送。

解:數字字符為增量進二進制碼,‘0’對應0x30,‘1’= ‘0’+ 1 = 0x31,從‘0’到‘9’對應編碼為0x30到0x39,記憶二進制編碼較難,實際編程中用單引號括起對應字符表示引用該字符的二進制編碼值,如‘?’表示引用?號的編碼值。
在用11.0592MHz晶體時,9600bps的初始化分頻初值為-6,現晶頻加倍,如果其它條件不變,只有分頻初始加倍為-12,才能得到9600bps;如果想得到2400bps(速率降4倍),分頻初始自然加大4倍,即為-48。根據題意編得如下程序:
#include <at89x52.h>
void main( void )
{
        TMOD = (TMOD & 0x0F) | 0x20;
        TH1 = -12;
        PCON |= 0x80;        //SMOD = 1
        TR1 = 1;
        SCON = 0x42;
        while( 1 )
        {
                if( TI==1 )
                {
                        static unsigned char Dat=‘0’;
                        SBUF = Dat;
                        TI = 0;
                        If( ++Dat > ‘9’)        Dat=‘0’;
                }
        }
}
在上題的基礎上,改為2400bps,循環發送小寫字母‘a’到‘z’,然后是大寫字母‘A’到‘Z’。
#include <at89x52.h>
void main( void )
{
        TMOD = (TMOD & 0x0F) | 0x20;
        TH1 = -96;                //注意不用倍頻方式
        PCON &= 0x7F;        //SMOD = 0
        TR1 = 1;
        SCON = 0x42;
        while( 1 )
        {
                if( TI==1 )
                {
                        static unsigned char Dat=‘a’;
                        SBUF = Dat;
                        TI = 0;
                        //If( ++Dat > ‘9’)        Dat=‘0’;
                        ++Dat;
                        if( Dat == (‘z’+1) )  Dat=‘A’;
                        if( Dat == (‘Z’+1) )  Dat=‘a’;
                }
        }
}
上述改變值時,也可以再設一變量表示當前的大小寫狀態,比如寫成如下方式:
                        ++Dat;
                        {
                                static unsigned char Caps=1;
                                if( Caps != 0 )
                                        if( Dat>‘Z’){ Dat=‘a’; Caps=0; }
                                else
                                        if( Dat>‘z’){ Dat=‘A’; Caps=1; }
                        }
如下寫法有錯誤:因為小b比大Z的編碼值大,所以Dat總是‘a’
                        ++Dat;
                        if( Dat>‘Z’){ Dat=‘a’}
                        else if( Dat>‘z’){ Dat=‘A’}
有A和B兩臺單片機,晶體頻率分別為13MHz和14MHz,在容易編程的條件下,以最快的速度進行雙工串行通信,A給B循環發送大寫字母從‘A’到‘Z’,B給A循環發送小寫字母從‘a’到‘z’,雙方都用中斷方式進行收發。
解:由于晶體頻率不同,又不成2倍關系,所以只有通信方式1和方式3,由于方式3的幀比方式1多一位,顯然方式3的有效數據(9/11)比方式1(8/10)高,但要用方式3的第9位TB8來發送數據,編程難度較大,這里方式1較容易編程。
在計算最高速率時,由于單方程,雙未知數,又不知道波特率為多少,所以要綜合各方面的條件,估算出A和B的分頻常數,分別為-13和-14時,速率不但相同,且為最大值。如下給出A機的程序:
#include <at89x52.h>
void main( void )
{
        TMOD = (TMOD & 0x0F) | 0x20;
        TH1 = -13;                //注意用倍頻方式
        PCON |= 0x80;        //SMOD = 1
        TR1 = 1;
        SCON = 0x52;        //REN = 1
        ES = 1;
        EA = 1;
        while( 1 );
}
void RS232_intr( void ) interrupt 4                //注意RI和TI任一位變為1都中斷
{
        unsigned char rDat;
        if( RI == 1 ){ RI=0; rDat=SBUF; }
        if( TI==1 )
        {
                static unsigned char tDat=‘a’;
                SBUF = tDat;
                TI = 0;
                If( ++Dat > ‘z’)        Dat=‘a’;
        }
}

多機通位
在方式2和方式3,SM2只對接收有影響,當SM2=1時,只接收第9位等于1的幀(偽地址幀),而SM2=0時,第9位不影響接收。
多機通信中,地址的確認與本機程序有關,所以可以實現點對點、點對組、以及通播方式的通信。
如果收發共用一總線,任何時刻只有一個發送源能占用總線發送數據,否則發生沖突。由此可構造無競爭的令牌網;或者多主競爭總線網。
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:886808 發表于 2021-4-1 10:42 來自手機 | 只看該作者
這欄貼內容不全,換電腦板
回復

使用道具 舉報

板凳
ID:886808 發表于 2021-4-1 10:43 來自手機 | 只看該作者
這欄貼內容不全,換電腦板
回復

使用道具 舉報

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

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产特级毛片aaaaaa喷潮 | 欧美日韩亚洲一区 | 色天堂视频 | av入口| av大全在线| 91精品久久久 | 欧美亚洲国产日韩 | 干出白浆视频 | 成人精品国产 | 热99精品视频| 成人精品国产 | 欧美日韩在线一区二区 | 国产精品区二区三区日本 | 一区视频 | 国产二区在线播放 | 九九爱这里只有精品 | 国产精品国产三级国产aⅴ中文 | 国产精品久久久久久久久久免费 | 亚洲精选久久 | 亚洲三级国产 | 91天堂网 | 色天堂影院 | 99久久精品国产毛片 | 精品成人免费一区二区在线播放 | 欧美精品在线一区 | 欧美国产亚洲一区二区 | 午夜电影一区二区 | 久久精品视频网站 | 欧美日韩亚洲一区 | 亚洲天堂一区二区 | 亚洲国产精品久久 | 日韩久久久久久 | 影音先锋中文字幕在线观看 | 国产91丝袜在线18 | 永久免费av | 国产精品亚洲综合 | 欧美视频在线播放 | 精品亚洲第一 | 黄色一级免费 | 在线三级电影 | 91久久精品 |