STM32-通用定时器-定时器中断

article/2025/10/10 15:01:19

1 STM32的定时器

        STM32F103ZET6一共有8个定时器,其中分别为:高级定时器(TIM1、TIM8);通用定时器(TIM2、TIM3、TIM4、TIM5);基本定时器(TIM6、TIM7)
        他们之间的区别情况见下表:

定时器种类

位数

计数器模式

产生DMA请求

捕获/比较通道

互补输出

特殊应用场景

高级定时器

TIM1,TIM8)

16

向上,向下,向上/

可以

4

带死区控制盒紧急刹车,可应用于PWM电机控制

通用定时器(TIM2~TIM5

16

向上,向下,向上/

可以

4

通用。定时计数,PWM输出,输入捕获,输出比较

基本定时器

(TIM6,TIM7)

16

向上,向下,向上/

可以

0

主要应用于驱动DAC

2 STM32通用定时器

2.1 通用定时器功能特点描述

        STM32的通用定时器是由一个可编程预分频器(PSC)驱动的16位自动重装载计数器(CNT)构成,可用于测量输入脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。
        STM32 的通用TIMx(TIM2、TIM3、TIM4 和 TIM5)定时器功能特点包括:
                1)位于低速的APB1总线上(注意:高级定时器是在高速的APB2总线上);
                2)16位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数器(TIMx_CNT);
                3)16位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数 为 1~65535 之间的任意数值;
                4)4个独立通道(TIMx_CH1~4,这些通道可以用来作为:
                        ① 输入捕获
                        ② 输出比较
                        ③ PWM生成(边缘或中间对齐模式)
                        ④ 单脉冲模式输出 

                5)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。
                6)如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器): 
                        ① 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) 
                        ② 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
                        ③ 输入捕获 
                        ④ 输出比较 

                        ⑤ 支持针对定位的增量(正交)编码器和霍尔传感器电路 
                        ⑥ 触发输入作为外部时钟或者按周期的电流管理
        STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。   
        使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。

2.2 计数器模式

        通用定时器可以向上计数、向下计数、向上向下双向计数模式
                1)向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
                2)向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
                3)中央对齐模式(向上/向下计数)计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
        简单地理解三种计数模式,可以通过下面的图形:

3 通用定时器工作流程

         对于这个定时器框图,分成四部分来讲:最顶上的一部分(计数时钟的选择)、中间部分(时基单元)、左下部分(输入捕获)、右下部分(PWM输出)。这里主要介绍一下前两个,后两者的内容会在后面的文章中讲解到。

3.1 计数时钟的选择

        计数器时钟可由下列时钟源提供:

                1)内部时钟(TIMx_CLK) 
                2)外部时钟模式1:外部捕捉比较引脚(TIx)
                3)外部时钟模式2:外部引脚输入(TIMx_ETR)
                4)内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,
如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

3.1.1 内部时钟源(CK_INT)

        如果禁止了从模式控制器(TIMx_SMCR寄存器的SMS=000),则CEN、DIR(TIMx_CR1寄存器)和UG位(TIMx_EGR寄存器)是事实上的控制位,并且只能被软件修改(UG位仍被自动清除)。只要CEN位被写成’1’,预分频器的时钟就由内部时钟CK_INT提供。 下图显示了控制电路和向上计数器在一般模式下,不带预分频器时的操作。

3.1.2 外部时钟源模式1

        当TIMx_SMCR寄存器的SMS=111时,此模式被选中。计数器可以在选定输入端的每个上升沿或下降沿计数。

        例如,要配置向上计数器在T12输入端的上升沿计数,使用下列步骤:
                1)配置TIMx_CCMR1寄存器CC2S=’01’,配置通道2检测TI2输入的上升沿 
                2)配置TIMx_CCMR1寄存器的IC2F[3:0],选择输入滤波器带宽(如果不需要滤波器,保持IC2F=0000) 

                注: 捕获预分频器不用作触发,所以不需要对它进行配置 
                3)配置TIMx_CCER寄存器的CC2P=’0’,选定上升沿极性 
                4)配置TIMx_SMCR寄存器的SMS=’111’,选择定时器外部时钟模式1 
                5)配置TIMx_SMCR寄存器中的TS=’110’,选定TI2作为触发输入源 
                6)设置TIMx_CR1寄存器的CEN=’1’,启动计数器 

        当上升沿出现在TI2,计数器计数一次,且TIF标志被设置。在TI2的上升沿和计数器实际时钟之间的延时,取决于在TI2输入端的重新同步电路。

3.1.3 外部时钟源模式2

        选定此模式的方法为:令TIMx_SMCR寄存器中的ECE=1,计数器能够在外部触发ETR的每一个上升沿或下降沿计数。 下图是外部触发输入的框图:

        例如,要配置在ETR下每2个上升沿计数一次的向上计数器,使用下列步骤: 
                1)本例中不需要滤波器,置TIMx_SMCR寄存器中的ETF[3:0]=0000 
                2)设置预分频器,置TIMx_SMCR寄存器中的ETPS[1:0]=01 
                3)设置在ETR的上升沿检测,置TIMx_SMCR寄存器中的ETP=0 
                4)开启外部时钟模式2,置TIMx_SMCR寄存器中的ECE=1 
                5)启动计数器,置TIMx

        计数器在每2个ETR上升沿计数一次。 在ETR的上升沿和计数器实际时钟之间的延时取决于在ETRP信号端的重新同步电路。

        通用定时器的时钟源可以通过TIMx_SMCR的低3位来进行配置。默认是000内部时钟进行驱动

3.2 内部时钟源

         从图中可以看出:由AHB时钟经过APB1预分频系数转至APB1时钟,再通过某个规定转至TIMxCLK时钟(即内部时钟CK_INT、CK_PSC)。最终经过PSC预分频系数转至CK_CNT。首先我们我们的系统时钟(SYSCLK=72MHz)经过AHB分频器给APB1外设,但是APB1外设最大的只能到36Mhz,所以必须要系统时钟的二分频。下面又规定了如果APB1预分频系数为1则频率不变,否则频率X2至定时器2~7,所以定时器2~7的时钟频率还是72MHz。
        那么APB1时钟怎么转至TIMxCLK时钟呢?除非APB1的分频系数是1,否则通用定时器的时钟等于APB1时钟的2倍。
        例如:默认调用SystemInit函数情况下:SYSCLK=72M、AHB时钟=72M、APB1时钟=36M,所以APB1的分频系数=AHB/APB1时钟=2。所以,通用定时器时钟CK_INT=2*36M=72M。最终经过PSC预分频系数转至CK_CNT。

3.3 时基单元

        时基单元包含:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动装载寄存器(TIMx_ARR)三部分。
        对不同的预分频系数,计数器的时序图为:

3.4 计数模式

        此时,再来结合时钟的时序图和时基单元,分析一下各个计数模式:

        向上计数模式(时钟分频因子=1)

        向下计数模式(时钟分频因子=1)

        中央对齐模式(时钟分频因子=1  ARR=6)

4 通用定时器相关配置寄存器

4.1 计数器当前值寄存器(TIMx_CNT)

         作用:存放计数器的当前值。

4.2 预分频寄存器(TIMx_PSC)

        作用:对CK_PSC进行预分频。此时需要注意:CK_CNT计算的时候,预分频系数要+1。

4.3 自动重装载寄存器(TIMx_ARR)

        作用:包含将要被传送至实际的自动重装载寄存器的数值。

        注意:该寄存器在物理上实际上对应着2个寄存器。一个是我们直接操作的,另一个是我们看不到的,这个看不到的寄存器叫做影子寄存器。实际上真正起作用的是影子寄存器。根据TIMx_CR1位的APRE位的设置,APRE=0时,预装载寄存器的内容就可以随时传送到影子寄存器,此时两者是互通的;APRE=1时,在每一次更新事件时,才将预装在寄存器的内容传送至影子寄存器。

4.4 控制寄存器(TIMx_CR1)

        作用:对计数器的计数方式、使能位等进行设置。

        这里有ARPE位:自动重装载预装载允许位。ARPE=0时,TIMx_ARR寄存器没有缓冲;ARPE=1时,TIMx_ARR寄存器被装入缓冲器。

 4.5 DMA/中断使能寄存器(TIMx_DIER)

        作用:对DMA/中断使能进行配置。

5 通用定时器超时时间

        初始化定时器的时候指定我们分频系数psc,这里是将我们的系统时钟(72MHz)进行分频。然后指定重装载值arr,这个重装载值的意思就是当我们的定时器的计数值达到这个arr时,定时器就会重新装载其他值。例如当我们设置定时器为向上计数时,定时器计数的值等于arr之后就会被清0重新计数。定时器计数的值被重装载一次被就是一个更新(Update)
        超出(溢出)时间计算:Tout=(ARR+1)(PSC+1)/TIMxCLK
                其中:Tout的单位为us,TIMxCLK是定时器时钟源,在这里就是72Mhz。我们将分配的时钟进行分频,指定分频值为psc,就将我们的TIMxCLK分了psc+1,我们定时器的最终频率就是TIMxCLK/(psc+1) MHz。这里的频率的意思就是1s中记 TIMxCLK/(psc+1) M个数 (1M=10的6次方) ,每记一个数的时间为(psc+1)/Tclk ,很好理解频率的倒数是周期,这里每一个数的周期就是(psc+1)/TIMxCLK 秒。然后我们从0记到arr 就是 (arr+1)*(psc+1)/TIMxCLK
        这里需要注意的是:PSC预分频系数需要加1,同时自动重加载值也需要加1。
                1)为什么自动重加载值需要加1,因为从ARR到0之间的数字是ARR+1个;
                2)为什么预分频系数需要加1,因为为了避免预分频系数不设置的时候取0的情况,使之从1开始。

        这里需要和之前的预分频进行区分:由于通用定时器的预分频系数为1~65535之间的任意数值,为了从1开始,所以当预分频系数寄存器为0的时候,代表的预分频系数为1。而之前的那些预分频系数都是固定的几个值,比如1、4、8、16、32、64等等,而且可能0x000代表1,0x001代表4,0x010代表8等等。也就是说,一边是随意的定义(要从1开始),另一边是宏定义了某些值(只有特定的一些值)。
        比如,想要设置超出时间为1s,并配置中断,我们设置arr=7199,psc=9999。我们将72MHz (1M等于10的6次方) 分成了(9999+1)等于 7200Hz,就是一秒钟记录9000数,每记录一个数就是1/7200秒。我们这里记录9000个数进入定时器更新(7199+1)*(1/7200)=1s,也就是1s进入一次更新Update。

6 通用定时器相关配置库函数

6.1 1个初始化函数

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//定时器参数初始化
typedef struct
{uint16_t TIM_Prescaler;//预分频系数的设置      uint16_t TIM_CounterMode;//计数模式   uint16_t TIM_Period;//自动装载值uint16_t TIM_ClockDivision;//输入捕获会用到 uint8_t TIM_RepetitionCounter;//高级定时器会用到
} TIM_TimeBaseInitTypeDef; 

        作用:用于对预分频系数、计数方式、自动重装载计数值、时钟分频因子等参数的设置。

6.2 2个使能函数

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);//定时器使能函数
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//定时器中断使能函数

        作用:前者使能定时器,后者使能定时器中断。 

6.3 4个状态标志位获取函数

FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

        作用:前两者获取(或清除)状态标志位,后两者为获取(或清除)中断状态标志位。

7 定时器中断的一般步骤

        实例要求:通过TIM3的中断来控制DS1的亮灭,DS1是直接连接在PE5上的。

                1)使能定时器时钟。调用函数:RCC_APB1PeriphClockCmd();
                2)初始化定时器,配置ARR、PSC。调用函数:TIM_TimeBaseInit();
                3)开启定时器中断,配置NVIC。调用函数:void TIM_ITConfig();NVIC_Init();
                4)使能定时器。调用函数:TIM_Cmd();
                5)编写中断服务函数。调用函数:TIMx_IRQHandler()。

        下面按照这个一般步骤来进行一个简单的定时器中断程序:

//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //TIM3时钟使能//定时器TIM3初始化,简单进行定时器初始化,设置 预装载值 和 分频系数TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断//中断优先级NVIC设置NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器TIM_Cmd(TIM3, ENABLE);  //使能TIMx					 
}
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否{TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 LED1=!LED1;   //状态取反}
}
 int main(void){		delay_init();	    	 //延时函数初始化	  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级LED_Init();			     //LED端口初始化TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms  while(1){LED0=!LED0;delay_ms(200);		   }	 }	 

        定时器中断的程序和串口中断的程序非常类似,可以将两者结合起来进行比对着学习STM32串口通信 。
        同时强调一下,在中断处理函数内,需要判断中断来源和及时清除中断标志位。

        问题:各种教程中,都只解释中断的机制、使用。但对于中断标志的清理顺序,没多少官方准确的资料。今天在F429的代码里,又遇到问题:进中断后卡死跳不出来,各种排查没发现问题。快要出门时,才突然想起来,要把清理中断的语句,从最后一行,移到第一行。重新编译烧录,马上通过。真是奇怪的问题。
       例1:后清理,卡死:

void TIM6_DAC_IRQHandler()
{LED_BLUE_TOGGLE ;       // 反转LEDTIM6->SR &= ~(0x01);    // 清理中断标志     
}

        中断服务函数如下:上面的两行示例代码,死活没法子看出有啥毛病,但程序卡死。几乎耗了一个上午排查周边代码。
        修改成如下顺序,先清理中断标志,马上顺利通过。

void TIM6_DAC_IRQHandler()
{TIM6->SR &= ~(0x01);         // 重点,重点,重点,必须先清中断后处理其它事情,否则卡死LED_BLUE_TOGGLE ;            // 反转LED
}

        例2:先清理后清理,没事:

        问题:必须是先清中断标志吗?不是的。发现有很多中断函数不是必须先清理。
        测试环境:F429IG + 外部中断线中断(项目中是做按键的)

void EXTI15_10_IRQHandler(void)
{// EXTI->PR |= KEY_2_PIN ;      // 位置 1LED_BLUE_TOGGLE ;    EXTI->PR |= KEY_2_PIN ;         // 位置 2              
}

        结论:在进入中断后首先要清除中断标志。 

8 中断子程序中不能使用延时和过长的程序

        1)通常在中断子程序中是不调用延时子程序的,这样会增加中断处理时间,如果有其它低级中断了,就会延误响应中断了。所以,中断子程序中不要写调用延时子程序,中断子程序也不要写得过长,处理过多的任务,要尽快处理后及时返回,如果中断一次有很多任务需要执行完全,可以在中断子程序中设置一个标志位,在主程序中查这个标志位,当标志为1时,就在主程序中完成这些任务,这样就不会影响其它中断源的中断,也不会使中断产生混乱。
        2)首先,对于CPU频率的理解,1Mhz的频率CPU周期就是1us(1 / 1000000秒)。既然1Mhz对应1us(也就是1us对应一个指令周期,不考虑流水线的单指令周期),这样,一个指令周期就对应一条指令。假设每条指令都有2个字节大小(16位指令),这样,1ms时间内1Mhz的CPU可以大约运行2KB的代码。因此如果中断处理函数的代码越接近2KB,则越容易产生中断。假设一个10KHz外设,中断处理程序允许的最大安全尺寸是多少?以Cortex M3为例,支持16/32位指令操作(大部分为单周期指令),假设主频为72Mhz,100KHz相当于100us 。则如果是16位指令(前面算过1ms 2KB代码),72 * 100 * 2到72 * 100 * 4之间(14.4~28.8),取最小14.4KB。

9 STM32 定时器有时一开启就进中断

        在使用STM32定时器的更新中断时,发现有些情形下只要开启定时器就立即进入一次中断。准确说,只要使能更新中断允许位就立即响应一次更新中断(当然前提是相关NVIC也已经配置好)。换言之,只要使能了相关定时器更新中断,不管你定时间隔多长甚至不在乎你是否启动了相关定时器,它都会立即进入一次定时器更新中断服务程序。
        以STM32F051芯片为例,做了几种不同顺序的组合测试。根据测试发现,的确有些情况下一运行TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); (即使能更新中断)就立即进入更新中断服务程序。当然后面的中断都是正常的。
        老实说,这个问题比较容易忽视,有些情况下也无关紧要,但有些情况可能会给应用带来困扰。从ST MCU相关技术手册似乎并不能明显地找到关于这个问题的很合适或者逻辑性很强的前因后果。经过验证测试,如果注意一下相关指令代码顺序是可以回避这个问题的。

        先做更新中断标志的清除操作,即清除TIMx->SR寄存器里的UIF标志,然后做定时器更新中断的使能操作。至于开启相关定时器的指令摆放位置并不严格。下面是相关动作的操作顺序及结果,可以参考、验证之。这里共罗列了6种写法,其中有3种情形是会立即进入中断的,另外3种不会。

/*(1)不会立即进入更新中断程序。*/
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除更新中断请求位
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);  //使能定时器1更新中断
TIM_Cmd(TIM1, ENABLE);                      //启动定时器/*(2)不会立即进入更新中断程序。*/
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除更新中断请求位
TIM_Cmd(TIM1, ENABLE);                      //启动定时器
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);  //使能定时器1更新中断/*(3)不会立即进入更新中断程序。*/ 
TIM_Cmd(TIM1, ENABLE);                      //启动定时器
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除更新中断请求位
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);  //使能定时器1更新中断/*(4)立即进入更新中断程序。*/
TIM_Cmd(TIM1, ENABLE);                      //启动定时器
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);  //使能定时器1更新中断
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除更新中断请求位/*(5)立即进入更新中断程序。*/
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);  //使能定时器1更新中断
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除更新中断请求位
TIM_Cmd(TIM1, ENABLE);                      //启动定时器/*(6)立即进入更新中断程序。*/ 
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);  //使能定时器1更新中断
TIM_Cmd(TIM1, ENABLE);                      //启动定时器
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除更新中断请求位

        顺便提下关于定时器里UG位URS位的使用,分别在TIMx->EGRTIMx->CR1寄存器里。对UG位置1可以产生更新事件并对相关计数器和寄存器重新初始化,如果URS位为0的话,同时会产生更新中断。如果不希望对UG位置1的同时产生更新中断,得置URS位为1,否则会立即进入更新中断。


http://chatgpt.dhexx.cn/article/DV2b1R7v.shtml

相关文章

Python——定时器

1.定时器 Timer定时器源码实现,和自定义一个线程方式一样,都是继承Thread类,重写了run()方法,只是实现的功能是延时执行一个函数或方法。 (1)线程定时器(Timer)解释: Timer类是Thread的子类&a…

[JavaEE]定时器

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录: 1.定时器的概念 2.标准库中的定时器 3.实现定时…

定时器的作用

一、简介。 在很多时候,我们设计网页时,为了某种表现形式,会使用到定时器这一功能,如:为了保证用户有仔细阅读我们的用户条款,我们会给确认按钮设置只有条款被打开,并超过5秒才允许点击。 二、…

定时器简介

文章目录 一.定时器基本介绍A.CPU时序B.定时器的原理 二.定时/计数器的相关寄存器A.定时器工作方式寄存器(TMOD)B.控制寄存器(TCON) 三.定时器的四种工作方式图解 一.定时器基本介绍 A.CPU时序 振荡周期:CPU外部晶振…

定时器基本常识

1.概念解读 1.1定时器和计数器,电路一样 1.2定时或者计数的本质就是让单片机某个部件数数 1.3当定时器用的时候,靠内部震荡电路数数 1.4当计数器用的时候,书外面的信号,读取针脚的数据 2.定时器怎么定时 定时器的本质原理&a…

定时器详解

1. 什么是定时器(timer) 定时器实际上就是Soc当中的一个内部外设。 (1)定时器与计数器 定时器常与计数器扯到一起,计数器也是soc当中的一个内部外设,计数器顾名思义是用来计数的,就和我们的秒…

定时器(Timer)

一、定时器是什么? 定时器类似于我们生活中的闹钟,可以设定一个时间来提醒我们。 而定时器是指定一个时间去执行一个任务,让程序去代替人工准时操作。 标准库中的定时器: Timer 方法作用void schedule(TimerTask task, long delay)指定dela…

STM32-定时器详解

目录 前言 一、定时器基本介绍 1. STM32定时器 2. 通用定时器功能和特点 3. 计数器模式 4. 定时器工作原理 a.定时器框图 b.时钟产生器部分 c.时基单元 d.输入捕获通道 e.输出比较通道(PWM) 二、定时器中断应用 1.内部时钟选择 2.计数器模式 …

typedef和#define

typedef是c语言中一个重要的关键字其作用是为一种数据类型定义了一个新的名字这里的类型包括(int,char,double 等)和自定义数据类型,通俗一点来说就是为一种数据类型起一个别名 举个例子: 定义一个整型变量a并将其初始化为666&a…

typedef和define的区别、typedef的具体用法

typedef最核心的用法:给数据类型取别名,这个别名既可以是此数据类型的替换,也是指向此数据类型的指针。 具体用法(对普通数据类型取别名): 对结构体数据类型取别名: typedef与define的区别&…

#define与typedef的区别

目录 (1)原理不同 (2)功能不同 (3)作用域不同 (4)对指针的操作不同 (5)补充 a.指针常量 b.常量指针 typedef和define都是替一个对象取一个别名&#x…

C语言中的typedef

C语言中的"typedef" 一、什么是typedef typedef是用于定义新的类型名,在编程中可以用typedef来定义新的类型名来代替已有的类型名 格式: typedef 已有类型名 新的类型名 通俗点说,就是为已有的类型取别名,例如 老鼠&am…

define 与typedef的区别

define 与typedef大体功能都是使用时给一个对象取一个别名,增强程序的可读性,但它们在使用时有以下几点区别: 1.定义不一样 define定义后面不用加分号,并且它的别名在对象的前面 typedef需要加分号,并且它的别后面替…

C语言学习笔记---typedef 简介

在单片机和操作系统中 typedef 会经常用到,它可以为某一个类型自定义名称。和#define比较类似。但是又有不同的地方。 typedef 创建的符号只能用于数据类型,不能用于值。而#define 创建的符号可以用于值。typedef 是由编译器来解释,而不是预…

typedef介绍

[20210330更新]:这篇博客写的时间有点久了:)。本次更新修改了博客内容中的错误和表述不当的地方。 本文介绍C语言中的关键字 typedef 的用法。 1 概述 typedef 为C语言的关键字,作用是为一种数据类型定义一个新名字,这里的数据类型包括内部…

【C语言】typedef的使用

目录 一、什么是typedef 二、typedef用法 1、对于数据类型使用例如: 2、对于指针的使用例如 3、对于结构体的使用 三、进阶typedef 1、数组指针 2、指针函数 3、Int *(*array[3])(int); 4、Void (*funA(int,void(*funB)(int)))(int); 四、Typedef与defin…

ubuntu etc 设置权限777带来的问题

ubuntu etc 设置权限777带来的问题--sudoers权限错误 在一次工作中,在ubutu通过命令行输入 sudo chmod -R 777 /etc 命令,误将etc目录权限更改为了777(rwxrwxrwx) 导致服务器无法ssh远程连接,而且sudo命令无法使用 提…

Linux修改文件权限为777

将文件权限改为777的命令为chmod 777 文件名 777说明: 你可以在linux终端先输入ll,可以看到如: -rwx-r--r-- (一共10个参数) 第一个跟参数跟chmod无关,先不管. 2-4参数:属于user 5-7参数:属于group 8-10参数:属于others 接下来就简单了:r>可读 w>可写 x&…

文件权限777

一个文件的权限为777(linux中 ls -l xxx.xxx (xxx.xxx是文件名,查看的是xxx文件之中的文件权限)) 这三个数字分别表示:不同用户或用户组的权限。 第一个数字表示文件所有者的权限 第二个数字 表示与文件所有者同属一个用户组的其他用户的权限 第三个数…

Linux-权限管理(你听过777、755、644吗)

文章目录 组rwx权限修改权限-chmod修改文件所有者-chown修改文件所在组-chgrp 组 linux 中每个文件有所有者、所在组、其它组的概念。 类似linux 中的每个用户必须属于一个组,不能独立于组外,组的相关操作可参考:Linux-用户管理 所有者 文件…