xiaoxia9623
发表于 2025-2-15 21:06:22
找到了,在 STC 系列单片机中实现自适应时钟的 1 毫秒延时函数,其核心思路是根据单片机的时钟频率来调整定时器的配置,以确保延时时间准确为 1 毫秒。以下为你详细介绍实现步骤并给出示例代码。
实现步骤
确定时钟频率:需要明确 STC 单片机所使用的时钟频率,该频率会影响定时器的定时时间。
选择定时器:通常会选用定时器 0 或者定时器 1 来实现延时功能。
计算定时器初值:依据时钟频率和定时器的工作模式,计算出定时器需要加载的初值,从而实现 1 毫秒的延时。
初始化定时器:对定时器的工作模式、初值等参数进行设置。
启动定时器并等待溢出:启动定时器,等待定时器溢出,以此达到延时的目的。
// 定义系统时钟频率(单位:Hz),这里假设为11.0592MHz
#define SYSCLK 11059200
// 自适应时钟的1毫秒延时函数
void delay_1ms() {
// 定时器模式寄存器,设置定时器0为模式1(16位定时器)
TMOD &= 0xF0;// 清除定时器0的模式位
TMOD |= 0x01;// 设置定时器0为模式1
// 计算定时器初值
// 定时器计数一次的时间 = 1 / 时钟频率 * 12(12T模式)
// 1毫秒需要计数的次数 = 1ms / 定时器计数一次的时间
// 定时器初值 = 65536 - 1毫秒需要计数的次数
unsigned int timer_initial_value = 65536 - (SYSCLK / 12000);
// 设置定时器0的初值
TH0 = (unsigned char)(timer_initial_value >> 8);
TL0 = (unsigned char)timer_initial_value;
// 启动定时器0
TR0 = 1;
// 等待定时器溢出
while (!TF0);
// 清除定时器溢出标志
TF0 = 0;
// 停止定时器0
TR0 = 0;
}
xiaoxia9623
发表于 2025-2-16 17:35:34
第八集讲的是蜂鸣器,首先讲了蜂鸣器的分类,然后讲到了原理图,再就是结合生活中的产品应用 ,讲到了应用 的场景,这样更能够加深理解,
void Test(void)
{
if( KEY1 == 0)
{
delay_ms(10);
if(KEY1 == 0 )
{
while(KEY1 == 0 );
if( Run_Flag==0 )
{
Run_Flag = 1;
BEEP = 0;
delay_ms(10);
BEEP = 1;
P40 = 0;
P6 = 0X00;
delay_ms(200);
P6 = 0XFF;
}
else
{
Run_Flag = 0;
BEEP = 0;
delay_ms(10);
BEEP = 1;
P6 = 0XFF;
Run_Mode = 0;
}
}
}
if( KEY2 == 0 )
{
delay_ms( 10 );
if( KEY2 == 0 )
{
while( KEY2 == 0);
BEEP = 0;
delay_ms(10);
BEEP = 1;
Run_Mode ++;
if( Run_Mode>8 )
Run_Mode = 1;
//P6 = 0XFE; P6<<1+1
P6 = ~(1<< (Run_Mode-1)); //1<<1 0000 0001 -> 1111 1110
}
}
}
一步一步的跟着写下来,感觉不一样,印象能深一些。
xiaoxia9623
发表于 2025-2-16 17:41:27
void delay_ms(u16 ms)
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
}while(--ms);
}
其实这个延时函数我更喜欢这样写:
void delay_ms(u16 ms)
{
u16 i;
while(ms--)
{
i = MAIN_Fosc/6000;
while(--i);
}
}
xiaoxia9623
发表于 2025-2-16 17:47:06
xiaoxia9623 发表于 2025-2-16 17:41
void delay_ms(u16 ms)
{
u16 i;
或者是这样写:
void delay_ms(u16 ms)
{
u16 j,i;
for(j=0;j<ms;j++)
{
i = MAIN_Fosc/6000;
while(--i);
}
}
xiaoxia9623
发表于 2025-2-17 16:17:50
P70 = 0; //
P6 = SEG_Tab;
if( KEY1 ==0 )
{
delay_ms(10);
if( KEY1 ==0 )
{
BEEP = 0;
delay_ms(10);
BEEP = 1;
while( KEY1 ==0 );
if( num<9 )
{
num++;
}
}
}
if( KEY2 ==0 )
{
delay_ms(10);
if( KEY2 ==0 )
{
BEEP = 0;
delay_ms(10);
BEEP = 1;
while( KEY2 ==0 );
if( num>0 )
num--;
}
}
}
}
本课用共阳极数码管显示了0-9的数字,结合按键 实现0-9和9-0的伦流显示,P70等于0打开COM端,然后P6口赋值,显示NUM相对应的数值。一看就会,过两天就会忘掉。一看就会,一写就废。所以说这个东西得经常练习才行,要不然,放下了,也就忘掉了,什么也不会了,必须经常练习。
xiaoxia9623
发表于 2025-2-17 16:21:58
xiaoxia9623 发表于 2025-2-17 16:17
P70 = 0; //
这种单按键是最简单的按键,一般都是低电平有效,我现在是想学会 电容式感应按键,这种按键在产品中应用最广。最好能做成隔空感应,就更好了,不知道STC的电容式按键,能不能够实现隔空感应?
xiaoxia9623
发表于 2025-2-17 22:58:08
第十集介绍了动态数码管的显示,本图描述了一共4位数码管的轮流显示时——也就是需要延时等待的时间,时间不能低于1毫秒,不管总共有几位数码管,总共的一个轮流显示完毕的时间不能超过20毫秒。如果刷新太慢,也就是如果刷新率太低,频率低于50赫滋,人们会看着一闪一闪的不舒服。动态刷新多们显示跟静态显示的区别在于,需要采用一个数组,来选择打开哪一个位。段码显示内容,位码决定显示在哪一个位上面。位码的显示用到了嵌套数组,这样就可以不按加一的方式显示数组中其它顺序位的内容。{:4_165:}
xiaoxia9623
发表于 2025-2-17 23:02:32
xiaoxia9623 发表于 2025-2-17 22:58
第十集介绍了动态数码管的显示,本图描述了一共4位数码管的轮流显示时——也就是需要延时等待的时间,时间 ...
其实也可以用重新排列段码值的方式,来显示段码的不同的内容的,比如0XC0可以排在第一个位置,也可以排列在第N个位置上面。这样就不需要嵌套数组,但是会看着很乱,其实段码是一个常规的库,一般不建议重新排列的。
xiaoxia9623
发表于 2025-2-18 12:49:46
第十一集讲了定时器,一上来就讲了定时器中断和软件延时的区别。软件不管是delay还是while中的++,一直占用CPU,一但出现程序等待时,++就会停止,这样以来,时间就会增加,就不能准确的定时了,所以这种情况下应该使用定时器,定时器是硬件定时,不受程序等待的影响,定时准确一些,减少了定时的误差避免了定时时间的错误。
xiaoxia9623
发表于 2025-2-18 13:00:09
定时器的应用有的时候是必须的,普通的软件延时是无法完成的,比如让 STC8H 1K 08 定时器 0 在 72 小时的时间后中断,若在这 72小时之内按一下一个键P32,它就取消,从 0 开始。如果不使用定时器,用软件延时的方法,那么程序就什么也不用干了,啥也干不了了呢。
#include <STC8H.H>
sbit Key = P3^2;// 定义按键引脚
unsigned long timer0_count = 0;// 定时器0计数变量
bit timer0_running = 0;// 定时器0运行标志
// 定时器0初始化函数
void Timer0_Init() {
TMOD &= 0xF0;// 清除定时器0模式位
TMOD |= 0x01;// 设置定时器0为模式1(16位定时器)
TL0 = 0x00;// 初值低8位
TH0 = 0x00;// 初值高8位
TF0 = 0;// 清除定时器0溢出标志
ET0 = 1;// 使能定时器0中断
TR0 = 0;// 停止定时器0
timer0_count = 0;
timer0_running = 0;
}
// 中断服务函数
void Timer0_ISR(void) interrupt 1 {
TL0 = 0x00;// 重装初值低8位
TH0 = 0x00;// 重装初值高8位
timer0_count++;
if (timer0_count >= 72 * 3600 * 1000) {// 72小时(假设1ms中断一次)
TR0 = 0;// 停止定时器0
timer0_running = 0;
// 在这里添加72小时后中断的处理代码
}
}
// 主函数
void main() {
Timer0_Init();// 初始化定时器0
EA = 1;// 使能总中断
while (1) {
if (Key == 0) {// 检测按键是否按下
while (Key == 0);// 等待按键释放
if (timer0_running) {
TR0 = 0;// 停止定时器0
timer0_count = 0;// 清零计数
TR0 = 1;// 重新启动定时器0
} else {
TR0 = 1;// 启动定时器0
timer0_running = 1;
}
}
}
}
代码说明
1. 变量定义:
- Key 定义为按键连接的引脚 P3.2 。
- timer0_count 用于记录定时器0的计数值,以毫秒为单位计算时间。
- timer0_running 标志位用于表示定时器0是否正在运行。
2. 定时器0初始化函数 Timer0_Init :
- 设置定时器0为模式1(16位定时器)。
- 初始化定时器0的初值为0,清除溢出标志,使能定时器0中断,初始时停止定时器0。
3. 中断服务函数 Timer0_ISR :
- 每次定时器0溢出时,重新加载初值, timer0_count 加1。
- 当 timer0_count 达到72小时对应的毫秒数( 72 * 3600 * 1000 )时,停止定时器0并可在相应位置添加72小时后中断的处理代码。
4. 主函数 main :
- 初始化定时器0并使能总中断。
- 在主循环中,不断检测按键状态。当按键按下并释放后,如果定时器0正在运行,则停止定时器0,清零计数值,然后重新启动定时器0;如果定时器0未运行,则启动定时器0并设置运行标志。