51单片机从入门到精通:理论与实践指南入门篇(三)

发布于:2024-11-28 ⋅ 阅读:(10) ⋅ 点赞:(0)

上一节博客给大家基本介绍了51单片机

51单片机从入门到精通:理论与实践指南入门篇(二)

https://blog.csdn.net/speaking_me/article/details/144068159?spm=1001.2014.3001.5501

那么这节课就来介绍——花样流水灯的实现

花样流水灯电路设计

1. 所需材料

  • 51系列单片机(如AT89S51)
  • LED灯若干(根据需要的数量选择)
  • 限流电阻若干(每个LED灯都需要一个)
  • 开关电源或电池
  • 连接线
  • 实验板或PCB板

2. 电路连接

  • 将每个LED灯的一端通过一个限流电阻连接到51单片机的一个I/O口(例如P1.0到P1.7),另一端连接到地(GND)。这样,当对应的I/O口输出高电平时,LED灯就会亮起;当输出低电平时,LED灯熄灭。
  • 确保电源电压适合51单片机的工作电压(通常是5V)。

C51语言知识精讲

C51语言是专门为51系列单片机设计的一种C语言变种,它在标准C语言的基础上增加了一些特定于51单片机的特性和关键字,以便更好地适应嵌入式系统的开发需求。以下是C51语言的一些关键特性和概念,这些特性使得C51语言成为51单片机编程的理想选择。

C51语言的关键特性

1. 特殊功能寄存器(SFR)和位操作
  • sfr:用于定义特殊功能寄存器(Special Function Register)。这些寄存器用于控制单片机的各种功能,如I/O端口、定时器、中断等。
    sfr P1 = 0x90;  // 定义P1端口寄存器,地址为0x90
  • sfr16:用于定义16位的特殊功能寄存器。
    sfr16 DPTR = 0x82;  // 定义数据指针寄存器,地址为0x82
  • sbit:用于定义特殊功能寄存器中的某一位。
    sbit LED = P1^0;  // 定义P1端口的第0位为LED
2. 存储器类型

C51语言支持多种存储器类型,这些类型决定了变量或函数在单片机内存中的存储位置。

  • code:程序存储器,用于存放常量和函数。
    const char str[] code = "Hello, World!";  // 存储在程序存储器中的字符串
  • data:内部RAM的直接寻址区域,用于存放小型变量。
    unsigned char var1 data;  // 存储在内部RAM中的变量
  • xdata:外部RAM,用于存放大型变量。
    unsigned int var2 xdata;  // 存储在外部RAM中的变量
  • idata:内部RAM的间接寻址区域。
    unsigned char var3 idata;  // 存储在内部RAM间接寻址区域的变量
  • pdata:外部RAM的256字节区域,使用间接寻址。
    unsigned char var4 pdata;  // 存储在外部RAM间接寻址区域的变量
  • bdata:内部RAM的位寻址区域。
    unsigned char var5 bdata;  // 存储在内部RAM位寻址区域的变量
3. 位变量和位操作
  • bit:用于定义位变量,值只能是0或1。
    bit flag;  // 定义一个位变量
    flag = 1;  // 设置位变量为1
  • 位操作符:C51支持位操作符,如&(按位与)、|(按位或)、^(按位异或)、~(按位取反)等。
    unsigned char a = 0x0F;
    unsigned char b = 0xF0;
    unsigned char c = a | b;  // c = 0xFF
4. 中断函数

C51语言支持中断函数的定义,这些函数用于处理单片机的中断事件。

  • interrupt:用于定义中断函数。
    void Timer0_ISR() interrupt 1 {
        // 中断服务例程
    }
5. 数据类型

C51语言支持多种数据类型,包括标准C语言中的数据类型以及一些扩展的数据类型。

  • 基本数据类型charintlongfloat等。
  • 扩展数据类型bitsfrsfr16sbit等。

与C语言相比

C51语言在很多方面与标准C语言是非常相似的。然而,也有一些细微的差别和额外的功能,这些是为了更好地适应嵌入式系统开发的需求。以下是一些主要的相似之处和差异:

相似之处
  1. 基本语法

    • 变量声明:变量的声明和初始化方式与标准C语言相同。
      int a = 10;
      char b = 'A';
      float c = 3.14;
    • 控制结构ifelseforwhiledo-while等控制结构的使用方式与标准C语言相同。
      if (a > 0) {
          // 执行某些操作
      } else {
          // 执行其他操作
      }
      
      for (int i = 0; i < 10; i++) {
          // 循环体
      }
      
      while (condition) {
          // 循环体
      }
  2. 函数定义

    • 函数的定义和调用方式与标准C语言相同。
      void myFunction(int param) {
          // 函数体
      }
      
      int main() {
          myFunction(5);
          return 0;
      }
  3. 运算符

    • 基本的算术运算符、关系运算符、逻辑运算符等与标准C语言相同。
      int sum = a + b;
      int product = a * b;
      int result = (a > b) ? a : b;
  4. 数组和指针

    • 数组和指针的使用方式与标准C语言相同。
      int array[5] = {1, 2, 3, 4, 5};
      int *ptr = array;
      int value = *ptr;
差异之处
  1. 存储器类型

    • C51语言引入了特定的存储器类型关键字,如dataxdataidatapdatabdatacode等,这些关键字用于指定变量或函数在单片机内存中的存储位置。
      unsigned char data var1;  // 存储在内部RAM中的变量
      unsigned int xdata var2;  // 存储在外部RAM中的变量
      const char str[] code = "Hello, World!";  // 存储在程序存储器中的字符串
  2. 特殊功能寄存器(SFR)

    • C51语言提供了sfrsfr16关键字,用于定义特殊功能寄存器。
      sfr P1 = 0x90;  // 定义P1端口寄存器,地址为0x90
      sfr16 DPTR = 0x82;  // 定义数据指针寄存器,地址为0x82
  3. 位操作

    • C51语言提供了bitsbit关键字,用于定义位变量和特殊功能寄存器中的位。
      bit flag;  // 定义一个位变量
      sbit LED = P1^0;  // 定义P1端口的第0位为LED
  4. 中断函数

    • C51语言支持中断函数的定义,这些函数用于处理单片机的中断事件。
      void Timer0_ISR() interrupt 1 {
          // 中断服务例程
      }
  5. 库函数

    • C51语言的库函数与标准C语言有所不同,许多标准C库函数可能不适用于嵌入式系统,而C51库函数则专门针对51单片机进行了优化。
      #include <intrins.h>  // 包含内建函数头文件
      _nop_();  // 插入一个空操作指令
  6. 存储模式

    • C51语言支持不同的存储模式,如SMALLCOMPACTLARGE,这些模式影响变量和指针的默认存储位置。
      #pragma small  // 设置存储模式为SMALL

示例程序:51单片机控制LED流水灯

以下是一个使用C51语言编写的示例程序,该程序控制51单片机的P1端口上的8个LED灯以流水灯的形式依次点亮。

#include <reg51.h>

// 定义延时函数
void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 1275; j++);
}

void main() {
    // 设置P1口为输出模式
    P1 = 0xFF;   // 初始状态下所有LED灯都关闭

    while(1) {   // 无限循环
        P1 = 0xFE; // 第一个LED灯亮,其余熄灭
        delay(500);
        P1 = 0xFD; // 第二个LED灯亮,其余熄灭
        delay(500);
        P1 = 0xFB; // 第三个LED灯亮,其余熄灭
        delay(500);
        P1 = 0xF7; // 第四个LED灯亮,其余熄灭
        delay(500);
        P1 = 0xEF; // 第五个LED灯亮,其余熄灭
        delay(500);
        P1 = 0xDF; // 第六个LED灯亮,其余熄灭
        delay(500);
        P1 = 0xBF; // 第七个LED灯亮,其余熄灭
        delay(500);
        P1 = 0x7F; // 第八个LED灯亮,其余熄灭
        delay(500);
        P1 = 0xFF; // 所有LED灯熄灭
        delay(500);
    }
}

学习其他方法控制流水灯

1. 位操作控制流水灯

思路

通过直接操作寄存器的某一位来控制LED灯的亮灭。使用sbit关键字定义特定的位。

代码
#include <reg51.h>

sbit LED1 = P1^0;
sbit LED2 = P1^1;
sbit LED3 = P1^2;
sbit LED4 = P1^3;
sbit LED5 = P1^4;
sbit LED6 = P1^5;
sbit LED7 = P1^6;
sbit LED8 = P1^7;

void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 1275; j++);
}

void main() {
    while(1) {
        LED1 = 0; delay(500); LED1 = 1;
        LED2 = 0; delay(500); LED2 = 1;
        LED3 = 0; delay(500); LED3 = 1;
        LED4 = 0; delay(500); LED4 = 1;
        LED5 = 0; delay(500); LED5 = 1;
        LED6 = 0; delay(500); LED6 = 1;
        LED7 = 0; delay(500); LED7 = 1;
        LED8 = 0; delay(500); LED8 = 1;
    }
}

2. 字节操作控制流水灯

思路

通过操作整个字节来控制一组LED灯。使用字节操作符来改变P1端口的值。

代码
#include <reg51.h>

void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 1275; j++);
}

void main() {
    while(1) {
        P1 = 0xFE; delay(500);  // 第一个LED灯亮
        P1 = 0xFD; delay(500);  // 第二个LED灯亮
        P1 = 0xFB; delay(500);  // 第三个LED灯亮
        P1 = 0xF7; delay(500);  // 第四个LED灯亮
        P1 = 0xEF; delay(500);  // 第五个LED灯亮
        P1 = 0xDF; delay(500);  // 第六个LED灯亮
        P1 = 0xBF; delay(500);  // 第七个LED灯亮
        P1 = 0x7F; delay(500);  // 第八个LED灯亮
    }
}

3. 移位运算符控制流水灯

思路

通过移位运算符来改变字节的值,从而控制LED灯的亮灭。

代码
#include <reg51.h>

void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 1275; j++);
}

void main() {
    unsigned char mask = 0x01;
    while(1) {
        for(int i = 0; i < 8; i++) {
            P1 = ~mask;  // 取反,使LED灯亮
            delay(500);
            mask <<= 1;  // 左移一位
        }
        mask = 0x01;  // 重置mask
    }
}

4. 库函数控制流水灯

思路

使用51单片机的内置库函数来实现循环移位,简化代码。

代码
#include <reg51.h>
#include <intrins.h>

void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 1275; j++);
}

void main() {
    unsigned char mask = 0x01;
    while(1) {
        for(int i = 0; i < 8; i++) {
            P1 = ~mask;  // 取反,使LED灯亮
            delay(500);
            mask = _crol_(mask, 1);  // 循环左移一位
        }
        mask = 0x01;  // 重置mask
    }
}

5. 条件控制语句控制流水灯

思路

使用if语句来控制LED灯的亮灭。

代码
#include <reg51.h>

void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 1275; j++);
}

void main() {
    unsigned char state = 0x01;
    while(1) {
        if (state == 0x01) {
            P1 = 0xFE; delay(500);
            state = 0x02;
        } else if (state == 0x02) {
            P1 = 0xFD; delay(500);
            state = 0x04;
        } else if (state == 0x04) {
            P1 = 0xFB; delay(500);
            state = 0x08;
        } else if (state == 0x08) {
            P1 = 0xF7; delay(500);
            state = 0x10;
        } else if (state == 0x10) {
            P1 = 0xEF; delay(500);
            state = 0x20;
        } else if (state == 0x20) {
            P1 = 0xDF; delay(500);
            state = 0x40;
        } else if (state == 0x40) {
            P1 = 0xBF; delay(500);
            state = 0x80;
        } else if (state == 0x80) {
            P1 = 0x7F; delay(500);
            state = 0x01;
        }
    }
}

6. switch语句控制流水灯

思路

使用switch语句来控制LED灯的亮灭。

代码
#include <reg51.h>

void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 1275; j++);
}

void main() {
    unsigned char state = 0x01;
    while(1) {
        switch(state) {
            case 0x01:
                P1 = 0xFE; delay(500); state = 0x02; break;
            case 0x02:
                P1 = 0xFD; delay(500); state = 0x04; break;
            case 0x04:
                P1 = 0xFB; delay(500); state = 0x08; break;
            case 0x08:
                P1 = 0xF7; delay(500); state = 0x10; break;
            case 0x10:
                P1 = 0xEF; delay(500); state = 0x20; break;
            case 0x20:
                P1 = 0xDF; delay(500); state = 0x40; break;
            case 0x40:
                P1 = 0xBF; delay(500); state = 0x80; break;
            case 0x80:
                P1 = 0x7F; delay(500); state = 0x01; break;
        }
    }
}

7. 数组控制流水灯

思路

使用数组来存储每个LED灯的状态,通过索引来控制LED灯的亮灭。

代码
#include <reg51.h>

void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 1275; j++);
}

void main() {
    unsigned char led_states[] = {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F};
    while(1) {
        for(int i = 0; i < 8; i++) {
            P1 = led_states[i];
            delay(500);
        }
    }
}

8. 指针控制流水灯

思路

使用指针来遍历数组,通过指针来控制LED灯的亮灭。

代码
#include <reg51.h>

void delay(unsigned int time) {
    unsigned int i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 1275; j++);
}

void main() {
    unsigned char led_states[] = {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F};
    unsigned char *ptr = led_states;
    while(1) {
        for(int i = 0; i < 8; i++) {
            P1 = *ptr;
            delay(500);
            ptr++;
        }
        ptr = led_states;  // 重置指针
    }
}

总结

以上是使用位操作、字节操作、移位运算符、库函数、条件控制语句、switch语句、数组和指针这8种方法来控制51单片机上的流水灯的详细讲解和代码示例。每种方法都有其独特的优点和适用场景,通过学习这些方法,你可以更灵活地控制51单片机的I/O端口,实现各种复杂的功能。