51单片机模块学习——定时器、UART串口

开发软件:Keil4(编写程序)、STC-ISP(烧录下载)
开发平台:普中科技51单片机A4版本
参考:B站江协科技51单片机学习视频

七、定时器

定时器是单片机里的重要模块。
它可以实现软件计时,使程序每隔一个固定时间完成一项操作。同时它也可以替代长时间的Delay,在Delay中,单片机的CPU是不工作的,使用定时器的话,可以让单片机在计时的过程中保证运作,提高CPU的运行效率和处理速度。
在这里插入图片描述

STC89C52有3个定时器:
定时器0(T0)和定时器1(T1):传统8051的定时器,功能类似。
定时器2(T2):增强型定时器,支持更多模式(如捕获、自动重装等)。
我们这里主要讲解定时器0/1。

STC89C52的T0和T1均有四种工作模式,由TMOD参数控制:
模式0:13位定时器/计数器
模式1:16位定时器/计数器(常用)
模式2:8位自动重装模式
模式3:两个8位计数器
在这里插入图片描述
由图可见,若我们想启用定时器0模式1,我们需要将TMOD的后半段的M1、M0设置为0、1,GATE设置为0。为了引入内部中断(即计时结束时运行中断函数),C/T‾C/\overline{T}C/T设置为0。
由于TMOD不可位寻址(进行位操作),所以我们需要用这样的代码进行设置

TMOD &= 0xF0;		//保留定时器1的设置
TMOD |= 0x01;		//设置定时器0为模式1,引入内部中断

在这里插入图片描述
设定TR0激活定时器0后,我们要设置计数初值TL0与TH0,单片机会从TL0一直数到TH0(每下间隔为1/晶振频率,11.0592MHZ的晶振每次间隔约为1.085us,一个计数有256个间隔),然后将TF0设置为1,引发中断,进入中断函数。
除了上述参数外,我们还要设置
ET0:这是定时器0的中断使能位,取1时允许定时器0溢出时触发中断,取0时禁止定时器0中断
EA:总中断开关,取1时允许出现中断,取0则禁止所有中断
PT0:定时器0的中断优先级控制位,取1定时器0中断设为高优先级,取0则定时器0中断设为低优先级(默认值)
我们可以写出一个定时器模块文件

#include <REGX52.H>

/**
  * @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;
}

/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/

那么具体定时器怎么用呢?我们来看这样的一个代码

//按键控制LED流水灯模式
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>

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++;		//T0Count计次,对中断频率进行分频
	if(T0Count>=500)//分频500次,500ms
	{
		T0Count=0;
		if(LEDMode==0)			//模式判断
			P2=_crol_(P2,1);	//LED输出
		if(LEDMode==1)
			P2=_cror_(P2,1);
	}
}

代码首先点亮第一个LED,然后获取独立按键按下的键码,K1按键按下,模式变为1,再次按下则变为0。
每经过1ms,进入一次中断,累计进入500次中断(即过去500ms)后,根据模式选择LED小灯变化,模式1则LED小灯左移,模式2则LED右移
这是一个按键控制LED流水灯模式的代码

结合之前的LCD1602液晶屏模块,我们可以写一个定时器时钟的代码

//定时器时钟
#include <REGX52.H>
#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
				}
			}
		}
	}
}

八、UART串口

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。
在这里插入图片描述
以下我们讲解模式1
在这里插入图片描述
在这里插入图片描述
使用代码,激活串口模式1,此时代码只可以发送,而不可以接收

SCON = 0x40;        // 设置串口控制寄存器

波特率是串口通信中的重要概念,在基于8051的UART通信中,定时器扮演着**波特率发生器(Baud Rate Generator)**的关键角色。
我们利用定时器1工作在模式2,通过计算定时器溢出率来产生精确的时钟信号。

TMOD |= 0x20;    // 设置定时器1为模式2(8位自动重载)
TL1 = TH1 = 0xF3; // 定时器初值设置
TR1 = 1;         // 启动定时器

我们可以计算波特率
定时器溢出周期 = (256 - 0xF3) × 1μs = 13μs
溢出率 = 1/13μs ≈ 76923 Hz
波特率 = (1 × 76923) / 32 ≈ 2400bps
我们将PCON的SMOD设置为0,波特率可以翻倍,变成4800bps
由此我们可以获得串口初始化代码

/**
  * @brief  串口初始化,4800bps@12.000MHz
  * @param  无
  * @retval 无
  */
void UART_Init()
{
	SCON=0x40;
	PCON |= 0x80;
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x20;		//设置定时器模式
	TL1 = 0xF3;		//设定定时初值
	TH1 = 0xF3;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}

结合SBUF(暂存发送的字节数据),TI(发送完成标识,数据发送完成时自动变成1)
我们可以写出UART的模块代码

#include <REGX52.H>

/**
  * @brief  串口初始化,4800bps@12.000MHz
  * @param  无
  * @retval 无
  */
void UART_Init()
{
	SCON=0x40;
	PCON |= 0x80;
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x20;		//设置定时器模式
	TL1 = 0xF3;		//设定定时初值
	TH1 = 0xF3;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}

/**
  * @brief  串口发送一个字节数据
  * @param  Byte 要发送的一个字节数据
  * @retval 无
  */
void UART_SendByte(unsigned char Byte)
{
	SBUF=Byte;
	while(TI==0);
	TI=0;
}

单片机向电脑发送数据的代码如下

//串口向电脑发送数据
#include <REGX52.H>
#include "Delay.h"
#include "UART.h"

unsigned char Sec;

void main()
{
	UART_Init();			//串口初始化
	while(1)
	{
		UART_SendByte(Sec);	//串口发送一个字节
		Sec++;				//Sec自增
		Delay(1000);		//延时1秒
	}
}

学会了利用UART串口从单片机向电脑发送数据后,我们就需要思考,能否从电脑向单片机发送数据呢?
我们可以做一个电脑通过串口控制LED的程序。
我们需要修改之前的SCON,设置为0x50,同时开启中断使能位,即EA=1(允许所有中断),ES=1,(允许串口中断)
这样设置后,当单片机接收到数据后,可以进入中断函数,读取数据进行LED的设置,然后再将接收标志位置0,进行下一个数据的接收

//UART接受模块代码
#include <REGX52.H>

/**
  * @brief  串口初始化,4800bps@12.000MHz
  * @param  无
  * @retval 无
  */
void UART_Init()
{
	SCON=0x50;
	PCON |= 0x80;
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x20;		//设置定时器模式
	TL1 = 0xF3;		//设定定时初值
	TH1 = 0xF3;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
	EA=1;
	ES=1;
}

/**
  * @brief  串口发送一个字节数据
  * @param  Byte 要发送的一个字节数据
  * @retval 无
  */
void UART_SendByte(unsigned char Byte)
{
	SBUF=Byte;
	while(TI==0);
	TI=0;
}

/*串口中断函数模板
void UART_Routine() interrupt 4
{
	if(RI==1)
	{
		
		RI=0;
	}
}
*/

//电脑通过串口控制LED
#include <REGX52.H>
#include "Delay.h"
#include "UART.h"

void main()
{
	UART_Init();		//串口初始化
	while(1)
	{
		
	}
}

void UART_Routine() interrupt 4
{
	if(RI==1)					//如果接收标志位为1,接收到了数据
	{
		P2=~SBUF;				//读取数据,取反后输出到LED
		UART_SendByte(SBUF);	//将受到的数据发回串口
		RI=0;					//接收标志位清0
	}
}

/
/
/
/
/
作者是第一次在CSDN上分享学习内容,写的不好请多多包涵。
欢迎大家在评论区和作者讨论。
码字不易,求各位看官点个关注~

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐