从零学习51单片机和STM32单片机入门指南:适合零基础C语言学习者的完整教程

[id_[id_[id_58112289[id_925087132]]061267869]6526215[id_1555932089]5]

从零学习51单片机和STM32单片机入门指南:适合零基础C语言学习者的完整教程

专栏适用的人群是:想要从零基础开始学习入门单片机的童鞋,并且这些童鞋要有一定的 C 语言基础。

专栏的目标是从零基础开始,能够进入 51 单片机和 STM32 单片机的领域。希望在玩好单片机的同时,还能够知晓一些计算机的基本概念,以及了解电路及其元器件的基本理论等。

专栏的主要内容为:主要去学习 51 单片机的功能;主要去学习 51 单片机的各个模块;主要去学习 51 单片机的外设;主要去学习 51 单片机的驱动等。最终的目的是要玩好单片机和单片机的外设,并且在整个过程中都要手敲代码,以实现我们所想要实现的功能。

专栏说明:若文章中的知识点存在错误之处,欢迎大家随时在文章下方进行评论,我会即刻进行改正。让我们共同学习,共同进步。

专栏主页:

本学习过程参考:

单片机安装软件、各种资料以及源码的路径:

提取码:gdzf

本节主要介绍与定时器相关的功能。其中包含定时器的简介,定时器的原理,计数器、时钟以及中断系统等基础知识。并且通过两个小实验来进行练习,一个实验是利用定时器来实现按键控制流水灯模式,另一个实验是利用定时器来实现定时器时钟。

[id_[id_1322479884]591101]

1.1.1 定时器的简介。

STC89C52 具有定时器资源,其具体情况如下图所示。如需参考相关内容,可参考手册:\51 单片机入门教程资料\课件及程序源码\相关资料\STC89C52.pdf 的第七章 定时器/计数器。

1.1.2 定时器原理

下面会学习什么是中断系统。

定时器的工作模式如下:

本文只学习模式 1,即 16 位定时器/计数器。这种模式是最常用的。而其他几个模式基本不常用。

模式1的框图如下图所示:

可将其分成三部分,分别是时钟部分、计数器部分以及中断部分,就如下面的图所展示的那样:

1.1.2.1 计数器

计数器是如何进行计数的呢?上图中我们能看到计数器部分包含 TL0 和 TH0。TL0 和 TH0 每个都能存放 8 字节数据。它们总共可以存储的数字范围是从 0 到 65535。左边的时钟给 TL0 和 TL1 提供脉冲。每提供一个脉冲,这 16 位计数器里面的值就会增加 1。当值加到最大值 65535 时,下一个脉冲到来,就会导致计数器溢出。计数器溢出时,会给 TF0 一个标志位。收到标志位后,TF0 会给中断系统一个中断信号申请中断。

图中的TR0是控制定时器是否启动;

1.1.2.2 时钟

从图中能够看出,时钟的来源存在两个。其中一个是 SYSclk,它指的是系统时钟;另一个是 T0Pin,T0Pin 属于单片机外部的接口。如下图所示。

在关键图中可以看出,14引脚的位置就是T0Pin;

所以时钟能够由系统时钟(SYSclk)提供,也能够通过外部引脚提供。当通过外部引脚提供时钟时,我们的定时器就成为一个计数器。因为外部来一个脉冲,计数器就加一,所以它就相当于一个脉冲计数器。计数器名字的由来也与此相关。

当然最常用的是由系统时钟来提供脉冲。系统时钟就是晶振周期。晶振在开发部的位置如下图呈现:

本开发板上的晶振频率是 12MHz。因此,该开发板上的系统时钟为 12MHz。

系统时钟输入为 12MHz;12T 模式会对输入的 12MHz 系统时钟进行 12 分频;分频后输出的频率是 1MHz;所以定时器的周期是 1 微秒;Hz 代表每秒周期震动 1 次;60Hz 代表每秒周期震动 60 次;1MHz 等于 1000000Hz;所以 1MHz 为一秒震动一百万次。在图中所画红色线路(C/T = 0)的情形下,定时器的计数单元会每隔一微秒进行一次计数。

我们本节选择定时器时钟的配置如下:先选择分频模式为 12 分频,接着选择 C/T = 0,也就是选择定时器功能(当 C/T = 1 时,是连接下面的计数器功能);所以整体的流程是:系统时钟(SYSclk)经过 12 分频(12T 模式)后进入定时器模式(C/T = 0),如下图所示:

1.1.2.3 中断系统

中断系统是 51 单片机的一个重要知识点。本节的定时器需要中断系统,后面要学习的串口也需要中断系统。i/o 口也需要中断系统,还有很多其他设备也需要中断系统。所以中断系统会与许多外设进行交互。

中断系统的介绍情况如下:可参考 51 单片机入门教程资料中的课件及程序源码相关资料 STC89C52.pdf 的第 6 章 中断系统,其具体介绍如下图所示。

总结几个重点:

1、中断是可以使CPU拥有对外界紧急事件的实时处理能力

2、高优先级的中断可以打断中优先级的中断

中断的处理流程

中断的处理流程如下图所示:

主程序在运行时,若突然有中断请求到来,CPU 会对中断请求作出响应,接着去处理中断处理程序,中断处理程序执行完毕后,会返回主程序继续执行主程序的任务。

中断资源

中断资源与单片机的型号有关联。不同种类的单片机,其中断资源可能不同。比如,中断源个数不同,优先级个数也不同等。

下图是STC89C5的中断资源:

1.1.2.4 定时器和中断系统

3. 此图即为具体关系的呈现形式(以传统 51 单片机为例)。

整体的流程呈现为红线部分的样子,起始于时钟,接着到定时器,最后到中断系统;对于整个流程的具体代码实现,接下来将会进行学习。

1.1.2.5 定时器相关寄存器

我们之前学过,单片机能够通过对寄存器进行配置,以此来控制内部线路的连接。并且,通过内部线路的不同连接方式,就可以实现不同的功能。

下图列出了和定时器有关的寄存器已经和中断有关的寄存器:

寄存器具体由什么作用呢?

前面讲到的具体模式的配置,即上图中的各个开关,具体要拨到哪个位置,是由寄存器来控制的。如下图所示:

请参考手册中的相关内容,即\51 单片机入门教程资料\课件及程序源码\相关资料\STC89C52.pdf 的第 7.1.1 节,以获取具体的介绍。

视频介绍请参考(的第48分钟后)

总结如下:

1、寄存器是连接软硬件的媒介

在单片机中,寄存器是一段特殊的 RAM 存储器。其一,它能够存储数据并进行读取操作;其二,每一个寄存器都与一根导线相连接,通过这根导线控制着电路的连接方式。

3、寄存器相当于一个复杂机器的“操作按钮

所以,我们在程序中把相应的寄存器配置好,那么单片机的电路就会按照我们审定的模式进行工作。

1.2 本节目标

目标1:按键控制LED流水灯模式

前面学习 LED 灯时已经演示过流水灯的效果。本节的任务是通过定时器,把按键与流水灯结合起来。其效果依然是流水灯的效果。

目标2:使用定时器实现时钟

二、按键控制LED流水灯模式

代码的路径为 51 单片机入门教程资料中的课件及程序源码部分,其中包含程序源码,具体在 KeilProject 文件夹下的 7 - 1 按键控制 LED 流水灯模式。

代码的编写过程,请参考

具体代码:

#include 
#include "Timer0.h"
#include "Key.h"
#include 
unsigned char KeyNum,LEDMode;
void main()
{
	P2=0xFE;
	Timer0Init();
	while(1)
	{
		KeyNum=Key();		//获取独立按键键码
		if(KeyNum)			//如果按键按下
		{
			if(KeyNum==1)	//如果K1按键按下
			{
				LEDMode++;	//模式切换
				if(LEDMode>=2)LEDMode=0;
			}
		}
	}
}
//定时器中断函数
void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;		[id_939195004]
	if(T0Count>=500)//分频500次,500ms
	{
		T0Count=0;
		if(LEDMode==0)			//模式判断
			P2=_crol_(P2,1);	//LED输出
		if(LEDMode==1)
			P2=_cror_(P2,1);
	}
}

其中,Timer0.c如下:

#include 
/**
定时器 0 进行初始化操作,其定时时间为 1 毫秒,当前系统的时钟频率为 12.000MHz。
  * @param  无
  * @retval 无
  */
void Timer0Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}
/*定时器中断函数模板
在中断 1 处执行 Timer0_Routine 函数的操作。 此函数在中断 1 的情况下被触发并执行相关的定时任务。 它是与 Timer0 相关的中断处理程序,用于在特定的中断事件发生时进行相应的处理。 当 Timer0 产生中断信号且中断号为 1 时,就会调用 Timer0_Routine 函数来执行相应的操作。 该函数在中断处理流程中起到关键作用,用于处理 Timer0 相关的定时事件和操作。 它会根据 Timer0 的定时设置和中断配置,在特定的时间点被触发并执行相应的代码逻辑。
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/

Key.c的内容如下:

#include 
#include "Delay.h"
/**
  * @brief  获取独立按键键码
  * @param  无
按下按键后,会返回一个键码,其范围在 0 到 4 之间。当没有按键按下时,返回值为 0。
  */
unsigned char Key()
{
	unsigned char KeyNumber=0;
	
	if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
	if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
	if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
	if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
	
	return KeyNumber;
}

代码解释,主要对 Timer0.c 中的 Timer0Init 函数进行解释。

/**
  * @brief  定时器0初始化,1毫秒@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer0Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}

首先是配置定时器模式TMOD:

TMOD 进行按位与操作后结果为 0xF0,这意味着 TMOD 的低四位被清零了,而高四位依然保持原来的状态不变。

TMOD 进行按位或运算后等于 0x01,这意味着将 TMOD 的最低位设置为 1,而其他位保持不变;

这两步操作,其效果与 TMOD = 0x01 相同。好处在于,能防止在同时操作多个寄存器时出现串位的情况,并且在实现配置高四位的同时,不会对低四位造成影响。

TL0 的值为 0x18;TH0 的值为 0xFC;这意味着进行了定时初值的设置,其中 TL0 处于低位,TH0 处于高位。

TF0=0清除TF0标志;

TR0 等于 1。在如上图所示的情况中,当 TR0 为 1 时,与门的输出结果仅仅取决于 TR0 下面的那部分。

ET0=1,打开ET0的开关;

EA=1,打开EA的开关;

PT0=0,设置为低优先级。

其实定时器的设置并非一定要我们通过手敲代码来完成。有一种简单的方法,那就是利用 STC-ISP 软件里的功能来生成。具体步骤如下:

假设我们要设置一个1ms的定时器:

根据下图来进行配置,接着点击生成 C 代码,这样就能生成一个 1ms 的定时器代码。

AUXR 是相对于最新版本的,而我们的单片机不需要它,直接将其删掉就可以。可以看出,这与我们自己上面写的代码是一致的。不过其中少了中断的配置,我们把中断的配置加上就可以了。

配置中断:

ET0=1,打开ET0的开关;

EA=1,打开EA的开关;

PT0=0,设置为低优先级。

后面我们写定时器的代码时,可直接利用 STC - ISP 软件生成代码,接着在其后添加上述配置中断的代码就可以了。

前面讲了定时器的配置。定时器达到定时时间后,需要触发中断。然后转而去执行中断处理程序。中断处理程序如下所示:在函数名定义后面加上 interrupt 1,就表示这是中断处理程序。当定时器达到时间后,会自动触发中断去执行该中断处理程序。

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;		//T0Count计次,对中断频率进行分频
	if(T0Count>=500)//分频500次,500ms
	{
		T0Count=0;
		if(LEDMode==0)			//模式判断
			P2=_crol_(P2,1);	//LED输出
		if(LEDMode==1)
			P2=_cror_(P2,1);
	}
}

执行中断处理程序时,需要给 TL0 和 TH0 赋初值。具体要赋的值参考上面 STC-ISP 生成的代码里的值即可。

T0Count 的作用是控制时间。例如,我们期望达到 0.5 秒的间隔后再去处理某件事,然而定时器每次触发的频率是 1 毫秒。因此,我们定义了 T0Count 。每次触发中断处理函数时,T0Count 就会加 1,这意味着过去了 1 毫秒。当 T0Count 增加到 500 时,就表示过去了 500 毫秒,也就是 0.5 秒。

再后面的代码(中断控制函数里的代码)就是控制独立按键了。

_crol_函数在 INTRINS.H 函数库中,其含义是循环左移;_cror_函数也在 INTRINS.H 函数库中,其含义是循环右移。

执行完中断处理函数后,继续执行主程序的代码。

最终的效果就是目标1了,即用按键控制流水灯模式。

三、定时器时钟

代码的路径为 51 单片机入门教程资料中的课件及程序源码部分,其中包含程序源码,该程序源码位于 KeilProject 文件夹下,具体是 7 - 2 定时器时钟这个文件。

具体代码:

#include 
#include "Delay.h"
#include "LCD1602.h"
#include "Timer0.h"
unsigned char Sec=55,Min=59,Hour=23;
void main()
{
	LCD_Init();
	Timer0Init();
	
	LCD_ShowString(1,1,"Clock:");	//上电显示静态字符串
	LCD_ShowString(2,1,"  :  :");
	
	while(1)
	{
		LCD_ShowNum(2,1,Hour,2);	//显示时分秒
		LCD_ShowNum(2,4,Min,2);
		LCD_ShowNum(2,7,Sec,2);
	}
}
void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=1000)	//定时器分频,1s
	{
		T0Count=0;
		Sec++;			//1秒到,Sec自增
		if(Sec>=60)
		{
			Sec=0;		//60秒到,Sec清0,Min自增
			Min++;
			if(Min>=60)
			{
				Min=0;	//60分钟到,Min清0,Hour自增
				Hour++;
				if(Hour>=24)
				{
					Hour=0;	//24小时到,Hour清0
				}
			}
		}
	}
}

你可能想看:
« 上一篇
下一篇 »

相关推荐

FreeBasic:强大免费开源的64位basic编译器及应用优势

2025年05月10日

82阅读

脊柱侧弯矫正方法大盘点:运动康复、支具矫正等多种方式

2025年05月09日

87阅读

如何在winForm标题栏去掉标题文字且任务栏图标正常显示

2025年05月09日

86阅读

十一黄金周促销广告词大全,国庆购物狂欢等你来

2025年05月09日

78阅读

关闭快速编辑模式及插入模式,设置控制台指定文字颜色举例

2025年05月09日

63阅读

黄金搭档低糖高钙饮品包装及成分功效全解析,适合人群要知晓

2025年05月09日

91阅读