1659 字
8 分钟
STC TM0/TM1实现计时和串口
2022-08-26

前言#

这次使用的芯片依旧是 STC8G1K08A-8Pin,它只有2个定时器 T0 和 T1。

需要实现的功能是:使用定时器 TM0/TM1,同时实现计时和串口

自定义串口方法#

具体实现的功能是:

初始化后,通过串口输出1次 Ready By?\r\n 通过代码延时,实现每秒输出1次 TM1 Here!\r\n 通过定时器 T0 中断,实现每3秒修改 P54 的输出电平,并在功能2输出的同时,输出1次 TM0 Here! %d \r\n

定时器配置#

AUXR 是 辅助寄存器1,B11000000,表示定时器 TM0 和 TM1 速度处于 1T模式(不分频),且串口1选择 定时器 TM1 作为波特率发生器。

TMOD 是 定时器0/1模式寄存器,B00000000,表示定时器 TM0 和 TM1 工作模式处于 模式0(16位自动重载模式)。

SCON 是 串口1控制寄存器,B0101000,表示串口1工作模式处于 模式1(可变波特率8位数据方式),并 允许串口接收数据。

定时器 TM0 和 TM1 的装载值,可以使用 STC-ISP 的 定时器计算器 和 波特率计算器 直接获取。

void TimeInit(void)
{
AUXR = 0xC0; // 定时器0/1 时钟1T模式
TMOD = 0x00; // 定时器0/1 16位自动重载
SCON = 0x50; // 8位数据,可变波特率,允许读写
TL0 = 0xCD; // 设置 T0 初始值(低8)
TH0 = 0xD4; // 设置 T0 初始值(高8)
TL1 = 0xE8; // 设置 T1 初始值(低8)
TH1 = 0xFF; // 设置 T1 初始值(高8)
TF0 = 0; // 清除 TF0 标志
TR0 = 1; // 定时器0 开始计时
ET0 = 1; // 使能 定时器0 中断
ET1 = 0; // 禁止 定时器1 中断
TR1 = 1; // 定时器1 开始计时
ES = 1; // 使能 UART1 中断
EA = 1; // 使能 总中断控制
}

中断响应#

定时器0的中断响应

由于 11MHz IRC、16位自动重载 和 1T时钟 的情况下,定时器0的最长定时中断时间不超过 5.9ms,所以设置每次定时中断的时间为 1ms,然后通过一个计数器,当计数满 3000 次时才允许进行操作。

unsigned int TM0_COUNT = 0;
unsigned int TM0_MAX = 3000;
void TM0_Isr() interrupt 1
{
TM0_COUNT++;
if (TM0_COUNT++ >= TM0_MAX)
{
TM0_COUNT = 0;
Dat_Ready = 1;
P54 = !P54;
}
}

串口的中断响应#

这部分不详细描述了,就是对 TI 和 RI 的中断响应,见之前的文章 STC 串口通信

完整代码#

代码 - 点击查看详情
#include <STC8G.h>
#include <intrins.h>
bit busy;
bit Dat_Ready = 0;
unsigned int TM0_COUNT = 0;
unsigned int TM0_MAX = 3000;
void delayMicroseconds(unsigned int times);
void delayMilliseconds(unsigned int times);
void TimeInit(void);
void UartSend(char dat);
void UartSendStr(char *p);
void main()
{
// I/O 设置
P3M0 = 0x00;
P3M1 = 0x00;
P5M0 = 0x00;
P5M1 = 0x00;
// 定时器 初始化
TimeInit();
delayMilliseconds(500);
UartSendStr("Ready By?\r\n");
delayMilliseconds(500);
while (1)
{
if (Dat_Ready)
{
Dat_Ready = 0;
UartSendStr("TM0 Here!\r\n");
}
UartSendStr("TM1 Here!\r\n");
delayMilliseconds(1000);
}
}
void delayMicroseconds(unsigned int times)
{ // @11.0592MH
do {
_nop_();
} while (--times);
}
void delayMilliseconds(unsigned int times)
{
do {
delayMicroseconds(980);
} while (--times);
}
void TM0_Isr() interrupt 1
{
TM0_COUNT++;
if (TM0_COUNT++ >= TM0_MAX)
{
TM0_COUNT = 0;
Dat_Ready = 1;
P54 = !P54;
}
}
void UartIsr() interrupt 4
{
if (TI)
{
TI = 0;
busy = 0;
}
if (RI)
{
RI = 0;
}
}
void TimeInit(void)
{
AUXR = 0xC0; // 定时器0/1 时钟1T模式
TMOD = 0x00; // 定时器0/1 16位自动重载
SCON = 0x50; // 8位数据,可变波特率,允许读写
TL0 = 0xCD; // 设置 T0 初始值(低8)
TH0 = 0xD4; // 设置 T0 初始值(高8)
TL1 = 0xE8; // 设置 T1 初始值(低8)
TH1 = 0xFF; // 设置 T1 初始值(高8)
TF0 = 0; // 清除 TF0 标志
TR0 = 1; // 定时器0 开始计时
ET0 = 1; // 使能 定时器0 中断
ET1 = 0; // 禁止 定时器1 中断
TR1 = 1; // 定时器1 开始计时
ES = 1; // 使能 UART1 中断
EA = 1; // 使能 总中断控制
}
void UartSend(char dat)
{
while (busy);
busy = 1;
SBUF = dat;
}
void UartSendStr(char *p)
{
while (*p)
{
UartSend(*p++);
}
}

stdio.h 标准库#

我使用 stdio.h 标准库就是图方便,但实际花了整整一天在两个定时器的中断上。😂

完全不知道标准库的定时中断怎么定义的,感觉在没有读写的情况下它也在一直中断,如果沿用自定义串口那个中断配置,会导致定时器0的中断完全没有响应。

基础的配置修改很多遍都没用,想了大半天,才发觉可能和中断优先级有关,最后也是通过提高定时器0的中断优先级才解决的问题。

定时器配置#

大部分配置和自定义串口时的配置都相同,只是需要提高 中断源 Time0 的中断优先级以及使能 TI 让标准库能正常读写。

在 STC8G 中所有的中断源默认的优先级都是 0(最低优先级),所有只需要将 Time0 的中断优先级提高一级至 1(较低优先级),比 UART1 高一级即可。

控制 Time0 中断优先级的控制位分别是 IPH 寄存器的 PT0H 和 IP 寄存器的 PT0。

中断优先级,详见手册 11.2 STC8G 中断结构图 & 11.4.3 中断优先级寄存器

void TimeInit(void)
{
IPH = 0x00; // PT0H 0
IP = 0x02; // PT0 1
...
}

中断响应#

定时器0的中断响应,和自定义串口相同。

串口的中断响应,由 stdio.h 标准库定义。

完整代码#

代码 - 点击查看详情
#include <STC8G.h>
#include <intrins.h>
#include <stdio.h>
bit Dat_Ready = 0;
unsigned int TM0_COUNT = 0;
unsigned int TM0_MAX = 3000;
void delayMicroseconds(unsigned int times);
void delayMilliseconds(unsigned int times);
void TimeInit(void);
void main()
{
// I/O 设置
P3M0 = 0x00;
P3M1 = 0x00;
P5M0 = 0x00;
P5M1 = 0x00;
// 定时器 初始化
TimeInit();
delayMilliseconds(500);
printf("Ready By?\r\n");
delayMilliseconds(500);
while (1)
{
printf("TM1 Here! %d \r\n", Dat_Ready);
if (Dat_Ready)
{
Dat_Ready = 0;
printf("TM0 Here!\r\n");
}
delayMilliseconds(1000);
}
}
void TM0_Isr() interrupt 1
{
TM0_COUNT++;
if (TM0_COUNT++ >= TM0_MAX)
{
TM0_COUNT = 0;
Dat_Ready = 1;
P54 = !P54;
}
}
void TimeInit(void)
{
AUXR = 0xC0; // 定时器0/1 时钟1T模式
TMOD = 0x00; // 定时器0/1 16位自动重载
SCON = 0x50; // 8位数据,可变波特率,允许读写
TL0 = 0xCD; // 设置 T0 初始值(低8)
TH0 = 0xD4; // 设置 T0 初始值(高8)
TL1 = 0xE8; // 设置 T1 初始值(低8)
TH1 = 0xFF; // 设置 T1 初始值(高8)
IPH = 0x00; // PT0H 0
IP = 0x02; // PT0 1
TF0 = 0; // 清除 TF0 标志
TR0 = 1; // 定时器0 开始计时
ET0 = 1; // 使能 定时器0 中断
ET1 = 0; // 禁止 定时器1 中断
TR1 = 1; // 定时器1 开始计时
ES = 1; // 使能 UART1 中断
EA = 1; // 使能 总中断控制
TI = 1;
}
STC TM0/TM1实现计时和串口
https://fuwari.vercel.app/posts/嵌入式/stc/stc-tm0-tm1实现计时和串口/
作者
Asuwee
发布于
2022-08-26
许可协议
CC BY-NC-SA 4.0