应用8255A控制LED小灯开闭(附代码注释)

设8255A的A口和B口都工作在方式0,A口作为输出口,接有8个开关;B口为输出口,接有8个发光二极管.系统硬件电路中不断扫描开关Ki,当K0闭合时,点亮LED0,LED2,LED4,LED6,其他LED灯暗,当K1闭合时,点亮LED1,:LED3,LED5,LED7,其他LED暗;当开关K0和K1同时闭合时全灭.设8255A的端口地址为200H~206H.试编程实现上述控制.

参考程序:

        CODE SEGMENT

        ASSUME CS : CODE

START: MOV AL,90H                          ;8255A的方式选择控制字10010000B = 90H

               MOV DX,206H                       ;206H为8255A的控制端口地址

               OUT DX,AL                             ;把8255A的控制字写进控制端口

 AGAIN: MOV DX,200H                        ;8255A的A口端口地址

               IN AL,DX                                  ;AL的值写进数据端口

               TEST AL,03H                          ;检测AL中的值与03H = 0000 0011B是否一致,检测开关K0,K1的状态

               JZ TURNOFF                           ;一样则跳转 TURNOFF  不一样执行下面的语句

               TEST AL,02H                           ;比较AL?= 0000 0010B 检测K0的状态

                JZ DISP_0                               ;K0 = 1开关闭合 跳转DISP_0程序

                TEST AL,02H                          ;比较AL?= 0000 0001B 检测K1的状态

                JZ DISP_1                                ;K1 = 1开关闭合 跳转DISP_1程序

                JMP AGAIN                              ;重复程序

DISP_0: MOV AL,55H                            ;55H = 0101 0101B 控制LED灯的状态  偶位亮

                MOV DX,202H                         ;端口地址赋值给DX寄存器

                OUT DX,AL                               ;把LED灯的状态写入B口端口地址

                JMP AGAIN                               ;跳转回主程序

DISP_1:MOV AL,0AAH                           ;0AAH = 1010 1010B 控制LED灯的状态  奇位亮

               MOV DX,202H                           ;端口地址赋值给DX寄存器

               OUT DX,AL                                 ;把LED灯的状态写入B口端口地址

               JMP AGAIN                                 ;跳转回主程序

TURNOFF : 

                MOV AL,00H                             ;赋值 LEd灯的状态 全灭

                MOV DX,202H 

                OUT DX,AL                                ;输出到B口端口地址

                JMP AGAIN                               

CODE ENDS

             END START

<think>好的,用户想用外部中断0控制8LED跑马灯,需要初始全亮、全灭,按键触发后循环跑马。我需要根据提供的引用资料来构建代码。 首先,用户提供的引用中有关于中断和流水灯的例子。比如引用[1]里提到了使用定时器中断来处理流水灯,避免阻塞其他中断。引用[2]中的主函数配置了TCON和IE,外部中断0的触发方式,以及中断服务函数的结构。 用户需要完整的C代码,所以我要整合这些元素。首先初始化LED端口,比如P2口。然后配置外部中断0,设置IT0为下降沿触发,开启中断使能。在主循环中可能需要处理不同的状态,比如初始全亮、全灭,然后等待中断触发跑马灯。 跑马灯的实现可以用循环移位,引用[1]中的flow_forward和flow_back函数,不过用户可能需要单向循环。这里可能需要定时器中断来定时移位,避免使用阻塞延时。比如,当外部中断触发后,启动定时器,每隔一定时间移位LED状态。 但用户的问题是否要求使用定时器?原问题没有明确,但用户提供的引用中有提到用定时器解决延时问题,所以可能更优。不过如果用户希望简单,可能用软件延时,但会阻塞。需要权衡。 根据引用[2]中的例子,主函数配置了TCON和IE,中断函数处理闪灯。但那里的中断处理用了delay函数,是软件延时,会阻塞。这可能有问题,比如中断处理时间过长。所以更好的方法是用定时器,如引用[1]中的方法,在定时器中断中改变标志位,主循环根据标志位移位LED。 所以代码结构可能是: 1. 初始化:配置P2口为输出,初始全亮或全灭。 2. 配置外部中断0为下降沿触发,开启中断。 3. 配置定时器中断,设置定时时间,比如每500ms触发一次。 4. 在外部中断的中断服务函数中设置标志位,启动跑马灯。 5. 主循环中根据标志位状态,执行相应的LED显示,而定时器中断负责改变LED的状态。 但用户可能需要初始状态是全部亮或灭,然后按下按键后开始跑马灯循环。所以初始时,可能让LED全灭,然后中断触发后开始跑马。 需要确定跑马灯的方向,比如左移还是右移。引用[1]中的循环左移和右移函数可以作为参考,但用户可能只需要单向。 另外,引用[2]中的delay函数使用了双重循环,但会阻塞。如果使用定时器,可以非阻塞。例如,定时器中断每隔一定时间改变一次LED的状态,主循环中不需要延时,只需要检查定时标志。 所以整合后的步骤: - 初始化P1或P2口控制LED,初始全灭。 - 配置INT0为下降沿触发,中断服务函数设置一个运行标志run_flag,初始为0,中断触发后run_flag翻转。 - 配置定时器0,比如模式1,每50ms中断一次,累计20次达到1秒。定时器中断中设置一个定时标志,比如timer_flag,每次中断timer_count加1,当达到20次时,执行LED移位,并重置计数器。 - 主函数中根据run_flag的状态决定是否执行跑马灯。当run_flag为1时,定时器中断会触发LED移位;为0时,停止。 同时,用户需要初始全亮或全灭。比如初始化时,P2=0x00全灭,或者0xff全亮。需要明确用户的需求。用户的问题中提到了初始全亮、全灭,可能是指上电后先全亮,再全灭,然后等待按键触发。或者可能初始状态是全亮,然后按键后全灭并开始跑马。需要进一步分析。 用户的问题描述是:“初始全亮、全灭以及按键触发后的循环跑马功能”。可能指的是初始时LED全亮,然后全灭,之后按键触发开始跑马。或者初始状态包括全亮和全灭两种,通过某种方式切换?或者上电后先全亮,再全灭,然后等待按键触发跑马? 可能需要更明确的处理。比如,上电后LED全亮,延时后全灭,然后等待按键。当按下按键触发INT0后,开始跑马灯循环。或者初始状态是全亮,按下按键后全灭,再开始跑马?可能需要用户澄清,但根据问题描述,应该是在初始时全亮和全灭,可能指初始时执行全亮和全灭的显示,然后进入等待,按键后开始跑马。或者可能用户希望初始状态是全亮,然后全灭,之后按键触发跑马。或者初始时LED全亮,当按键按下后,先全灭,然后开始跑马。 例如,代码可能的结构: - 主函数初始化: - LED端口初始化,P2=0xff(全灭,假设LED是共阳)。 - 延时,显示全亮(P2=0x00)一段时间,再全灭(P2=0xff)。 - 配置INT0,开启中断。 - 进入循环等待中断。 当中断发生时,开始跑马灯效果,即循环左移或右移。但如何实现循环?需要定时器中断来定时改变LED的状态。 因此,代码的大致流程: 1. 初始化部分: - 设置P2为输出,初始状态全亮(比如P2=0x00),延时后全灭(P2=0xff)。 - 配置外部中断0,下降沿触发。 - 配置定时器0,用于定时移位,但初始时定时器不开启。 2. 外部中断0服务函数: - 当按键按下时,触发中断,在此函数中设置一个标志位,如run=1,并初始化LED状态(如P2=0xfe,第一个灯亮)。 3. 定时器中断服务函数: - 当run=1时,定时器每隔一段时间触发中断,执行LED移位。 但需要处理定时器的启动和停止。例如,在外部中断触发后启动定时器,开始跑马灯。当再次按下按键时,可能停止。 但用户可能需要按下一次按键后开始循环跑马,再次按下停止?或者一直循环直到复位? 根据用户的问题,是“按键触发后的循环跑马功能”,所以可能是一次触发后开始循环跑马,直到再次触发或其他操作。 但可能用户希望按下按键后,跑马灯开始循环,并且持续运行。需要明确,但根据引用中的例子,可能使用标志位控制状态。 综合所有因素,编写代码: - 使用P1控制8LED(根据引用[1]中的代码使用P1进行流水灯)。 - 初始时,P1全亮(0x00),延时后全灭(0xff)。 - 当INT0触发时,启动跑马灯,使用定时器中断每隔一定时间移位。 - 跑马灯效果是循环左移,每次移位后更新P1的值。 - 使用一个标志位run_flag,初始为0,中断服务函数中切换run_flag的状态。或者按下按键后开始跑马,再次按下停止?或者每次按键触发切换状态? 用户的问题可能需要按键触发后开始循环跑马,所以可能需要每次按下按键,跑马灯开始或停止。或者第一次按下开始,再次按下停止。 但根据用户的问题描述,需要“按键触发后的循环跑马功能”,所以可能按键触发后开始无限循环跑马,直到复位或其他操作。或者用户可能需要通过按键切换不同的模式,如全亮、全灭、跑马。但根据问题,更可能的是初始全亮、全灭,按键后开始跑马。 需要明确初始化的步骤:比如,程序启动后,LED全亮,延时,全灭,然后等待中断。当中断发生时,进入跑马灯模式,循环左右移。 因此,可能的代码结构如下: 主函数: - 配置P1为输出,初始P1=0x00(全亮),延时,然后P1=0xff(全灭)。 - 配置INT0,开启中断。 - 配置定时器0,设置定时中断,但初始时不开启定时器中断。 - 进入while(1)循环,等待中断。 INT0的中断服务函数: - 当按键按下时,触发中断,设置run_flag=1,并启动定时器。 定时器中断服务函数: - 当run_flag为1时,执行LED移位,比如循环左移。 但需要处理定时器的配置,如定时器模式、重装值等。 另外,引用[1]中提到使用定时器中断来处理延时,避免阻塞,因此代码需要配置定时器,例如每500ms移位一次。 例如,使用定时器0,模式116位定时),假设晶振为12MHz,机器周期1us,定时50ms需要计数50000次,因此初值为65536-50000=15536=0x3CB0。 在定时器中断中,每次中断将计数器加1,当达到10次时(即0.5秒),执行移位。 但具体实现可能需要调整定时器设置。 综上,整合后的代码可能如下: 包含头文件,定义变量,配置中断服务函数,初始化等。 现在需要将这些思路转化为具体的代码,并确保符合用户的需求:初始全亮、全灭,按键触发后跑马灯循环。</think>以下是使用51单片机实现外部中断0控制8LED跑马灯的完整代码,包含初始全亮/全灭和按键触发的循环跑马功能: ```c #include <reg51.h> #define LED_PORT P1 // 假设LED连接在P1口 unsigned char run_flag = 0; // 运行标志位 unsigned char led_pattern = 0xFE; // 初始LED模式(第1个灯亮) /* 定时器0初始化 */ void timer0_init() { TMOD |= 0x01; // 定时器0模式116位定时) TH0 = 0xFC; // 1ms定时初值(12MHz晶振) TL0 = 0x18; ET0 = 1; // 允许定时器0中断 } /* 外部中断0初始化 */ void int0_init() { IT0 = 1; // 下降沿触发 EX0 = 1; // 允许外部中断0 EA = 1; // 开启总中断 } /* 主函数 */ void main() { LED_PORT = 0x00; // 初始全亮 timer0_init(); int0_init(); // 延时保持全亮状态 for(unsigned int i=0; i<30000; i++); LED_PORT = 0xFF; // 全灭 TR0 = 0; // 初始定时器停止 while(1) { if(run_flag) { TR0 = 1; // 启动定时器 } else { TR0 = 0; // 停止定时器 LED_PORT = 0xFF; // 恢复全灭 } } } /* 外部中断0服务函数 */ void int0_isr() interrupt 0 { run_flag = !run_flag; // 切换运行状态 led_pattern = 0xFE; // 重置LED模式 } /* 定时器0中断服务函数 */ void timer0_isr() interrupt 1 { static unsigned int counter = 0; TH0 = 0xFC; // 重装初值 TL0 = 0x18; if(++counter >= 500) { // 500ms间隔 counter = 0; LED_PORT = led_pattern; led_pattern = (led_pattern << 1) | (led_pattern >> 7); // 循环左移 } } ``` ### 代码说明 1. **初始化状态**: - 上电后LED全亮($P1=0x00$),延时后全灭($P1=0xFF$) - 外部中断0配置为下降沿触发[^2] 2. **中断控制**: - 按下按键触发INT0中断,切换`run_flag`标志位 - 定时器0提供500ms的时间基准,控制LED移位速度[^1] 3. **跑马灯实现**: - 使用循环左移操作:`led_pattern = (led_pattern << 1) | (led_pattern >> 7)` - LED模式存储在`led_pattern`变量中,每次定时器中断更新显示 4. **非阻塞设计**: - 使用定时器中断实现延时,避免使用空循环阻塞程序[^1] - 主循环仅检查状态标志,保证系统响应能力 ### 电路连接建议 1. 连接8LED到P1口(共阴接法) 2. 按键连接P3.2(INT0引脚)与GND之间,需加上拉电阻 3. 按键按下时产生下降沿信号
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值