51單片機定時器使用

来源:https://www.cnblogs.com/TomDing/archive/2018/05/19/9060181.html
-Advertisement-
Play Games

51單片機定時器 考試考完了,閑得無聊,正好手頭有一塊51學習板,搗鼓一下。控制任務如下: P1.0 控制一個LED燈,亮0.5s,滅0.5s。 設計思路:這裡我們只用定時器,不用軟延時。51的定時器最多定時60ms,所以我們設置定時器每 50ms中斷一次,通過在中斷程式設置一個變數來統計中斷次數, ...


51單片機學習

一直想給女兒做一個平衡小車玩具,想用PLC做,感覺難度,用單片機吧,都快20年沒用了。
最近考試考完了,時間和資源都有,正好可以搗鼓一下。看了郭天翔的視頻,講的很不錯,
邊學邊寫。

定時器使用

控制任務:

P1.0 控制一個LED燈,亮0.5s,滅0.5s。

設計思路:這裡我們只用定時器,不用軟延時。51的定時器最多定時60ms,所以我們設置定時器每
50ms中斷一次,通過在中斷程式設置一個變數來統計中斷次數,從而實現較長時間的定時。這裡我們
是每500ms執行一次燈亮燈滅的動作,所以每10個中斷等於500ms(50ms x 10)。第6行,全局變數
timer50msCount 就是中斷次數。第19-23行,當timer50msCount 為10時,代表500ms時間到,把
P1.0 取反,動作一次。

這裡有個繁瑣的地方,定時器的初值需要手工計算。不過前人開發了一下小程式,直接拿過來用就可以了。
見下圖。把自動生成代碼中的第一行刪掉就可以了。

程式如下:

#include <reg52.h>
typedef unsigned int uint;
typedef unsigned char uchar;

sbit P10 = P1 ^ 0;
// 第 6 行
uchar timer50msCount = 0;   

void Timer0Init(void);

void main()
{
    EA = 1;         // 開總中斷
    ET0 = 1;        // 開定時器 0 中斷
    Timer0Init();

    while (1)
    {
        // 每500ms允許if語句塊中的程式
        // 19 - 23 行
        if (timer50msCount == 10)
        {
            timer50msCount = 0;
            P10 = ~P10;
        }
    }
}

void Timer0Init(void) //[email protected]
{
    TMOD &= 0xF0; // 設置定時器模式,這裡為T0
    TMOD |= 0x01; // 設置定時器工作方式1,為16為定時器
    TL0 = 0x00;   // 設置定時器低位初值
    TH0 = 0x4C;   // 設置定時器高位初值
    TF0 = 0;      // 清楚TF0溢出標誌位
    TR0 = 1;      // 啟動定時器0開始計時
}


// 定時器0中斷子程式
void timer0Interrupt() interrupt 1
{
    timer50msCount++;
    // 每次中斷時,定時器初值為0,需重新設置定時器初值,保持50ms
    // 時間不變
    TL0 = 0x00;
    TH0 = 0x4C;
}

獨立鍵盤

控制任務:

按下 s2 鍵,led 燈亮,鬆手燈滅。

見面的程式用到一個10ms的延時函數,可以用軟體自動生成,見下圖。
圖片選了1ms,和10ms的延時函數是不一致。1ms的程式里包含了_nop()_指令,
使用這條指令,需要在在代碼中使用頭文件intrins.h。

#include <intrins.h>

程式如下:

#include <reg52.h>

typedef unsigned int uint;
typedef unsigned char uchar;

sbit wela = P2 ^ 7;
sbit dula = P2 ^ 6;
sbit s2 = P3 ^ 4;       // s2 按鍵
sbit d1 = P1 ^ 0;      //  led 燈

uchar code table[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71};

uchar code welaTable[] = {
    0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf};

void Delay10ms();

void main()
{
    uchar num = 0;
    wela = 1;
    P0 = 0xfe;
    wela = 0;

    // 把P3口置高電平
    P3 = 0xff;
    while (1)
    {
        // s2 == 0, 表示 s2 變為低電平,此時按鍵按下
        if (s2 == 0)
        {
            Delay10ms();  // 去按下時的抖動
            if (s2 == 0)
            {
                d1 = 0;  // 點亮led
                num++;   // 按鍵次數計數
                if (num > 9)  // led 數位管計數最大為9, 超出置為0
                {
                    num = 0;
                }
            }
            
            // 按鍵從按下到釋放,時間有100-200ms,單片機的
            // 掃描時間很快,此處防止鍵盤按的過程中,num值反覆加
            while (!s2)
                ;
            // 鍵盤鬆手是去毛刺延時
            Delay10ms();
        }
        else
        {
            d1 = 1;
        }

        dula = 1;
        P0 = table[num];
        dula = 0;
    }
}

void Delay10ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 18;
    j = 235;
    do
    {
        while (--j)
            ;
    } while (--i);
}

矩陣鍵盤

控制任務:

4 x 4 的矩陣鍵盤,按下第0個鍵,6個數位管都顯示0,按下第一個鍵,6個數位管顯示1,一次類推。

程式如下:

#include <reg52.h>

typedef unsigned int uint;
typedef unsigned char uchar;

sbit wela = P2 ^ 7;
sbit dula = P2 ^ 6;

// 數字碼表,數字最後個0x00為不顯示.
uchar code numberTable[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71,
    0x00};

// 行掃描編碼表, 每個元素代表一行
uchar code keyTable[] = {
    0xfe, 0xfd, 0xfb, 0xf7};

void delay10ms();
uchar keyScan();
void show(uchar num);

// 51的c編譯器比較原始,函數中返回的值只能用
// 全局變數
uchar num = 16;

void main()
{
    // 此處傳入16,數字碼表選擇0x00,
    // 上電時關閉數位管,防止顯示亂碼, 
    show(16);

    wela = 1;
    P0 = 0xc0;
    wela = 0;

    while (1)
    {
        // keyScan函數返回 0 - 15 數字
        uchar keyNum = keyScan();
        show(keyNum);
    }
}

uchar keyScan()
{

    // 鍵盤為4X4的鍵盤矩陣,每個for迴圈掃描一行,while迴圈中掃描列,
    // num變數返回這個鍵盤矩陣的鍵位,
    // 比如第0個鍵按下,返回0,第一個返回1,以此類推
    uchar i = 0, temp;
    for (; i < 4; i++)
    {
        P3 = keyTable[i];
        temp = P3 & 0xf0;
        while (temp != 0xf0)
        {
            delay10ms();
            temp = P3 & 0xf0;
            switch (temp)
            {
            case 0xe0:
                num = 4 * i + 0;
                break;
            case 0xd0:
                num = 4 * i + 1;
                break;
            case 0xb0:
                num = 4 * i + 2;
                break;
            case 0x70:
                num = 4 * i + 3;
                break;
            }
        }
    }
    return num;
}

void show(uchar num2)
{
    dula = 1;
    P0 = numberTable[num2];
    dula = 0;
}

void delay10ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 18;
    j = 235;
    do
    {
        while (--j)
            ;
    } while (--i);
}

動態顯示

控制任務:

在六個數位管上分別顯示1,2,3,4,5,6

程式如下:

#include <reg52.h>

typedef unsigned int uint;
typedef unsigned char uchar;

sbit wela = P2 ^ 7;
sbit dula = P2 ^ 6;

// 段碼表
uchar code table[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71};

// 位碼表
uchar code welaTable[] = {
    0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf};

void Delay1ms();

void main()
{
    uchar i;

    while (1)
    {
        // 依次只顯示一個數位管,但因速度很快,
        // 從而造成眼睛的錯覺
        for (i = 0; i < 6; i++)
        {
            P0 = 0xFF;  // 消影
            wela = 1;
            P0 = welaTable[i];
            wela = 0;
            dula = 1;
            P0 = table[i + 1];
            dula = 0;
            Delay1ms();  // 務必要加延時
        }
    }
}

void Delay1ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 2;
    j = 199;
    do
    {
        while (--j)
            ;
    } while (--i);
}

ADC0804 模數轉換

/************************
 *  通過AD晶元把模擬量電壓值轉換為數字量 *
 *  轉換後的數字量顯示在LED數位管上   *
 ************************/


#include <reg52.h>
#include <intrins.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit dula = P2 ^ 6;     // 段選信號
sbit wela = P2 ^ 7;     // 位選信號
sbit adWr = P3 ^ 6;     // AD 開始轉換信號,低電平有效
sbit adRd = P3 ^ 7;     // AD 開始讀取信號,低電平有效
sbit adCs = P0 ^ 7;     // AD 片選信號,低電平有效

// LED 數位管段選碼表
uchar code numberTable[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71,
    0x00};

// LED 數位管位選碼表
uchar code welaTable[] = {
    0x5f, 0x6f, 0x77, 0x7b, 0x7d, 0x7e};

void delay5ms();

void showNumOnSelectedLED(uchar number, uchar welaNum);

void main()
{
    uchar i, onesPlace, tensPlace, hundredsPlace, adValue;

    // AD 晶元片選使能
    adCs = 0;

    while (1)
    {
        // 轉換開始
        _nop_();
        adWr = 0;
        _nop_();
        adWr = 1;

        // 顯示數值,放在adWr後,是為了增加AD晶元的轉換時間
        for (i = 0; i < 10; i++)
        {
            showNumOnSelectedLED(onesPlace, 0);
            showNumOnSelectedLED(tensPlace, 1);
            showNumOnSelectedLED(hundredsPlace, 2);
        }

        // adRd讀取轉換後的數字量
        _nop_();
        adRd = 0;
        _nop_();
        adValue = P1;
        adRd = 1;

        // adValue 是讀取到的數字量,然後把個、十、百分離開來
        hundredsPlace = adValue / 100;
        tensPlace = adValue % 100 / 10;
        onesPlace = adValue % 10;
    }
}

/** 
 * @brief  在指定的LED數位上顯示指定的數字
 * @note   例如,number = 3, placeOfNum = 0, 那麼為最右邊的第0個數位管顯示數字3 
 * @param  number: 顯示的數字 0 - F 
 * @param  placeOfNum:  顯示的位置,從右到左,分別是 0 - 6 位
 * @retval None
 */
void showNumOnSelectedLED(uchar number, uchar placeOfNum)
{
    // 送位選數據前關閉所有顯示,防止打開位選鎖存時,
    // 原來段選數據通過位選鎖存器造成混亂
    P0 = 0xff;


    wela = 1;
    P0 = welaTable[placeOfNum];
    wela = 0;
    dula = 1;
    P0 = numberTable[number];
    dula = 0;
    delay5ms();
}

/** 
 * @brief  延時函數
 * @note   
 * @retval None
 */
void delay5ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 9;
    j = 244;
    do
    {
        while (--j)
            ;
    } while (--i);
}

DAC0832 數模轉換


/*
單片機控制DAC0832晶元輸出電流,讓發光二極體D12由滅均勻變到
最亮,再有最亮均勻熄滅。最亮和最暗時蜂鳴器發聲
*/

#include <intrins.h>
#include <reg52.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit dula = P2 ^ 6;
sbit wela = P2 ^ 7;
sbit daWr = P3 ^ 6; 
sbit daCs = P3 ^ 2; 
sbit beep = P2 ^ 3; // 蜂鳴器

void delay100ms();
void delay50ms();

void main() {
  uchar flag, value;

  // 關閉數位管
  dula = 0;
  wela = 0;

  // daCs 和 daWr 低電平時,開始DA開始轉換
  daCs = 0;
  daWr = 0;

  // P0 口清零
  P0 = 0x00;

  while (1) {
    if (flag == 0) {
      value += 5;
      P0 = value;
      if (value == 255) {
        flag = 1;
        beep = 0;
        delay100ms();
        beep = 1;
      }
      delay50ms();
    } else {
      value -= 5;
      P0 = value;
      if (value == 0) {
        flag = 0;
        beep = 0;
        delay50ms();
        beep = 1;
      }
      delay50ms();
    }
  }
}

void delay100ms()  //@11.0592MHz
{
  unsigned char i, j;

  i = 180;
  j = 73;
  do {
    while (--j)
      ;
  } while (--i);
}

void delay50ms()  //@11.0592MHz
{
  unsigned char i, j;

  i = 90;
  j = 163;
  do {
    while (--j)
      ;
  } while (--i);
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在我們使用WPF過程中,不可避免並且超級喜歡使用MVVM框架。 那麼,使用MVVM的出發點是視覺與業務邏輯分離,即UI與數據分離 諸如下麵的問題: 刪除操作,假如需要先執行一部分數據的處理,然後刪除界面列表中的子項,之後再執行其它數據的處理。請問此業務該放置於Xaml.cs文件,還是ViewMode ...
  • 回到目錄 Monitor的介紹 1.Monitor.Enter(object)方法是獲取鎖,Monitor.Exit(object)方法是釋放鎖,這就是Monitor最常用的兩個方法,當然在使用過程中為了避免獲取鎖之後因為異常,致鎖無法釋放,所以需要在try{} catch(){}之後的finall ...
  • 工作上和另一個公司對接,調對方的api需要用到md5加密,加密前要使用HttpUtility.UrlEncode,對方介面一直返回驗證錯誤,定位了問題發現是中文編碼使用HttpUtility.UrlEncode導致的。 C#中HttpUtility.UrlEncode返回的url編碼是小寫的,但對方 ...
  • 一、 集合概述 沒有掌握集合的知識之前,我們通常都是用數組來存儲元素。數組有一個很明顯的特點就是,長度是固定的。假設,用數組存儲一組員工信息,顯然公司的員工數量是有流動性的,如果來了新員工,那麼以現有的知識來思考,這個數組只能重新定義。能否建立一個動態的 “數組”,使我們可以對其進行動態的操作呢?下 ...
  • 學習瞭解.NET CORE有段時間,沒有用其做項目的主要原因就是這麼多年積累的類庫相容問題。今天就先解決SqlHelper的相容性: 建立類庫,目標框架選擇.NET Core2.0,複製粘貼代碼。 問題:SqlCommand 解決:NuGet-瀏覽選項卡:System.Data.SqlClient- ...
  • 目錄 一、進階命令 二、系統命令 三、壓縮和歸檔 3.1、歸檔 3.2、壓縮 3.3、歸檔並壓縮 歸檔、接檔:tar -cf、tar -tvf 壓縮、解壓:gzip、gunzip 歸檔並壓縮:tar -czf 接檔並解壓:tar -xzf 一、進階命令 查看幫助和方法 二、Linux系統命令 三、壓 ...
  • 索引: 開源Spring解決方案--lm.solution 參看代碼 GitHub: intelij-idea.txt 一、Linux (DeepinOS) 環境 1.官網下載 2.解壓 3.設置執行許可權 4.安裝 5.手動創建桌面快捷圖標 蒙 2018-05-19 20:50 周六 ...
  • 背景 和女朋友異地戀一年多,為了保持感情我提議每天晚上視頻聊天一次。 從好上開始,到現在,一年多也算堅持下來了。 問題 有時候聊天的過程中,我的網路或者她的網路可能會不好,視頻就會卡住,聽不到對方的聲音,過一會兒之後才會恢復。 中間雙方可能就要不斷的確認網路是否恢復,但是有時候會: 她:“你可以聽到 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...