找回密码
 立即注册
楼主: lclbf

STC8H8K64U开发箱V9.62学习笔记

[复制链接]
  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-10 08:23:54 | 显示全部楼层
第十八课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
        上一节讲了 74HC595 的驱动程序。为了更加方便操作 74HC595 输出的每个 IO
        状态,这节讲如何把 74HC595驱动程序翻译成类似单片机 IO 口直接驱动的方式。
        要教会大家两个知识点:
第一点:如何灵活运用与和非的运算符来实现位的操作。
第二点:如何灵活运用一个更新变量来实现静态刷新输出或者静态刷新显示的功能。
具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。
(2)实现功能:两片联级的 74HC595 驱动的 16 个 LED 灯交叉闪烁。比如,先是第 1,2,5,6,9,10,13,14 八个
                        灯亮,其它的灯都灭。然后再反过来,原来亮的就灭,原来灭的就亮。交替闪烁。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_time_level 200

void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void led_flicker();
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void led_update(); //LED 更新函数
void T0_time(); //定时中断函数

sbit hc595_sh_dr=P6^0;
sbit hc595_st_dr=P6^1;
sbit hc595_ds_dr=P6^2;

unsigned char ucLed_dr1=0; //代表 16 个灯的亮灭状态,0 代表灭,1 代表亮
unsigned char ucLed_dr2=0;
unsigned char ucLed_dr3=0;
unsigned char ucLed_dr4=0;
unsigned char ucLed_dr5=0;
unsigned char ucLed_dr6=0;
unsigned char ucLed_dr7=0;
unsigned char ucLed_dr8=0;
unsigned char ucLed_dr9=0;
unsigned char ucLed_dr10=0;
unsigned char ucLed_dr11=0;
unsigned char ucLed_dr12=0;
unsigned char ucLed_dr13=0;
unsigned char ucLed_dr14=0;
unsigned char ucLed_dr15=0;
unsigned char ucLed_dr16=0;
unsigned char ucLed_update=0; //刷新变量。每次更改 LED 灯的状态都要更新一次。
unsigned char ucLedStep=0; //步骤变量
unsigned int  uiTimeCnt=0; //统计定时中断次数的延时计数器
unsigned char ucLedStatus16_09=0; //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedStatus08_01=0; //代表底层 74HC595 输出状态的中间变量


void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        
        while(1)
        {
                led_flicker();
                led_update(); //LED 更新函数
        }
}
/* 注释一:
* 把 74HC595 驱动程序翻译成类似单片机 IO 口直接驱动方式的过程。
* 每次更新 LED 输出,记得都要把 ucLed_update 置 1 表示更新。
*/
void led_update() //LED 更新函数
{
        if(ucLed_update==1)
        {
                ucLed_update=0; //及时清零,让它产生只更新一次的效果,避免一直更新。
                if(ucLed_dr1==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x01;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfe;
                }
                if(ucLed_dr2==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x02;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfd;
                }
                if(ucLed_dr3==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x04;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfb;
                }
                if(ucLed_dr4==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x08;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xf7;
                }
                if(ucLed_dr5==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x10;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xef;
                }
                if(ucLed_dr6==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x20;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xdf;
                }
                if(ucLed_dr7==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x40;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xbf;
                }
                if(ucLed_dr8==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x80;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0x7f;
                }
                if(ucLed_dr9==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x01;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfe;
                }
                if(ucLed_dr10==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x02;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfd;
                }
                if(ucLed_dr11==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x04;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfb;
                }
                if(ucLed_dr12==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x08;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xf7;
                }
                if(ucLed_dr13==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x10;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xef;
                }
                if(ucLed_dr14==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x20;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xdf;
                }
                if(ucLed_dr15==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x40;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xbf;
                }
                if(ucLed_dr16==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x80;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0x7f;
                }
               
                hc595_drive(ucLedStatus16_09,ucLedStatus08_01); //74HC595 底层驱动函数
        }
}


void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
        unsigned char i;
        unsigned char ucTempData;
        
        hc595_sh_dr=0; //初始74HC595
        hc595_st_dr=0; //初始74HC595
        hc595_ds_dr=0; //初始74HC595
        
        
        ucTempData=ucLedStatusTemp16_09; //先送高 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
               
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        
        ucTempData=ucLedStatusTemp08_01; //再先送低 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)hc595_ds_dr=1;
                else hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        
        hc595_st_dr=0; //ST 引脚把两个寄存器的数据更新输出到 74HC595 的输出引脚上并且锁存起来
        delay_short(15);
        hc595_st_dr=1;
        delay_short(15);
        hc595_sh_dr=0; //拉低,抗干扰就增强
        hc595_st_dr=0;
        hc595_ds_dr=0;
}


void led_flicker() ////第三区 LED 闪烁应用程序
{
        switch(ucLedStep)
        {
                case 0:
                if(uiTimeCnt>=const_time_level) //时间到
                {
                        uiTimeCnt=0; //时间计数器清零
                        ucLed_dr1=1; //每个变量都代表一个 LED 灯的状态
                        ucLed_dr2=1;
                        ucLed_dr3=0;
                        ucLed_dr4=0;
                        ucLed_dr5=1;
                        ucLed_dr6=1;
                        ucLed_dr7=0;
                        ucLed_dr8=0;
                        ucLed_dr9=1;
                        ucLed_dr10=1;
                        ucLed_dr11=0;
                        ucLed_dr12=0;
                        ucLed_dr13=1;
                        ucLed_dr14=1;
                        ucLed_dr15=0;
                        ucLed_dr16=0;
                        ucLed_update=1; //更新显示
                        ucLedStep=1; //切换到下一个步骤
                }
                break;
               
        case 1:
                if(uiTimeCnt>=const_time_level) //时间到
                {
                        uiTimeCnt=0; //时间计数器清零
                        ucLed_dr1=0; //每个变量都代表一个 LED 灯的状态
                        ucLed_dr2=0;
                        ucLed_dr3=1;
                        ucLed_dr4=1;
                        ucLed_dr5=0;
                        ucLed_dr6=0;
                        ucLed_dr7=1;
                        ucLed_dr8=1;
                        ucLed_dr9=0;
                        ucLed_dr10=0;
                        ucLed_dr11=1;
                        ucLed_dr12=1;
                        ucLed_dr13=0;
                        ucLed_dr14=0;
                        ucLed_dr15=1;
                        ucLed_dr16=1;
                        ucLed_update=1; //更新显示
                        ucLedStep=0; //返回到上一个步骤
                }
                break;
        }
}


void T0_time() interrupt 1
{
        TF0=0; //清除中断标志
        TR0=0; //关中断
        if(uiTimeCnt<0xffff) //设定这个条件,防止 uiTimeCnt 超范围。
        {
                uiTimeCnt++; //累加定时中断的次数,
        }
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1; //开中断
}


void delay_short(unsigned int uiDelayShort)
{
        unsigned int i;
        for(i=0;i<uiDelayShort;i++)
        {
                ; //一个分号相当于执行一条空语句
        }
}


void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++) //内嵌循环的空指令数量
                {
                        ; //一个分号相当于执行一条空语句
                }
        }
}


void initial_myself() //第一区 初始化单片机
{
        P6M0 = 0X00;        //P6口设置为准双向口输出
        P6M1 = 0X00;
        
        TMOD=0x01; //设置定时器 0 为工作方式 1
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1; //开总中断
        ET0=1; //允许定时中断
        TR0=1; //启动定时中断
}

//总结陈词:
// 这节讲了把 74HC595 驱动程序翻译成类似单片机 IO 口直接驱动的方式,接下来,我们该如何来运用这种驱
//动方式实现跑马灯的程序?欲知详情,请听下回分解-----依次逐个点亮 LED 之后,再依次逐个熄灭 LED 的跑马
//灯程序。

18 把 74HC595 驱动程序翻译成类似单片机 IO 口直接驱.rar

51.9 KB, 下载次数: 99

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:529
  • 最近打卡:2025-05-08 07:17:33
已绑定手机

34

主题

459

回帖

2293

积分

金牌会员

积分
2293
发表于 2024-1-10 09:19:39 | 显示全部楼层
lcl*** 发表于 2024-1-3 10:33
第三课:
/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机 ...

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-11 08:16:54 | 显示全部楼层
第十九课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
        上一节讲了把74HC595驱动程序翻译成类似单片机IO口直接驱动的方式。
        这节在上一节的驱动程序基础上,开始讲跑马灯程序。我的跑马灯程序
        看似简单而且重复,其实蕴含着鸿哥的大智慧。它是基于鸿哥的 switch
        状态机思想,领略到了它的简单和精髓,以后任何所谓复杂的工程项目,
        都不再复杂。要教会大家一个知识点:通过本跑马灯程序,加深理解鸿哥
        所有实战项目中 switch 状态机的思想精髓。具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。
(2)实现功能:第 1 个至第 8 个 LED 灯,先依次逐个亮,再依次逐个灭。
                第 9 至第 16 个 LED 灯一直灭。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_time_level_01_08 200                 //第 1 个至第 8 个 LED 跑马灯的速度延时时间

void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void led_flicker_01_08();                                                 // 第 1 个至第 8 个 LED 的跑马灯程序,逐个亮,逐个灭.
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void led_update();                                                                                 //LED 更新函数
void T0_time();                                                                                         //定时中断函数

sbit hc595_sh_dr=P6^0;
sbit hc595_st_dr=P6^1;
sbit hc595_ds_dr=P6^2;

unsigned char ucLed_dr1=0; //代表 16 个灯的亮灭状态,0 代表灭,1 代表亮
unsigned char ucLed_dr2=0;
unsigned char ucLed_dr3=0;
unsigned char ucLed_dr4=0;
unsigned char ucLed_dr5=0;
unsigned char ucLed_dr6=0;
unsigned char ucLed_dr7=0;
unsigned char ucLed_dr8=0;
unsigned char ucLed_dr9=0;
unsigned char ucLed_dr10=0;
unsigned char ucLed_dr11=0;
unsigned char ucLed_dr12=0;
unsigned char ucLed_dr13=0;
unsigned char ucLed_dr14=0;
unsigned char ucLed_dr15=0;
unsigned char ucLed_dr16=0;
unsigned char ucLed_update=0; //刷新变量。每次更改 LED 灯的状态都要更新一次。
unsigned char ucLedStep_01_08=0; //第 1 个至第 8 个 LED 跑马灯的步骤变量
unsigned int  uiTimeCnt_01_08=0; //第 1 个至第 8 个 LED 跑马灯的统计定时中断次数的延时计数器
unsigned char ucLedStatus16_09=0; //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedStatus08_01=0; //代表底层 74HC595 输出状态的中间变量

void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        while(1)
        {
                led_flicker_01_08(); // 第 1 个至第 8 个 LED 的跑马灯程序,逐个亮,逐个灭.
                led_update();                                 //LED 更新函数
        }
}


void led_update()                                 //LED 更新函数
{
        if(ucLed_update==1)
        {
                ucLed_update=0;                         //及时清零,让它产生只更新一次的效果,避免一直更新。
                if(ucLed_dr1==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x01;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfe;
                }
                if(ucLed_dr2==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x02;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfd;
                }
                if(ucLed_dr3==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x04;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfb;
                }
                if(ucLed_dr4==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x08;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xf7;
                }
                if(ucLed_dr5==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x10;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xef;
                }
                if(ucLed_dr6==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x20;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xdf;
                }
                if(ucLed_dr7==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x40;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xbf;
                }
                if(ucLed_dr8==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x80;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0x7f;
                }
                if(ucLed_dr9==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x01;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfe;
                }
                if(ucLed_dr10==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x02;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfd;
                }
                if(ucLed_dr11==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x04;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfb;
                }
                if(ucLed_dr12==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x08;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xf7;
                }
                if(ucLed_dr13==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x10;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xef;
                }
                if(ucLed_dr14==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x20;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xdf;
                }
                if(ucLed_dr15==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x40;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xbf;
                }
                if(ucLed_dr16==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x80;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0x7f;
                }
                hc595_drive(ucLedStatus16_09,ucLedStatus08_01); //74HC595 底层驱动函数
        }
}


void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
        unsigned char i;
        unsigned char ucTempData;
        
        hc595_sh_dr=0;        //初始化74HC95
        hc595_st_dr=0;        //初始化74HC95
        hc595_ds_dr=0;        //初始化74HC95
        
        ucTempData=ucLedStatusTemp16_09; //先送高 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
               
                hc595_sh_dr=0;                                                                         //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        
        ucTempData=ucLedStatusTemp08_01;         //再先送低 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
               
                hc595_sh_dr=0;                                                                                 //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        hc595_st_dr=0;                                 //ST 引脚把两个寄存器的数据更新输出到 74HC595 的输出引脚上并且锁存起来
        delay_short(15);
        hc595_st_dr=1;
        delay_short(15);
        hc595_sh_dr=0;                                 //拉低,抗干扰就增强
        hc595_st_dr=0;
        hc595_ds_dr=0;
}
/* 注释一:
* 以下程序,看似简单而且重复,其实蕴含着鸿哥的大智慧。
* 它是基于鸿哥的 switch 状态机思想,领略到了它的简单和精髓,
* 以后任何所谓复杂的工程项目,都不再复杂。
*/
void led_flicker_01_08() //第 1 个至第 8 个 LED 的跑马灯程序,逐个亮,逐个灭.
{
        switch(ucLedStep_01_08)
        {
                case 0:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr1=1;                                                                                                                         //第 1 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=1;                                                                                          //切换到下一个步骤
                }
                break;
               
                case 1:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr2=1;                                                                                                                         //第 2 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=2;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 2:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr3=1;                                                                                                                         //第 3 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=3;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 3:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr4=1;                                                                                                                         //第 4 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=4;                                                                                          //切换到下一个步骤
                }
                break;
               
                case 4:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr5=1;                                                                                                                         //第 5 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=5;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 5:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr6=1;                                                                                                                         //第 6 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=6;                                                                                          //切换到下一个步骤
                }
                break;
               
                case 6:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr7=1;                                                                                                                         //第 7 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=7;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 7:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr8=1;                                                                                                                         //第 8 个亮
                        ucLed_update=1;                                                                                                                //更新显示
                        ucLedStep_01_08=8;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 8:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr8=0;                                                                                                                         //第 8 个灭
                        ucLed_update=1;                                                                                                          //更新显示
                        ucLedStep_01_08=9;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 9:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr7=0;                                                                                                                         //第 7 个灭
                        ucLed_update=1;                                                                                                          //更新显示
                        ucLedStep_01_08=10;                                                                                          //切换到下一个步骤
                }
                break;
               
                case 10:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr6=0;                                                                                                                         //第 6 个灭
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=11;                                                                                         //切换到下一个步骤
                }
                break;
               
                case 11:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr5=0;                                                                                                                         //第 5 个灭
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=12;                                                                                          //切换到下一个步骤
                }
                break;
               
                case 12:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr4=0;                                                                                                                         //第 4 个灭
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=13;                                                                                         //切换到下一个步骤
                }
                break;
               
               
                case 13:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr3=0;                                                                                                                         //第 3 个灭
                        ucLed_update=1;                                                                                                                //更新显示
                        ucLedStep_01_08=14;                                                                                                //切换到下一个步骤
                }
                break;
               
                case 14:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr2=0;                                                                                                                         //第 2 个灭
                        ucLed_update=1;                                                                                                                //更新显示
                        ucLedStep_01_08=15;                                                                                                //切换到下一个步骤
                }
                break;
               
                case 15:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr1=0;                                                                                                                         //第 1 个灭
                        ucLed_update=1;                                                                                                                //更新显示
                        ucLedStep_01_08=0;                                                                                                 //返回到最开始处,重新开始新的一次循环。
                }
                break;
        }
}


void T0_time() interrupt 1
{
        TF0=0;                                                                                         //清除中断标志
        TR0=0;                                                                                         //关中断
        if(uiTimeCnt_01_08<0xffff)         //设定这个条件,防止 uiTimeCnt 超范围。
        {
                uiTimeCnt_01_08++;                                 //累加定时中断的次数,
        }
        TH0=0xf8;                                                                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1;                                                                                         //开中断
}


void delay_short(unsigned int uiDelayShort)
{
        unsigned int i;
        for(i=0;i<uiDelayShort;i++)
        {
                ;                                                         //一个分号相当于执行一条空语句
        }
}


void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++)         //内嵌循环的空指令数量
                {
                        ;                                                                 //一个分号相当于执行一条空语句
                }
        }
}


void initial_myself() //第一区 初始化单片机
{
        P6M0 = 0X00;        //P6口设置为准双向口
        P6M1 = 0X00;
        
        TMOD=0x01;         //设置定时器 0 为工作方式 1
        TH0=0xf8;         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1;         //开总中断
        ET0=1;         //允许定时中断
        TR0=1;         //启动定时中断
}

//总结陈词:
// 这节讲了在第 1 个至第 8 个 LED 灯中,先依次逐个亮再依次逐个灭的跑马灯程序。下一节我们略作修改,
//继续做跑马灯的程序,要求在第 9 个至第 16 个 LED 灯中,依次逐个亮灯并且每次只能亮一个灯(其它的都灭),
//依次循环,我们该如何编写程序?欲知详情,请听下回分解-----依次逐个亮灯并且每次只能亮一个灯的跑马灯程
//序。

19 依次逐个点亮 LED 之后,再依次逐个熄灭 LED 的跑.rar

55.15 KB, 下载次数: 100

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-11 08:18:00 | 显示全部楼层
第二十课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
上一节讲了先依次逐个亮再依次逐个灭的跑马灯程序。这一节在上一节
的基础上,略作修改,继续讲跑马灯程序。我的跑马灯程序看似简单而
且重复,其实蕴含着鸿哥的大智慧。它是基于鸿哥的 switch 状态机思
想,领略到了它的简单和精髓,以后任何所谓复杂的工程项目,都不再
复杂。要教会大家一个知识点:通过本跑马灯程序,加深理解鸿哥所有
实战项目中 switch 状态机的思想精髓。具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。
(2)实现功能:第 9 个至第 16 个 LED 灯,依次逐个亮灯并且每次只能亮一个灯。第 1 至第 8 个 LED 灯一直灭。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_time_level_09_16 300 //第 9 个至第 16 个 LED 跑马灯的速度延时时间

void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void led_flicker_09_16(); // 第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void led_update(); //LED 更新函数
void T0_time(); //定时中断函数

sbit hc595_sh_dr=P6^0;
sbit hc595_st_dr=P6^1;
sbit hc595_ds_dr=P6^2;

unsigned char ucLed_dr1=0; //代表 16 个灯的亮灭状态,0 代表灭,1 代表亮
unsigned char ucLed_dr2=0;
unsigned char ucLed_dr3=0;
unsigned char ucLed_dr4=0;
unsigned char ucLed_dr5=0;
unsigned char ucLed_dr6=0;
unsigned char ucLed_dr7=0;
unsigned char ucLed_dr8=0;
unsigned char ucLed_dr9=0;
unsigned char ucLed_dr10=0;
unsigned char ucLed_dr11=0;
unsigned char ucLed_dr12=0;
unsigned char ucLed_dr13=0;
unsigned char ucLed_dr14=0;
unsigned char ucLed_dr15=0;
unsigned char ucLed_dr16=0;
unsigned char ucLed_update=0; //刷新变量。每次更改 LED 灯的状态都要更新一次。
unsigned char ucLedStep_09_16=0; //第 9 个至第 16 个 LED 跑马灯的步骤变量
unsigned int  uiTimeCnt_09_16=0; //第 9 个至第 16 个 LED 跑马灯的统计定时中断次数的延时计数器
unsigned char ucLedStatus16_09=0; //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedStatus08_01=0; //代表底层 74HC595 输出状态的中间变量


void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        
        while(1)
        {
                led_flicker_09_16();         // 第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
                led_update();                                         //LED 更新函数
        }
}


void led_update() //LED 更新函数
{
        if(ucLed_update==1)
        {
                ucLed_update=0; //及时清零,让它产生只更新一次的效果,避免一直更新。
                if(ucLed_dr1==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x01;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfe;
                }
                if(ucLed_dr2==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x02;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfd;
                }
                if(ucLed_dr3==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x04;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfb;
                }
                if(ucLed_dr4==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x08;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xf7;
                }
                if(ucLed_dr5==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x10;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xef;
                }
                if(ucLed_dr6==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x20;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xdf;
                }
                if(ucLed_dr7==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x40;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xbf;
                }
                if(ucLed_dr8==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x80;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0x7f;
                }
                if(ucLed_dr9==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x01;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfe;
                }
                if(ucLed_dr10==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x02;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfd;
                }
                if(ucLed_dr11==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x04;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfb;
                }
                if(ucLed_dr12==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x08;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xf7;
                }
                if(ucLed_dr13==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x10;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xef;
                }
                if(ucLed_dr14==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x20;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xdf;
                }
                if(ucLed_dr15==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x40;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xbf;
                }
                if(ucLed_dr16==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x80;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0x7f;
                }
               
                hc595_drive(ucLedStatus16_09,ucLedStatus08_01); //74HC595 底层驱动函数
        }
}


void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
        unsigned char i;
        unsigned char ucTempData;
        
        hc595_sh_dr=0; //初始化74HC595
        hc595_st_dr=0; //初始化74HC595
        hc595_ds_dr=0; //初始化74HC595
        
        ucTempData=ucLedStatusTemp16_09; //先送高 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        ucTempData=ucLedStatusTemp08_01; //再先送低 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        hc595_st_dr=0; //ST 引脚把两个寄存器的数据更新输出到 74HC595 的输出引脚上并且锁存起来
        delay_short(15);
        hc595_st_dr=1;
        delay_short(15);
        hc595_sh_dr=0; //拉低,抗干扰就增强
        hc595_st_dr=0;
        hc595_ds_dr=0;
}
/* 注释一:
* 以下程序,看似简单而且重复,其实蕴含着鸿哥的大智慧。
* 它是基于鸿哥的 switch 状态机思想,领略到了它的简单和精髓,
* 以后任何所谓复杂的工程项目,都不再复杂。
*/
void led_flicker_09_16() //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
{
        switch(ucLedStep_09_16)
        {
                case 0:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr16=0;                                                                                                                 //第 16 个灭
                        ucLed_dr9=1;                                                                                                                         //第 9 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=1;                                                                                          //切换到下一个步骤
                }
                break;
               
                case 1:
                if(uiTimeCnt_09_16>=const_time_level_09_16)         //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                         //时间计数器清零
                        ucLed_dr9=0;                                                                                                                                 //第 9 个灭
                        ucLed_dr10=1;                                                                                                                         //第 10 个亮
                        ucLed_update=1;                                                                                                                 //更新显示
                        ucLedStep_09_16=2;                                                                                                         //切换到下一个步骤
                }
                break;
               
                case 2:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr10=0;                                                                                                                 //第 10 个灭
                        ucLed_dr11=1;                                                                                                                  //第 11 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=3;                                                                                          //切换到下一个步骤
                }
                break;
               
                case 3:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr11=0;                                                                                                                 //第 11 个灭
                        ucLed_dr12=1;                                                                                                                 //第 12 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=4;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 4:
                if(uiTimeCnt_09_16>=const_time_level_09_16)         //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                         //时间计数器清零
                        ucLed_dr12=0;                                                                                                                         //第 12 个灭
                        ucLed_dr13=1;                                                                                                                         //第 13 个亮
                        ucLed_update=1;                                                                                                                 //更新显示
                        ucLedStep_09_16=5;                                                                                                         //切换到下一个步骤
                }
                break;
               
                case 5:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr13=0;                                                                                                                 //第 13 个灭
                        ucLed_dr14=1;                                                                                                                 //第 14 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=6;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 6:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr14=0;                                                                                                                 //第 14 个灭
                        ucLed_dr15=1;                                                                                                                 //第 15 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=7;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 7:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr15=0;                                                                                                                 //第 15 个灭
                        ucLed_dr16=1;                                                                                                                 //第 16 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=0;                                                                                                 //返回到开始处,重新开始新的一次循环
                }
        break;
        }
}


void T0_time() interrupt 1
{
        TF0=0;                                                                                         //清除中断标志
        TR0=0;                                                                                         //关中断
        
        if(uiTimeCnt_09_16<0xffff)         //设定这个条件,防止 uiTimeCnt 超范围。
        {
                uiTimeCnt_09_16++;                                 //累加定时中断的次数,
        }
        
        TH0=0xf8;                                                                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1;                                                                                         //开中断
}


void delay_short(unsigned int uiDelayShort)
{
        unsigned int i;
        for(i=0;i<uiDelayShort;i++)
        {
                ;                                 //一个分号相当于执行一条空语句
        }
}


void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++) //内嵌循环的空指令数量
                {
                        ; //一个分号相当于执行一条空语句
                }
        }
}


void initial_myself() //第一区 初始化单片机
{
        P6M0 = 0X00;   //设置P6口为准双向口
        P6M1 = 0X00;
        
        TMOD=0x01;                                         //设置定时器 0 为工作方式 1
        TH0=0xf8;                                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1;         //开总中断
        ET0=1;         //允许定时中断
        TR0=1;         //启动定时中断
}

//总结陈词:
// 上一节和这一节讲了两种不同的跑马灯程序,如果要让这两种不同的跑马灯程序都能各自独立运行,就涉及
//到多任务并行处理的程序框架。没错,下一节就讲多任务并行处理这方面的知识,欲知详情,请听下回分解-----
//多任务并行处理两路跑马灯。

20 依次逐个亮灯并且每次只能亮一个灯的跑马灯程序.rar

53.97 KB, 下载次数: 101

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-11 08:19:03 | 显示全部楼层
第二十一课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
        上一节讲了依次逐个亮灯并且每次只能亮一个灯的跑马灯程序。这一节
        要结合前面两节的内容,实现多任务并行处理两路跑马灯。要教会大家
        一个知识点:利用鸿哥的 switch 状态机思想,实现多任务并行处理的程序。
        具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。
(2)实现功能:
        第一路独立运行的任务是:第 1 个至第 8 个 LED 灯,先依次逐个亮,再依次逐个灭。
        第二路独立运行的任务是:第 9 个至第 16 个 LED 灯,依次逐个亮灯并且每次只能亮一个灯。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_time_level_01_08 200 //第 1 个至第 8 个 LED 跑马灯的速度延时时间
#define const_time_level_09_16 300 //第 9 个至第 16 个 LED 跑马灯的速度延时时间

void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void led_flicker_01_08(); //第一路独立运行的任务 第 1 个至第 8 个 LED 的跑马灯程序,逐个亮,逐个灭.
void led_flicker_09_16(); //第二路独立运行的任务 第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮
//一个.
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void led_update(); //LED 更新函数
void T0_time(); //定时中断函数

sbit hc595_sh_dr=P6^0;
sbit hc595_st_dr=P6^1;
sbit hc595_ds_dr=P6^2;

unsigned char ucLed_dr1=0; //代表 16 个灯的亮灭状态,0 代表灭,1 代表亮
unsigned char ucLed_dr2=0;
unsigned char ucLed_dr3=0;
unsigned char ucLed_dr4=0;
unsigned char ucLed_dr5=0;
unsigned char ucLed_dr6=0;
unsigned char ucLed_dr7=0;
unsigned char ucLed_dr8=0;
unsigned char ucLed_dr9=0;
unsigned char ucLed_dr10=0;
unsigned char ucLed_dr11=0;
unsigned char ucLed_dr12=0;
unsigned char ucLed_dr13=0;
unsigned char ucLed_dr14=0;
unsigned char ucLed_dr15=0;
unsigned char ucLed_dr16=0;
unsigned char ucLed_update=0; //刷新变量。每次更改 LED 灯的状态都要更新一次。
unsigned char ucLedStep_01_08=0; //第 1 个至第 8 个 LED 跑马灯的步骤变量
unsigned int uiTimeCnt_01_08=0; //第 1 个至第 8 个 LED 跑马灯的统计定时中断次数的延时计数器
unsigned char ucLedStep_09_16=0; //第 9 个至第 16 个 LED 跑马灯的步骤变量
unsigned int uiTimeCnt_09_16=0; //第 9 个至第 16 个 LED 跑马灯的统计定时中断次数的延时计数器
unsigned char ucLedStatus16_09=0; //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedStatus08_01=0; //代表底层 74HC595 输出状态的中间变量

void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        while(1)
        {
                led_flicker_01_08(); //第一路独立运行的任务 第 1 个至第 8 个 LED 的跑马灯程序,逐个亮,逐个灭.
                led_flicker_09_16(); //第二路独立运行的任务 第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能
                //亮一个.
                led_update(); //LED 更新函数
        }
}


void led_update() //LED 更新函数
{
         if(ucLed_update==1)
         {
                        ucLed_update=0; //及时清零,让它产生只更新一次的效果,避免一直更新。
                        if(ucLed_dr1==1)
                        {
                                ucLedStatus08_01=ucLedStatus08_01|0x01;
                        }
                        else
                        {
                                ucLedStatus08_01=ucLedStatus08_01&0xfe;
                        }
                        if(ucLed_dr2==1)
                        {
                                ucLedStatus08_01=ucLedStatus08_01|0x02;
                        }
                        else
                        {
                                ucLedStatus08_01=ucLedStatus08_01&0xfd;
                        }
                        if(ucLed_dr3==1)
                        {
                        ucLedStatus08_01=ucLedStatus08_01|0x04;
                        }
                        else
                        {
                                ucLedStatus08_01=ucLedStatus08_01&0xfb;
                        }
                        if(ucLed_dr4==1)
                        {
                                ucLedStatus08_01=ucLedStatus08_01|0x08;
                        }
                        else
                        {
                                ucLedStatus08_01=ucLedStatus08_01&0xf7;
                        }
                        if(ucLed_dr5==1)
                        {
                                ucLedStatus08_01=ucLedStatus08_01|0x10;
                        }
                        else
                        {
                                ucLedStatus08_01=ucLedStatus08_01&0xef;
                        }
                        if(ucLed_dr6==1)
                        {
                                ucLedStatus08_01=ucLedStatus08_01|0x20;
                        }
                        else
                        {
                                ucLedStatus08_01=ucLedStatus08_01&0xdf;
                        }
                        if(ucLed_dr7==1)
                        {
                                ucLedStatus08_01=ucLedStatus08_01|0x40;
                        }
                        else
                        {
                                ucLedStatus08_01=ucLedStatus08_01&0xbf;
                        }
                        if(ucLed_dr8==1)
                        {
                                ucLedStatus08_01=ucLedStatus08_01|0x80;
                        }
                        else
                        {
                                ucLedStatus08_01=ucLedStatus08_01&0x7f;
                        }
                        if(ucLed_dr9==1)
                        {
                                ucLedStatus16_09=ucLedStatus16_09|0x01;
                        }
                        else
                        {
                                ucLedStatus16_09=ucLedStatus16_09&0xfe;
                        }
                        if(ucLed_dr10==1)
                        {
                                ucLedStatus16_09=ucLedStatus16_09|0x02;
                        }
                        else
                        {
                                ucLedStatus16_09=ucLedStatus16_09&0xfd;
                        }
                        if(ucLed_dr11==1)
                        {
                                ucLedStatus16_09=ucLedStatus16_09|0x04;
                        }
                        else
                        {
                                ucLedStatus16_09=ucLedStatus16_09&0xfb;
                        }
                        if(ucLed_dr12==1)
                        {
                                ucLedStatus16_09=ucLedStatus16_09|0x08;
                        }
                        else
                        {
                                ucLedStatus16_09=ucLedStatus16_09&0xf7;
                        }
                        if(ucLed_dr13==1)
                        {
                                ucLedStatus16_09=ucLedStatus16_09|0x10;
                        }
                        else
                        {
                                ucLedStatus16_09=ucLedStatus16_09&0xef;
                        }
                        if(ucLed_dr14==1)
                        {
                                ucLedStatus16_09=ucLedStatus16_09|0x20;
                        }
                        else
                        {
                                ucLedStatus16_09=ucLedStatus16_09&0xdf;
                        }
                        if(ucLed_dr15==1)
                        {
                                ucLedStatus16_09=ucLedStatus16_09|0x40;
                        }
                        else
                        {
                                ucLedStatus16_09=ucLedStatus16_09&0xbf;
                        }
                        if(ucLed_dr16==1)
                        {
                                ucLedStatus16_09=ucLedStatus16_09|0x80;
                        }
                        else
                        {
                                ucLedStatus16_09=ucLedStatus16_09&0x7f;
                        }
                        hc595_drive(ucLedStatus16_09,ucLedStatus08_01); //74HC595 底层驱动函数
         }
}


void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
        unsigned char i;
        unsigned char ucTempData;
        hc595_sh_dr=0;
        hc595_st_dr=0;
        ucTempData=ucLedStatusTemp16_09; //先送高 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        ucTempData=ucLedStatusTemp08_01; //再先送低 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        hc595_st_dr=0; //ST 引脚把两个寄存器的数据更新输出到 74HC595 的输出引脚上并且锁存起来
        delay_short(15);
        hc595_st_dr=1;
        delay_short(15);
        hc595_sh_dr=0; //拉低,抗干扰就增强
        hc595_st_dr=0;
        hc595_ds_dr=0;
}
/* 注释一:
* 以下程序,看似简单而且重复,其实蕴含着鸿哥的大智慧。
* 它是基于鸿哥的 switch 状态机思想,领略到了它的简单和精髓,
* 以后任何所谓复杂的工程项目,都不再复杂。
*/
void led_flicker_01_08() //第一路独立运行的任务 第 1 个至第 8 个 LED 的跑马灯程序,逐个亮,逐个灭.
{
        switch(ucLedStep_01_08)
        {
                case 0:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr1=1;                                                                                                                         //第 1 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=1;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 1:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr2=1;                                                                                                                         //第 2 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=2;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 2:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr3=1;                                                                                                                         //第 3 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=3;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 3:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr4=1;                                                                                                                         //第 4 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=4;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 4:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                //时间计数器清零
                        ucLed_dr5=1;                                                                                                                         //第 5 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=5;                                                                                                //切换到下一个步骤
                }
                break;
               
                case 5:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                //时间计数器清零
                        ucLed_dr6=1;                                                                                                                         //第 6 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=6;                                                                                                //切换到下一个步骤
                }
                break;
               
                case 6:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0;                                                                                                 //时间计数器清零
                        ucLed_dr7=1;                                                                                                                         //第 7 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_01_08=7;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 7:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0; //时间计数器清零
                        ucLed_dr8=1; //第 8 个亮
                        ucLed_update=1; //更新显示
                        ucLedStep_01_08=8; //切换到下一个步骤
                }
                break;
               
                case 8:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0; //时间计数器清零
                        ucLed_dr8=0; //第 8 个灭
                        ucLed_update=1; //更新显示
                        ucLedStep_01_08=9; //切换到下一个步骤
                }
                break;
               
                case 9:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0; //时间计数器清零
                        ucLed_dr7=0; //第 7 个灭
                        ucLed_update=1; //更新显示
                        ucLedStep_01_08=10; //切换到下一个步骤
                }
                break;
               
                case 10:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0; //时间计数器清零
                        ucLed_dr6=0; //第 6 个灭
                        ucLed_update=1; //更新显示
                        ucLedStep_01_08=11; //切换到下一个步骤
                }
                break;
               
                case 11:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0; //时间计数器清零
                        ucLed_dr5=0; //第 5 个灭
                        ucLed_update=1; //更新显示
                        ucLedStep_01_08=12; //切换到下一个步骤
                }
                break;
               
                case 12:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0; //时间计数器清零
                        ucLed_dr4=0; //第 4 个灭
                        ucLed_update=1; //更新显示
                        ucLedStep_01_08=13; //切换到下一个步骤
                }
                break;
               
                case 13:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0; //时间计数器清零
                        ucLed_dr3=0; //第 3 个灭
                        ucLed_update=1; //更新显示
                        ucLedStep_01_08=14; //切换到下一个步骤
                }
                break;
               
                case 14:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0; //时间计数器清零
                        ucLed_dr2=0; //第 2 个灭
                        ucLed_update=1; //更新显示
                        ucLedStep_01_08=15; //切换到下一个步骤
                }
                break;
               
                case 15:
                if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到
                {
                        uiTimeCnt_01_08=0; //时间计数器清零
                        ucLed_dr1=0; //第 1 个灭
                        ucLed_update=1; //更新显示
                        ucLedStep_01_08=0; //返回到最开始处,重新开始新的一次循环。
                }
                break;
        }
}

void led_flicker_09_16() //第二路独立运行的任务 第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
{
        switch(ucLedStep_09_16)
        {
                case 0:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr16=0;                                                                                                                 //第 16 个灭
                        ucLed_dr9=1;                                                                                                                         //第 9 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=1;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 1:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                //时间计数器清零
                        ucLed_dr9=0;                                                                                                                         //第 9 个灭
                        ucLed_dr10=1;                                                                                                                 //第 10 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=2;                                                                                                //切换到下一个步骤
                }
                break;
               
                case 2:                        
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr10=0;                                                                                                                 //第 10 个灭
                        ucLed_dr11=1;                                                                                                                 //第 11 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=3;                                                                                                 //切换到下一个步骤
                }                                                                                       
                break;
               
                case 3:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr11=0;                                                                                                                 //第 11 个灭
                        ucLed_dr12=1;                                                                                                                 //第 12 个亮
                        ucLed_update=1;                                                                                                                 //更新显示
                        ucLedStep_09_16=4;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 4:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                //时间计数器清零
                        ucLed_dr12=0;                                                                                                                 //第 12 个灭
                        ucLed_dr13=1;                                                                                                                 //第 13 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=5;                                                                                                //切换到下一个步骤
                }
                break;
               
                case 5:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr13=0;                                                                                                                 //第 13 个灭
                        ucLed_dr14=1;                                                                                                                 //第 14 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=6;                                                                                                 //切换到下一个步骤
                }
                break;
               
                case 6:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                uiTimeCnt_09_16=0;                                                                                                         //时间计数器清零
                ucLed_dr14=0;                                                                                                                                 //第 14 个灭
                ucLed_dr15=1;                                                                                                                         //第 15 个亮
                ucLed_update=1;                                                                                                                 //更新显示
                ucLedStep_09_16=7;                                                                                                         //切换到下一个步骤
                }
                break;
               
                case 7:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                                                                                                 //时间计数器清零
                        ucLed_dr15=0;                                                                                                                 //第 15 个灭
                        ucLed_dr16=1;                                                                                                                         //第 16 个亮
                        ucLed_update=1;                                                                                                         //更新显示
                        ucLedStep_09_16=0;                                                                                                 //返回到开始处,重新开始新的一次循环
                }
                break;

        }
        }

void T0_time() interrupt 1
{
        TF0=0; //清除中断标志
        TR0=0; //关中断
        
        if(uiTimeCnt_01_08<0xffff) //设定这个条件,防止 uiTimeCnt 超范围。
        {
        
                uiTimeCnt_01_08++;                                 //累加定时中断的次数,
        }
        if(uiTimeCnt_09_16<0xffff) //设定这个条件,防止 uiTimeCnt 超范围。
        {
        
                uiTimeCnt_09_16++;                                 //累加定时中断的次数,
        }
        
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1; //开中断
}

void delay_short(unsigned int uiDelayShort)
{
        unsigned int i;
        for(i=0;i<uiDelayShort;i++)
        {
                ; //一个分号相当于执行一条空语句
        }
}

void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++)                         //内嵌循环的空指令数量
                {
                        ;                                                                                 //一个分号相当于执行一条空语句
                }
        }
}

void initial_myself() //第一区 初始化单片机
{
        P6M0 = 0X00;
        P6M1 = 0X00;
        
        TMOD=0x01; //设置定时器 0 为工作方式 1
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
         EA=1;         //开总中断
         ET0=1; //允许定时中断
         TR0=1; //启动定时中断
}

//总结陈词:
//这一节讲了多任务并行处理两路跑马灯的程序,从下一节开始,将会在跑马灯的基础上,新加入按键这个元
//素。如何把按键跟跑马灯的任务有效的关联起来,欲知详情,请听下回分解-----独立按键控制跑马灯的方向。

21 多任务并行处理两路跑马灯.rar

56.95 KB, 下载次数: 109

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-14 08:14:20 | 显示全部楼层
第二十二课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
        上一节讲了独立按键控制跑马灯的方向。这一节继续要教会大家一个
        知识点:如何通过一个中间变量把按键跟跑马灯的速度有效关联起来。
        具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。用矩阵键盘中的 SW32 键作
                为改变方向的独立按键,记得把输出线P0.0 一直输出低电平,模拟
                独立按键的触发地 GND。
(2)实现功能:
                第 1 个至第 8 个 LED 灯一直不亮。在第 9 个至第 16 个 LED 灯,
                依次逐个亮灯并且每次只能亮一个灯。按一次独立按键 SW32,将会
                更改跑马灯的运动方向。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_time_level_09_16         300 //第 9 个至第 16 个 LED 跑马灯的速度延时时间
#define const_voice_short                         40         //蜂鸣器短叫的持续时间
#define const_key_time1                                 20         //按键去抖动延时的时间

void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void led_flicker_09_16(); //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void led_update();                                 //LED 更新函数
void T0_time();                                         //定时中断函数
void key_service();                         //按键服务的应用程序
void key_scan();                                         //按键扫描函数 放在定时中断里

sbit hc595_sh_dr=P6^0;
sbit hc595_st_dr=P6^1;
sbit hc595_ds_dr=P6^2;
sbit beep_dr=P5^4;                 //蜂鸣器的驱动 IO 口
sbit key_sr1=P0^6;                 //对应STC8H8K64U开发箱9.62版SW32键
sbit key_gnd_dr=P0^0; //模拟独立按键的地 GND,因此必须一直输出低电平

unsigned char ucKeySec=0;                         //被触发的按键编号
unsigned int  uiKeyTimeCnt1=0;         //按键去抖动延时计数器
unsigned char ucKeyLock1=0;                 //按键触发后自锁的变量标志
unsigned int  uiVoiceCnt=0;                        //蜂鸣器鸣叫的持续时间计数器
unsigned char ucLed_dr1=0;                         //代表 16 个灯的亮灭状态,0 代表灭,1 代表亮
unsigned char ucLed_dr2=0;
unsigned char ucLed_dr3=0;
unsigned char ucLed_dr4=0;
unsigned char ucLed_dr5=0;
unsigned char ucLed_dr6=0;
unsigned char ucLed_dr7=0;
unsigned char ucLed_dr8=0;
unsigned char ucLed_dr9=0;
unsigned char ucLed_dr10=0;
unsigned char ucLed_dr11=0;
unsigned char ucLed_dr12=0;
unsigned char ucLed_dr13=0;
unsigned char ucLed_dr14=0;
unsigned char ucLed_dr15=0;
unsigned char ucLed_dr16=0;
unsigned char ucLed_update=0; //刷新变量。每次更改 LED 灯的状态都要更新一次。
unsigned char ucLedStep_09_16=0; //第 9 个至第 16 个 LED 跑马灯的步骤变量
unsigned int  uiTimeCnt_09_16=0; //第 9 个至第 16 个 LED 跑马灯的统计定时中断次数的延时计数器
unsigned char ucLedStatus16_09=0; //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedStatus08_01=0; //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedDirFlag=0; //方向变量,把按键与跑马灯关联起来的核心变量,0 代表正方向,1 代表反方向


void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        
        while(1)
        {
                led_flicker_09_16();         //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
                led_update();                                 //LED 更新函数
                key_service();                                 //按键服务的应用程序
        }
}


void key_scan()//按键扫描函数 放在定时中断里
{
        if(key_sr1==1)//IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock1=0; //按键自锁标志清零
                uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
        }
        else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt1++; //累加定时中断次数
                if(uiKeyTimeCnt1>const_key_time1)
                {
                        uiKeyTimeCnt1=0;
                        ucKeyLock1=1; //自锁按键置位,避免一直触发
                        ucKeySec=1; //触发 1 号键
                }
        }
}


void key_service() //按键服务的应用程序
{
        switch(ucKeySec) //按键服务状态切换
        {
                case 1:// 改变跑马灯方向的按键 对应朱兆祺学习板的 S1 键
                if(ucLedDirFlag==0) //通过中间变量改变跑马灯的方向
                {
                        ucLedDirFlag=1;
                }
                else
                {
                        ucLedDirFlag=0;
                }
                uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;

        }
}


void led_update() //LED 更新函数
{
        if(ucLed_update==1)
        {
                ucLed_update=0; //及时清零,让它产生只更新一次的效果,避免一直更新。
                if(ucLed_dr1==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x01;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfe;
                }
                if(ucLed_dr2==1)
                {
                                ucLedStatus08_01=ucLedStatus08_01|0x02;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfd;
                }
                if(ucLed_dr3==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x04;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfb;
                }
                if(ucLed_dr4==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x08;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xf7;
                }
                if(ucLed_dr5==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x10;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xef;
                }
                if(ucLed_dr6==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x20;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xdf;
                }
                if(ucLed_dr7==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x40;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xbf;
                }
                if(ucLed_dr8==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x80;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0x7f;
                }
                if(ucLed_dr9==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x01;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfe;
                }
                if(ucLed_dr10==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x02;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfd;
                }
                if(ucLed_dr11==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x04;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfb;
                }
                if(ucLed_dr12==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x08;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xf7;
                }
                if(ucLed_dr13==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x10;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xef;
                }
                if(ucLed_dr14==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x20;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xdf;
                }
                if(ucLed_dr15==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x40;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xbf;
                }
                if(ucLed_dr16==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x80;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0x7f;
                }
                hc595_drive(ucLedStatus16_09,ucLedStatus08_01); //74HC595 底层驱动函数
        }
}


void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
        unsigned char i;
        unsigned char ucTempData;
        
        hc595_sh_dr=0;
        hc595_st_dr=0;
        hc595_ds_dr=0;
        
        ucTempData=ucLedStatusTemp16_09; //先送高 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        ucTempData=ucLedStatusTemp08_01; //再先送低 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        hc595_st_dr=0; //ST 引脚把两个寄存器的数据更新输出到 74HC595 的输出引脚上并且锁存起来
        delay_short(15);
        hc595_st_dr=1;
        delay_short(15);
        hc595_sh_dr=0; //拉低,抗干扰就增强
        hc595_st_dr=0;
        hc595_ds_dr=0;
}


/* 注释一:
* 以下程序,要学会如何通过中间变量,把按键和跑马灯的任务关联起来
*/
void led_flicker_09_16() //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
{
        switch(ucLedStep_09_16)
        {
                case 0:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                        {
                        uiTimeCnt_09_16=0;                 //时间计数器清零
                        if(ucLedDirFlag==0)         //正方向
                        {
                                ucLed_dr16=0;                                 //第 16 个灭
                                ucLed_dr9=1;                                 //第 9 个亮
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=1;         //切换到下一个步骤
                        }
                        else                                                                         //反方向
                        {
                                ucLed_dr15=1;                         //第 15 个亮
                                ucLed_dr16=0;                         //第 16 个灭
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=7;         //返回上一个步骤
                        }
                }
                break;
               
                case 1:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                 //时间计数器清零
                        if(ucLedDirFlag==0)         //正方向
                        {
                                ucLed_dr9=0;                                 //第 9 个灭
                                ucLed_dr10=1;                         //第 10 个亮
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=2;         //切换到下一个步骤
                        }
                        else                                                                         //反方向
                        {
                                ucLed_dr16=1;                         //第 16 个亮
                                ucLed_dr9=0;                                 //第 9 个灭
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=0;         //返回上一个步骤
                        }
                }
                break;
               
                case 2:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                 //时间计数器清零
                        if(ucLedDirFlag==0)         //正方向
                        {
                                ucLed_dr10=0;                         //第 10 个灭
                                ucLed_dr11=1;                         //第 11 个亮
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=3;         //切换到下一个步骤
                        }
                        else                                                                         //反方向
                        {
                                ucLed_dr9=1;                                 //第 9 个亮
                                ucLed_dr10=0;                         //第 10 个灭
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=1;         //返回上一个步骤
                        }
                }
                break;
               
                case 3:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                 //时间计数器清零
                        if(ucLedDirFlag==0)         //正方向
                        {
                                ucLed_dr11=0;                         //第 11 个灭
                                ucLed_dr12=1;                         //第 12 个亮
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=4;         //切换到下一个步骤
                        }
                        else                                                                         //反方向
                        {
                                ucLed_dr10=1;                         //第 10 个亮
                                ucLed_dr11=0;                         //第 11 个灭
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=2;         //返回上一个步骤
                        }
                }
                break;
               
                case 4:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                 //时间计数器清零
                        if(ucLedDirFlag==0)         //正方向
                        {
                                ucLed_dr12=0;                         //第 12 个灭
                                ucLed_dr13=1;                         //第 13 个亮
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=5;         //切换到下一个步骤
                        }
                        else                                                                         //反方向
                        {
                                ucLed_dr11=1;                         //第 11 个亮
                                ucLed_dr12=0;                         //第 12 个灭
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=3;         //返回上一个步骤
                        }
                }
                break;
               
                case 5:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                 //时间计数器清零
                        if(ucLedDirFlag==0)         //正方向
                        {
                                ucLed_dr13=0;                         //第 13 个灭
                                ucLed_dr14=1;                         //第 14 个亮
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=6;         //切换到下一个步骤
                        }
                        else                                                                         //反方向
                        {
                                ucLed_dr12=1;                         //第 12 个亮
                                ucLed_dr13=0;                         //第 13 个灭
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=4;         //返回上一个步骤
                        }
                }
                break;
               
                case 6:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                 //时间计数器清零
                        if(ucLedDirFlag==0)         //正方向
                        {
                                ucLed_dr14=0;                         //第 14 个灭
                                ucLed_dr15=1;                         //第 15 个亮
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=7;         //切换到下一个步骤
                        }
                        else                                                                         //反方向
                        {
                                ucLed_dr13=1;                         //第 13 个亮
                                ucLed_dr14=0;                         //第 14 个灭
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=5;         //返回上一个步骤
                        }
                }
                break;
               
                case 7:
                if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
                {
                        uiTimeCnt_09_16=0;                 //时间计数器清零
                        if(ucLedDirFlag==0)         //正方向
                        {
                                ucLed_dr15=0;                         //第 15 个灭
                                ucLed_dr16=1;                         //第 16 个亮
                                ucLed_update=1;                 //更新显示
                                ucLedStep_09_16=0;         //返回到开始处,重新开始新的一次循环
                        }
                        else                                                                 //反方向
                        {
                                ucLed_dr14=1;                 //第 14 个亮
                                ucLed_dr15=0;                 //第 15 个灭
                                ucLed_update=1;         //更新显示
                                ucLedStep_09_16=6; //返回上一个步骤
                        }
                }
                break;

        }
}


void T0_time() interrupt 1
{
        TF0=0; //清除中断标志
        TR0=0; //关中断
        if(uiTimeCnt_09_16<0xffff) //设定这个条件,防止 uiTimeCnt 超范围。
        {
                uiTimeCnt_09_16++; //累加定时中断的次数,
        }
        key_scan(); //按键扫描函数
        if(uiVoiceCnt!=0)
        {
                uiVoiceCnt--; //每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫
                beep_dr=0; //蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。
        }
        else
        {
                ; //此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。
                beep_dr=1; //蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。
        }
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1; //开中断
}


void delay_short(unsigned int uiDelayShort)
{
        unsigned int i;
        for(i=0;i<uiDelayShort;i++)
        {
                ; //一个分号相当于执行一条空语句
        }
}


void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++)         //内嵌循环的空指令数量
                {
                        ;                                                                         //一个分号相当于执行一条空语句
                }
        }
}
void initial_myself() //第一区 初始化单片机
{
/* 注释二:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把 key_gnd_dr 输出低电平。
* STC8H8K64U开发箱9.62版的SW32 就是本程序中用到的一个独立按键。
*/
        P0M0 = 0X00;
        P0M1 = 0X00;
        
        P5M0 = 0X00;
        P5M1 = 0X00;
        
        P6M0 = 0X00;
        P6M1 = 0X00;
        
        key_gnd_dr=0;         //模拟独立按键的地 GND,因此必须一直输出低电平
        beep_dr=1;                         //用 PNP 三极管控制蜂鸣器,输出高电平时不叫。
        TMOD=0x01;                         //设置定时器 0 为工作方式 1
        TH0=0xf8;                         //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1;         //开总中断
        ET0=1;         //允许定时中断
        TR0=1;         //启动定时中断
}


//总结陈词:
//这一节讲了独立按键控制跑马灯的方向。如果按键要控制跑马灯的速度,我们该怎么编写程序呢?欲知详情,
//请听下回分解-----独立按键控制跑马灯的速度。

22 独立按键控制跑马灯的方向.rar

58.64 KB, 下载次数: 109

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-14 08:15:35 | 显示全部楼层
第二十三课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
        上一节讲了独立按键控制跑马灯的方向。这一节继续要教会大家一个
        知识点:如何通过一个中间变量把按键跟跑马灯的速度有效关联起来。
具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。在上一节的基础上,
                增加一个加速按键和一个减速按键,用矩阵键盘中的SW36键作为加
                速独立按键,用矩阵键盘中的SW37键作为减速独立按键,记得把输
                出线 P0.0一直输出低电平,模拟独立按键的触发地 GND。
(2)实现功能:
                在上一节的基础上,第 1 个至第 8 个 LED 灯一直不亮。在第 9
                个至第 16 个 LED 灯,依次逐个亮灯并且每次只能亮一个灯。每
                按一次独立按键 SW36,速度都会加快。每按一次独立按键 SW22,速度
                都会减慢。跟上一节一样,用SW32来改变方向。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_voice_short 40 //蜂鸣器短叫的持续时间
#define const_key_time1         20 //按键去抖动延时的时间
#define const_key_time2         20 //按键去抖动延时的时间
#define const_key_time3         20 //按键去抖动延时的时间

void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void led_flicker_09_16(); //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void led_update();                 //LED 更新函数
void T0_time();                         //定时中断函数
void key_service();         //按键服务的应用程序
void key_scan();                         //按键扫描函数 放在定时中断里

sbit hc595_sh_dr=P6^0;
sbit hc595_st_dr=P6^1;
sbit hc595_ds_dr=P6^2;
sbit beep_dr=P5^4;                 //蜂鸣器的驱动 IO 口
sbit key_sr1=P0^6;                 //对应STC8H8K64U开发箱9.62版SW32键
sbit key_sr2=P0^7;                 //对应STC8H8K64U开发箱9.62版SW36键
sbit key_sr3=P3^5;                 //对应STC8H8K64U开发箱9.62版SW22键
sbit key_gnd_dr=P0^0; //模拟独立按键的地 GND,因此必须一直输出低电平

unsigned char ucKeySec=0;                         //被触发的按键编号
unsigned int  uiKeyTimeCnt1=0;         //按键去抖动延时计数器
unsigned char ucKeyLock1=0;                 //按键触发后自锁的变量标志
unsigned int  uiKeyTimeCnt2=0;         //按键去抖动延时计数器
unsigned char ucKeyLock2=0;                 //按键触发后自锁的变量标志
unsigned int  uiKeyTimeCnt3=0;         //按键去抖动延时计数器
unsigned char ucKeyLock3=0;                 //按键触发后自锁的变量标志
unsigned int  uiVoiceCnt=0;                 //蜂鸣器鸣叫的持续时间计数器

unsigned char ucLed_dr1=0;                         //代表 16 个灯的亮灭状态,0 代表灭,1 代表亮
unsigned char ucLed_dr2=0;
unsigned char ucLed_dr3=0;
unsigned char ucLed_dr4=0;
unsigned char ucLed_dr5=0;
unsigned char ucLed_dr6=0;
unsigned char ucLed_dr7=0;
unsigned char ucLed_dr8=0;
unsigned char ucLed_dr9=0;
unsigned char ucLed_dr10=0;
unsigned char ucLed_dr11=0;
unsigned char ucLed_dr12=0;
unsigned char ucLed_dr13=0;
unsigned char ucLed_dr14=0;
unsigned char ucLed_dr15=0;
unsigned char ucLed_dr16=0;

unsigned char ucLed_update=0;                                         //刷新变量。每次更改 LED 灯的状态都要更新一次。
unsigned char ucLedStep_09_16=0;                                 //第 9 个至第 16 个 LED 跑马灯的步骤变量
unsigned int         uiTimeCnt_09_16=0;                                 //第 9 个至第 16 个 LED 跑马灯的统计定时中断次数的延时计数器
unsigned char ucLedStatus16_09=0;                         //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedStatus08_01=0;                         //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedDirFlag=0;                                         //方向变量,把按键与跑马灯关联起来的核心变量,0 代表正方向,1 代表反方向
unsigned int         uiSetTimeLevel_09_16=300; //速度变量,此数值越大速度越慢,此数值越小速度越快。


void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        while(1)
        {
                led_flicker_09_16();         //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
                led_update();                                 //LED 更新函数
                key_service();                                 //按键服务的应用程序
        }
}


void key_scan()                                                        //按键扫描函数 放在定时中断里
{
        if(key_sr1==1)                                                //IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock1=0;                                         //按键自锁标志清零
                uiKeyTimeCnt1=0;                                //按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
        }
        else if(ucKeyLock1==0)                //有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt1++;                                 //累加定时中断次数
                if(uiKeyTimeCnt1>const_key_time1)
                {
                        uiKeyTimeCnt1=0;
                        ucKeyLock1=1;                                 //自锁按键置位,避免一直触发
                        ucKeySec=1;                                         //触发 1 号键
        }
        }
        if(key_sr2==1)                                                //IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock2=0;                                         //按键自锁标志清零
                uiKeyTimeCnt2=0;                                //按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
        }
        else if(ucKeyLock2==0)                //有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt2++;                                 //累加定时中断次数
                if(uiKeyTimeCnt2>const_key_time2)
                {
                        uiKeyTimeCnt2=0;
                        ucKeyLock2=1;                                 //自锁按键置位,避免一直触发
                        ucKeySec=2;                                         //触发 2 号键
                }
        }
        if(key_sr3==1)                                                //IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock3=0;                                         //按键自锁标志清零
                uiKeyTimeCnt3=0;                                //按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
        }
        else if(ucKeyLock3==0)                //有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt3++;                                 //累加定时中断次数
                if(uiKeyTimeCnt3>const_key_time3)
                {
                        uiKeyTimeCnt3=0;
                        ucKeyLock3=1;                                 //自锁按键置位,避免一直触发
                        ucKeySec=3;                                         //触发 3 号键
                }
        }
}



void key_service()                                 //按键服务的应用程序
{
        switch(ucKeySec)                                 //按键服务状态切换
        {
                case 1:                                                                // 改变跑马灯方向的按键 对应STC8H8K64U开发箱9.62版SW32键
                if(ucLedDirFlag==0)         //通过中间变量改变跑马灯的方向
                {
                        ucLedDirFlag=1;
                }
                else
                {
                        ucLedDirFlag=0;
                }
                uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                ucKeySec=0;                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;

                case 2:                                                                                                // 加速按键STC8H8K64U开发箱9.62版SW36键 uiSetTimeLevel_09_16 越小速度越快
                uiSetTimeLevel_09_16=uiSetTimeLevel_09_16-10;
                if(uiSetTimeLevel_09_16<50)         //最快限定在 50
                {
                        uiSetTimeLevel_09_16=50;
                }
                uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                ucKeySec=0;                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
               
                case 3:                                                                                                        // 减速按键 对应STC8H8K64U开发箱9.62版SW22键 uiSetTimeLevel_09_16 越大速度越慢
                uiSetTimeLevel_09_16=uiSetTimeLevel_09_16+10;
                if(uiSetTimeLevel_09_16>550)         //最慢限定在 550
                {
                        uiSetTimeLevel_09_16=550;
                }
                uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                ucKeySec=0;                                                                         //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        }
}


void led_update() //LED 更新函数
{
        if(ucLed_update==1)
        {
                ucLed_update=0; //及时清零,让它产生只更新一次的效果,避免一直更新。
                if(ucLed_dr1==1)
                {
                ucLedStatus08_01=ucLedStatus08_01|0x01;
                }
                else
                {
                ucLedStatus08_01=ucLedStatus08_01&0xfe;
                }
                if(ucLed_dr2==1)
                {
                ucLedStatus08_01=ucLedStatus08_01|0x02;
                }
                else
                {
                ucLedStatus08_01=ucLedStatus08_01&0xfd;
                }
                if(ucLed_dr3==1)
                {
                ucLedStatus08_01=ucLedStatus08_01|0x04;
                }
                else
                {
                ucLedStatus08_01=ucLedStatus08_01&0xfb;
                }
                if(ucLed_dr4==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x08;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xf7;
                }
                if(ucLed_dr5==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x10;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xef;
                }
                if(ucLed_dr6==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x20;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xdf;
                }
                if(ucLed_dr7==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x40;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xbf;
                }
                if(ucLed_dr8==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x80;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0x7f;
                }
                if(ucLed_dr9==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x01;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfe;
                }
                if(ucLed_dr10==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x02;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfd;
                }
                if(ucLed_dr11==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x04;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfb;
                }
                if(ucLed_dr12==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x08;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xf7;
                }
                if(ucLed_dr13==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x10;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xef;
                }
                if(ucLed_dr14==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x20;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xdf;
                }
                if(ucLed_dr15==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x40;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xbf;
                }
                if(ucLed_dr16==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x80;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0x7f;
                }
                hc595_drive(ucLedStatus16_09,ucLedStatus08_01); //74HC595 底层驱动函数
        }
}


void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
        unsigned char i;
        unsigned char ucTempData;
        
        hc595_sh_dr=0;
        hc595_st_dr=0;
        
        ucTempData=ucLedStatusTemp16_09; //先送高 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        ucTempData=ucLedStatusTemp08_01; //再先送低 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        hc595_st_dr=0; //ST 引脚把两个寄存器的数据更新输出到 74HC595 的输出引脚上并且锁存起来
        delay_short(15);
        hc595_st_dr=1;
        delay_short(15);
        hc595_sh_dr=0; //拉低,抗干扰就增强
        hc595_st_dr=0;
        hc595_ds_dr=0;
}
/* 注释一:
* 以下程序,要学会如何通过中间变量,把按键和跑马灯的任务关联起来
*/
void led_flicker_09_16() //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
{
        switch(ucLedStep_09_16)
        {
                case 0:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr16=0; //第 16 个灭
                                ucLed_dr9=1; //第 9 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=1; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr15=1; //第 15 个亮
                                ucLed_dr16=0; //第 16 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=7; //返回上一个步骤
                        }
                }
                break;
               
                case 1:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr9=0; //第 9 个灭
                                ucLed_dr10=1; //第 10 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=2; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr16=1; //第 16 个亮
                                ucLed_dr9=0; //第 9 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=0; //返回上一个步骤
                        }
                }
                break;
               
                case 2:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr10=0; //第 10 个灭
                                ucLed_dr11=1; //第 11 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=3; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr9=1; //第 9 个亮
                                ucLed_dr10=0; //第 10 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=1; //返回上一个步骤
                        }
                }
                break;
               
                case 3:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr11=0; //第 11 个灭
                                ucLed_dr12=1; //第 12 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=4; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr10=1; //第 10 个亮
                                ucLed_dr11=0; //第 11 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=2; //返回上一个步骤
                        }
                }
                break;
               
                case 4:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr12=0; //第 12 个灭
                                ucLed_dr13=1; //第 13 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=5; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr11=1; //第 11 个亮
                                ucLed_dr12=0; //第 12 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=3; //返回上一个步骤
                        }
                }
                break;
               
                case 5:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr13=0; //第 13 个灭
                                ucLed_dr14=1; //第 14 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=6; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr12=1; //第 12 个亮
                                ucLed_dr13=0; //第 13 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=4; //返回上一个步骤
                        }
                }
                break;
               
                case 6:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr14=0; //第 14 个灭
                                ucLed_dr15=1; //第 15 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=7; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr13=1; //第 13 个亮
                                ucLed_dr14=0; //第 14 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=5; //返回上一个步骤
                        }
                }
                break;
               
                case 7:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr15=0; //第 15 个灭
                                ucLed_dr16=1; //第 16 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=0; //返回到开始处,重新开始新的一次循环
                        }
                        else //反方向
                        {
                                ucLed_dr14=1; //第 14 个亮
                                ucLed_dr15=0; //第 15 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=6; //返回上一个步骤
                        }
                }
                break;

        }
}


void T0_time() interrupt 1
{
        TF0=0; //清除中断标志
        TR0=0; //关中断
        if(uiTimeCnt_09_16<0xffff) //设定这个条件,防止 uiTimeCnt 超范围。
        {
                uiTimeCnt_09_16++;  //累加定时中断的次数,
        }
        key_scan(); //按键扫描函数
        if(uiVoiceCnt!=0)
        {
                uiVoiceCnt--; //每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫
                beep_dr=0; //蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。
        }
        else
        {
                ; //此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。
                beep_dr=1; //蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。
        }
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
        TR0=1; //开中断
}


void delay_short(unsigned int uiDelayShort)
{
        unsigned int i;
        for(i=0;i<uiDelayShort;i++)
        {
                ; //一个分号相当于执行一条空语句
        }
}

void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++) //内嵌循环的空指令数量
                {
                        ; //一个分号相当于执行一条空语句
                }
        }
}

void initial_myself() //第一区 初始化单片机
{
/* 注释二:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把 key_gnd_dr 输出低电平。
* 朱兆祺 51 学习板的 S1 就是本程序中用到的一个独立按键。
*/
        
        P0M0 = 0X00;
        P0M1 = 0X00;

        P3M0 = 0X00;
        P3M1 = 0X00;
        
        P5M0 = 0X00;
        P5M1 = 0X00;
        
        P6M0 = 0X00;
        P6M1 = 0X00;
        
        key_gnd_dr=0; //模拟独立按键的地 GND,因此必须一直输出低电平
        beep_dr=1; //用 PNP 三极管控制蜂鸣器,输出高电平时不叫。
        TMOD=0x01; //设置定时器 0 为工作方式 1
        TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}

void initial_peripheral() //第二区 初始化外围
{
        EA=1; //开总中断
        ET0=1; //允许定时中断
        TR0=1; //启动定时中断
}

//总结陈词:
//这一节讲了独立按键控制跑马灯的速度。如果按键要控制跑马灯的启动和暂停,我们该怎么编写程序呢?欲
//知详情,请听下回分解-----独立按键控制跑马灯的启动和暂停。

23 独立按键控制跑马灯的速度.rar

60.9 KB, 下载次数: 109

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:343
  • 最近打卡:2025-05-08 08:57:21

19

主题

205

回帖

840

积分

高级会员

积分
840
发表于 2024-1-14 08:16:39 | 显示全部楼层
第二十四课:

/*******************************************
本学习程序来源于吴坚鸿老师《手把手教你单片机程序框架》,所有
权属于吴坚鸿老师,本人是用来学习,移植STC8H8K64U开发箱9.62版。
到吴坚鸿老师若有异议,我立即删除!

开场白:
        上一节讲了独立按键控制跑马灯的速度。这一节继续要教会大家一个
        知识点:如何通过一个中间变量把按键跟跑马灯的启动和暂停有效关联起来。
具体内容,请看源代码讲解。
(1)硬件平台:STC8H8K64U开发箱9.62版。在上一节的基础上,增加一个启
        动和暂停按键,用矩阵键盘中的SW32 键作为启动和暂停独立按键,记得把
        输出线 P0.0 一直输出低电平,模拟独立按键的触发地 GND。
(2)实现功能:
        在上一节的基础上,第1个至第8个LED灯一直不亮。在第9个至第
        16个LED灯,依次逐个亮灯并且每次只能亮一个灯。每按一次独立按键
        SW32键,原来运行的跑马灯会暂停,原来暂停的跑马灯会运行。其它跟上
        一节一样,用 SW32来改变方向,用 SW36 和 SW22来改变速度。
(3)源代码讲解如下:
**********************************************/

#include "STC8H.H"

#define const_voice_short 40 //蜂鸣器短叫的持续时间
#define const_key_time1         20 //按键去抖动延时的时间
#define const_key_time2         20 //按键去抖动延时的时间
#define const_key_time3         20 //按键去抖动延时的时间
#define const_key_time4         20 //按键去抖动延时的时间

void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void led_flicker_09_16(); //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void led_update(); //LED 更新函数
void T0_time(); //定时中断函数
void key_service(); //按键服务的应用程序
void key_scan(); //按键扫描函数 放在定时中断里

sbit hc595_sh_dr=P6^0;
sbit hc595_st_dr=P6^1;
sbit hc595_ds_dr=P6^2;

sbit beep_dr=P5^4; //蜂鸣器的驱动 IO 口
sbit key_sr1=P0^6; //对应STC8H8K64U开发箱9.62版SW32键
sbit key_sr2=P0^7; //对应STC8H8K64U开发箱9.62版SW36键
sbit key_sr3=P3^4; //对应STC8H8K64U开发箱9.62版SW21键
sbit key_sr4=P3^5; //对应STC8H8K64U开发箱9.62版SW22键
sbit key_gnd_dr=P0^0; //模拟独立按键的地 GND,因此必须一直输出低电平

unsigned char ucKeySec=0; //被触发的按键编号
unsigned int         uiKeyTimeCnt1=0; //按键去抖动延时计数器
unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
unsigned int         uiKeyTimeCnt2=0; //按键去抖动延时计数器
unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
unsigned int         uiKeyTimeCnt3=0; //按键去抖动延时计数器
unsigned char ucKeyLock3=0; //按键触发后自锁的变量标志
unsigned int         uiKeyTimeCnt4=0; //按键去抖动延时计数器
unsigned char ucKeyLock4=0; //按键触发后自锁的变量标志
unsigned int         uiVoiceCnt=0; //蜂鸣器鸣叫的持续时间计数器
unsigned char ucLed_dr1=0; //代表 16 个灯的亮灭状态,0 代表灭,1 代表亮
unsigned char ucLed_dr2=0;
unsigned char ucLed_dr3=0;
unsigned char ucLed_dr4=0;
unsigned char ucLed_dr5=0;
unsigned char ucLed_dr6=0;
unsigned char ucLed_dr7=0;
unsigned char ucLed_dr8=0;
unsigned char ucLed_dr9=0;
unsigned char ucLed_dr10=0;
unsigned char ucLed_dr11=0;
unsigned char ucLed_dr12=0;
unsigned char ucLed_dr13=0;
unsigned char ucLed_dr14=0;
unsigned char ucLed_dr15=0;
unsigned char ucLed_dr16=0;
unsigned char ucLed_update=0; //刷新变量。每次更改 LED 灯的状态都要更新一次。
unsigned char ucLedStep_09_16=0; //第 9 个至第 16 个 LED 跑马灯的步骤变量
unsigned int         uiTimeCnt_09_16=0; //第 9 个至第 16 个 LED 跑马灯的统计定时中断次数的延时计数器
unsigned char ucLedStatus16_09=0; //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedStatus08_01=0; //代表底层 74HC595 输出状态的中间变量
unsigned char ucLedDirFlag=0; //方向变量,把按键与跑马灯关联起来的核心变量,0 代表正方向,1 代表反方向
unsigned int         uiSetTimeLevel_09_16=300; //速度变量,此数值越大速度越慢,此数值越小速度越快。
unsigned char ucLedStartFlag=1; //启动和暂停的变量,0 代表暂停,1 代表启动


void main()
{
        initial_myself();
        delay_long(100);
        initial_peripheral();
        while(1)
        {
                led_flicker_09_16(); //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
                led_update(); //LED 更新函数
                key_service(); //按键服务的应用程序
        }
}


void key_scan()//按键扫描函数 放在定时中断里
{
        if(key_sr1==1)//IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock1=0; //按键自锁标志清零
                uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
        }
        else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt1++; //累加定时中断次数
                if(uiKeyTimeCnt1>const_key_time1)
        {
        uiKeyTimeCnt1=0;
        ucKeyLock1=1; //自锁按键置位,避免一直触发
        ucKeySec=1; //触发 1 号键
        }
        }
        
        if(key_sr2==1)//IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock2=0; //按键自锁标志清零
                uiKeyTimeCnt2=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
        }
        else if(ucKeyLock2==0)//有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt2++; //累加定时中断次数
                if(uiKeyTimeCnt2>const_key_time2)
                {
                        uiKeyTimeCnt2=0;
                        ucKeyLock2=1; //自锁按键置位,避免一直触发
                        ucKeySec=2; //触发 2 号键
                }
        }
        
        if(key_sr3==1)//IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock3=0; //按键自锁标志清零
                uiKeyTimeCnt3=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
        }
        else if(ucKeyLock3==0)//有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt3++; //累加定时中断次数
                if(uiKeyTimeCnt3>const_key_time3)
                {
                        uiKeyTimeCnt3=0;
                        ucKeyLock3=1; //自锁按键置位,避免一直触发
                        ucKeySec=3; //触发 3 号键
                }
        }
        
        if(key_sr4==1)//IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
        {
                ucKeyLock4=0; //按键自锁标志清零
                uiKeyTimeCnt4=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
        }
        else if(ucKeyLock4==0)//有按键按下,且是第一次被按下
        {
                uiKeyTimeCnt4++; //累加定时中断次数
                if(uiKeyTimeCnt4>const_key_time4)
                {
                        uiKeyTimeCnt4=0;
                        ucKeyLock4=1; //自锁按键置位,避免一直触发
                        ucKeySec=4; //触发 4 号键
                }
        }
}


void key_service() //按键服务的应用程序
{
        switch(ucKeySec) //按键服务状态切换
        {
                case 1:// 改变跑马灯方向的按键 对应朱兆祺学习板的 S1 键
                if(ucLedDirFlag==0) //通过中间变量改变跑马灯的方向
                {
                        ucLedDirFlag=1;
                }
                else
                {
                        ucLedDirFlag=0;
                }
                uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;

                case 2:// 加速按键 对应朱兆祺学习板的 S5 键 uiSetTimeLevel_09_16 越小速度越快
                uiSetTimeLevel_09_16=uiSetTimeLevel_09_16-10;
                if(uiSetTimeLevel_09_16<50) //最快限定在 50
                {
                        uiSetTimeLevel_09_16=50;
                }
                uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
               
                case 3:// 减速按键 对应朱兆祺学习板的 S9 键 uiSetTimeLevel_09_16 越大速度越慢
                uiSetTimeLevel_09_16=uiSetTimeLevel_09_16+10;
                if(uiSetTimeLevel_09_16>550) //最慢限定在 550
                {
                        uiSetTimeLevel_09_16=550;
                }
                uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;

                case 4:// 启动和暂停按键 对应朱兆祺学习板的 S13 键 ucLedStartFlag 为 0 时代表暂停,为 1 时代表启动
                if(ucLedStartFlag==1) //启动和暂停两种状态循环切换
                {
                        ucLedStartFlag=0;
                }
                else //启动和暂停两种状态循环切换
                {
                        ucLedStartFlag=1;
                }
                uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
                ucKeySec=0; //响应按键服务处理程序后,按键编号清零,避免一致触发
                break;
        }
}


void led_update() //LED 更新函数
{
        if(ucLed_update==1)
        {
                ucLed_update=0; //及时清零,让它产生只更新一次的效果,避免一直更新。
                if(ucLed_dr1==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x01;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfe;
                }
                if(ucLed_dr2==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x02;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfd;
                }
                if(ucLed_dr3==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x04;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xfb;
                }
                if(ucLed_dr4==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x08;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xf7;
                }
                if(ucLed_dr5==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x10;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xef;
                }
                if(ucLed_dr6==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x20;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xdf;
                }
                if(ucLed_dr7==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x40;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0xbf;
                }
                if(ucLed_dr8==1)
                {
                        ucLedStatus08_01=ucLedStatus08_01|0x80;
                }
                else
                {
                        ucLedStatus08_01=ucLedStatus08_01&0x7f;
                }
                if(ucLed_dr9==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x01;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfe;
                }
                if(ucLed_dr10==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x02;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfd;
                }
                if(ucLed_dr11==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x04;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xfb;
                }
                if(ucLed_dr12==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x08;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xf7;
                }
                if(ucLed_dr13==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x10;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xef;
                }
                if(ucLed_dr14==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x20;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xdf;
                }
                if(ucLed_dr15==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x40;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0xbf;
                }
                if(ucLed_dr16==1)
                {
                        ucLedStatus16_09=ucLedStatus16_09|0x80;
                }
                else
                {
                        ucLedStatus16_09=ucLedStatus16_09&0x7f;
                }
                hc595_drive(ucLedStatus16_09,ucLedStatus08_01); //74HC595 底层驱动函数
        }
}


void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
        unsigned char i;
        unsigned char ucTempData;
        
        hc595_sh_dr=0;
        hc595_st_dr=0;
        
        ucTempData=ucLedStatusTemp16_09; //先送高 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        ucTempData=ucLedStatusTemp08_01; //再先送低 8 位
        for(i=0;i<8;i++)
        {
                if(ucTempData>=0x80)
                        hc595_ds_dr=1;
                else
                        hc595_ds_dr=0;
                hc595_sh_dr=0; //SH 引脚的上升沿把数据送入寄存器
                delay_short(15);
                hc595_sh_dr=1;
                delay_short(15);
                ucTempData=ucTempData<<1;
        }
        hc595_st_dr=0; //ST 引脚把两个寄存器的数据更新输出到 74HC595 的输出引脚上并且锁存起来
        delay_short(15);
        hc595_st_dr=1;
        delay_short(15);
        hc595_sh_dr=0; //拉低,抗干扰就增强
        hc595_st_dr=0;
        hc595_ds_dr=0;
}


/* 注释一:
* 以下程序,要学会如何通过中间变量,把按键和跑马灯的任务关联起来
*/
void led_flicker_09_16() //第 9 个至第 16 个 LED 的跑马灯程序,逐个亮并且每次只能亮一个.
{
        if(ucLedStartFlag==1) //此变量为 1 时代表启动
        {
                switch(ucLedStep_09_16)
                {
                case 0:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr16=0; //第 16 个灭
                                ucLed_dr9=1; //第 9 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=1; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr15=1; //第 15 个亮
                                ucLed_dr16=0; //第 16 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=7; //返回上一个步骤
                        }
                }
                break;
               
                case 1:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr9=0; //第 9 个灭
                                ucLed_dr10=1; //第 10 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=2; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr16=1; //第 16 个亮
                                ucLed_dr9=0; //第 9 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=0; //返回上一个步骤
                        }
                }
                break;
               
                case 2:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr10=0; //第 10 个灭
                                ucLed_dr11=1; //第 11 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=3; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr9=1; //第 9 个亮
                                ucLed_dr10=0; //第 10 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=1; //返回上一个步骤
                        }
                }
                break;
               
                case 3:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr11=0; //第 11 个灭
                                ucLed_dr12=1; //第 12 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=4; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr10=1; //第 10 个亮
                                ucLed_dr11=0; //第 11 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=2; //返回上一个步骤
                        }
                }
                break;
               
                case 4:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr12=0; //第 12 个灭
                                ucLed_dr13=1; //第 13 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=5; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr11=1; //第 11 个亮
                                ucLed_dr12=0; //第 12 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=3; //返回上一个步骤
                        }
                }
                break;
               
                case 5:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr13=0; //第 13 个灭
                                ucLed_dr14=1; //第 14 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=6; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr12=1; //第 12 个亮
                                ucLed_dr13=0; //第 13 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=4; //返回上一个步骤
                        }
                }
                break;
               
                case 6:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr14=0; //第 14 个灭
                                ucLed_dr15=1; //第 15 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=7; //切换到下一个步骤
                        }
                        else //反方向
                        {
                                ucLed_dr13=1; //第 13 个亮
                                ucLed_dr14=0; //第 14 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=5; //返回上一个步骤
                        }
                }
                break;
               
                case 7:
                if(uiTimeCnt_09_16>=uiSetTimeLevel_09_16) //时间到
                {
                        uiTimeCnt_09_16=0; //时间计数器清零
                        if(ucLedDirFlag==0) //正方向
                        {
                                ucLed_dr15=0; //第 15 个灭
                                ucLed_dr16=1; //第 16 个亮
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=0; //返回到开始处,重新开始新的一次循环
                        }
                        else //反方向
                        {
                                ucLed_dr14=1; //第 14 个亮
                                ucLed_dr15=0; //第 15 个灭
                                ucLed_update=1; //更新显示
                                ucLedStep_09_16=6; //返回上一个步骤
                        }
                }
                break;

                }
        }
}


void T0_time() interrupt 1
{
        TF0=0; //清除中断标志
        TR0=0; //关中断
        
        if(uiTimeCnt_09_16<0xffff) //设定这个条件,防止 uiTimeCnt 超范围。
        {
                if(ucLedStartFlag==1) //此变量为 1 时代表启动
                {
                        uiTimeCnt_09_16++; //累加定时中断的次数,
                }
        }
        
        key_scan(); //按键扫描函数
        
        if(uiVoiceCnt!=0)
        {
                uiVoiceCnt--; //每次进入定时中断都自减 1,直到等于零为止。才停止鸣叫
                beep_dr=0; //蜂鸣器是 PNP 三极管控制,低电平就开始鸣叫。
        }
        else
        {
                ; //此处多加一个空指令,想维持跟 if 括号语句的数量对称,都是两条指令。不加也可以。
                beep_dr=1; //蜂鸣器是 PNP 三极管控制,高电平就停止鸣叫。
        }
        
TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
TL0=0x2f;
TR0=1; //开中断
}


void delay_short(unsigned int uiDelayShort)
{
        unsigned int i;
        
        for(i=0;i<uiDelayShort;i++)
        {
                ; //一个分号相当于执行一条空语句
        }
}


void delay_long(unsigned int uiDelayLong)
{
        unsigned int i;
        unsigned int j;
        for(i=0;i<uiDelayLong;i++)
        {
                for(j=0;j<500;j++)         //内嵌循环的空指令数量
                {
                        ;                                                                 //一个分号相当于执行一条空语句
                }
        }
}


void initial_myself() //第一区 初始化单片机
{
/* 注释二:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把 key_gnd_dr 输出低电平。
* STC8H8K64U开发箱9.62版的SW32就是本程序中用到的一个独立按键。
*/
        P0M0 = 0X00;
        P0M1 = 0X00;

        P3M0 = 0X00;
        P3M1 = 0X00;
        
        P5M0 = 0X00;
        P5M1 = 0X00;

        P6M0 = 0X00;
        P6M1 = 0X00;
        
        key_gnd_dr=0; //模拟独立按键的地 GND,因此必须一直输出低电平
        beep_dr=1;                 //用 PNP 三极管控制蜂鸣器,输出高电平时不叫。
        TMOD=0x01;                 //设置定时器 0 为工作方式 1
        TH0=0xf8;                 //重装初始值(65535-2000)=63535=0xf82f
        TL0=0x2f;
}


void initial_peripheral() //第二区 初始化外围
{
        EA=1;         //开总中断
        ET0=1;         //允许定时中断
        TR0=1;         //启动定时中断
}

//总结陈词:
//这几节循序渐进地讲了独立按键控制跑马灯各种状态的程序。在很多实际工控项目中,经常会涉及到运动的
//自动控制,运动的自动控制就必然会涉及到感应器。下一节我将会讲感应器和运动控制的程序框架,欲知详情,
//请听下回分解-----用 LED 灯和按键来模拟工业自动化设备的运动控制。

24 独立按键控制跑马灯的启动和暂停.rar

62.54 KB, 下载次数: 117

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:361
  • 最近打卡:2025-05-08 19:31:33

2

主题

87

回帖

906

积分

高级会员

积分
906
发表于 2024-1-14 09:42:25 | 显示全部楼层
感谢推荐 手把手教你单片机程序框架 看着不错,谢谢
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:61
  • 最近打卡:2025-05-08 08:36:09

719

主题

1万

回帖

1万

积分

管理员

积分
15717
发表于 2024-1-14 10:45:24 | 显示全部楼层
回复 支持 反对

使用道具 举报 送花

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

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-9 03:42 , Processed in 0.204316 second(s), 109 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表