前言
这次使用的芯片依旧是 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;}