【单片机】51单片机练习代码

【单片机】51单片机练习代码

    • [1. 端口定义](#1. 端口定义)
    • [2. 独立按键程序编写](#2. 独立按键程序编写)
    • [3. 数码管显示](#3. 数码管显示)
    • [4. 外部中断初始化](#4. 外部中断初始化)
    • [5. 中断函数程序编写](#5. 中断函数程序编写)
    • [6. 串口程序初始化](#6. 串口程序初始化)
    • [7. LCD602写数据和写命令](#7. LCD602写数据和写命令)
    • [8. 用定时器实现秒表](#8. 用定时器实现秒表)
    • [9. 流水灯(数组实现)](#9. 流水灯(数组实现))
    • [10. 花样流水两边往中间(数组实现)](#10. 花样流水两边往中间(数组实现))
    • [11. 用定时器编写等宽方波](#11. 用定时器编写等宽方波)
    • [12. 用定时器编写非等宽方波](#12. 用定时器编写非等宽方波)

1. 端口定义

LED 灯端口

c 复制代码
#include <reg52.h>

sbit led0 = P1^0;		// 定义 LED 灯端口
void main(){
    while (1){
        led0 = 0;		// P1^0 = 0;
    }
}

蜂鸣器端口

c 复制代码
#include <reg52.h>

sbit BUZZER = P2^3;	// 定义端口

// 延时函数 - 用于生成蜂鸣器所需的脉冲间隔
// 通过嵌套循环实现约255×255个机器周期的延时
void Delay(){
    unsigned char i, j;
    for (i = 0; i < 255; i++)
        for (j = 0; j < 255; j++);
}

void main(){
    while (1){
        BUZZER = 0;      // 向蜂鸣器发送低电平信号(触发发声)
        Delay();         // 保持低电平一段时间
        BUZZER = 1;      // 向蜂鸣器发送高电平信号(停止发声)
        Delay();         // 保持高电平一段时间
    }
}

2. 独立按键程序编写

c 复制代码
#include <reg52.h>

sbit key1 = P3^4;
sbit key2 = P3^5;
sbit key3 = P3^6;
sbit key4 = P3^7;

sbit led0 = P1^0;
sbit led1 = P1^1;
sbit led2 = P1^2;
sbit led3 = P1^3;

void delay(unsigned char p){
    unsigned char m, n;
    for (m = p; m > 0; m--)
        for (n = 125; n > 0; n--);
}

void main(){
    while (1){
        if (key1 == 0) {
            delay(10);
            if (key1 == 0) {
                while (!key1);
                led0 = ~led0;
            }
        }
        if (key2 == 0){
            delay(10);
            if (key2 == 0){
                while (!key2);
                led1 = ~led1;
            }
        }
        if (key3 == 0){
            delay(10);
            if (key3 == 0){
                while (!key3);
                led2 = ~led2;
            }
        }
        if (key4 == 0){
            delay(10);
            if (key4 == 0){
                while (!key4);
                led3 = ~led3;
            }
        }
    }
}

3. 数码管显示

用数组方式显示3位数,例如1.23或123

c 复制代码
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int

// 定义数码管段选和位选控制引脚
sbit dula = P3^4;  // 段选锁存器控制端(连接到74HC573的LE引脚)
sbit wela = P1^6;  // 位选锁存器控制端(连接到74HC573的LE引脚)

// 共阴数码管段码表(0-9,不带小数点)
uchar code TableDula[] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

// 数码管位选码表(6位数码管,低电平有效)
uchar code TableWela[] = {
    0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF
};

// 延时函数,x参数表示延时单位(约2ms*x)
void delay(uchar x) {
    uchar j;
    while (x--) {
        for (j = 0; j < 125; j++);  // 125次循环约等于2ms延时
    }
}

void main(){
    uchar i;
    uchar displayData[3] = {1, 2, 3};  // 要显示的数据
    uchar pointFlag[3] = {1, 0, 0};    // 小数点显示标志(1表示显示小数点)
    
    // 无限循环,持续刷新显示
    while (1) {  
        for (i = 0; i < 3; i++) {  	// 循环显示3位数码管
            P0 = 0x00;           	// 清除显示,防止残影
            dula = 0;
            wela = 0;
            
            P0 = TableWela[i];   	// 选择当前要显示的数码管位置
            wela = 1;            	// 打开位选锁存器
            wela = 0;            	// 锁存位选数据
            
            // 获取当前位的段码,并添加小数点(如果需要)
            uchar point = pointFlag[i] ? 0x80 : 0x00;
            // 使用按位或操作添加小数点
            P0 = TableDula[displayData[i]] | point;
            
            dula = 1;            // 打开段选锁存器
            dula = 0;            // 锁存段选数据
            
            delay(2);            // 延时,控制显示亮度和扫描速度
        }
    }
}

4. 外部中断初始化

c 复制代码
EA = 0;		// 开总中断
IT1 = 1;	// 外部中断1下降沿触发
IT0 = 1;	// 外部中断0下降沿触发
EX0 = 1;	// 打开外部中断0
EX1 = 1;	// 打开外部中断1

// 外部中断0处理函数
void warn() interrupt 0 {}

// 外部中断1处理函数
void warn() interrupt 2 {}

5. 中断函数程序编写

c 复制代码
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char

sbit beep = P2^3;      // 定义蜂鸣器
sbit led0 = P1^0;      // 定义指示灯0
sbit led1 = P1^1;      // 定义指示灯1

uchar time;            // 延时循环计数器

// 毫秒级延时函数
void delay(uint xms){
    uint i, j;
    for (i = xms; i > 0; i--)
        for (j = 110; j > 0; j--);  // 110次循环约等于1ms延时
}

void main(){
    // 中断系统初始化
    EA = 1;          // 开启总中断
    IT0 = 1;         // 设置外部中断0(INT0/P3.2)为下降沿触发
    IT1 = 1;         // 设置外部中断1(INT1/P3.3)为下降沿触发
    EX0 = 1;         // 使能外部中断0
    EX1 = 1;         // 使能外部中断1
    
    // 主循环
    while (1) {
        led0 = 0;    // 默认关闭LED0
        led1 = 0;    // 默认关闭LED1
    }
}

// 外部中断0服务函数
void warn1() interrupt 0 {
    EX0 = 0;         // 关闭外部中断0,防止嵌套触发
    for (time = 0; time < 10; time++){
        beep = 0;    // 蜂鸣器响(低电平触发)
        led0 = 1;    // 点亮LED0
        delay(500);  // 延时500ms
        led0 = 0;    // 熄灭LED0
        beep = 1;    // 蜂鸣器停
        delay(500);  // 延时500ms
    }
    EX0 = 1;         // 重新使能外部中断0
}

// 外部中断1服务函数
void warn2() interrupt 2 {
    EX1 = 0;         // 关闭外部中断1
    for (time = 0; time < 10; time++){
        beep = 0;    // 蜂鸣器响
        led1 = 1;    // 点亮LED1
        delay(500);  // 延时500ms
        led1 = 0;    // 熄灭LED1
        beep = 1;    // 蜂鸣器停
        delay(500);  // 延时500ms
    }
    EX1 = 1;         // 重新使能外部中断1
}

6. 串口程序初始化

c 复制代码
TMOD = 0x20;  // 配置定时器1为模式2(8位自动重装模式)
TL1 = 0xFD;   // 设置定时器1初始值,定时约9600波特率(晶振11.0592MHz)
TH1 = 0xFD;   // 设置定时器1重装值,保证波特率稳定
TR1 = 1;      // 启动定时器1
REN = 1;      // 允许串口接收
SM0 = 0;      // 配置串口工作在模式1(8位数据,1位起始位,1位停止位)
SM1 = 1;      // 同上,与SM0共同决定串口工作模式
EA = 1;       // 开总中断
ES = 1;       // 开串口中断

7. LCD602写数据和写命令

  • 写数据

    c 复制代码
    void writedata(unsigned char x){
        RS = 1;    // RS置高,选择数据寄存器(RS=1时为数据操作)
        RW = 0;    // RW置低,选择写操作(RW=0为写,RW=1为读)
        E = 0;     // E使能信号先置低(下降沿触发数据传输)
        P0 = x;    // 将待写入的数据放到数据总线P0上
        delay(5);  // 短暂延时,确保数据稳定
        E = 1;     // E使能信号置高,准备发送数据
        delay(5);  // 延时,让LCD有时间处理数据
        E = 0;     // E使能信号下降沿,触发LCD锁存数据
    }
  • 写命令

    c 复制代码
    void writecommand(unsigned char x){
        RS = 0;    // RS置低,选择指令寄存器(RS=0时为命令操作)
        RW = 0;    // RW置低,选择写操作(RW=0为写,RW=1为读)
        E = 0;     // E使能信号先置低(下降沿触发命令执行)
        P0 = x;    // 将待执行的命令放到数据总线P0上
        delay(5);  // 短暂延时,确保命令稳定
        E = 1;     // E使能信号置高,准备发送命令
        delay(5);  // 延时,让LCD有时间处理命令
        E = 0;     // E使能信号下降沿,触发LCD执行命令
    }

8. 用定时器实现秒表

c 复制代码
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char

// 数码管段选和位选控制引脚
sbit dula = P2^6;  // 段选锁存器控制
sbit wela = P2^7;  // 位选锁存器控制

// 按键按键引脚
sbit key_set = P3^0;  // 设置键
sbit key_add = P3^1;  // 加数键
sbit key_sub = P3^2;  // 减数键

// 时间变量
uchar shi, ge, count, fen, miao, shizhong;  // shi/ge未使用,count用于定时计数
uchar mode = 0;       // 模式变量:0-正常计时,1-调时,2-调分,3-调秒
uchar set_blink = 0;  // 设置闪烁标志

// 0-9数字对应的数码管段码表(共阴数码管)
uchar code shuzi[] = {
    0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f
};

// 毫秒级延时函数
void delayms(uint ms){
    uint i, j;
    for (i = ms; i > 0; i--)
        for (j = 110; j > 0; j--);  // 110次循环约等于1ms(晶振11.0592MHz)
}

// 定时器0初始化函数
void init(){
    TMOD = 0x01;  // 设置定时器0为模式1(16位定时器)
    TH0 = (65536 - 50000) / 256;  // 定时初值高8位(50ms)
    TL0 = (65536 - 50000) % 256;  // 定时初值低8位
    EA = 1;    // 开总中断
    ET0 = 1;   // 开定时器0中断
    TR0 = 1;   // 启动定时器0
}

// 定时器0中断服务函数(每50ms触发一次)
void time0() interrupt 1 {
    // 重新装载定时初值
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    
    count++;  // 计数累加
    if (count >= 20) {  // 20次中断即1秒(50ms×20=1000ms)
        count = 0;
        if (mode == 0) {  // 正常计时模式
            miao++;  // 秒加1
            if (miao >= 60) {  // 秒满60
                miao = 0;
                fen++;  // 分加1
                if (fen >= 60){  // 分满60
                    fen = 0;
                    shizhong++;  // 时加1
                    if (shizhong >= 24){  // 时满24
                        shizhong = 0;  // 回到0点
                    }
                }
            }
        } else{  // 设置模式下,控制闪烁效果
            set_blink = !set_blink;  // 每1秒切换一次闪烁状态
        }
    }
}

// 数码管显示函数(动态扫描)
void display(){
    uchar i;
    // 计算时、分、秒的十位和个位
    uchar display_buf[6] = {
        shizhong / 10, shizhong % 10,  // 时的十位和个位
        fen / 10, fen % 10,            // 分的十位和个位
        miao / 10, miao % 10           // 秒的十位和个位
    };
    // 位选控制数组(从左到右依次点亮6个数码管)
    uchar weixuan[] = {
        0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf
    };
    
    for (i = 0; i < 6; i++){
        // 在设置模式下,控制对应位闪烁显示
        if ((mode == 1 && i < 2) || (mode == 2 && i >= 2 && i < 4) || (mode == 3 && i >= 4)){
            if (set_blink) continue;  // 闪烁状态为1时跳过显示
        }
        
        // 段选控制(输出数字段码)
        dula = 0;
        P0 = shuzi[display_buf[i]];  // 输出对应数字的段码
        dula = 1;  // 锁存段选数据
        dula = 0;
        
        P0 = 0xff;  // 消隐(防止残影)
        
        // 位选控制(选择要显示的数码管)
        wela = 0;
        P0 = weixuan[i];  // 输出位选信号
        wela = 1;  // 锁存位选数据
        wela = 0;
        
        delayms(2);  // 延时2ms,控制扫描速度
    }
}

// 按键扫描函数
void key_scan() {
    if(key_set == 0) {  // 检测设置键是否按下
        delayms(20);    // 消抖延时
        if(key_set == 0) {  // 确认按键按下
            mode = (mode + 1) % 4;  // 循环切换模式:0->1->2->3->0
            while(key_set == 0);    // 等待按键释放
        }
    }
    
    if(mode != 0) {  // 只有在设置模式下才响应加减键
        if(key_add == 0) {  // 检测加数键
            delayms(20);
            if(key_add == 0) {
                // 根据当前模式调整对应时间
                switch(mode) {
                    case 1: shizhong = (shizhong + 1) % 24; break;  // 调时(24小时制)
                    case 2: fen = (fen + 1) % 60; break;            // 调分
                    case 3: miao = (miao + 1) % 60; break;          // 调秒
                }
                while(key_add == 0);  // 等待按键释放
            }
        } 
        
        if(key_sub == 0) {  // 检测减数键
            delayms(20);
            if(key_sub == 0) {
                // 根据当前模式调整对应时间(减法通过加负数实现)
                switch(mode) {
                    case 1: shizhong = (shizhong + 23) % 24; break;  // 时减1(24小时制)
                    case 2: fen = (fen + 59) % 60; break;            // 分减1
                    case 3: miao = (miao + 59) % 60; break;          // 秒减1
                }
                while(key_sub == 0);  // 等待按键释放
            }
        }
    }
}

// 主函数
void main() {
    // 初始化时间为00:00:00
    shizhong = 0;
    fen = 0;
    miao = 0; 
    
    init();  // 初始化定时器
    
    // 主循环
    while(1) {
        display();  // 显示当前时间
        key_scan(); // 扫描按键输入
    }
}

9. 流水灯(数组实现)

c 复制代码
#include <reg52.h>

// 位选控制数组(共阴数码管位选信号,低电平有效)
// 0xfe对应第1位,0xfd对应第2位,依此类推
unsigned char table[] = {
    0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f  
};

// 延时函数(粗略延时,i越大延时越长)
void delay(unsigned char ms){
    unsigned char i, j;
    for (i = ms; i > 0; i--)
        for (j = 125; j > 0; j--);  // 125次循环约等于1ms(晶振11.0592MHz)
}

// 主函数
void main(){
    while (1){  // 无限循环
        unsigned char m;
        // 循环扫描8个数码管位
        for (m = 0; m < 8; m++){
            P1 = table[m];        // 输出位选信号,选中第m+1个数码管
            delay(1000);          // 延时约1秒(1000ms)
        }
    }
}

10. 花样流水两边往中间(数组实现)

c 复制代码
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int

// 流水灯控制码表(按位取反后输出到P1口)
// 0x81(10000001) 对应点亮第1和第8个LED
// 0x42(01000010) 对应点亮第2和第7个LED
// 0x24(00100100) 对应点亮第3和第6个LED
// 0x18(00011000) 对应点亮第4和第5个LED
uchar code dd[] = {
    0x81, 0x42, 0x24, 0x18    
};

// 毫秒级延时函数(晶振11.0592MHz时,约1ms/循环)
void delay(unsigned char ms){
    uchar i, j;
    for (i = ms; i > 0; i--)
        for (j = 110; j > 0; j--);
}

// 主函数
void main(){
    uchar m;
    while (1){  // 无限循环
        // 循环输出4种LED点亮模式
        for (m = 0; m < 4; m++){
            P1 = ~dd[m];  // 取反后输出(假设LED为共阳极,低电平点亮)
            delay(1000);  // 延时1秒,控制流水灯速度
        }
    }
}

11. 用定时器编写等宽方波

  • 周期 1ms 的等宽方波

    c 复制代码
    #include <reg52.h>
    sbit output = P2^0;     // 定义输出引脚为P2.0
    
    // 定时器初值宏定义
    // 晶振11.0592MHz时500μs为461
    // 晶振12MHz时500μs为500
    #define TIMER0_INIT ((65536 - 500))
    
    // 定时器0初始化函数
    void init(){
        TMOD = 0x01;        // 设置定时器0为模式1(16位定时器)
        TH0 = TIMER0_INIT / 256;  // 定时初值高8位
        TL0 = TIMER0_INIT % 256;  // 定时初值低8位
        ET0 = 1;            // 使能定时器0中断
        EA = 1;             // 开总中断
        TR0 = 1;            // 启动定时器0
    }
    
    void main(){
        init();             // 初始化定时器
        while (1);          // 主循环空转,等待中断
    }
    
    // 定时器0中断服务函数(每500μs触发一次)
    void timer() interrupt 1 {
        output = ~output;   		// 直接翻转输出电平
        TH0 = TIMER0_INIT / 256;  	// 重新装载初值
        TL0 = TIMER0_INIT % 256;
    }

12. 用定时器编写非等宽方波

  • 周期 1ms ,占空比 20% 的非等宽方波

    c 复制代码
    #include <reg52.h>
    int flag = 0;           // 标志位:0-高电平时间,1-低电平时间
    sbit output = P2^0;     // 定义输出引脚为P2.0
    
    // 定时器0初始化函数
    void init(){
        TMOD = 0x01;        // 设置定时器0为模式1(16位定时器)
        TH0 = (65536 - 200) / 256;  // 高电平定时初值(约200μs)
        TL0 = (65536 - 200) % 256;
        ET0 = 1;            // 使能定时器0中断
        EA = 1;             // 开总中断
        TR0 = 1;            // 启动定时器0
    }
    
    void main(){
        init();             // 初始化定时器
        while(1);           // 主循环空转,等待中断
    }
    
    // 定时器0中断服务函数
    void timer() interrupt 1 {
        if (flag == 0){     // 当前为高电平时间
            output = 0;     // 切换到低电平
            TH0 = (65536 - 800) / 256;  // 装载低电平定时初值(约800μs)
            TL0 = (65536 - 800) % 256;
            flag = 1;       // 标记为低电平状态
        }
        else if (flag == 1){// 当前为低电平时间
            output = 1;     // 切换到高电平
            TH0 = (65536 - 200) / 256;  // 装载高电平定时初值(约200μs)
            TL0 = (65536 - 200) % 256;
            flag = 0;       // 标记为高电平状态
        }
    }
相关推荐
dujunqiu2 小时前
S32DS上进行S32K328的时钟配置,LPUART时钟配置步骤详解
单片机·mcu
Peter_Deng.4 小时前
单片机 - STM32F407 ADC 模式详解:单次转换、连续转换、扫描模式、非扫描模式
stm32·单片机·嵌入式硬件
华普微HOPERF6 小时前
让温度“说话”,数字温度传感器如何智能感知温度?
科技·单片机·嵌入式硬件·物联网·智能家居
iCxhust6 小时前
PC16550 UART接收中断处理完整示例代码
c语言·开发语言·stm32·单片机·嵌入式硬件
NEWEVA__zzera227 小时前
记录存储的使用
经验分享·单片机
iFulling8 小时前
【单片机】51单片机学习笔记
单片机·学习·51单片机
YONYON-R&D9 小时前
STM32L431中,低功耗模式下的仿真调试功能受到限制
单片机·嵌入式硬件
起个网名真难~11 小时前
HAL库STM32中使用printf打印数据到串口
stm32·单片机·嵌入式硬件
今日待办11 小时前
Arduino Nano 33 BLE Sense Rev 2开发板使用指南之【外设开发】
c语言·单片机·嵌入式硬件·mcu