1. 程式人生 > >51微控制器入門教程(2)——實現流水燈

51微控制器入門教程(2)——實現流水燈

一、搭建流水燈電路

在Proteus中搭建流水燈電路如圖 在這裡插入圖片描述

二、流水燈程式

我們可以把流水燈看作依次點亮若干個燈。 程式如下:

#include <reg52.h>
sbit led1 = P2^0;
sbit led2 = P2^1;
sbit led3 = P2^2;
sbit led4 = P2^3;
sbit led5 = P2^4;
sbit led6 = P2^5;
sbit led7 = P2^6;
sbit led8 = P2^7;

void main()
{
    //點亮第一個燈
    led1 = 1;
    led2 = 0;
    led3 = 0;
    led4 = 0;
    led5 = 0;
    led6 = 0;
    led7 = 0;
    led8 = 0;
    //點亮第二個燈
    led1 = 0;
    led2 = 1;
    led3 = 0;
    led4 = 0;
    led5 = 0;
    led6 = 0;
    led7 = 0;
    led8 = 0;    
    //點亮剩餘的燈
    //省略……
    while(1);
}

編譯並下載程式到模擬中,觀察現象發現只有第二個燈是亮的??? 什麼鬼???

2.1 延時程式

微控制器的執行指令速度非常快,一個晶振是12MHz的微控制器執行一條指令的速度是微秒級的,所以點亮第一個燈的時間太短了,以至於我們根本沒有察覺。 因此我們需要一個延時的語句。 實現延時的方法就是迴圈執行很多次空指令。程式如下:

//延時一秒的程式
int i,j;
for(i = 0;i < 110; ++i)
{
  for(j = 0; j < 1000; ++j)
  {
    ;//什麼也不做
  }
}

然後我們就可以把流水燈的程式改成這樣的:

#include <reg52.h>
sbit led1 = P2^0;
sbit led2 = P2^1;
sbit led3 = P2^2;
sbit led4 = P2^3;
sbit led5 = P2^4;
sbit led6 = P2^5;
sbit led7 = P2^6;
sbit led8 = P2^7;

void main()
{
    int i,j;
   //點亮第一個燈
    led1 = 1;
    led2 = 0;
    led3 = 0;
    led4 = 0;
    led5 = 0;
    led6 = 0;
    led7 = 0;
    led8 = 0;
    //延時1秒 
	for(i = 0;i < 110; ++i)
	{
	  for(j = 0; j < 1000; ++j)
	  {
	    ;//什麼也不做
	  }
	}
    //點亮第二個燈
    led1 = 0;
    led2 = 1;
    led3 = 0;
    led4 = 0;
    led5 = 0;
    led6 = 0;
    led7 = 0;
    led8 = 0;    
    //點亮剩餘的燈
    //省略……
    while(1);
}

編譯並下載程式到模擬中,觀察現象發現首先第一個燈亮,過了一會兒第二個燈亮。

2.2 延時函式

我們剩下的任務就是依次點亮每個燈,但是每次點亮一個燈就需要寫一段延時程式,很麻煩! 為了程式的可讀性(toulan),可以把延時程式寫成一個子函式,隨時供我們使用。 C語言中子函式的定義方式如下

返回值型別 函式名 (引數1,引數2,……)
{
  函式體;
}

這樣我們就可以把延時函式寫成這樣:

void delay1s()
{
  int i,j;
  for(i = 0; i<110;++i)
  {
    for(j = 0; j<1000;++j)
    {
      //什麼也不做
    }
  }
}

幾點說明:

  • void:因為該延時函式不需要返回值,所以寫為void
  • delay1s:該函式的函式名,命名需要符合C語言的識別符號命名規則。
  • (): 不需要傳入引數,所以括號中為空 至此我們可以把流水燈程式寫為以下形式:
#include <reg52.h>

sbit led1 = P2^0;
sbit led2 = P2^1;
sbit led3 = P2^2;
sbit led4 = P2^3;
sbit led5 = P2^4;
sbit led6 = P2^5;
sbit led7 = P2^6;
sbit led8 = P2^7;

//延時1s
void delay1s()
{	 
    int i ,j;
   	for(i = 0;i<110; ++i){
	  for(j = 0;j<1000;++j){
	    ;
	  }
	}
}

void main()
{
    //點亮第一個燈
    led1 = 1;
    led2 = 0;
    led3 = 0;
    led4 = 0;
    led5 = 0;
    led6 = 0;
    led7 = 0;
    led8 = 0;

	//延時1s
	delay1s();

    //點亮第二個燈
    led1 = 0;
    led2 = 1;
    led3 = 0;
    led4 = 0;
    led5 = 0;
    led6 = 0;
    led7 = 0;
    led8 = 0;    
    //點亮剩餘的燈
    //省略……
    while(1);
}

2.3 按位元組定址

我們可以看到,上面的程式碼十分冗長,每次點亮一個燈需要8條語句,那麼如何簡化? 比如把 led1 = 1;led2 = 0; led3 = 0; led4 = 0; led5 = 0; led6 = 0; led7 = 0; led8 = 0; 這8條語句替代為P2 = 0000 0001??? 答案是可以的。程式碼如下

unsigned char a = 0x01;  //0x01是0000 0001的16進位制形式
P2 = a;//相當於led1 = 1;led2 = 0; led3 = 0;  led4 = 0; led5 = 0; led6 = 0; led7 = 0; led8 = 0;

至此,我們可以把流水的程式碼優化為如下形式:

#include <reg52.h>

//延時1s
void delay1s()
{	 
    int i ,j;
   	for(i = 0;i<110; ++i){
	  for(j = 0;j<1000;++j){
	    ;
	  }
	}
}

void main()
{
	unsigned char a1 = 0x01 ;    // 0000 0001
	unsigned char a2 = 0x02;    // 0000 0010
    //點亮第一個燈
	P2 = a1;

	//延時1s
	delay1s();

    //點亮第二個燈
	P2 = a2;    
    //點亮剩餘的燈
    //省略……
    while(1);
}

2.4 邏輯移位

依次點亮8個燈,每點亮一個燈都需要一句賦值語句還是很麻煩 。 所以可以使用邏輯移位語句,每次賦值後,將數值左移一位。 C語言邏輯左移程式碼如下:

unsigned char a = 0x01;  //a = 0000 0001
unsigned char b = a<<1;  // b = 0000 0010
usingned char c = a<<3;   //c = 0000 1000

至此,我們可以把流水燈的程式碼優化如下:

#include <reg52.h>
//延時1s
void delay1s()
{	 
    int i ,j;
   	for(i = 0;i<110; ++i){
	  for(j = 0;j<1000;++j){
	    ;
	  }
	}
}

void main()
{
    //初始化
	unsigned char a = 0x01;
    while(1)
	{
	  //迴圈點亮流水燈
	  P2 = a;   
	  a = a<<1;
	  delay1s();
	}
}

編譯並下載程式到模擬中,觀察現象發現8個燈依次亮過之後不再亮了。

2.5 條件判斷

因為在移位操作中,當變數a的值為1000 0000時,再次執行左移操作,a 中的1就溢位了,因此a的值變為0000 0000,此時我們需要加一個判斷,使a再次恢復為0000 0001 C語言中,if條件判斷使用方式如下

if(判斷條件)
{
  //語句
}

當判斷條件為真時,執行{ }中的語句。 至此,流水燈程式碼可改成如下形式:

#include <reg52.h>

//延時1s
void delay1s()
{	 
    int i ,j;
   	for(i = 0;i<110; ++i){
	  for(j = 0;j<1000;++j){
	    ;
	  }
	}
}

void main()
{
	unsigned char a = 0x01;
    while(1)
	{
	  if(a == 0x00)   //如果高位溢位
	  {
	    a = 0x01;      //則恢復
	  }
	  //迴圈點亮led燈
	  P2 = a;
	  a = a<<1;
	  delay1s();
	}
}