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

相关文章

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

一文解析C#中的StringSplitOptions枚举

《一文解析C#中的StringSplitOptions枚举》StringSplitOptions是C#中的一个枚举类型,用于控制string.Split()方法分割字符串时的行为,核心作用是处理分割后... 目录C#的StringSplitOptions枚举1.StringSplitOptions枚举的常用

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

MyBatis延迟加载与多级缓存全解析

《MyBatis延迟加载与多级缓存全解析》文章介绍MyBatis的延迟加载与多级缓存机制,延迟加载按需加载关联数据提升性能,一级缓存会话级默认开启,二级缓存工厂级支持跨会话共享,增删改操作会清空对应缓... 目录MyBATis延迟加载策略一对多示例一对多示例MyBatis框架的缓存一级缓存二级缓存MyBat

基于Python开发Windows自动更新控制工具

《基于Python开发Windows自动更新控制工具》在当今数字化时代,操作系统更新已成为计算机维护的重要组成部分,本文介绍一款基于Python和PyQt5的Windows自动更新控制工具,有需要的可... 目录设计原理与技术实现系统架构概述数学建模工具界面完整代码实现技术深度分析多层级控制理论服务层控制注

前端缓存策略的自解方案全解析

《前端缓存策略的自解方案全解析》缓存从来都是前端的一个痛点,很多前端搞不清楚缓存到底是何物,:本文主要介绍前端缓存的自解方案,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、为什么“清缓存”成了技术圈的梗二、先给缓存“把个脉”:浏览器到底缓存了谁?三、设计思路:把“发版”做成“自愈”四、代码

Java集合之Iterator迭代器实现代码解析

《Java集合之Iterator迭代器实现代码解析》迭代器Iterator是Java集合框架中的一个核心接口,位于java.util包下,它定义了一种标准的元素访问机制,为各种集合类型提供了一种统一的... 目录一、什么是Iterator二、Iterator的核心方法三、基本使用示例四、Iterator的工