百度关键词怎么做排名小说网站怎么做seo
百度关键词怎么做排名,小说网站怎么做seo,网站弹广告是什么样做的,网站的关键词在哪里设置1. 从“黄瓜”说起#xff1a;定时器与计数器的本质
大家好#xff0c;我是老张#xff0c;在单片机这行摸爬滚打十几年了#xff0c;从最老的8051玩到现在的各种ARM内核#xff0c;但说实话#xff0c;STC15这种增强型51单片机#xff0c;在中小项目里依然是“定海神针…1. 从“黄瓜”说起定时器与计数器的本质大家好我是老张在单片机这行摸爬滚打十几年了从最老的8051玩到现在的各种ARM内核但说实话STC15这种增强型51单片机在中小项目里依然是“定海神针”般的存在。今天咱们不聊虚的就掰开了、揉碎了讲讲STC15里定时器/计数器那个“16位自动重装载模式”。这功能用好了你的程序会变得异常简洁和稳定就像给闹钟上了个自动上弦的发条再也不用你手动去拧了。很多人一听到“定时器”、“计数器”就头大觉得是两个东西。其实啊它俩在单片机内部就是同一个硬件模块就像一根黄瓜你拍碎了拌蒜是凉菜切片了敷脸是美容东西还是那个东西就看你怎么用。在STC15里有T0到T4一共5个这样的“黄瓜”啊不是定时器/计数器模块。那“定时”和“计数”到底差在哪儿呢核心就一点脉冲的来源不同。定时模式数的是单片机自己心跳的节拍。单片机干活得有节奏这个节奏就是主时钟比如内部IRC或外部晶振产生的11.0592MHz信号。定时模式就是让这个硬件模块去数主时钟的脉冲个数。数够一定数量就代表过去了一段具体的时间。比如你的时钟是12MHz那每个脉冲周期就是1/12微秒数12个脉冲就是1微秒。这就像你看着自己的手表秒针数它跳了多少下来计算时间。计数模式数的则是来自外部世界的“敲门声”。这个“门”就是单片机的某个IO口比如P3.4对应T0计数。每当这个引脚上的电平发生一次从高到低或从低到高的跳变具体看你怎么设置硬件模块就计一个数。这就像你在门口放个计数器进来一个人就按一下用来统计客流量。所以当你需要做精准延时、产生PWM波、测量时间间隔时就用定时模式当你想数一数外部发生了多少件事比如旋转编码器转了多少格、光电传感器被遮挡了多少次就用计数模式。理解了这一点后面的配置你就不会迷糊了。2. 为什么STC15跑得快1T模式的“涡轮增压”揭秘玩过传统89C51的朋友都知道那家伙速度有点“肉”。STC官方总说STC15快快在哪很多人第一反应是“哦晶振频率更高了。” 这没错但只对了一半。更关键的秘密在于计数速率模式也就是1T和12T的区别。我打个比方传统51单片机它的“心脏”CPU核心和“手脚”执行单元之间装了一个12倍的减速齿轮。外部晶振“咚咚咚”敲了12下内部的定时器才懒洋洋地数一下“1”。这就是所谓的12T模式每12个时钟周期定时器的计数值才加1。哪怕你外接个24MHz的晶振实际定时器的计数频率也只有2MHz。STC15系列单片机厉害就厉害在它给你提供了一个“直通”模式也就是1T模式。在这个模式下那个“减速齿轮”被拆掉了。主时钟每“咚”一下定时器的计数值就“唰”地加一下。同样是24MHz的时钟定时器计数频率就是实打实的24MHz。光这一项在定时和计数场景下速度理论上限就提升了12倍这就像给老爷车换上了跑车的发动机响应速度完全不是一个级别。那怎么选择用1T还是12T呢这取决于你的实际需求。如果你需要极高的定时精度或非常快的计数响应比如做高速脉冲捕获那肯定选1T模式。如果你的项目是从老51平台移植过来的或者对时间精度要求不高只是想兼容原有的代码逻辑和定时参数那用12T模式会更省事。这个选择是通过配置一个叫做AUXR辅助寄存器的特定位来实现的后面我们会手把手教你设置。3. 核心武器拆解什么是16位自动重装载好了铺垫了这么多主角该登场了——16位自动重装载模式。这个名字听起来挺唬人咱们把它拆成“16位”和“自动重装载”两部分来理解保证你一听就懂。首先说“16位”。这指的是定时器内部那个核心的计数寄存器它的宽度是16个二进制位。16位能表示的最大数值是多少呢二进制是1111 1111 1111 1111十六进制是0xFFFF十进制就是65535。也就是说这个计数器从0开始数最多能数到65535下一秒再想数就溢出了变成0。然后是“自动重装载”。这是这个模式最精髓、最好用的地方。想象一下普通模式计数器从0数到65535溢出后它就归零了。如果你想让它下次从10000开始数就得在溢出中断里手动用代码把TH0和TL0这两个寄存器重新写成10000对应的值。这就好比一个水桶水满了溢出倒掉后你得自己再去接一桶水来。而“自动重装载”模式相当于给这个水桶配了一个隐藏的备用储水箱和自动上水阀。你事先把希望的水位比如对应10000的初始值放在这个储水箱里。当水桶的水满溢出时阀门自动打开储水箱里的水“哗”一下就又把水桶灌到了你预设的水位10000然后接着开始下一次计数。整个过程完全由硬件自动完成不需要任何CPU干预。这就实现了精准、不间断的周期性定时或计数而且极大地减轻了CPU的负担中断服务程序可以写得非常干净。在STC15的数据手册里这个“隐藏的储水箱”有专门的名字对于定时器0来说它们叫做RL_TH0和RL_TL0重装载寄存器。它们和咱们平时操作的TH0、TL0共用同一个地址但在不同时机下读写操作的实际对象会由硬件智能切换。这个精妙的设计是理解自动重装载的关键我们会在第5节结合寄存器详细说明。4. 实战配置第一步必须搞懂的6个关键寄存器想驾驭好16位自动重装载模式你得跟单片机的几个“功能开关”打交道它们就是寄存器。别怕咱们一个一个来我保证用最白的话讲清楚。咱们以最常用的定时器0T0为例。4.1 速度开关辅助寄存器AUXR这个寄存器负责给定时器“调档位”决定它跑1T还是12T。关键位T0x12AUXR的第7位T0x12 0定时器0工作在1T模式。这是高速档计数频率等于系统主频。T0x12 1定时器0工作在12T模式。这是兼容档计数频率等于系统主频除以12。 在代码里我们通常用位操作来设置。比如想用1T模式就写AUXR | 0x80;把最高位置1。想用12T模式就写AUXR 0x7F;把最高位清0。4.2 模式与门控开关TMOD寄存器这个寄存器像个多功能旋钮设定定时器的工作模式和启动条件。我们只关心T0相关的低4位。GATE位TMOD.3门控位。设为1定时器启动需要双条件——TR01且外部中断0引脚INT0/P3.2为高电平。这常用于测量外部脉冲宽度。设为0最常用只要TR01定时器就启动。简单粗暴我们一般就用这个。C/T位TMOD.2定时/计数选择位。设为1计数器模式数的是外部引脚T0/P3.4的脉冲。设为0定时模式定时器模式数的是内部系统时钟。M1和M0位TMOD.1, TMOD.0工作模式选择位。M10, M00这就是我们今天要用的模式0即16位自动重装载模式。 所以要让T0工作在自动重装载定时模式且无需门控通常的设置是TMOD 0x00;低4位全是0。4.3 计数寄存器TH0与TL0这就是前面说的那个“水桶”一个16位的计数器由高8位TH0和低8位TL0拼成。在自动重装载模式下初始化时你写给它们的值实际上会被硬件偷偷存到后面要说的重装载寄存器里。计算初始值是个关键公式是初始值 65536 - 所需计数值比如系统时钟是11.0592MHz工作在1T模式想定时1ms即计数11059.2次。由于计数值必须是整数我们取整为11059。那么初始值 65536 - 11059 54477转换成十六进制是0xD4CD。那么TH0 0xD4;TL0 0xCD;。4.4 控制与标志寄存器TCON这里有两个位对我们最重要。TR0位TCON.4运行控制位。软件把它置1定时器就开始数数清0定时器就暂停。它是定时器的总开关。TF0位TCON.5溢出中断标志位。当计数器从65535翻到0的瞬间硬件会自动把这个位置1表示“我溢出了”。如果中断被允许CPU就会跳转到中断服务程序。进入中断后硬件会自动把TF0清0所以我们一般不用在中断函数里手动清它。4.5 中断使能开关IE寄存器定时器溢出只是个“事件”想让CPU响应这个事件去执行你的代码得打开中断开关。EA位IE.7全局中断总开关。必须置1CPU才允许响应任何中断。ET0位IE.1定时器0溢出中断使能位。置1才允许T0溢出时触发中断。4.6 中断优先级IP寄存器PT0位IP.1定时器0中断优先级控制位。默认是0低优先级。如果你的程序里只有一个定时器中断那不用管它。如果同时有多个中断比如还有串口中断你可以通过置1把T0中断设为高优先级确保它的响应更及时。5. 灵魂所在STC15自动重装载的硬件机制详解理解了上面那些寄存器我们再深入一层看看STC15单片机是怎么用硬件魔术实现“自动重装载”的。这是很多教程语焉不详的地方但搞懂了你就能彻底玩转这个模式。关键就在于那对隐藏的重装载寄存器RL_TH0和RL_TL0。它们和TH0、TL0共用物理地址但单片机在不同的时间点会把你的读写操作导向不同的寄存器非常聪明。我画个简单的逻辑图用文字描述你就明白了初始化阶段TR00定时器停止时你执行TH0 0xD4;和TL0 0xCD;。此时数据不仅写入了可见的计数寄存器TH0/TL0同时也被硬件自动拷贝到了隐藏的重装载寄存器RL_TH0/RL_TL0里。相当于你把预设值放进了“储水箱”。运行阶段TR01定时器运行时计数器[TH0, TL0]开始随着时钟脉冲递增。此时如果你试图再去写TH0或TL0数据将不会进入正在运行的计数寄存器而是会写入到隐藏的RL_TH0/RL_TL0中这意味着你可以在定时器运行中动态修改下一次重装载的值而不会影响当前正在进行的计数周期。这个特性非常有用。溢出重装载瞬间当[TH0, TL0]从0xFFFF加到0x0000溢出时硬件除了置位TF0还会自动将[RL_TH0, RL_TL0]中的值整体装载到[TH0, TL0]中。于是计数器不是从0开始下一轮计数而是直接从你预设的初始值比如0xD4CD开始。整个过程无需一条指令参与由硬件一气呵成。这种设计的好处是巨大的它保证了定时周期的绝对精确没有因为软件重装值带来的任何时间抖动同时大大简化了中断服务程序你只需要在中断里处理你的业务逻辑比如翻转一个LED灯完全不用操心重装初值的事情。6. 手把手代码实战从1ms定时到动态修改周期理论说了一箩筐不上代码都是纸上谈兵。下面我给出一个完整、可编译、可下载到STC15单片机里运行的实例并且会演示两个高级技巧精准定时和运行中修改定时周期。#include STC15F2K60S2.h // 根据你的具体型号包含头文件 #include intrins.h #define FOSC 11059200UL // 定义系统时钟频率11.0592MHz #define T1MS (65536UL - (FOSC/1000)) // 1T模式下1ms需要的初始值计算 sbit LED P1^0; // 假设LED接在P1.0 /* 定时器0初始化函数 */ void Timer0_Init(void) { // 1. 设置计数速率1T模式 (AUXR.7 1) AUXR | 0x80; // 高速模式 // AUXR 0x7F; // 如果要用12T模式就用这行 // 2. 设置工作模式16位自动重装载 (TMOD低4位为0000) TMOD 0xF0; // 高4位是T1的我们清零低4位不影响T1 TMOD | 0x00; // 明确设置为模式0也可直接写 TMOD 0x00; // 3. 初始化定时器重装载值此时TR00会同时写入计数器和重装载寄存器 TL0 T1MS; // 低8位 TH0 T1MS 8; // 高8位 // 4. 清除溢出标志打开定时器开关 TF0 0; // 软件清除溢出标志虽然初始化时通常是0 TR0 1; // 启动定时器0 // 5. 使能中断 ET0 1; // 允许定时器0中断 EA 1; // 打开CPU总中断 } /* 主函数 */ void main() { Timer0_Init(); // 初始化定时器 LED 1; // 初始状态假设LED高电平熄灭 while(1) { // 主循环可以干其他事情定时任务完全由中断处理 // 这里演示一个需求5秒后让LED的闪烁速度加快一倍周期从1ms*1000变为0.5ms*1000 // 通常我们会用另一个变量在中断里计数这里为了简化假设有个延时 _nop_(); } } /* 定时器0中断服务程序 */ void timer0_isr(void) interrupt 1 // 中断号1对应定时器0 { static unsigned int count 0; // 静态变量用于累计ms数 // 注意在自动重装载模式下完全不需要在此重装TH0/TL0 // 硬件已经自动完成了重装载。 count; if(count 1000) // 每1000ms1秒执行一次 { count 0; LED !LED; // 每秒翻转一次LED实现1Hz闪烁 } }代码精讲T1MS的宏定义是关键。65536 - (FOSC/1000)计算的是1ms对应的计数值。因为FOSC是11059200除以1000得11059.2取整11059。65536-1105954477(0xD4CD)。初始化时TR00所以写TH0/TL0会同时设置计数初值和重装载值。中断函数interrupt 1是KEIL C51的语法表示这是定时器0的中断服务程序。中断里用一个静态变量count来累加ms数实现秒级定时。这是单片机编程的常用技巧。高级技巧运行中动态修改定时周期假设程序运行中你想通过某个按键把LED闪烁频率加快一倍即定时中断周期从1ms改为0.5ms。你不需要重启定时器只需要修改重装载值。因为定时器正在运行TR01此时写TH0/TL0实际修改的是隐藏的RL_TH0/RL_TL0会在当前计数周期结束后下一次溢出时立即生效。// 假设这个函数在按键按下时被调用 void Change_Timer_Period(void) { unsigned int new_value; // 计算0.5ms对应的重装载值 (1T模式) new_value 65536UL - (FOSC/2000); // FOSC/2000 5529.6 ≈ 5530 // 定时器正在运行直接写入TH0/TL0即可实际写入重装载寄存器 TL0 new_value; // 写入新的低8位重装载值 TH0 new_value 8; // 写入新的高8位重装载值 // 注意当前正在进行的1ms定时周期会完整执行完毕然后下一个周期开始就是0.5ms了。 }7. 避坑指南与进阶应用思考用了这么多年我也踩过不少坑这里分享几个关键点能帮你省下大量调试时间。坑1计算溢出值时的数据类型陷阱注意我代码里用的65536UL和FOSC/1000。UL表示无符号长整型。如果不用UL65536对于16位int来说已经溢出了因为int最大32767计算会出错。务必在常量后面加UL或者把变量定义为unsigned long。坑212T模式与1T模式下的计算混淆这是新手最容易出错的地方上面的计算T1MS 65536 - (FOSC/1000)只适用于1T模式。如果你设置的是12T模式AUXR 0x7F那么公式应该是T1MS_12T 65536 - (FOSC/12/1000)。 因为12T模式下计数器每12个系统时钟才加1所以实际计数频率是FOSC/12。很多朋友直接套用1T的公式发现定时时间差了12倍问题就出在这儿。坑3中断服务程序执行时间过长自动重装载保证了定时周期的绝对精准但前提是你的中断服务程序执行时间不能超过定时周期比如你定时1ms中断一次但中断函数里的代码执行完需要2ms那就会导致中断嵌套或丢失系统逻辑全乱。中断函数里只做最必要、最简短的操作比如置标志位、清缓冲区等。复杂的运算和流程控制应该放到主循环里根据标志位去处理。进阶思考这个模式还能玩出什么花高精度PWM生成结合IO口翻转可以在中断里产生非常稳定的PWM信号适用于对频率稳定性要求高但对分辨率要求不极致的场合。软件串口波特率发生器当硬件串口不够用时可以用定时器的自动重装载模式产生精准的位定时来实现软件模拟串口Software UART其波特率稳定性远超用循环延时实现的软串口。频率测量与脉冲计数将模式设置为计数模式C/T1利用自动重装载和中断可以非常方便地实现对外部脉冲的累计计数或者通过计算固定时间内的脉冲数来测量频率。最后我想说STC15这个16位自动重装载模式是我认为其定时器体系中最实用、最该优先掌握的模式。它把开发者从繁琐的初值重装中解放出来让定时就像呼吸一样自然。刚开始接触时多花点时间把那几个寄存器关系和硬件机制理顺后面你就会发现很多复杂的定时任务用这个模式来实现代码会变得异常清爽和可靠。好了今天的分享就到这儿希望能帮你打通任督二脉。如果在实际项目中遇到具体问题欢迎随时来交流讨论。