当前位置: 首页 > news >正文

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

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

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

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 charsbit beep = P2^3;      // 定义蜂鸣器
sbit led0 = P1^0;      // 定义指示灯0
sbit led1 = P1^1;      // 定义指示灯1uchar 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;         // 使能外部中断0EX1 = 1;         // 使能外部中断1// 主循环while (1) {led0 = 0;    // 默认关闭LED0led1 = 0;    // 默认关闭LED1}
}// 外部中断0服务函数
void warn1() interrupt 0 {EX0 = 0;         // 关闭外部中断0,防止嵌套触发for (time = 0; time < 10; time++){beep = 0;    // 蜂鸣器响(低电平触发)led0 = 1;    // 点亮LED0delay(500);  // 延时500msled0 = 0;    // 熄灭LED0beep = 1;    // 蜂鸣器停delay(500);  // 延时500ms}EX0 = 1;         // 重新使能外部中断0
}// 外部中断1服务函数
void warn2() interrupt 2 {EX1 = 0;         // 关闭外部中断1for (time = 0; time < 10; time++){beep = 0;    // 蜂鸣器响led1 = 1;    // 点亮LED1delay(500);  // 延时500msled1 = 0;    // 熄灭LED1beep = 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++;  // 秒加1if (miao >= 60) {  // 秒满60miao = 0;fen++;  // 分加1if (fen >= 60){  // 分满60fen = 0;shizhong++;  // 时加1if (shizhong >= 24){  // 时满24shizhong = 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->0while(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;            // 分减1case 3: miao = (miao + 59) % 60; break;          // 秒减1}while(key_sub == 0);  // 等待按键释放}}}
}// 主函数
void main() {// 初始化时间为00:00:00shizhong = 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;       // 标记为高电平状态}
    }
    
http://www.lryc.cn/news/572404.html

相关文章:

  • GRBL_UNO R3编译下载
  • Spring Boot 虚拟线程 vs WebFlux:谁更胜一筹?
  • Spring-创建第一个SpringBoot项目
  • apisix-使用hmac-auth插件进行接口签名身份验证\apisix consumer
  • SpringBoot项目启动时自动加载数据到Redis的完整实现方案,用于存储字典,定时任务,登录用户等
  • Spring @Autowired 依赖注入全解析
  • 语音情感识别:CNN-LSTM 和注意力增强 CNN-LSTM 模型的比较分析
  • Hive优化详细讲解
  • Redis 的优势有哪些,它是CP 还是 AP?CAP 理论又是什么?
  • C#的泛型和匿名类型
  • Ubuntu最新版本(Ubuntu22.04LTS)安装nfs服务器
  • Java八股文——计算机网络「传输层篇」
  • CppCon 2017 学习:Everything You Ever Wanted to Know about DLLs
  • CppCon 2017 学习:dynamic_cast from scratch
  • 【AJAX 实战】图书管理系统上 渲染图书列表+新增图书+删除图书
  • windows系统JDK1.8 与JDK 17切换
  • css3 文本效果(text-shadow、text-overflow、word-wrap、word-break)文本阴影、文本换行、文本溢出并隐藏显示省略号
  • 数据结构 6(算法)
  • CMake实践:指定gcc版本编译和交叉编译
  • 华为OD机试-最佳植树距离-二分(JAVA 2025A卷)
  • DeserializationViewer使用说明
  • Java并发编程实战 Day 29:大数据处理的并行计算模型
  • Arduino Nano 33 BLE Sense Rev 2开发板使用指南之【环境搭建 / 点灯】
  • FPGA基础 -- Verilog 命名事件
  • React 19中如何向Vue那样自定义状态和方法暴露给父组件。
  • 什么是Spark
  • 服务器如何从http升级到https(nginx)
  • Kaggle-Plant Seedlings Classification-(多分类+CNN+图形处理)
  • HashMap算法高级应用实战:频率类子数组问题的5种破解模式
  • ThreadLocal以及内存泄露原理的源码解析