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将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Java字符串处理全解析(String、StringBuilder与StringBuffer)

《Java字符串处理全解析(String、StringBuilder与StringBuffer)》:本文主要介绍Java字符串处理全解析(String、StringBuilder与StringBu... 目录Java字符串处理全解析:String、StringBuilder与StringBuffer一、St

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三

C#中async await异步关键字用法和异步的底层原理全解析

《C#中asyncawait异步关键字用法和异步的底层原理全解析》:本文主要介绍C#中asyncawait异步关键字用法和异步的底层原理全解析,本文给大家介绍的非常详细,对大家的学习或工作具有一... 目录C#异步编程一、异步编程基础二、异步方法的工作原理三、代码示例四、编译后的底层实现五、总结C#异步编程

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比