【单片机】51单片机练习代码
-
- [1. 端口定义](#1. 端口定义)
-
- [LED 灯端口](#LED 灯端口)
- 蜂鸣器端口
- [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写数据和写命令
-
写数据
cvoid 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锁存数据 }
-
写命令
cvoid 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; // 标记为高电平状态 } }