[经验分享] 51单片机练习代码

[复制链接]
 楼主| xiaoqizi 发表于 2025-7-10 16:57 | 显示全部楼层 |阅读模式
1. 端口定义
LED 灯端口
#include <reg52.h>

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


蜂鸣器端口
#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. 独立按键程序编写
#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

#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. 外部中断初始化
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. 中断函数程序编写
#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. 串口程序初始化
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写数据和写命令
写数据

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锁存数据
}


写命令

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. 用定时器实现秒表
#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. 流水灯(数组实现)
#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. 花样流水两边往中间(数组实现)
#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 的等宽方波

#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% 的非等宽方波
#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;       // 标记为高电平状态
    }
}



————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/realoser/article/details/148771066

您需要登录后才可以回帖 登录 | 注册

本版积分规则

126

主题

4320

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部