一起玩儿Proteus仿真(C51)——06. 红绿灯仿真(二)

news2024/11/26 9:36:51

摘要:本文介绍如何仿真红绿灯

今天来看一下红绿灯仿真程序的具体实现方法。先来看一下整个程序的原理图。

在这个红绿灯仿真实验中,每个路口需要控制的设备是2位数码管显示倒计时以及红黄绿灯的亮灭。先来看一下数码管的连接方法。

数码管的8根LED显示引脚都连接到了一起,使用了一组单片机端口。另外的公共端则由单片机引脚来单独的控制。这样,在程序中通过数码管公共端引脚循环控制数码管点亮。

下面就来看一下具体的实现方法。首先需要了解一下程序中使用到的全局变量。首先看一下与显示相关的全局变量:

uchar tab[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,

0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E, 0xBF}; //显示码值表

uchar dis_buff[4]; // 显示数组

其中的tab记录了0~9这些字符对应的码值。在这个示例中,采用的是共阴极数码管,因此是高电平的时候数码管对应的LED亮起,低电平的时候数码管LED熄灭。dis_buf数组记录了当前数码管显示的字符。dis_buf[0]对应东西方向的个位数码管,dis_buf[1]对应东西方向十位数码管。同样的,dis_buf[2]对应南北方向的个位数码管,dis_buf[3]对应南北方向的十位数码管。

只要将dis_buff数组,赋予要显示的字符后,调用display()函数,就可以将字符显示在数码管上了。根据前面的介绍,这个display()函数需要被连续调用,才能让眼睛觉得这些字符一直是亮着的。display()函数的实现方法如下:

void display(void)

{

P2 = 0x01;

P0 = tab[dis_buff[1]];

delay(2);

P2 = 0x02;

P0 = tab[dis_buff[0]];

delay(2);

P2 = 0x04;

P0 = tab[dis_buff[3]];

delay(2);

P2 = 0x08;

P0 = tab[dis_buff[2]];

delay(2);

}

需要提醒的是,本例中所使用的数码管的公共端是接到P2的响应引脚上的。所以通过控制P2的状态就能将输出字符显示在数码管上。

本仿真实验,是将定时器的时间中断设置在了10ms,这样100次的中断就是1分钟,时间中断计数器变量的初值是0xDC00,那么时间中断的初始化方法如下:

void int_init(void)

{

TMOD = 0x01;

TH0 = 0xDC;

TL0 = 0x00;

TR0 = 1;

ET0 = 1;

EA = 1;

}

接下来就是本实验的核心环节的实现了。主要包括两个地方,一个是红绿灯的循环倒计时显示,另一个是按键的处理。

先讲解一下红绿灯的循环显示是如何实现的。在这里利用的是一个状态变量status来标记当前红绿灯的运行状态。简单的说,红绿灯包括了以下几个运动状态:

状态0:东西绿灯,南北红灯,两边同时倒计时,以东西绿灯时间为基准,那么计算出来的南北红灯的时间就是:东西绿灯的时间+黄灯的时间。当东西绿灯时间减至0时,进入状态1。

状态1:东西绿灯熄灭,黄灯点亮,并以黄灯的时间开始倒计时。南北的红灯状态不变,南北的倒计时时间与东西的黄灯相同。当两者同时倒计时到0时,进入状态2。

状态2:南北变成绿灯,并开始倒计时。东西变成红灯,也开始倒计时,东西的红灯倒计时时间为南北的绿灯时间+黄灯时间。当南北绿灯倒计时至0时,进入状态3。

状态3:南北变成黄灯,自黄灯闪烁时间开始倒计时,东西延续之前的状态,继续倒计时。当南北黄灯倒计时至0时。放回状态0,依次循环,就是红绿灯的运行过程。

接下来再来看一下按键的处理逻辑,一方面就是通过按键改变预先定义的东西方向绿灯变量的值和南北方向绿灯变量的值。另外,就是改变完成之后,将东西方向的灯显示东西的绿灯时间,南北方向的灯显示南北的绿灯时间。显示的时长默认为5秒(定义了全局变量count,可以随时调整这个时长)。

根据上面这两个要点,来看一下如何实现红绿灯的仿真。先来看一下全局变量的定义:

uchar sec100; // 10ms计数变量

uchar count=0; // 修改时长后的显示时长变量

// 红黄绿灯控制引脚,低电平点亮,高电平熄灭

sbit dr = P1^0; // 东西红灯控制引脚

sbit dy = P1^1; // 东西黄灯控制引脚

sbit dg = P1^2; // 东西绿灯控制引脚

sbit nr = P1^3; // 南北红灯控制引脚

sbit ny = P1^4; // 南北黄灯控制引脚

sbit ng = P1^5; // 南北绿灯控制引脚

uchar dxTotal=10,nbTotal=15; // 定义东西和南北总时间

uchar yellowTime = 3; // 黄灯时间

/** 红绿等状态变量

 * 0:东西绿灯,南北红灯

 * 1:东西黄灯,南北红灯

 * 2:南北绿灯,东西红灯

 * 3:南北黄灯,东西红灯

 */

uchar status = 0;

uchar lastTime = 0; // 倒计时时间

// 按键控制引脚

sbit kd1 = P3^0; //东西绿灯时长增加

sbit kd2 = P3^1; //东西绿灯时长减少

sbit kn1 = P3^2; //南北绿灯时长增加

sbit kn2 = P3^3; //南北绿灯时长减少

其中的sec100时中断的计数器,当其累加到100时,表示到达一秒钟时长了,这个时候需要变换红绿灯的显示了。count为使用按键修改绿灯时长后,显示修改结果的计数器,count的值大于0,表示需要显示东西和南北方向的绿灯时长。count为0时,则表示红绿灯正常运行。

后边还定义了三个时间。dxTotal表示东西绿灯的时长,nbTotal表示南北绿灯的时长。yellowTime表示黄灯的时长。

之后是状态变量status的定义,其取值范围是0、1、2和3。代表了红绿灯运行的4个状态。lastTime表示当前状态下绿灯或者黄灯还剩余的时长。下面就是处理逻辑的核心——中断函数的实现方法。

// 定时器中断处理函数

void timer0() interrupt 1

{

TH0 = 0xDC;

TL0 = 0x00;

sec100++;

if( sec100>=100 ) // 达到1秒

{

sec100 = 0;

if(count==0) // 正常运行状态

{

if( status==0 || status==2 ) { // 状态0、2

lastTime--;

if( status==0 ) { // 状态0:东西为倒计时时间

dis_buff[0] = lastTime%10;

dis_buff[1] = lastTime/10%10;

dis_buff[2] = (lastTime+yellowTime)%10; // 南北为倒计时时间+黄灯时长

dis_buff[3] = (lastTime+yellowTime)/10%10;

} else {

dis_buff[0] = (lastTime+yellowTime)%10; // 状态2,与上一种情况东西和南北对调

dis_buff[1] = (lastTime+yellowTime)/10%10;

dis_buff[2] = lastTime%10;

dis_buff[3] = lastTime/10%10;

}

if( lastTime==0 ) { // 剩余时间为0,改变LED状态

if(status==0) // 状态0

{

dg = 1; // 东西绿灯灭,黄灯量

dy = 0;

} else

{

ng = 1; // 南北绿灯灭,黄灯量

ny = 0;

}

lastTime = yellowTime; // 进入黄灯状态,倒计时为黄灯时间

status++;

}

} else if( status==1 || status==3 ) { // 状态1和3

lastTime--;

dis_buff[0] = (lastTime)%10; /

dis_buff[1] = lastTime/10%10;

dis_buff[2] = lastTime%10;

dis_buff[3] = lastTime/10%10;

if( lastTime==0 ) { // 倒计时为0,切换状态

if( status==1 ) {

status = 2;

lastTime = nbTotal;

ng = 0;

nr = 1;

dy = 1;

dr = 0;

} else {

status = 0;

lastTime = dxTotal;

dg = 0;

dr = 1;

ny = 1;

nr = 0;

}

}

}

}

else

{

count--;

}

}

}

下面来看一下按键处理函数。

// 判断按键状态,返回按键值

uchar getkey(void)

{

if((P3&0x0F)!=0x0F)

{

uchar kvalue = ~(P3&0x0F);

delay(5);

if((P3&0x0F)!=0x0F)

{

while((P3&0x0F)!=0x0F);

return kvalue;

}

}

return 0;

}

// 处理按键

void key(void)

{

uchar kvalue = getkey();

if(kvalue!=0)

{

if((kvalue&0x01)!=0)     // 东西时间+1

{

dxTotal++;

} else if ((kvalue&0x02)!=0) {   // 东西时间-1

dxTotal--;

} else if ((kvalue&0x04)!=0) {    // 南北时间+1

nbTotal++;

} else {       // 南北时间-1

nbTotal--;

}

count = 5;      // 倒计时显示5秒

}

}

getkey()函数用来返回按下的按键。当这个方法返回0x0F时,表示无按键按下,返回值的低4位,任意一位不为1,则表示该位对应的引脚被按下。key()函数根据getkey()函数的返回值,对东西和南北绿灯的时长做相应的修改。并将显示的倒计时时长设置为5秒。

最后来看一下主程序,主程序的作用就是初始化中断和各个变量。然后循环驱动数码管显示(根据count是否大于0,变换显示的内容),并检测看是否有按键按下。代码如下所示:

void main(void)

{

int_init();

sec100 = 0;

status = 0; // 初始化东西绿灯

lastTime = dxTotal;

dg = 0;

nr = 0;

while(1)

{

if(count>0)

{

dis_buff[0]=dxTotal%10;

dis_buff[1]=dxTotal/10%10;

dis_buff[2]=nbTotal%10;

dis_buff[3]=nbTotal/10%10;

}

display();

key();

}

}

整个程序的所有代码都讲解完了,接下来运行看一下结果吧。如下所示:

运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1449110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Windows更新后无法启动的十种办法,总有一种适合你

你可能已经更新了操作系统以修复错误或使用最新功能。但是,如果Windows在更新后无法启动呢? 如果你面临这样的问题,主要是由于安装文件中的错误或你的系统与最新更新不兼容。此外,损坏的MBR或驱动程序也会阻止电脑启动。 不管是什么原因,本文将用十种简单的技术来指导你…

【蓝桥杯单片机入门记录】认识单片机

目录 单片机硬件平台 单片机的发展过程 单片机开发板 单片机基础知识 电平 数字电路中只有两种电平:高和低 二进制(8421码) 十六进制 二进制数的逻辑运算 “与” “或” “异或” 标准C与C51 如何学好单片机 端正学习的态度、培…

2024年云南省考报名详细流程图解,招聘5710人!

云南省考公告出来了!招5710人! ✔️报名时间:2024年2月19日9:00至2月23日18:00 ✔️缴费时间:2024年2月20日0:00至2月25日24:00 ✔️公共科目笔试时间为: 2024年3月16日上午 9:00-11:00 行政职业能力测验 2…

【leetcode热题100】不同的二叉搜索树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n 3 输出:5示例 2: 输入:n 1 输出:1 …

平时积累的FPGA知识点(7)

平时在FPGA群聊等积累的FPGA知识点,第七期: 11 描述扇出的xilinx官方文档是? 解释:ug949 12 在BD中如何指定某个IP用global,其他的用OOC模式?因为某个模块引用的IP带着XPM,综合不了 解释&am…

【MySQL】高度为2和3时B+树能够存储的记录数量的计算过程

文章目录 题目答案高度为2时的B树高度为3时的B树总结 GPT4 对话过程 题目 InnoDB主键索引的Btree在高度分别为 2 和 3 时,可以存储多少条记录? 答案 高度为2时的B树 计算过程: 使用公式 ( n 8 ( n 1 ) 6 16 1024 ) (n \times 8 …

ELAdmin 隐藏添加编辑按钮

使用场景 做了一个监控模块,数据都是定时生成的,所以不需要手动添加和编辑功能。 顶部不显示 可以使用 true 或者 false 控制现实隐藏 created() {this.crud.optShow {add: false,edit: false,del: true,download: true,reset: true}},如果没有 crea…

python守护进程--supervisor 使用教程

supervisor 使用教程python守护进程1.安装 pip3 install supervisor -i https://pypi.tuna.tsinghua.edu.cn/simple 2.使用supervisor 启动 python main.py 文件 vim /etc/supervisor/conf.d/demo.conf添加以下内容:[program:demo] #项目名称为democommandp…

oppo手机QQ上传文件所在位置

一、打开手机“文件管理”APP 点击“点击查看”,按钮,会进入到新的根目录。 寻找下面的目录进入

StarUML无法安装扩展的解决方案

StarUML无法安装扩展解决方案 版本:StarUML3.2.2 遇到问题 Unable to access the extension registry, Please try again later. 解决方案 第一步 https://docs.staruml.io/user-guide/managing-extensions#install-extension官网给了怎么手动安装扩展器的方法…

【leetcode】深搜、暴搜、回溯、剪枝(C++)2

深搜、暴搜、回溯、剪枝(C)2 一、括号生成1、题目描述2、代码3、解析 二、组合1、题目描述2、代码3、解析 三、目标和1、题目描述2、代码3、解析 四、组合总和1、题目描述2、代码3、解析 五、字母大小写全排列1、题目描述2、代码3、解析 六、优美的排列1…

【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱5(附带项目源码)

效果演示 文章目录 效果演示系列目录前言制作系统定义制作配方 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列!本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第25篇中,我们将探索如何用unity制作一个3D背包、库存、制…

(四)【Jmeter】 JMeter的界面布局与组件概述

JMeter的界面布局 中文版: 英文版: JMeter的主界面包括菜单栏、工具栏、树形结构面板、视图面板等部分。 菜单栏:菜单栏包含了文件(File)、编辑(Edit)、查找(Search)、选项(Options)、工具(Tools)、帮助(Help)等菜单项,用于对…

Compose高级别API动画指南

前文讲了Compose中的低级别API动画,与之对应的,还有高级别API动画,同样也符合Material-Design规范。所有高级别动画 API 都是在低级别动画 API 的基础上构建而成,其对应关系如图: 接下来就对其高级别API逐个分析&…

2024LeetCode分类刷题

一、数组 88. 合并两个有序数组 public void merge(int[] nums1, int m, int[] nums2, int n) {int p1 0, p2 0;int[] sorted new int[m n];while (p1 < m || p2 < n) {int current;if (p1 m) {current nums2[p2];} else if (p2 n) {current nums1[p1];} else i…

单体工程结构

本文主要说明下单体项目的工程结构如何设计&#xff0c;目前业界存在两种主流的应用工程结构&#xff1a;一种是阿里推出的《Java开发手册》中推荐的&#xff0c;另外一种是基于DDD(领域驱动设计)推荐的。下面我们来看下两种工程结构是怎样的。 一、 基于阿里《Java开发手册》…

基于FPGA的UDP实现(包含源工程文件)

1、概括 前文通过FPGA实现了ARP和ICMP协议&#xff0c;ARP协议一般用来获取目的IP地址主机的MAC地址&#xff0c;ICMP通过回显请求和回显应答来判断以太网链路是否通畅&#xff0c;这两个协议都不是用来传输用户数据的。如果用户需要向PC端传输大量数据&#xff0c;那么就必须使…

Swift Combine 通过用户输入更新声明式 UI 从入门到精通十五

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三Swift Combine 发布者publisher的生命周期 从入门到精通四Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五Swift Com…

证明之缺角正方形网格的铺地砖问题

缺角正方形网格的铺地砖问题 “挑战难题&#xff1a;多米诺骨牌与无法覆盖的方格” 这里有个著名的难题。画八横八纵正方形网格&#xff0c;去掉相对的两个角。你能用多米诺骨牌形状的地砖——每一块正好覆盖两个相邻方格&#xff0c;把剩余部分覆盖吗&#xff1f;我在下图中…

bert-vits2本地部署报错疑难问题汇总

环境&#xff1a; bert-vits2.3 win 和wsl 问题描述&#xff1a; bert-vits2本地部署报错疑难问题汇总 解决方案&#xff1a; 问题1: Conda安装requirements里面依赖出现ERROR: No matching distribution found for opencc1.1.6 解决方法 需要在 Python 3.11 上使用 Op…