1.编写程序点亮 P2 口第 1个 LEDP2.0并保持常亮。#include reg52.h // 包含 51 单片机寄存器定义头文件 sbit LED1 P2^0; // 将开发板上的 LED1 连接到 P2.0 引脚 void main() { LED1 0; // 输出低电平点亮 LED1 }LED1 BIT P2.0 ; 定义 LED1对应单片机 P2.0 引脚 ORG 0000H ; 程序复位入口地址 SJMP MAIN ; 单片机上电后跳转到主程序 MAIN ORG 0030H ; 主程序存放地址从 0030H 开始 MAIN: CLR LED1 ; 将 P2.0 清零输出低电平 ; 如果 LED 采用低电平点亮方式则此时 LED 亮 SJMP MAIN ; 一直跳回 MAIN形成死循环 ; 保持 LED 常亮状态不变 END ; 程序结束2.编写程序让 P2 口 8 个 LED“全亮 1 秒→全灭 1 秒” 循环软件延时。#include reg52.h // 51 单片机寄存器定义头文件 #define LED_PORT P2 // 将 P2 端口定义为 LED 控制端口 /* * 函数名称delay_1ms * 功能说明软件延时函数 * 参数说明time —— 延时的毫秒数大致值 * 备注 * 该延时方式通过空循环实现延时时间与单片机晶振频率有关 * 因此这里只能作为近似延时使用。 */ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时毫秒数 for (i 0; i 110; i); // 内层空循环形成约 1ms 延时 } /* * 函数名称main * 功能说明主函数 * 程序功能 * 让连接在 P2 端口上的 LED 灯全部同时闪烁 * 亮 1 秒灭 1 秒循环重复执行。 */ void main() { while (1) // 无限循环程序一直运行 { LED_PORT 0xff; // P2 端口输出高电平LED 全灭常见低电平点亮方式 delay_1ms(1000); // 延时约 1000ms即 1 秒 LED_PORT 0x00; // P2 端口输出低电平LED 全亮 delay_1ms(1000); // 延时约 1000ms即 1 秒 } }LED_PORT EQU P2 ; 将 P2 端口定义为 LED 控制端口 ORG 0000H ; 程序复位入口地址 SJMP MAIN ; 上电后先跳转到主程序 MAIN ORG 0030H ; 从 0030H 地址开始存放子程序和主程序 ;------------------------------------------------ ; 子程序名称DELAY1000MS ; 功能说明延时约 1000ms1 秒 ; 晶振频率11.0592MHz ; 说明 ; 通过多重循环实现软件延时。 ; 进入子程序后先保护 30H、31H、32H 单元中的原数据 ; 延时结束后再恢复避免影响主程序中的其他数据。 ;------------------------------------------------ DELAY1000MS: PUSH 30H ; 保存 30H 单元原值 PUSH 31H ; 保存 31H 单元原值 PUSH 32H ; 保存 32H 单元原值 MOV 30H,#8 ; 设置外层循环初值 MOV 31H,#1 ; 设置中间层循环初值 MOV 32H,#236 ; 设置内层循环初值 NEXT: DJNZ 32H,NEXT ; 内层循环减 1未到 0 则继续 DJNZ 31H,NEXT ; 中间层循环减 1未到 0 则继续 DJNZ 30H,NEXT ; 外层循环减 1未到 0 则继续 POP 32H ; 恢复 32H 单元原值 POP 31H ; 恢复 31H 单元原值 POP 30H ; 恢复 30H 单元原值 RET ; 子程序返回 ;------------------------------------------------ ; 主程序 MAIN ; 功能说明 ; 让连接在 P2 端口上的 LED 灯循环闪烁 ; 先全灭 1 秒再全亮 1 秒不断重复。 ; 说明 ; 若开发板上的 LED 为“低电平点亮”方式 ; 则 0FFH 表示全灭00H 表示全亮。 ;------------------------------------------------ MAIN: MOV LED_PORT,#0FFH ; P2 端口输出高电平LED 全灭 ACALL DELAY1000MS ; 延时 1 秒 MOV LED_PORT,#00H ; P2 端口输出低电平LED 全亮 ACALL DELAY1000MS ; 延时 1 秒 SJMP MAIN ; 无限循环保持 LED 持续闪烁 END ; 汇编结束3.实现 P2 口 LED “从左到右流水灯”每个 LED 点亮 500ms 后切换到下一个。#include reg52.h // 51 单片机寄存器定义头文件 #define LED_PORT P2 // 将 P2 端口定义为 LED 输出端口 /* * 函数名称delay_1ms * 功能说明软件延时函数 * 参数说明time - 延时的毫秒数近似值 * 备注 * 该延时函数通过空循环实现实际延时时间会受到晶振频率影响。 */ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时次数 for (i 0; i 110; i); // 内层空循环约 1ms 延时 } /* * 函数名称main * 功能说明主函数 * 程序功能 * 让连接在 P2 端口上的 8 个 LED 灯依次点亮 * 每次点亮一个间隔约 500ms循环往复形成流水灯效果。 */ void main() { while (1) // 无限循环使流水灯一直运行 { static unsigned char i; // 定义循环变量用于控制当前点亮第几个 LED for (i 0; i 8; i) // 从第 1 个 LED 到第 8 个 LED 依次控制 { LED_PORT ~(0x01 i); // 将第 i 位输出为低电平其余位为高电平 // 若 LED 为低电平点亮方式则当前只有一个 LED 点亮 delay_1ms(500); // 延时约 500ms方便观察流水效果 } } }LED_PORT EQU P2 ; 定义 LED 端口P2 口连接 8 个 LED ORG 0000H ; 程序复位入口地址 SJMP MAIN ; 上电后跳转到主程序 MAIN ORG 0030H ; 从 0030H 地址开始存放子程序和主程序 ;------------------------------------------------ ; 子程序名称DELAY500MS ; 功能说明延时约 500ms ; 晶振频率11.0592MHz ; 说明 ; 该子程序通过三重循环实现软件延时。 ; 使用 30H、31H、32H 作为循环计数单元 ; 进入子程序前先保护原数据退出前再恢复。 ;------------------------------------------------ DELAY500MS: PUSH 30H ; 保存 30H 单元原值 PUSH 31H ; 保存 31H 单元原值 PUSH 32H ; 保存 32H 单元原值 MOV 30H,#4 ; 外层循环初值 MOV 31H,#129 ; 中层循环初值 MOV 32H,#112 ; 内层循环初值 NEXT: DJNZ 32H,NEXT ; 内层计数减 1未到 0 则继续循环 DJNZ 31H,NEXT ; 中层计数减 1未到 0 则继续循环 DJNZ 30H,NEXT ; 外层计数减 1未到 0 则继续循环 POP 32H ; 恢复 32H 单元原值 POP 31H ; 恢复 31H 单元原值 POP 30H ; 恢复 30H 单元原值 RET ; 子程序返回 ;------------------------------------------------ ; 主程序 MAIN ; 功能说明 ; 控制 P2 口上的 LED 依次移动点亮形成循环流水灯效果。 ; 说明 ; 初始值 0FEH 表示最低位为 0其余位为 1。 ; 若 LED 为低电平点亮方式则初始时点亮第 1 个 LED。 ; 每延时 500ms 后将当前亮灯位置循环左移一位。 ;------------------------------------------------ MAIN: MOV LED_PORT,#0FEH ; 初始状态仅最低位对应的 LED 点亮 LED_LOOP: ACALL DELAY500MS ; 延时约 500ms MOV A,LED_PORT ; 读取当前 LED 状态到累加器 A RL A ; 将 A 中数据循环左移 1 位 MOV LED_PORT,A ; 把新的状态送回 P2 口更新 LED 显示 SJMP LED_LOOP ; 无限循环持续实现流水灯效果 END ; 程序结束4.实现 P2 口 LED “往返流水灯”从 P2.0→P2.7 再返回 P2.0每个 LED 点亮 300ms。#include reg52.h // 包含 51 单片机寄存器定义头文件 #define LED_PORT P2 // 将 P2 端口定义为 LED 输出端口 /* * 函数名称delay1ms * 功能说明软件延时函数 * 参数说明time - 延时时间单位约为毫秒 * 备注 * 该函数通过空循环实现延时实际时间会受到晶振频率影响 * 因此这里只能认为是近似延时。 */ void delay1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时次数 for (i 0; i 110; i); // 内层空循环形成约 1ms 延时 } /* * 函数名称main * 功能说明主函数 * 程序功能 * 控制 P2 口上的 8 个 LED 先从左到右依次点亮 * 再从右到左依次点亮形成“来回流水灯”效果。 */ void main() { while (1) // 无限循环让流水灯持续运行 { static unsigned char i, j; // 定义循环变量控制当前点亮的 LED 位置 /* 第一阶段从第 1 个 LED 依次移动到第 8 个 LED */ for (i 0; i 8; i) { LED_PORT ~(0x01 i); // 让第 i 位输出低电平其余位输出高电平 // 如果 LED 为低电平点亮方式则当前只有一个 LED 亮 delay1ms(300); // 延时约 300ms便于观察流水效果 } /* 第二阶段从倒数第 2 个 LED 移回到第 2 个 LED */ for (j 6; j 0; j--) { LED_PORT ~(0x01 j); // 让第 j 位对应的 LED 点亮 delay1ms(300); // 延时约 300ms } } }LED_PORT EQU P2 ; 定义 LED 端口P2 口连接 8 个 LED ORG 0000H ; 程序复位入口地址 SJMP MAIN ; 上电后跳转到主程序 MAIN ORG 0030H ; 从 0030H 开始存放子程序和主程序 ;------------------------------------------------ ; 子程序名称DELAY300MS ; 功能说明延时约 300ms ; 晶振频率11.0592MHz ; 说明 ; 该子程序采用三重循环实现软件延时。 ; 使用 30H、31H、32H 作为循环计数单元 ; 进入子程序前先保存原值退出前恢复避免影响主程序。 ;------------------------------------------------ DELAY300MS: PUSH 30H ; 保存 30H 单元原值 PUSH 31H ; 保存 31H 单元原值 PUSH 32H ; 保存 32H 单元原值 MOV 30H,#3 ; 外层循环初值 MOV 31H,#26 ; 中层循环初值 MOV 32H,#216 ; 内层循环初值 NEXT: DJNZ 32H,NEXT ; 内层循环减 1未到 0 则继续 DJNZ 31H,NEXT ; 中层循环减 1未到 0 则继续 DJNZ 30H,NEXT ; 外层循环减 1未到 0 则继续 POP 32H ; 恢复 32H 单元原值 POP 31H ; 恢复 31H 单元原值 POP 30H ; 恢复 30H 单元原值 RET ; 子程序返回 ;------------------------------------------------ ; 主程序 MAIN ; 功能说明 ; 控制 P2 口上的 LED 先向左依次移动点亮 ; 再向右依次移动点亮形成来回流水灯效果。 ;------------------------------------------------ MAIN: MOV R0,#7 ; R0 用作左移次数计数共移动 7 次 MOV R1,#7 ; R1 用作右移次数计数共移动 7 次 MOV LED_PORT,#0FEH ; 初始状态最低位为 0其余位为 1 ; 若 LED 为低电平点亮方式则第 1 个 LED 点亮 LED_LOOP1: ACALL DELAY300MS ; 延时约 300ms MOV A,LED_PORT ; 读取当前 LED 状态 RL A ; 循环左移 1 位亮灯位置向左移动 MOV LED_PORT,A ; 更新 LED 状态 DJNZ R0,LED_LOOP1 ; 左移 7 次后结束第一阶段 LED_LOOP2: ACALL DELAY300MS ; 延时约 300ms MOV A,LED_PORT ; 读取当前 LED 状态 RR A ; 循环右移 1 位亮灯位置向右移动 MOV LED_PORT,A ; 更新 LED 状态 DJNZ R1,LED_LOOP2 ; 右移 7 次后结束第二阶段 SJMP MAIN ; 跳回主程序重复执行来回流水灯 END ; 汇编结束5.实现 P2 口 LED “奇偶交替亮”奇数位亮 1 秒→偶数位亮 1 秒循环。#include reg52.h // 包含 51 单片机寄存器定义头文件 #define LED_PORT P2 // 将 P2 端口定义为 LED 输出端口 /* * 函数名称delay1ms * 功能说明软件延时函数 * 参数说明time - 延时时间单位约为毫秒 * 备注 * 该函数通过空循环实现延时实际时间会受到晶振频率影响 * 因此这里只能认为是近似延时。 */ void delay1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时次数 for (i 0; i 110; i); // 内层空循环形成约 1ms 延时 } /* * 函数名称main * 功能说明主函数 * 程序功能 * 控制 P2 口上的 8 个 LED 交替显示 10101010 和 01010101 * 每种状态保持约 1000ms循环往复。 */ void main() { while (1) // 无限循环让 LED 持续交替闪烁 { LED_PORT 0xAA; // 二进制 10101010 delay1ms(1000); // 延时约 1000ms LED_PORT 0x55; // 二进制 01010101 delay1ms(1000); // 延时约 1000ms } }LED_PORT EQU P2 ; 定义LED端口为P2端口用来控制LED灯 ORG 0000H ; 程序起始地址 SJMP MAIN ; 跳转到主程序MAIN避免执行中间区域 ORG 0030H ; 将子程序放在0030H地址开始 ; ; 1000ms 延时子程序 ; 晶振频率11.0592MHz ; 通过三层循环产生约1秒延时 ; DELAY1000MS: PUSH 30H ; 保存寄存器30H原来的值保护现场 PUSH 31H ; 保存寄存器31H PUSH 32H ; 保存寄存器32H MOV 30H,#8 ; 外层循环计数 8 MOV 31H,#1 ; 中间循环计数 1 MOV 32H,#236 ; 内层循环计数 236 NEXT: DJNZ 32H,NEXT ; 内层循环32H减1不为0则继续循环 DJNZ 31H,NEXT ; 中间循环31H减1不为0则继续循环 DJNZ 30H,NEXT ; 外层循环30H减1不为0则继续循环 POP 32H ; 恢复寄存器32H原来的值 POP 31H ; 恢复寄存器31H原来的值 POP 30H ; 恢复寄存器30H原来的值 RET ; 返回主程序 ; ; 主程序 ; 控制LED交替闪烁 ; MAIN: MOV LED_PORT,#10101010B ; P2输出10101010偶数位LED亮奇数位灭 ACALL DELAY1000MS ; 调用1秒延时 MOV LED_PORT,#01010101B ; P2输出01010101LED状态反转 ACALL DELAY1000MS ; 再延时1秒 SJMP MAIN ; 无限循环LED持续交替闪烁 END ; 程序结束6.利用定时器 PWM 实现 P2.0 的 “呼吸灯”亮度从暗→亮→暗循环。#include reg52.h // 51单片机寄存器定义头文件 sbit LED0 P2^0; // 定义LED连接在P2.0口低电平点亮 unsigned int cnt 0; // PWM计数变量 unsigned int duty 50; // 占空比取值范围0~50 bit dir 0; // 变化方向标志0表示亮度减小1表示亮度增大 /* * 定时器0初始化函数 * 定时器0工作在方式116位定时器 */ void timer0_init() { TMOD | 0x01; // 设置定时器0为方式1 TL0 0xCD; // 装入定时初值低8位 TH0 0xD4; // 装入定时初值高8位 EA 1; // 开总中断 ET0 1; // 允许定时器0中断 TF0 0; // 清除定时器0溢出标志 TR0 1; // 启动定时器0 } /* * 定时器0中断服务函数 * 每次定时溢出后重新装载初值并使PWM计数加1 */ void timer0_isr() interrupt 1 { TL0 0xCD; // 重装定时初值低8位 TH0 0xD4; // 重装定时初值高8位 cnt; // PWM计数加1 } /* * 主函数 * 通过软件PWM控制LED亮度实现渐亮渐灭效果 */ void main() { timer0_init(); // 初始化定时器0 while (1) { if (!dir) // 当dir0时LED亮度逐渐减小 { if (cnt 50) // 一个PWM周期结束 { cnt 0; // 计数清零开始下一周期 if (duty 0) { duty--; // 占空比减小亮度降低 } else { dir 1; // 占空比减到最小后切换为亮度增大 } } } else // 当dir1时LED亮度逐渐增大 { if (cnt 50) // 一个PWM周期结束 { cnt 0; // 计数清零开始下一周期 if (duty 50) { duty; // 占空比增大亮度提高 } else { dir 0; // 占空比增到最大后切换为亮度减小 } } } if (cnt duty) // 当计数值小于占空比时 { LED0 0; // LED点亮 } else // 当计数值大于等于占空比时 { LED0 1; // LED熄灭 } } }CNT EQU 30H ; 定义变量 CNT存放 PWM 计数值 DUTY EQU 31H ; 定义变量 DUTY存放占空比 DIR EQU 32H ; 定义变量 DIR存放亮度变化方向 LED0 BIT P2.0 ; 定义 LED0 接在 P2.0 引脚 ORG 0000H ; 程序复位入口地址 LJMP MAIN ; 上电后跳转到主程序 ORG 000BH ; 定时器0中断入口地址 SJMP TIMER0ISR ; 跳转到定时器0中断服务程序 ORG 0030H ; 从 0030H 开始存放用户程序 TIMER0INIT: MOV CNT, #0 ; PWM 计数初值设为 0 MOV DUTY, #0 ; 占空比初值设为 0 MOV DIR, #0 ; 方向初值设为 0表示亮度逐渐增加 MOV TL0,#0CDH ; 定时器0低8位装入初值 MOV TH0,#0D4H ; 定时器0高8位装入初值 ANL TMOD,#0F0H ; 清除定时器0的方式控制位 ORL TMOD,#01H ; 设置定时器0为方式116位定时器 SETB EA ; 开总中断 SETB ET0 ; 允许定时器0中断 SETB TR0 ; 启动定时器0 CLR TF0 ; 清除定时器0溢出标志位 RET ; 返回主程序 TIMER0ISR: PUSH ACC ; 保护累加器 ACC PUSH PSW ; 保护程序状态字 PSW MOV TL0,#0CDH ; 定时器0重装初值低8位 MOV TH0,#0D4H ; 定时器0重装初值高8位 CLR C ; 清除进位标志准备做减法比较 MOV A, CNT ; 将 CNT 送入累加器A SUBB A, DUTY ; A CNT - DUTY用于比较 CNT 和 DUTY JNC LED_OFF ; 如果 CNT DUTY则跳转关闭LED CLR LED0 ; 如果 CNT DUTY则点亮LED低电平点亮 SJMP CNT_INC ; 跳转到计数处理部分 LED_OFF: SETB LED0 ; 熄灭LED高电平灭 CNT_INC: INC CNT ; PWM计数值加1 MOV A, CNT ; 将 CNT 送入A CJNE A, #100, ISR_EXIT ; 若 CNT 不等于100则结束本次中断 MOV CNT, #0 ; 一个PWM周期结束计数清零 MOV A, DIR ; 读取方向标志 JNZ DUTY_DEC ; 如果 DIR 不为0则执行占空比递减 DUTY_INC: INC DUTY ; 占空比加1LED逐渐变亮 MOV A, DUTY ; 读取当前占空比 CJNE A, #100, ISR_EXIT ; 若 DUTY 不等于100则结束中断 MOV DIR, #1 ; 若 DUTY 达到最大值100则改变方向为递减 SJMP ISR_EXIT ; 跳转退出中断 DUTY_DEC: DEC DUTY ; 占空比减1LED逐渐变暗 MOV A, DUTY ; 读取当前占空比 JNZ ISR_EXIT ; 若 DUTY 不为0则结束中断 MOV DIR, #0 ; 若 DUTY 减到0则改变方向为递增 ISR_EXIT: POP PSW ; 恢复程序状态字 PSW POP ACC ; 恢复累加器 ACC RETI ; 中断返回 MAIN: ACALL TIMER0INIT ; 调用定时器0初始化子程序 LOOP: SJMP LOOP ; 主程序空转所有PWM控制都在中断中完成 END ; 程序结束7.编写程序随机点亮 P2 口 1 个 LED500ms 后切换新的随机 LED。#include reg51.h // 51单片机寄存器定义头文件 #define LED_PORT P2 // 定义LED连接的端口为P2口 unsigned int rand_seed 0x1234; // 16位伪随机数种子初值不能为0 unsigned char LED_value[] // LED显示数组低电平点亮某一位LED { 0xFE, // 点亮第1个LED 0xFD, // 点亮第2个LED 0xFB, // 点亮第3个LED 0xF7, // 点亮第4个LED 0xEF, // 点亮第5个LED 0xDF, // 点亮第6个LED 0xBF, // 点亮第7个LED 0x7F // 点亮第8个LED }; /* * 生成16位伪随机数 * 使用16位LFSR线性反馈移位寄存器 */ unsigned int random_16bit(void) { rand_seed (rand_seed 1) ^ ((rand_seed 0x8000) ? 0x1021 : 0); return rand_seed; } /* * 生成[min, max]范围内的随机数 */ unsigned int random_range(unsigned int min, unsigned int max) { return (random_16bit() % (max - min 1)) min; } /* * 粗略延时函数 * time 取值越大延时时间越长 */ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) { for (i 0; i 110; i); } } /* * 主函数 * 随机点亮8个LED中的一个并每隔500ms切换一次 */ void main(void) { unsigned int rand_val; // 存放随机生成的LED序号 while (1) { rand_val random_range(0, 7); // 生成0~7之间的随机数 LED_PORT LED_value[rand_val]; // 按随机值点亮对应LED delay_1ms(500); // 延时约500ms } }LED_PORT EQU P2 ; LED 连接在 P2 口 ORG 0000H SJMP MAIN ; 上电后跳转到主程序入口 ORG 0030H ; ; RAM变量定义 ; MAX EQU 30H ; 随机数上限 MIN EQU 31H ; 随机数下限 TEMP EQU 32H ; 临时变量保存范围大小 (MAX - MIN 1) RAD EQU 33H ; 最终生成的随机数 SEED_H EQU 34H ; 随机种子高字节 SEED_L EQU 35H ; 随机种子低字节 BIT15 EQU 36H ; 保存原随机种子最高位(bit15) ; ; 主程序 ; 功能 ; 1. 初始化堆栈、端口、随机种子、随机范围 ; 2. 不断生成 0~7 的随机数 ; 3. 根据随机数查表点亮对应LED ; 4. 每次显示后延时约500ms ; MAIN: MOV SP, #60H ; 设置堆栈指针避开低地址RAM变量区 MOV P2, #0FFH ; P2 初始全高电平LED全灭低电平点亮 MOV SEED_H, #12H ; 初始化随机种子高字节种子 0x1234 MOV SEED_L, #34H ; 初始化随机种子低字节 MOV MAX, #07H ; 随机数最大值 7 MOV MIN, #00H ; 随机数最小值 0 MAIN_LOOP: CALL RANDOM_LSFR_PROC ; 生成随机数结果存放在 RAD 中 MOV A, RAD ; A 随机数0~7 MOV DPTR, #LED_VALUE ; DPTR 指向 LED 显示查找表 MOVC A, ADPTR ; 从代码区查表得到对应LED控制字 MOV LED_PORT, A ; 输出到 P2点亮对应LED CALL DELAY_500MS ; 延时约500ms SJMP MAIN_LOOP ; 无限循环 ; ; LED查找表 ; 每个数据对应点亮一个LED低电平有效 ; 例如 ; 0 - 11111110B - 第0个LED亮 ; 1 - 11111101B - 第1个LED亮 ; ... ; LED_VALUE: DB 0FEH,0FDH,0FBH,0F7H,0EFH,0DFH,0BFH,07FH ; ; LFSR随机数子程序 ; ; 对应C逻辑 ; rand_seed (rand_seed 1) ^ ((rand_seed 0x8000) ? 0x1021 : 0); ; return (rand_seed % (max - min 1)) min; ; ; 功能 ; 1. 对16位随机种子进行一次 LFSR 变换 ; 2. 将结果限制在 [MIN, MAX] 范围内 ; 3. 最终结果存入 RAD ; RANDOM_LSFR_PROC: ;------------------------------------------ ; 第一步保存移位前的最高位 bit15 ; bit15 就是 SEED_H 的 bit7 ;------------------------------------------ MOV A, SEED_H ANL A, #80H ; 取出高字节最高位 MOV BIT15, A ; 保存原 bit15后面判断是否异或 0x1021 ;------------------------------------------ ; 第二步16位左移 1 位 ; 低字节先移位进位送到高字节 ;------------------------------------------ CLR C ; 清除进位 MOV A, SEED_L RLC A ; 低字节左移1位最高位进入C MOV SEED_L, A MOV A, SEED_H RLC A ; 高字节左移1位同时接收低字节移出的进位 MOV SEED_H, A ;------------------------------------------ ; 第三步如果原 bit15 1则异或 0x1021 ; 这是 LFSR / CRC 多项式反馈操作 ;------------------------------------------ MOV A, BIT15 JZ SKIP_XOR ; 若原 bit15 0则跳过异或 MOV A, SEED_L XRL A, #21H ; 低字节异或 0x21 MOV SEED_L, A MOV A, SEED_H XRL A, #10H ; 高字节异或 0x10 MOV SEED_H, A SKIP_XOR: ;------------------------------------------ ; 第四步计算随机范围大小 TEMP MAX - MIN 1 ; 例如 MAX7, MIN0则 TEMP8 ;------------------------------------------ MOV A, MAX CLR C SUBB A, MIN ; A MAX - MIN ADD A, #01H ; A MAX - MIN 1 MOV TEMP, A ; TEMP 保存范围长度 ;------------------------------------------ ; 第五步计算 rand_seed % TEMP ; ; 当前程序目标范围是 0~7因此 TEMP 8 ; 下面代码在 TEMP8 时可正常工作 ; ; 思路 ; 先处理高字节余数再处理低字节 ;------------------------------------------ ; 先计算高字节对 TEMP 的余数SEED_H % TEMP MOV A, SEED_H MOV B, TEMP DIV AB ; A商, B余数 MOV R1, B ; R1 SEED_H % TEMP ; 再计算低字节对 TEMP 的余数SEED_L % TEMP MOV A, SEED_L MOV B, TEMP DIV AB ; A商, B余数 MOV A, B ; A SEED_L % TEMP ; 保存低字节余数 MOV R0, A ;------------------------------------------ ; 理论上完整公式应为 ; (R1 * 256 SEED_L) % TEMP ; ((R1 * (256 % TEMP)) (SEED_L % TEMP)) % TEMP ; ; 当 TEMP 8 时 ; 256 % 8 0 ; 所以高字节贡献为 0最终只需用 SEED_L % TEMP ; ; 因此当前程序实际上是针对 TEMP8 做了简化 ;------------------------------------------ MOV A, R1 JZ MOD_FINAL ; 如果高字节余数为0直接结束 ;------------------------------------------ ; 下面原本准备做通用处理但实际未展开完成 ; 当前注释保留提醒阅读者这里只是“预留结构” ;------------------------------------------ MOV A, #0 SETB C CLR C MOV B, #0 CALC_256_MOD: ; 这里本意是计算 256 % TEMP ; 但由于当前 TEMP8高位贡献恒为0 ; 所以无需继续计算直接走到最终结果 MOV A, #0 MOV R4, #1 MOV R5, #0 ; 对 TEMP8 ; 256 % 8 0 ; 故高位部分不影响最终余数 MOD_FINAL: MOV A, R0 ; 最终余数 SEED_L % TEMP ;------------------------------------------ ; 第六步加上最小值 MIN ; 得到范围 [MIN, MAX] 内的随机数 ;------------------------------------------ ADD A, MIN MOV RAD, A ; 保存随机结果 RET ; ; 约500ms延时子程序 ; 实际延时时间与晶振频率有关 ; 这里是三重循环的软件延时 ; DELAY_500MS: MOV R3, #10 DLY2: MOV R4, #200 DLY1: MOV R5, #250 DLY0: DJNZ R5, DLY0 DJNZ R4, DLY1 DJNZ R3, DLY2 RET END8.让 P2 口 LED 按 “二进制计数” 显示0~255每个数值保持 300ms#include reg52.h #define LED_PORT P2 // 将 P2 口定义为 LED 端口 /*------------------------------------------------ 函数名称delay1ms 功能软件延时 参数time - 延时次数 说明 该延时并不是严格 1ms 实际时间与单片机晶振频率有关 ------------------------------------------------*/ void delay1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时总次数 for (i 0; i 110; i); // 内层空循环形成短延时 } /*------------------------------------------------ 函数名称main 功能主函数 程序功能 1. 初始化 P2 口为 0xFF 2. 让 P2 口的值不断减 1 3. 每减一次延时约 300 次单位时间 4. 从而实现 LED 按一定规律变化显示 ------------------------------------------------*/ void main() { LED_PORT 0xff; // 初始状态P2 全部输出高电平 while (1) // 死循环程序一直运行 { LED_PORT--; // P2 口数据减 1LED 显示状态依次变化 delay1ms(300); // 延时便于观察 LED 变化 } }LED_PORT EQU P2 ; 将 P2 端口定义为 LED 输出端口 ORG 0000H ; 程序从地址 0000H 开始 SJMP MAIN ; 跳转到主程序入口 MAIN ORG 0030H ; 从 0030H 开始存放主程序和子程序 ; ; 延时子程序约 300ms ; 晶振频率11.0592MHz ; 功能通过三重 DJNZ 循环实现软件延时 ; DELAY300MS: PUSH 30H ; 保护寄存器/内存单元 30H防止原数据被破坏 PUSH 31H ; 保护 31H PUSH 32H ; 保护 32H MOV 30H,#3 ; 最外层循环次数 MOV 31H,#26 ; 中间层循环次数 MOV 32H,#216 ; 最内层循环次数 NEXT: DJNZ 32H,NEXT ; 32H减1不为0则继续循环 DJNZ 31H,NEXT ; 31H减1不为0则继续循环 DJNZ 30H,NEXT ; 30H减1不为0则继续循环 POP 32H ; 恢复 32H 原来的值 POP 31H ; 恢复 31H 原来的值 POP 30H ; 恢复 30H 原来的值 RET ; 延时结束返回主程序 ; ; 主程序 ; 功能 ; 1. 初始化 LED 端口 ; 2. 让 P2 口的值不断减 1 ; 3. 每次变化后延时约 300ms ; MAIN: MOV LED_PORT,#0FFH ; 初始化 P2 0xFF ; 若 LED 为低电平点亮则此时所有 LED 熄灭 LED_LOOP: DEC LED_PORT ; P2 端口值减 1 ; 即FFH - FEH - FDH - FCH ... ; LED 显示会按二进制规律变化 ACALL DELAY300MS ; 调用 300ms 延时子程序 SJMP LED_LOOP ; 无限循环执行 END ; 程序结束9.实现 P2.0 与 P2.1 “交替闪烁”亮灭周期 1 秒。#include reg52.h /*-------------------------------- 定义两个 LED 引脚 LED1 接在 P2.0 LED2 接在 P2.1 --------------------------------*/ sbit LED1 P2^0; sbit LED2 P2^1; /*-------------------------------- 函数名delay_1ms 功能软件延时函数 参数time延时的毫秒数 说明在 11.0592MHz 晶振下大致接近 1ms --------------------------------*/ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时时间 for (i 0; i 110; i); // 内层空循环形成约1ms延时 } /*-------------------------------- 主函数 功能 让两个 LED 交替闪烁 --------------------------------*/ void main() { while (1) // 死循环程序一直运行 { LED1 0; // LED1 点亮假设低电平点亮 LED2 1; // LED2 熄灭 delay_1ms(1000); // 延时约1000ms 1秒 LED2 0; // LED2 点亮 LED1 1; // LED1 熄灭 delay_1ms(1000); // 再延时约1秒 } }ORG 0000H ; 程序存放起始地址 SJMP MAIN ; 上电后先跳转到主程序 MAIN ORG 0030H ; 从 0030H 开始存放用户程序 LED1 BIT P2.0 ; 定义 LED1 接在 P2.0 LED2 BIT P2.1 ; 定义 LED2 接在 P2.1 ; ; 延时子程序约 1000ms ; 晶振频率11.0592MHz ; 说明利用三重循环实现软件延时 ; DELAY1000MS: ; 延时约 1000ms PUSH 30H ; 保护 30H 单元原有数据 PUSH 31H ; 保护 31H 单元原有数据 PUSH 32H ; 保护 32H 单元原有数据 MOV 30H,#8 ; 最外层循环次数 MOV 31H,#1 ; 中间层循环次数 MOV 32H,#236 ; 最内层循环次数 NEXT: DJNZ 32H,NEXT ; 32H 减 1不为 0 则继续循环 DJNZ 31H,NEXT ; 31H 减 1不为 0 则继续循环 DJNZ 30H,NEXT ; 30H 减 1不为 0 则继续循环 POP 32H ; 恢复 32H 原值 POP 31H ; 恢复 31H 原值 POP 30H ; 恢复 30H 原值 RET ; 返回主程序 ; ; 主程序 ; 功能 ; 让 LED1 和 LED2 交替点亮每次间隔约 1 秒 ; MAIN: CLR LED1 ; LED1 输出低电平点亮 LED1低电平有效 SETB LED2 ; LED2 输出高电平熄灭 LED2 ACALL DELAY1000MS ; 延时约 1 秒 SETB LED1 ; LED1 输出高电平熄灭 LED1 CLR LED2 ; LED2 输出低电平点亮 LED2 ACALL DELAY1000MS ; 延时约 1 秒 SJMP MAIN ; 跳回 MAIN形成无限循环 END ; 程序结束10.程序启动后 LED 先全闪 3 次0.5 秒亮 / 灭再进入流水灯模式。#include reg52.h #define LED_PORT P2 // 将 P2 端口定义为 LED 输出端口 unsigned char flag 0; // 标志变量用于控制程序运行状态 /*------------------------------------------------ 函数名delay_1ms 功能软件延时函数 参数time - 延时长度 说明实际延时时间和晶振频率有关 ------------------------------------------------*/ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时次数 for (i 0; i 110; i); // 内层空循环形成约1ms延时 } /*------------------------------------------------ 主函数 功能 1. LED 全亮 500ms 2. LED 全灭 500ms 3. 每循环一次 flag 加 1 4. 当 flag 3 时进入流水灯模式 ------------------------------------------------*/ void main() { while (1) // 无限循环 { LED_PORT 0x00; // P2 输出 0x00若低电平点亮则8个LED全亮 delay_1ms(500); // 延时 500ms LED_PORT 0xFF; // P2 输出 0xFF若低电平点亮则8个LED全灭 delay_1ms(500); // 延时 500ms flag; // 标志变量加 1 while (flag 3) // 当 flag 等于 3 时进入流水灯循环 { static unsigned char i; // 静态局部变量只初始化一次值会被保留 for (i 0; i 7; i) // 依次点亮第 0~7 个 LED { LED_PORT ~(0x01 i); // 将第 i 位变为 0其余位为 1 // 若低电平点亮则表示第 i 个 LED 点亮 delay_1ms(500); // 每个LED保持 500ms } } } }LED_PORT EQU P2 ; 定义 LED 端口为 P2 FLAG EQU R0 ; 用寄存器 R0 作为计数标志位 ORG 0000H ; 程序起始地址 SJMP MAIN ; 上电后跳转到主程序 ORG 0030H ; 从 0030H 开始存放主程序和子程序 ; ; 延时子程序约 500ms ; 晶振频率11.0592MHz ; 说明通过三重循环实现软件延时 ; DELAY500MS: ; 延时约 500ms PUSH 30H ; 保护 30H 单元原来的值 PUSH 31H ; 保护 31H 单元原来的值 PUSH 32H ; 保护 32H 单元原来的值 MOV 30H,#4 ; 最外层循环次数 MOV 31H,#129 ; 中间层循环次数 MOV 32H,#112 ; 最内层循环次数 NEXT: DJNZ 32H,NEXT ; 32H 减 1不为 0 则继续循环 DJNZ 31H,NEXT ; 31H 减 1不为 0 则继续循环 DJNZ 30H,NEXT ; 30H 减 1不为 0 则继续循环 POP 32H ; 恢复 32H 原值 POP 31H ; 恢复 31H 原值 POP 30H ; 恢复 30H 原值 RET ; 返回调用处 ; ; 主程序 ; 功能 ; 1. 先让所有 LED 全亮 500ms ; 2. 再让所有 LED 全灭 500ms ; 3. 每执行一次上述过程FLAG 加 1 ; 4. 当 FLAG 3 时进入流水灯循环模式 ; MAIN: MOV LED_PORT,#00H ; P2 输出 00H ; 若 LED 为低电平点亮则此时全部 LED 点亮 ACALL DELAY500MS ; 延时 500ms MOV LED_PORT,#0FFH ; P2 输出 FFH ; 若 LED 为低电平点亮则此时全部 LED 熄灭 ACALL DELAY500MS ; 延时 500ms INC FLAG ; 标志变量 FLAG 加 1 MOV A,FLAG ; 将 FLAG 的值送入累加器 A CJNE A,#03H,LED_LOOP1 ; 判断 FLAG 是否等于 3 ; 若不等于 3则跳转到 LED_LOOP1继续执行主循环 MOV LED_PORT,#0FEH ; 当 FLAG 3 时先点亮第 1 个 LED ; 11111110B最低位为 0其余为 1 ; ; 流水灯循环 ; 功能通过循环左移让点亮的 LED 依次向左移动 ; LED_LOOP: MOV A,LED_PORT ; 取当前 LED 端口状态 RL A ; 循环左移 1 位 ; 例如11111110 - 11111101 - 11111011 ... MOV LED_PORT,A ; 将移位结果重新送回 P2 口 ACALL DELAY500MS ; 每次移动后延时 500ms SJMP LED_LOOP ; 无限循环持续执行流水灯效果 ; ; 若 FLAG 不等于 3则跳回 MAIN ; LED_LOOP1: SJMP MAIN ; 返回主程序重新执行“全亮-全灭”过程 END ; 程序结束
基于普中51单片机的LED练习
1.编写程序点亮 P2 口第 1个 LEDP2.0并保持常亮。#include reg52.h // 包含 51 单片机寄存器定义头文件 sbit LED1 P2^0; // 将开发板上的 LED1 连接到 P2.0 引脚 void main() { LED1 0; // 输出低电平点亮 LED1 }LED1 BIT P2.0 ; 定义 LED1对应单片机 P2.0 引脚 ORG 0000H ; 程序复位入口地址 SJMP MAIN ; 单片机上电后跳转到主程序 MAIN ORG 0030H ; 主程序存放地址从 0030H 开始 MAIN: CLR LED1 ; 将 P2.0 清零输出低电平 ; 如果 LED 采用低电平点亮方式则此时 LED 亮 SJMP MAIN ; 一直跳回 MAIN形成死循环 ; 保持 LED 常亮状态不变 END ; 程序结束2.编写程序让 P2 口 8 个 LED“全亮 1 秒→全灭 1 秒” 循环软件延时。#include reg52.h // 51 单片机寄存器定义头文件 #define LED_PORT P2 // 将 P2 端口定义为 LED 控制端口 /* * 函数名称delay_1ms * 功能说明软件延时函数 * 参数说明time —— 延时的毫秒数大致值 * 备注 * 该延时方式通过空循环实现延时时间与单片机晶振频率有关 * 因此这里只能作为近似延时使用。 */ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时毫秒数 for (i 0; i 110; i); // 内层空循环形成约 1ms 延时 } /* * 函数名称main * 功能说明主函数 * 程序功能 * 让连接在 P2 端口上的 LED 灯全部同时闪烁 * 亮 1 秒灭 1 秒循环重复执行。 */ void main() { while (1) // 无限循环程序一直运行 { LED_PORT 0xff; // P2 端口输出高电平LED 全灭常见低电平点亮方式 delay_1ms(1000); // 延时约 1000ms即 1 秒 LED_PORT 0x00; // P2 端口输出低电平LED 全亮 delay_1ms(1000); // 延时约 1000ms即 1 秒 } }LED_PORT EQU P2 ; 将 P2 端口定义为 LED 控制端口 ORG 0000H ; 程序复位入口地址 SJMP MAIN ; 上电后先跳转到主程序 MAIN ORG 0030H ; 从 0030H 地址开始存放子程序和主程序 ;------------------------------------------------ ; 子程序名称DELAY1000MS ; 功能说明延时约 1000ms1 秒 ; 晶振频率11.0592MHz ; 说明 ; 通过多重循环实现软件延时。 ; 进入子程序后先保护 30H、31H、32H 单元中的原数据 ; 延时结束后再恢复避免影响主程序中的其他数据。 ;------------------------------------------------ DELAY1000MS: PUSH 30H ; 保存 30H 单元原值 PUSH 31H ; 保存 31H 单元原值 PUSH 32H ; 保存 32H 单元原值 MOV 30H,#8 ; 设置外层循环初值 MOV 31H,#1 ; 设置中间层循环初值 MOV 32H,#236 ; 设置内层循环初值 NEXT: DJNZ 32H,NEXT ; 内层循环减 1未到 0 则继续 DJNZ 31H,NEXT ; 中间层循环减 1未到 0 则继续 DJNZ 30H,NEXT ; 外层循环减 1未到 0 则继续 POP 32H ; 恢复 32H 单元原值 POP 31H ; 恢复 31H 单元原值 POP 30H ; 恢复 30H 单元原值 RET ; 子程序返回 ;------------------------------------------------ ; 主程序 MAIN ; 功能说明 ; 让连接在 P2 端口上的 LED 灯循环闪烁 ; 先全灭 1 秒再全亮 1 秒不断重复。 ; 说明 ; 若开发板上的 LED 为“低电平点亮”方式 ; 则 0FFH 表示全灭00H 表示全亮。 ;------------------------------------------------ MAIN: MOV LED_PORT,#0FFH ; P2 端口输出高电平LED 全灭 ACALL DELAY1000MS ; 延时 1 秒 MOV LED_PORT,#00H ; P2 端口输出低电平LED 全亮 ACALL DELAY1000MS ; 延时 1 秒 SJMP MAIN ; 无限循环保持 LED 持续闪烁 END ; 汇编结束3.实现 P2 口 LED “从左到右流水灯”每个 LED 点亮 500ms 后切换到下一个。#include reg52.h // 51 单片机寄存器定义头文件 #define LED_PORT P2 // 将 P2 端口定义为 LED 输出端口 /* * 函数名称delay_1ms * 功能说明软件延时函数 * 参数说明time - 延时的毫秒数近似值 * 备注 * 该延时函数通过空循环实现实际延时时间会受到晶振频率影响。 */ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时次数 for (i 0; i 110; i); // 内层空循环约 1ms 延时 } /* * 函数名称main * 功能说明主函数 * 程序功能 * 让连接在 P2 端口上的 8 个 LED 灯依次点亮 * 每次点亮一个间隔约 500ms循环往复形成流水灯效果。 */ void main() { while (1) // 无限循环使流水灯一直运行 { static unsigned char i; // 定义循环变量用于控制当前点亮第几个 LED for (i 0; i 8; i) // 从第 1 个 LED 到第 8 个 LED 依次控制 { LED_PORT ~(0x01 i); // 将第 i 位输出为低电平其余位为高电平 // 若 LED 为低电平点亮方式则当前只有一个 LED 点亮 delay_1ms(500); // 延时约 500ms方便观察流水效果 } } }LED_PORT EQU P2 ; 定义 LED 端口P2 口连接 8 个 LED ORG 0000H ; 程序复位入口地址 SJMP MAIN ; 上电后跳转到主程序 MAIN ORG 0030H ; 从 0030H 地址开始存放子程序和主程序 ;------------------------------------------------ ; 子程序名称DELAY500MS ; 功能说明延时约 500ms ; 晶振频率11.0592MHz ; 说明 ; 该子程序通过三重循环实现软件延时。 ; 使用 30H、31H、32H 作为循环计数单元 ; 进入子程序前先保护原数据退出前再恢复。 ;------------------------------------------------ DELAY500MS: PUSH 30H ; 保存 30H 单元原值 PUSH 31H ; 保存 31H 单元原值 PUSH 32H ; 保存 32H 单元原值 MOV 30H,#4 ; 外层循环初值 MOV 31H,#129 ; 中层循环初值 MOV 32H,#112 ; 内层循环初值 NEXT: DJNZ 32H,NEXT ; 内层计数减 1未到 0 则继续循环 DJNZ 31H,NEXT ; 中层计数减 1未到 0 则继续循环 DJNZ 30H,NEXT ; 外层计数减 1未到 0 则继续循环 POP 32H ; 恢复 32H 单元原值 POP 31H ; 恢复 31H 单元原值 POP 30H ; 恢复 30H 单元原值 RET ; 子程序返回 ;------------------------------------------------ ; 主程序 MAIN ; 功能说明 ; 控制 P2 口上的 LED 依次移动点亮形成循环流水灯效果。 ; 说明 ; 初始值 0FEH 表示最低位为 0其余位为 1。 ; 若 LED 为低电平点亮方式则初始时点亮第 1 个 LED。 ; 每延时 500ms 后将当前亮灯位置循环左移一位。 ;------------------------------------------------ MAIN: MOV LED_PORT,#0FEH ; 初始状态仅最低位对应的 LED 点亮 LED_LOOP: ACALL DELAY500MS ; 延时约 500ms MOV A,LED_PORT ; 读取当前 LED 状态到累加器 A RL A ; 将 A 中数据循环左移 1 位 MOV LED_PORT,A ; 把新的状态送回 P2 口更新 LED 显示 SJMP LED_LOOP ; 无限循环持续实现流水灯效果 END ; 程序结束4.实现 P2 口 LED “往返流水灯”从 P2.0→P2.7 再返回 P2.0每个 LED 点亮 300ms。#include reg52.h // 包含 51 单片机寄存器定义头文件 #define LED_PORT P2 // 将 P2 端口定义为 LED 输出端口 /* * 函数名称delay1ms * 功能说明软件延时函数 * 参数说明time - 延时时间单位约为毫秒 * 备注 * 该函数通过空循环实现延时实际时间会受到晶振频率影响 * 因此这里只能认为是近似延时。 */ void delay1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时次数 for (i 0; i 110; i); // 内层空循环形成约 1ms 延时 } /* * 函数名称main * 功能说明主函数 * 程序功能 * 控制 P2 口上的 8 个 LED 先从左到右依次点亮 * 再从右到左依次点亮形成“来回流水灯”效果。 */ void main() { while (1) // 无限循环让流水灯持续运行 { static unsigned char i, j; // 定义循环变量控制当前点亮的 LED 位置 /* 第一阶段从第 1 个 LED 依次移动到第 8 个 LED */ for (i 0; i 8; i) { LED_PORT ~(0x01 i); // 让第 i 位输出低电平其余位输出高电平 // 如果 LED 为低电平点亮方式则当前只有一个 LED 亮 delay1ms(300); // 延时约 300ms便于观察流水效果 } /* 第二阶段从倒数第 2 个 LED 移回到第 2 个 LED */ for (j 6; j 0; j--) { LED_PORT ~(0x01 j); // 让第 j 位对应的 LED 点亮 delay1ms(300); // 延时约 300ms } } }LED_PORT EQU P2 ; 定义 LED 端口P2 口连接 8 个 LED ORG 0000H ; 程序复位入口地址 SJMP MAIN ; 上电后跳转到主程序 MAIN ORG 0030H ; 从 0030H 开始存放子程序和主程序 ;------------------------------------------------ ; 子程序名称DELAY300MS ; 功能说明延时约 300ms ; 晶振频率11.0592MHz ; 说明 ; 该子程序采用三重循环实现软件延时。 ; 使用 30H、31H、32H 作为循环计数单元 ; 进入子程序前先保存原值退出前恢复避免影响主程序。 ;------------------------------------------------ DELAY300MS: PUSH 30H ; 保存 30H 单元原值 PUSH 31H ; 保存 31H 单元原值 PUSH 32H ; 保存 32H 单元原值 MOV 30H,#3 ; 外层循环初值 MOV 31H,#26 ; 中层循环初值 MOV 32H,#216 ; 内层循环初值 NEXT: DJNZ 32H,NEXT ; 内层循环减 1未到 0 则继续 DJNZ 31H,NEXT ; 中层循环减 1未到 0 则继续 DJNZ 30H,NEXT ; 外层循环减 1未到 0 则继续 POP 32H ; 恢复 32H 单元原值 POP 31H ; 恢复 31H 单元原值 POP 30H ; 恢复 30H 单元原值 RET ; 子程序返回 ;------------------------------------------------ ; 主程序 MAIN ; 功能说明 ; 控制 P2 口上的 LED 先向左依次移动点亮 ; 再向右依次移动点亮形成来回流水灯效果。 ;------------------------------------------------ MAIN: MOV R0,#7 ; R0 用作左移次数计数共移动 7 次 MOV R1,#7 ; R1 用作右移次数计数共移动 7 次 MOV LED_PORT,#0FEH ; 初始状态最低位为 0其余位为 1 ; 若 LED 为低电平点亮方式则第 1 个 LED 点亮 LED_LOOP1: ACALL DELAY300MS ; 延时约 300ms MOV A,LED_PORT ; 读取当前 LED 状态 RL A ; 循环左移 1 位亮灯位置向左移动 MOV LED_PORT,A ; 更新 LED 状态 DJNZ R0,LED_LOOP1 ; 左移 7 次后结束第一阶段 LED_LOOP2: ACALL DELAY300MS ; 延时约 300ms MOV A,LED_PORT ; 读取当前 LED 状态 RR A ; 循环右移 1 位亮灯位置向右移动 MOV LED_PORT,A ; 更新 LED 状态 DJNZ R1,LED_LOOP2 ; 右移 7 次后结束第二阶段 SJMP MAIN ; 跳回主程序重复执行来回流水灯 END ; 汇编结束5.实现 P2 口 LED “奇偶交替亮”奇数位亮 1 秒→偶数位亮 1 秒循环。#include reg52.h // 包含 51 单片机寄存器定义头文件 #define LED_PORT P2 // 将 P2 端口定义为 LED 输出端口 /* * 函数名称delay1ms * 功能说明软件延时函数 * 参数说明time - 延时时间单位约为毫秒 * 备注 * 该函数通过空循环实现延时实际时间会受到晶振频率影响 * 因此这里只能认为是近似延时。 */ void delay1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时次数 for (i 0; i 110; i); // 内层空循环形成约 1ms 延时 } /* * 函数名称main * 功能说明主函数 * 程序功能 * 控制 P2 口上的 8 个 LED 交替显示 10101010 和 01010101 * 每种状态保持约 1000ms循环往复。 */ void main() { while (1) // 无限循环让 LED 持续交替闪烁 { LED_PORT 0xAA; // 二进制 10101010 delay1ms(1000); // 延时约 1000ms LED_PORT 0x55; // 二进制 01010101 delay1ms(1000); // 延时约 1000ms } }LED_PORT EQU P2 ; 定义LED端口为P2端口用来控制LED灯 ORG 0000H ; 程序起始地址 SJMP MAIN ; 跳转到主程序MAIN避免执行中间区域 ORG 0030H ; 将子程序放在0030H地址开始 ; ; 1000ms 延时子程序 ; 晶振频率11.0592MHz ; 通过三层循环产生约1秒延时 ; DELAY1000MS: PUSH 30H ; 保存寄存器30H原来的值保护现场 PUSH 31H ; 保存寄存器31H PUSH 32H ; 保存寄存器32H MOV 30H,#8 ; 外层循环计数 8 MOV 31H,#1 ; 中间循环计数 1 MOV 32H,#236 ; 内层循环计数 236 NEXT: DJNZ 32H,NEXT ; 内层循环32H减1不为0则继续循环 DJNZ 31H,NEXT ; 中间循环31H减1不为0则继续循环 DJNZ 30H,NEXT ; 外层循环30H减1不为0则继续循环 POP 32H ; 恢复寄存器32H原来的值 POP 31H ; 恢复寄存器31H原来的值 POP 30H ; 恢复寄存器30H原来的值 RET ; 返回主程序 ; ; 主程序 ; 控制LED交替闪烁 ; MAIN: MOV LED_PORT,#10101010B ; P2输出10101010偶数位LED亮奇数位灭 ACALL DELAY1000MS ; 调用1秒延时 MOV LED_PORT,#01010101B ; P2输出01010101LED状态反转 ACALL DELAY1000MS ; 再延时1秒 SJMP MAIN ; 无限循环LED持续交替闪烁 END ; 程序结束6.利用定时器 PWM 实现 P2.0 的 “呼吸灯”亮度从暗→亮→暗循环。#include reg52.h // 51单片机寄存器定义头文件 sbit LED0 P2^0; // 定义LED连接在P2.0口低电平点亮 unsigned int cnt 0; // PWM计数变量 unsigned int duty 50; // 占空比取值范围0~50 bit dir 0; // 变化方向标志0表示亮度减小1表示亮度增大 /* * 定时器0初始化函数 * 定时器0工作在方式116位定时器 */ void timer0_init() { TMOD | 0x01; // 设置定时器0为方式1 TL0 0xCD; // 装入定时初值低8位 TH0 0xD4; // 装入定时初值高8位 EA 1; // 开总中断 ET0 1; // 允许定时器0中断 TF0 0; // 清除定时器0溢出标志 TR0 1; // 启动定时器0 } /* * 定时器0中断服务函数 * 每次定时溢出后重新装载初值并使PWM计数加1 */ void timer0_isr() interrupt 1 { TL0 0xCD; // 重装定时初值低8位 TH0 0xD4; // 重装定时初值高8位 cnt; // PWM计数加1 } /* * 主函数 * 通过软件PWM控制LED亮度实现渐亮渐灭效果 */ void main() { timer0_init(); // 初始化定时器0 while (1) { if (!dir) // 当dir0时LED亮度逐渐减小 { if (cnt 50) // 一个PWM周期结束 { cnt 0; // 计数清零开始下一周期 if (duty 0) { duty--; // 占空比减小亮度降低 } else { dir 1; // 占空比减到最小后切换为亮度增大 } } } else // 当dir1时LED亮度逐渐增大 { if (cnt 50) // 一个PWM周期结束 { cnt 0; // 计数清零开始下一周期 if (duty 50) { duty; // 占空比增大亮度提高 } else { dir 0; // 占空比增到最大后切换为亮度减小 } } } if (cnt duty) // 当计数值小于占空比时 { LED0 0; // LED点亮 } else // 当计数值大于等于占空比时 { LED0 1; // LED熄灭 } } }CNT EQU 30H ; 定义变量 CNT存放 PWM 计数值 DUTY EQU 31H ; 定义变量 DUTY存放占空比 DIR EQU 32H ; 定义变量 DIR存放亮度变化方向 LED0 BIT P2.0 ; 定义 LED0 接在 P2.0 引脚 ORG 0000H ; 程序复位入口地址 LJMP MAIN ; 上电后跳转到主程序 ORG 000BH ; 定时器0中断入口地址 SJMP TIMER0ISR ; 跳转到定时器0中断服务程序 ORG 0030H ; 从 0030H 开始存放用户程序 TIMER0INIT: MOV CNT, #0 ; PWM 计数初值设为 0 MOV DUTY, #0 ; 占空比初值设为 0 MOV DIR, #0 ; 方向初值设为 0表示亮度逐渐增加 MOV TL0,#0CDH ; 定时器0低8位装入初值 MOV TH0,#0D4H ; 定时器0高8位装入初值 ANL TMOD,#0F0H ; 清除定时器0的方式控制位 ORL TMOD,#01H ; 设置定时器0为方式116位定时器 SETB EA ; 开总中断 SETB ET0 ; 允许定时器0中断 SETB TR0 ; 启动定时器0 CLR TF0 ; 清除定时器0溢出标志位 RET ; 返回主程序 TIMER0ISR: PUSH ACC ; 保护累加器 ACC PUSH PSW ; 保护程序状态字 PSW MOV TL0,#0CDH ; 定时器0重装初值低8位 MOV TH0,#0D4H ; 定时器0重装初值高8位 CLR C ; 清除进位标志准备做减法比较 MOV A, CNT ; 将 CNT 送入累加器A SUBB A, DUTY ; A CNT - DUTY用于比较 CNT 和 DUTY JNC LED_OFF ; 如果 CNT DUTY则跳转关闭LED CLR LED0 ; 如果 CNT DUTY则点亮LED低电平点亮 SJMP CNT_INC ; 跳转到计数处理部分 LED_OFF: SETB LED0 ; 熄灭LED高电平灭 CNT_INC: INC CNT ; PWM计数值加1 MOV A, CNT ; 将 CNT 送入A CJNE A, #100, ISR_EXIT ; 若 CNT 不等于100则结束本次中断 MOV CNT, #0 ; 一个PWM周期结束计数清零 MOV A, DIR ; 读取方向标志 JNZ DUTY_DEC ; 如果 DIR 不为0则执行占空比递减 DUTY_INC: INC DUTY ; 占空比加1LED逐渐变亮 MOV A, DUTY ; 读取当前占空比 CJNE A, #100, ISR_EXIT ; 若 DUTY 不等于100则结束中断 MOV DIR, #1 ; 若 DUTY 达到最大值100则改变方向为递减 SJMP ISR_EXIT ; 跳转退出中断 DUTY_DEC: DEC DUTY ; 占空比减1LED逐渐变暗 MOV A, DUTY ; 读取当前占空比 JNZ ISR_EXIT ; 若 DUTY 不为0则结束中断 MOV DIR, #0 ; 若 DUTY 减到0则改变方向为递增 ISR_EXIT: POP PSW ; 恢复程序状态字 PSW POP ACC ; 恢复累加器 ACC RETI ; 中断返回 MAIN: ACALL TIMER0INIT ; 调用定时器0初始化子程序 LOOP: SJMP LOOP ; 主程序空转所有PWM控制都在中断中完成 END ; 程序结束7.编写程序随机点亮 P2 口 1 个 LED500ms 后切换新的随机 LED。#include reg51.h // 51单片机寄存器定义头文件 #define LED_PORT P2 // 定义LED连接的端口为P2口 unsigned int rand_seed 0x1234; // 16位伪随机数种子初值不能为0 unsigned char LED_value[] // LED显示数组低电平点亮某一位LED { 0xFE, // 点亮第1个LED 0xFD, // 点亮第2个LED 0xFB, // 点亮第3个LED 0xF7, // 点亮第4个LED 0xEF, // 点亮第5个LED 0xDF, // 点亮第6个LED 0xBF, // 点亮第7个LED 0x7F // 点亮第8个LED }; /* * 生成16位伪随机数 * 使用16位LFSR线性反馈移位寄存器 */ unsigned int random_16bit(void) { rand_seed (rand_seed 1) ^ ((rand_seed 0x8000) ? 0x1021 : 0); return rand_seed; } /* * 生成[min, max]范围内的随机数 */ unsigned int random_range(unsigned int min, unsigned int max) { return (random_16bit() % (max - min 1)) min; } /* * 粗略延时函数 * time 取值越大延时时间越长 */ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) { for (i 0; i 110; i); } } /* * 主函数 * 随机点亮8个LED中的一个并每隔500ms切换一次 */ void main(void) { unsigned int rand_val; // 存放随机生成的LED序号 while (1) { rand_val random_range(0, 7); // 生成0~7之间的随机数 LED_PORT LED_value[rand_val]; // 按随机值点亮对应LED delay_1ms(500); // 延时约500ms } }LED_PORT EQU P2 ; LED 连接在 P2 口 ORG 0000H SJMP MAIN ; 上电后跳转到主程序入口 ORG 0030H ; ; RAM变量定义 ; MAX EQU 30H ; 随机数上限 MIN EQU 31H ; 随机数下限 TEMP EQU 32H ; 临时变量保存范围大小 (MAX - MIN 1) RAD EQU 33H ; 最终生成的随机数 SEED_H EQU 34H ; 随机种子高字节 SEED_L EQU 35H ; 随机种子低字节 BIT15 EQU 36H ; 保存原随机种子最高位(bit15) ; ; 主程序 ; 功能 ; 1. 初始化堆栈、端口、随机种子、随机范围 ; 2. 不断生成 0~7 的随机数 ; 3. 根据随机数查表点亮对应LED ; 4. 每次显示后延时约500ms ; MAIN: MOV SP, #60H ; 设置堆栈指针避开低地址RAM变量区 MOV P2, #0FFH ; P2 初始全高电平LED全灭低电平点亮 MOV SEED_H, #12H ; 初始化随机种子高字节种子 0x1234 MOV SEED_L, #34H ; 初始化随机种子低字节 MOV MAX, #07H ; 随机数最大值 7 MOV MIN, #00H ; 随机数最小值 0 MAIN_LOOP: CALL RANDOM_LSFR_PROC ; 生成随机数结果存放在 RAD 中 MOV A, RAD ; A 随机数0~7 MOV DPTR, #LED_VALUE ; DPTR 指向 LED 显示查找表 MOVC A, ADPTR ; 从代码区查表得到对应LED控制字 MOV LED_PORT, A ; 输出到 P2点亮对应LED CALL DELAY_500MS ; 延时约500ms SJMP MAIN_LOOP ; 无限循环 ; ; LED查找表 ; 每个数据对应点亮一个LED低电平有效 ; 例如 ; 0 - 11111110B - 第0个LED亮 ; 1 - 11111101B - 第1个LED亮 ; ... ; LED_VALUE: DB 0FEH,0FDH,0FBH,0F7H,0EFH,0DFH,0BFH,07FH ; ; LFSR随机数子程序 ; ; 对应C逻辑 ; rand_seed (rand_seed 1) ^ ((rand_seed 0x8000) ? 0x1021 : 0); ; return (rand_seed % (max - min 1)) min; ; ; 功能 ; 1. 对16位随机种子进行一次 LFSR 变换 ; 2. 将结果限制在 [MIN, MAX] 范围内 ; 3. 最终结果存入 RAD ; RANDOM_LSFR_PROC: ;------------------------------------------ ; 第一步保存移位前的最高位 bit15 ; bit15 就是 SEED_H 的 bit7 ;------------------------------------------ MOV A, SEED_H ANL A, #80H ; 取出高字节最高位 MOV BIT15, A ; 保存原 bit15后面判断是否异或 0x1021 ;------------------------------------------ ; 第二步16位左移 1 位 ; 低字节先移位进位送到高字节 ;------------------------------------------ CLR C ; 清除进位 MOV A, SEED_L RLC A ; 低字节左移1位最高位进入C MOV SEED_L, A MOV A, SEED_H RLC A ; 高字节左移1位同时接收低字节移出的进位 MOV SEED_H, A ;------------------------------------------ ; 第三步如果原 bit15 1则异或 0x1021 ; 这是 LFSR / CRC 多项式反馈操作 ;------------------------------------------ MOV A, BIT15 JZ SKIP_XOR ; 若原 bit15 0则跳过异或 MOV A, SEED_L XRL A, #21H ; 低字节异或 0x21 MOV SEED_L, A MOV A, SEED_H XRL A, #10H ; 高字节异或 0x10 MOV SEED_H, A SKIP_XOR: ;------------------------------------------ ; 第四步计算随机范围大小 TEMP MAX - MIN 1 ; 例如 MAX7, MIN0则 TEMP8 ;------------------------------------------ MOV A, MAX CLR C SUBB A, MIN ; A MAX - MIN ADD A, #01H ; A MAX - MIN 1 MOV TEMP, A ; TEMP 保存范围长度 ;------------------------------------------ ; 第五步计算 rand_seed % TEMP ; ; 当前程序目标范围是 0~7因此 TEMP 8 ; 下面代码在 TEMP8 时可正常工作 ; ; 思路 ; 先处理高字节余数再处理低字节 ;------------------------------------------ ; 先计算高字节对 TEMP 的余数SEED_H % TEMP MOV A, SEED_H MOV B, TEMP DIV AB ; A商, B余数 MOV R1, B ; R1 SEED_H % TEMP ; 再计算低字节对 TEMP 的余数SEED_L % TEMP MOV A, SEED_L MOV B, TEMP DIV AB ; A商, B余数 MOV A, B ; A SEED_L % TEMP ; 保存低字节余数 MOV R0, A ;------------------------------------------ ; 理论上完整公式应为 ; (R1 * 256 SEED_L) % TEMP ; ((R1 * (256 % TEMP)) (SEED_L % TEMP)) % TEMP ; ; 当 TEMP 8 时 ; 256 % 8 0 ; 所以高字节贡献为 0最终只需用 SEED_L % TEMP ; ; 因此当前程序实际上是针对 TEMP8 做了简化 ;------------------------------------------ MOV A, R1 JZ MOD_FINAL ; 如果高字节余数为0直接结束 ;------------------------------------------ ; 下面原本准备做通用处理但实际未展开完成 ; 当前注释保留提醒阅读者这里只是“预留结构” ;------------------------------------------ MOV A, #0 SETB C CLR C MOV B, #0 CALC_256_MOD: ; 这里本意是计算 256 % TEMP ; 但由于当前 TEMP8高位贡献恒为0 ; 所以无需继续计算直接走到最终结果 MOV A, #0 MOV R4, #1 MOV R5, #0 ; 对 TEMP8 ; 256 % 8 0 ; 故高位部分不影响最终余数 MOD_FINAL: MOV A, R0 ; 最终余数 SEED_L % TEMP ;------------------------------------------ ; 第六步加上最小值 MIN ; 得到范围 [MIN, MAX] 内的随机数 ;------------------------------------------ ADD A, MIN MOV RAD, A ; 保存随机结果 RET ; ; 约500ms延时子程序 ; 实际延时时间与晶振频率有关 ; 这里是三重循环的软件延时 ; DELAY_500MS: MOV R3, #10 DLY2: MOV R4, #200 DLY1: MOV R5, #250 DLY0: DJNZ R5, DLY0 DJNZ R4, DLY1 DJNZ R3, DLY2 RET END8.让 P2 口 LED 按 “二进制计数” 显示0~255每个数值保持 300ms#include reg52.h #define LED_PORT P2 // 将 P2 口定义为 LED 端口 /*------------------------------------------------ 函数名称delay1ms 功能软件延时 参数time - 延时次数 说明 该延时并不是严格 1ms 实际时间与单片机晶振频率有关 ------------------------------------------------*/ void delay1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时总次数 for (i 0; i 110; i); // 内层空循环形成短延时 } /*------------------------------------------------ 函数名称main 功能主函数 程序功能 1. 初始化 P2 口为 0xFF 2. 让 P2 口的值不断减 1 3. 每减一次延时约 300 次单位时间 4. 从而实现 LED 按一定规律变化显示 ------------------------------------------------*/ void main() { LED_PORT 0xff; // 初始状态P2 全部输出高电平 while (1) // 死循环程序一直运行 { LED_PORT--; // P2 口数据减 1LED 显示状态依次变化 delay1ms(300); // 延时便于观察 LED 变化 } }LED_PORT EQU P2 ; 将 P2 端口定义为 LED 输出端口 ORG 0000H ; 程序从地址 0000H 开始 SJMP MAIN ; 跳转到主程序入口 MAIN ORG 0030H ; 从 0030H 开始存放主程序和子程序 ; ; 延时子程序约 300ms ; 晶振频率11.0592MHz ; 功能通过三重 DJNZ 循环实现软件延时 ; DELAY300MS: PUSH 30H ; 保护寄存器/内存单元 30H防止原数据被破坏 PUSH 31H ; 保护 31H PUSH 32H ; 保护 32H MOV 30H,#3 ; 最外层循环次数 MOV 31H,#26 ; 中间层循环次数 MOV 32H,#216 ; 最内层循环次数 NEXT: DJNZ 32H,NEXT ; 32H减1不为0则继续循环 DJNZ 31H,NEXT ; 31H减1不为0则继续循环 DJNZ 30H,NEXT ; 30H减1不为0则继续循环 POP 32H ; 恢复 32H 原来的值 POP 31H ; 恢复 31H 原来的值 POP 30H ; 恢复 30H 原来的值 RET ; 延时结束返回主程序 ; ; 主程序 ; 功能 ; 1. 初始化 LED 端口 ; 2. 让 P2 口的值不断减 1 ; 3. 每次变化后延时约 300ms ; MAIN: MOV LED_PORT,#0FFH ; 初始化 P2 0xFF ; 若 LED 为低电平点亮则此时所有 LED 熄灭 LED_LOOP: DEC LED_PORT ; P2 端口值减 1 ; 即FFH - FEH - FDH - FCH ... ; LED 显示会按二进制规律变化 ACALL DELAY300MS ; 调用 300ms 延时子程序 SJMP LED_LOOP ; 无限循环执行 END ; 程序结束9.实现 P2.0 与 P2.1 “交替闪烁”亮灭周期 1 秒。#include reg52.h /*-------------------------------- 定义两个 LED 引脚 LED1 接在 P2.0 LED2 接在 P2.1 --------------------------------*/ sbit LED1 P2^0; sbit LED2 P2^1; /*-------------------------------- 函数名delay_1ms 功能软件延时函数 参数time延时的毫秒数 说明在 11.0592MHz 晶振下大致接近 1ms --------------------------------*/ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时时间 for (i 0; i 110; i); // 内层空循环形成约1ms延时 } /*-------------------------------- 主函数 功能 让两个 LED 交替闪烁 --------------------------------*/ void main() { while (1) // 死循环程序一直运行 { LED1 0; // LED1 点亮假设低电平点亮 LED2 1; // LED2 熄灭 delay_1ms(1000); // 延时约1000ms 1秒 LED2 0; // LED2 点亮 LED1 1; // LED1 熄灭 delay_1ms(1000); // 再延时约1秒 } }ORG 0000H ; 程序存放起始地址 SJMP MAIN ; 上电后先跳转到主程序 MAIN ORG 0030H ; 从 0030H 开始存放用户程序 LED1 BIT P2.0 ; 定义 LED1 接在 P2.0 LED2 BIT P2.1 ; 定义 LED2 接在 P2.1 ; ; 延时子程序约 1000ms ; 晶振频率11.0592MHz ; 说明利用三重循环实现软件延时 ; DELAY1000MS: ; 延时约 1000ms PUSH 30H ; 保护 30H 单元原有数据 PUSH 31H ; 保护 31H 单元原有数据 PUSH 32H ; 保护 32H 单元原有数据 MOV 30H,#8 ; 最外层循环次数 MOV 31H,#1 ; 中间层循环次数 MOV 32H,#236 ; 最内层循环次数 NEXT: DJNZ 32H,NEXT ; 32H 减 1不为 0 则继续循环 DJNZ 31H,NEXT ; 31H 减 1不为 0 则继续循环 DJNZ 30H,NEXT ; 30H 减 1不为 0 则继续循环 POP 32H ; 恢复 32H 原值 POP 31H ; 恢复 31H 原值 POP 30H ; 恢复 30H 原值 RET ; 返回主程序 ; ; 主程序 ; 功能 ; 让 LED1 和 LED2 交替点亮每次间隔约 1 秒 ; MAIN: CLR LED1 ; LED1 输出低电平点亮 LED1低电平有效 SETB LED2 ; LED2 输出高电平熄灭 LED2 ACALL DELAY1000MS ; 延时约 1 秒 SETB LED1 ; LED1 输出高电平熄灭 LED1 CLR LED2 ; LED2 输出低电平点亮 LED2 ACALL DELAY1000MS ; 延时约 1 秒 SJMP MAIN ; 跳回 MAIN形成无限循环 END ; 程序结束10.程序启动后 LED 先全闪 3 次0.5 秒亮 / 灭再进入流水灯模式。#include reg52.h #define LED_PORT P2 // 将 P2 端口定义为 LED 输出端口 unsigned char flag 0; // 标志变量用于控制程序运行状态 /*------------------------------------------------ 函数名delay_1ms 功能软件延时函数 参数time - 延时长度 说明实际延时时间和晶振频率有关 ------------------------------------------------*/ void delay_1ms(unsigned int time) { unsigned int i, j; for (j time; j 0; j--) // 外层循环控制延时次数 for (i 0; i 110; i); // 内层空循环形成约1ms延时 } /*------------------------------------------------ 主函数 功能 1. LED 全亮 500ms 2. LED 全灭 500ms 3. 每循环一次 flag 加 1 4. 当 flag 3 时进入流水灯模式 ------------------------------------------------*/ void main() { while (1) // 无限循环 { LED_PORT 0x00; // P2 输出 0x00若低电平点亮则8个LED全亮 delay_1ms(500); // 延时 500ms LED_PORT 0xFF; // P2 输出 0xFF若低电平点亮则8个LED全灭 delay_1ms(500); // 延时 500ms flag; // 标志变量加 1 while (flag 3) // 当 flag 等于 3 时进入流水灯循环 { static unsigned char i; // 静态局部变量只初始化一次值会被保留 for (i 0; i 7; i) // 依次点亮第 0~7 个 LED { LED_PORT ~(0x01 i); // 将第 i 位变为 0其余位为 1 // 若低电平点亮则表示第 i 个 LED 点亮 delay_1ms(500); // 每个LED保持 500ms } } } }LED_PORT EQU P2 ; 定义 LED 端口为 P2 FLAG EQU R0 ; 用寄存器 R0 作为计数标志位 ORG 0000H ; 程序起始地址 SJMP MAIN ; 上电后跳转到主程序 ORG 0030H ; 从 0030H 开始存放主程序和子程序 ; ; 延时子程序约 500ms ; 晶振频率11.0592MHz ; 说明通过三重循环实现软件延时 ; DELAY500MS: ; 延时约 500ms PUSH 30H ; 保护 30H 单元原来的值 PUSH 31H ; 保护 31H 单元原来的值 PUSH 32H ; 保护 32H 单元原来的值 MOV 30H,#4 ; 最外层循环次数 MOV 31H,#129 ; 中间层循环次数 MOV 32H,#112 ; 最内层循环次数 NEXT: DJNZ 32H,NEXT ; 32H 减 1不为 0 则继续循环 DJNZ 31H,NEXT ; 31H 减 1不为 0 则继续循环 DJNZ 30H,NEXT ; 30H 减 1不为 0 则继续循环 POP 32H ; 恢复 32H 原值 POP 31H ; 恢复 31H 原值 POP 30H ; 恢复 30H 原值 RET ; 返回调用处 ; ; 主程序 ; 功能 ; 1. 先让所有 LED 全亮 500ms ; 2. 再让所有 LED 全灭 500ms ; 3. 每执行一次上述过程FLAG 加 1 ; 4. 当 FLAG 3 时进入流水灯循环模式 ; MAIN: MOV LED_PORT,#00H ; P2 输出 00H ; 若 LED 为低电平点亮则此时全部 LED 点亮 ACALL DELAY500MS ; 延时 500ms MOV LED_PORT,#0FFH ; P2 输出 FFH ; 若 LED 为低电平点亮则此时全部 LED 熄灭 ACALL DELAY500MS ; 延时 500ms INC FLAG ; 标志变量 FLAG 加 1 MOV A,FLAG ; 将 FLAG 的值送入累加器 A CJNE A,#03H,LED_LOOP1 ; 判断 FLAG 是否等于 3 ; 若不等于 3则跳转到 LED_LOOP1继续执行主循环 MOV LED_PORT,#0FEH ; 当 FLAG 3 时先点亮第 1 个 LED ; 11111110B最低位为 0其余为 1 ; ; 流水灯循环 ; 功能通过循环左移让点亮的 LED 依次向左移动 ; LED_LOOP: MOV A,LED_PORT ; 取当前 LED 端口状态 RL A ; 循环左移 1 位 ; 例如11111110 - 11111101 - 11111011 ... MOV LED_PORT,A ; 将移位结果重新送回 P2 口 ACALL DELAY500MS ; 每次移动后延时 500ms SJMP LED_LOOP ; 无限循环持续执行流水灯效果 ; ; 若 FLAG 不等于 3则跳回 MAIN ; LED_LOOP1: SJMP MAIN ; 返回主程序重新执行“全亮-全灭”过程 END ; 程序结束