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开发一个极简版敏感词检测工具

《基于Java开发一个极简版敏感词检测工具》这篇文章主要为大家详细介绍了如何基于Java开发一个极简版敏感词检测工具,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录你是否还在为敏感词检测头疼一、极简版Java敏感词检测工具的3大核心优势1.1 优势1:DFA算法驱动,效率提升10

深度解析Python yfinance的核心功能和高级用法

《深度解析Pythonyfinance的核心功能和高级用法》yfinance是一个功能强大且易于使用的Python库,用于从YahooFinance获取金融数据,本教程将深入探讨yfinance的核... 目录yfinance 深度解析教程 (python)1. 简介与安装1.1 什么是 yfinance?

99%的人都选错了! 路由器WiFi双频合一还是分开好的专业解析与适用场景探讨

《99%的人都选错了!路由器WiFi双频合一还是分开好的专业解析与适用场景探讨》关于双频路由器的“双频合一”与“分开使用”两种模式,用户往往存在诸多疑问,本文将从多个维度深入探讨这两种模式的优缺点,... 在如今“没有WiFi就等于与世隔绝”的时代,越来越多家庭、办公室都开始配置双频无线路由器。但你有没有注

Python中的sort()和sorted()用法示例解析

《Python中的sort()和sorted()用法示例解析》本文给大家介绍Python中list.sort()和sorted()的使用区别,详细介绍其参数功能及Timsort排序算法特性,涵盖自适应... 目录一、list.sort()参数说明常用内置函数基本用法示例自定义函数示例lambda表达式示例o

Python开发简易网络服务器的示例详解(新手入门)

《Python开发简易网络服务器的示例详解(新手入门)》网络服务器是互联网基础设施的核心组件,它本质上是一个持续运行的程序,负责监听特定端口,本文将使用Python开发一个简单的网络服务器,感兴趣的小... 目录网络服务器基础概念python内置服务器模块1. HTTP服务器模块2. Socket服务器模块

SpringBoot加载profile全面解析

《SpringBoot加载profile全面解析》SpringBoot的Profile机制通过多配置文件和注解实现环境隔离,支持开发、测试、生产等不同环境的灵活配置切换,无需修改代码,关键点包括配置文... 目录题目详细答案什么是 Profile配置 Profile使用application-{profil

MySQL的触发器全解析(创建、查看触发器)

《MySQL的触发器全解析(创建、查看触发器)》MySQL触发器是与表关联的存储程序,当INSERT/UPDATE/DELETE事件发生时自动执行,用于维护数据一致性、日志记录和校验,优点包括自动执行... 目录触发器的概念:创建触www.chinasem.cn发器:查看触发器:查看当前数据库的所有触发器的定

Java中的volatile关键字多方面解析

《Java中的volatile关键字多方面解析》volatile用于保证多线程变量可见性与禁止重排序,适用于状态标志、单例模式等场景,但不保证原子性,相较synchronized更轻量,但需谨慎使用以... 目录1. volatile的作用1.1 保证可见性1.2 禁止指令重排序2. volatile的使用

Python lambda函数(匿名函数)、参数类型与递归全解析

《Pythonlambda函数(匿名函数)、参数类型与递归全解析》本文详解Python中lambda匿名函数、灵活参数类型和递归函数三大进阶特性,分别介绍其定义、应用场景及注意事项,助力编写简洁高效... 目录一、lambda 匿名函数:简洁的单行函数1. lambda 的定义与基本用法2. lambda

Java 与 LibreOffice 集成开发指南(环境搭建及代码示例)

《Java与LibreOffice集成开发指南(环境搭建及代码示例)》本文介绍Java与LibreOffice的集成方法,涵盖环境配置、API调用、文档转换、UNO桥接及REST接口等技术,提供... 目录1. 引言2. 环境搭建2.1 安装 LibreOffice2.2 配置 Java 开发环境2.3 配