51单片机开发综合实验程序结构解析

2024-02-01 11:18

本文主要是介绍51单片机开发综合实验程序结构解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、主程序main.c

所采用的单片机型号为:STC89C52RC(封装: LQFP-44)
按照惯例先看主函数:

/*************************
实验:综合实验程序
*************************/#include "main.h"void main()
{EA = 1;           //开总中断BeepInit();  //蜂鸣器初始化为不叫的状态ConfigTimer0(1);  //定时器0初始化定时为1msConfigTimer2(2);  //定时器2初始化定时为2msint0_Init();  //外部中断0int1_Init();  //外部中断1ConfigUART(9600);  //配置波特率为9600Ds1302Init();  	//DS1302函数的初始化,写入初始时间,此代码可以注释掉,如果不注释掉则单片机每次启动就会对DS1302进行时间重置Ds1302ReadTime();  //读取DS1302的时间while(1){KeyScan();  //按键扫描if(KeyFlag == 1) //如果有按键按下{if(KeyState == 1) DisplayData(); //在数码管按键移位缓存中对显示缓存数组进行移位DigDisplay();  //进行数码管动态扫描}else  //没有按键按下{DigDisplay();  //进行数码管动态扫描显示万年历}}
}

结合以上程序,在主函数中其结构如下:
主函数结构框图
即程序一开始就对各项功能进行初始化,之后就进入while(1)死循环,在while(1)循环中对矩阵按键进行扫描,并进行数码管动态扫描显示。
那么,在主函数中调用的各个子函数又做了什么呢,这里我们就可以跟着程序运行的步骤,挨个跳转到子函数的内容中去,查看各个子函数的功能,其具体操作步骤如下:
1)、编译整个keil工程,0错误0警告
在这里插入图片描述
2)、将鼠标移到某一子函数处,点击鼠标右键,然后点击Go To Definition Of “子函数”
在这里插入图片描述

1、BeepInit()

可以看到在BeepInit()中值是将变量beep进行了清零操作

#include "beep.h"void BeepInit()
{beep = 0;
}

对于变量beep,我们采用同样的方法,查看beep的内容
在这里插入图片描述
我们看到变量beep只是对单片机引脚P1.5进行了定义
也就是说蜂鸣器初始化函数只是将控制蜂鸣器的单片机引脚进行了拉低操作,我们结合原理图
在这里插入图片描述
蜂鸣器通过三极管Q8进行驱动,而P1.5引脚则可以控制三极管的通断

2、ConfigTimer0(1)

我们同样跳转到ConfigTimer0(1)中

/* 配置并启动T0,ms-T0定时时间 */
void ConfigTimer0(uint16 ms)
{uint32 tmp0;tmp0 = (SYS_MCLK*ms)/1000 ; //计算所需的计数值tmp0 = 65536 - tmp0;        //计算定时器重载值tmp0 = tmp0 + 33;           //补偿中断响应延时造成的误差   T0RH = (uint8)(tmp0>>8);   //定时器重载值拆分为高低字节T0RL = (uint8)tmp0;TMOD &= 0xF0;   //清零T0的控制位TMOD |= 0x01;   //配置T0为模式1TH0 = T0RH;     //加载T0重载值TL0 = T0RL;ET0 = 1;        //使能T0中断TR0 = 1;        //启动T0
}

子函数:ConfigTimer0(1)主要是对51单片机的定时器0进行初始化,其函数中的各个寄存器的配置过程大家不需要过于深究,像这类函数网上还有很多,在书本上也有对各个寄存器功能的介绍。我们通过传递参数ms,实现调用函数时直接配置定时器定时时间,也就是说在这里我们给Timer0配置了1ms的单位定时时间,每到1ms,单片机就会进入定时器0的中断服务函数,执行中断服务函数中的内容。
对于主函数中其他子函数,我们也可以采用同样的方法对其进行逐个分析,这里就不再赘述。

3、Ds1302Init();

在DS1302初始化函数中,主要是对DS1302相关寄存器写入数据:TIME[n]

/*******************************************************************************
* 函 数 名         : Ds1302Init
* 函数功能		     : 初始化DS1302.
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/void Ds1302Init()
{unsigned char n;Ds1302Write(0x8E,0X00);		 //禁止写保护,就是关闭写保护功能for (n=0; n<7; n++)//写入7个字节的时钟信号:分秒时日月周年{Ds1302Write(WRITE_RTC_ADDR[n],TIME[n]);	}Ds1302Write(0x8E,0x80);		 //打开写保护功能
}

我们采用Go To Definition Of “子函数”的方式查看数组TIME[n]

//---DS1302时钟初始化2020年12月30日星期三12点30分00秒。---//
//---存储顺序是秒分时日月周年,存储格式是用BCD码---//
unsigned char TIME[7] = {0x00,0x30, 0x12, 0x30, 0x12, 0x03, 0x20};
///秒///分时日月/周///年///

这里我们代码注释写的也很清楚了,如果在main.c中调用了该函数,则每次程序复位都会将DS1302中的时间初始化为我们TIME数组中设置的时间。
如果开发板上装有纽扣电池,则我们在第一次下载程序时调用Ds1302Init(),将DS1302中的时间设置为当前的时间,再将main函数中Ds1302Init()这一函数给注释掉,这样在之后程序复位时,就不会再将DS1302中的时间进行初始化,同时因为我们有纽扣电池给DS1302供电,所有即使开发板断电,DS1302中的时间也会继续走,也就是说,下次再开启开发板,main函数中调用Ds1302ReadTime();,读取DS1302中的时间即为当前准确时间。

二、单片机内部资源的使用

通过刚才主函数的分析,大家也可以看到,主函数中的内容十分简洁,那么我们是如何通过简洁的代码实现我们开发板上复杂的功能的呢,这里就要用到我们单片机的内部资源(Timer0、Timer1、Timer2、INT0、INT1)了,下面我们来介绍一下在我们这个综合实验程序中,我们利用单片机的各项内部资源都做了些什么。

1、定时器0(Timer0)

关于Timer0所做的工作,我们需要结合定时器0的中断服务函数来进行分析,大家可以参考以下流程图
在这里插入图片描述
这里我们对各项功能都分别定义了独立的定时参数,例如led_time、AD_time、mov_time等,当T0计时到1ms,进入中断服务函数,分别对各项功能的定时参数进行累加,通过if语句判定定时参数的值,当定时参数累加到我们设定值时,就执行相应的动作。

2、定时器1(Timer1)

在主函数中看似没有Timer1相关函数的使用,实际上关于T1的配置,我们是在ConfigUART(9600)中完成的,我们将T1配置为了工作方式二,作为波特率发生器,以此来实现单片机的串口通讯功能,在ConfigUART(9600)函数中,我们将串口通讯波特率设置为了9600,这里大家也可以修改为其他波特率进行使用,常见的波特率有4800、9600、115200等,波特率越高通讯速度越快,但其通讯出错的概率也会越大,这主要是由于我们通过定时器1来计算波特率时实际采用的是我们单片机的晶振来作为计时的基本时钟,而在计算高波特率时,我们所采用的晶振频率(11.0592MHz)可能无法精确计算到这一频率,会存在些许误差,在误差允许范围内,依然是可以完成串口通讯的,而当误差较大时,就会造成通讯波特率不一致的情况,这时通讯就会出错。
这里既然说到了定时器1作为波特率发生器实现串口通讯,那么我们就顺便也把我们开发板上的串口通讯内容进行分析,当单片机串口接收到数据时,程序进入串口中断函数中,在usart.c文件中我们可以找到这一函数:

/****************************
*串口中断服务函数
****************************/
void InterruptUART() interrupt 4    //串口中断是中断4
{EA=0;if(RI)  //接收数据完成(RI){INT_NUM++;dataRead[INT_NUM] = SBUF;UART_Explain();RI = 0; }EA=1;TR2 = 1;       //启动T2
}

在串口中断服务函数中,主要调用的函数就是UART_Explain()了,该函数是对串口收到的数据进行解析,当数据是我们的目标数据时,就执行相应的动作。

3、定时器2(Timer2)

我们所采用的单片机STC89C52RC是有定时器2的,这里我们将定时器2用作步进电机的步进状态切换定时,除了主函数中调用的初始化函数ConfigTimer2(2)(配置T2为16为重转载模式)之外,我们还可以在timer2.c文件中找到T2中断服务函数,其函数中的内容即为与步进电机相关内容的配置。
这里也顺便说明一下关于步进电机接线的问题,我们所采用的4相5线5V步进电机排线中有一根红色的线,这根红色的线接我们的VCC,即将红色的线对应开发板上VCC所在引脚,然后将排线对应插入排针即可
在这里插入图片描述
为了使电机转动现象明显,可以在电机轴上粘贴一片小纸条或者绕上一段焊锡丝
在这里插入图片描述
关于串口通讯和利用串口进行程序下载可以参考https://blog.csdn.net/Stark_/article/details/111466231
串口通讯中,我们可以发送:正转、反转、加速、减速、停止等指令控制电机转动。

4、外部中断0(INT0)和外部中断1(INT1)

在我们的exti.c文件中,我们对外部中断服务函数的内容进行了配置,

void led_water() interrupt 0 //每一个外部中断信号都会使得LED移位方向改变
{led_flag=~led_flag;
}void beep_on() interrupt 2
{beep = 1;
}

其中led_water()函数是由我们开发板上的外部中断独立按键触发,在我们开发板上,除了矩阵按键之外,还有两个独立按键,一个是单片机复位按键,当程序出错时通过复位使系统重启,一个是外部中断触发按键,按下即可触发流水灯流动方向的改变。
而beep_on()函数是由我们的红外对管进行触发,通过遮挡红外对管,触发蜂鸣器鸣叫。

这篇关于51单片机开发综合实验程序结构解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/667111

相关文章

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

使用Python绘制3D堆叠条形图全解析

《使用Python绘制3D堆叠条形图全解析》在数据可视化的工具箱里,3D图表总能带来眼前一亮的效果,本文就来和大家聊聊如何使用Python实现绘制3D堆叠条形图,感兴趣的小伙伴可以了解下... 目录为什么选择 3D 堆叠条形图代码实现:从数据到 3D 世界的搭建核心代码逐行解析细节优化应用场景:3D 堆叠图

深度解析Python装饰器常见用法与进阶技巧

《深度解析Python装饰器常见用法与进阶技巧》Python装饰器(Decorator)是提升代码可读性与复用性的强大工具,本文将深入解析Python装饰器的原理,常见用法,进阶技巧与最佳实践,希望可... 目录装饰器的基本原理函数装饰器的常见用法带参数的装饰器类装饰器与方法装饰器装饰器的嵌套与组合进阶技巧

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

深度解析Spring Boot拦截器Interceptor与过滤器Filter的区别与实战指南

《深度解析SpringBoot拦截器Interceptor与过滤器Filter的区别与实战指南》本文深度解析SpringBoot中拦截器与过滤器的区别,涵盖执行顺序、依赖关系、异常处理等核心差异,并... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实