TIM定时中断之定时器定时中断定时器外部时钟应用案例

news2024/11/14 18:12:51

文章目录

  • 前言
  • 一、定时器定时中断应用案例
    • 1.1 应用案例简介
    • 1.2 电路接线图
    • 1.3 应用案例代码
    • 1.4 应用案例分析
      • 1.4.1 初始化定时器
      • 1.4.2 编写定时器中断函数
  • 二、定时器外部时钟应用案例
    • 2.1 应用案例简介
    • 2.2 电路接线图
    • 2.3 应用案例代码
    • 2.4 应用案例分析


前言

提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者

本文主要探讨基于TIM定时中断实现定时器定时中断和定时器外部时钟的功能。


一、定时器定时中断应用案例

1.1 应用案例简介

本案例实现了一个定时器定时中断的功能。定时器使用内部时钟定了一个1秒的时间,每隔1秒申请一次中断,然后在中断函数里执行Num++,最后在OLED上显示Num。

1.2 电路接线图

在这里插入图片描述

1.3 应用案例代码

定时器头文件Timer.h:

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);

#endif

定时器实现文件Timer.c:

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

/*
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

主程序文件main.c:

 #include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;

int main(void)
{
	OLED_Init();
	Timer_Init();
	
	OLED_ShowString(1, 1, "Num:");
	
	while (1)
	{
		OLED_ShowNum(1, 5, Num, 5);
	}
}

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		Num ++;
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

1.4 应用案例分析

首先,初始化定时器。那怎么初始化定时器呢?我们可以看一下下面这个定时中断的整体框架图,我们只需要把这里的每个模块都打通就可以让定时器工作。

在这里插入图片描述
大体上的步骤如下:
第一步,RCC开启时钟。打开时钟后,定时器的基准时钟和整个外设的工作时钟就都会同时打开。
第二步,选择时基单元的时钟源。对于定时中断,我们就选择内部时钟源。
第三步,配置时基单元。包括这里的预分频器、自动重装器以及计数模式等等。
第四步,配置输出中断控制。允许更新中断输出到NVIC。
第五步,配置NVIC。在NVIC中打开定时器中断的通道,并分配一个优先级。
第六步,运行控制。整个模块配置完成后,我们还需要使能一下计数器,要不然计数器是不会运行的。当定时器使能后,计数器就会开始计数了,当计数器更新时,触发中断。

最后我们再写一个定时器的中断函数,这样,这个中断函数每隔一段时间就能自动执行一次了。

以上这些就是初始化定时器的大体思路了。

1.4.1 初始化定时器

先看一下定时器的库函数有哪些,找到stm32f10x_tim.h文件,拖到最后,可以看到这些库函数的数量还是非常多的,我们只挑选本案例需要用到的进行讲解。

void TIM_DeInit(TIM_TypeDef* TIMx);
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, uint16_t TIM_ICPolarity, uint16_t ICFilter);
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);

首先是第一个,TIM_DeInit,恢复缺省配置。

第二个,TIM_TimeBaseInit,时基单元初始化。这个函数比较重要,它就是用来配置时基单元的。第一个参数TIMx,选择某个定时器。第二个是结构体TIM_TimeBaseInitTypeDef,里面包含了配置时基单元的一些参数。

接着看第三个函数TIM_TimeBaseStructInit,这个函数可以把结构体变量赋一个默认值。

然后是第四个函数TIM_Cmd,这个是用来使能计数器的。对应的就是定时中断的整体框架图中的运行控制。第一个参数TIMx,选择定时器。第二个NewState,新的状态,也就是选择使能还是失能。使能,计数器就可以运行,失能,计数器就不运行。

然后第五个是函数TIM_ITConfig,这个是用来使能中断输出信号的。对应的就是定时中断的整体框架图中的中断输出控制。第一个参数TIMx,选择定时器。第二个参数TIM_IT,选择要配置哪个中断输出。第三个参数NewState,新的状态,使能还是失能。

接下来看一下TIM_InternalClockConfig、TIM_ITRxExternalClockConfig、TIM_TIxExternalClockConfig、TIM_ETRClockMode1Config、TIM_ETRClockMode2Config以及TIM_ETRConfig这六个函数,这六个函数对应的就是定时中断的整体框架图中的时基单元的时钟选择部分,可以选择RCC内部时钟、ETR外部时钟、ITRx其他定时器、TIx捕获通道这些。

  • TIM_InternalClockConfig函数,选择内部时钟。
  • TIM_ITRxExternalClockConfig函数,选择ITRx其他定时器的时钟。第一个参数TIMx,选择要配置的定时器。第二个参数TIM_InputTriggerSource,选择要接入哪个的其他定时器。
  • TIM_TIxExternalClockConfig函数,选择TIx捕获通道的时钟。第一个参数TIMx,选择要配置的定时器。第二个参数TIM_TIxExternalCLKSource,选择TIx具体的某个引脚。接下来还有TIM_ICPolarity和ICFilter,输入的极性和滤波器。对于外部引脚的波形,一般都会有极性选择和滤波器,这样更灵活一些。
  • TIM_ETRClockMode1Config函数,选择ETR通过外部时钟模式1输入的时钟。参数TIM_ExtTRGPrescaler,外部触发预分频器,这里可以对ETR的外部时钟再提前做一个分频。接下来参数TIM_ExtTRGPolarity和ExtTRGFilter,也是一样,极性和滤波器。
  • TIM_ETRClockMode2Config函数,选择ETR通过外部时钟模式2输入的时钟。它的参数和TIM_ETRClockMode1Config函数一摸一样,这里就不再介绍。对于ETR输入的外部时钟而言,这两个函数是等效的,它们的参数也是一样的,如果不需要触发输入的功能,那两个函数可以互换。
  • TIM_ETRConfig函数,这个不是用来选择时钟的,是用来单独配置ETR引脚的预分频器、极性、滤波器这些参数的。

到这里,关键部分的函数就讲完了。时钟源选择用TIM_InternalClockConfig、TIM_ITRxExternalClockConfig、TIM_TIxExternalClockConfig、TIM_ETRClockMode1Config、TIM_ETRClockMode2Config以及TIM_ETRConfig这六个函数。时基单元,用TIM_TimeBaseInit函数。中断输出控制,用TIM_ITConfig函数。运行控制,用TIM_Cmd函数。这样初始化就基本ok了。

接下来,我们再看几个函数。因为在TIM_TimeBaseInit函数里在初始化结构体里有很多关键的参数,比如自动重装值和预分频值等等,这些参数可能会在初始化之后还需要更改,如果为了改某个参数还要再调一次初始化函数,这样做太麻烦了。所以这里有一些单独的函数可以方便地更改这些关键参数。

比如,TIM_PrescalerConfig函数,单独写预分频值。参数Prescaler就是要写入的预分频值。参数TIM_PSCReloadMode,写入的模式。预分频器有个缓冲器,写入的值是在更新事件发生后才有效的。所以这里有个写入的模式,可以选择是听从安排,再更新事件生效,或者是在写入后,手动产生一个更新事件,让这个值立即生效。不过这些都是细节问题,影响不大,你只需要知道这个是写预分频值的函数就行了。

然后下一个,TIM_CounterModeConfig函数,用来改变计数器的计数模式。参数TIM_CounterMode,选择新的计数器模式。

然后再往下,TIM_ARRPreloadConfig函数,自动重装器预装功能配置。

然后再往下,TIM_SetCounter函数,给计数器写入一个值。如果你想要手动给一个计数值,就可以用这个函数。

然后再往下,TIM_SetAutoreload函数,给自动重装器写一个值。

再往下,TIM_GetCounter函数,获取当前计数器的值。

再往下,TIM_GetPrescaler函数,获取当前预分频器的值。

最后,TIM_GetFlagStatus、TIM_ClearFlag、TIM_GetITStatus以及TIM_ClearITPendingBit函数,这些函数就是用来获取标志位和清除标志位的,

好,现在开始写代码,在这里我准备初始化的是TIM2,也就是通用定时器。

1. RCC开启时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

这里注意,要使用APB1的开启时钟函数,因为TIM2是APB1总线的外设。

2. 选择时基单元时钟

TIM_InternalClockConfig(TIM2);

在这里我们选择内部时钟源,这样TIM2的时基单元就由内部时钟来驱动了。

3. 配置时基单元

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

各个参数解析如下:

  • TIM_ClockDivision :指定时钟分频。在这里选择TIM_CKD_DIV1,1分频。
  • TIM_CounterMode :计数模式。在这里选择向上计数模式TIM_CounterMode_Up。
  • TIM_Period :ARR自动重装器的值。
  • TIM_Prescaler :PSC预分频器的值。
  • TIM_RepetitionCounter :重复计数器的值。这个是高级定时器才有的,我们不需要用,直接给0即可。

在这里定时时间为1s,计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

4. 配置输出中断控制

TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

注意:在是能TIM2之前手动清除一下更新中断标志位,这样可以避免刚初始化完就进入中断的问题。

5. 配置NVIC

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);

6. 运行控制(使能计时器)

TIM_Cmd(TIM2, ENABLE);

1.4.2 编写定时器中断函数

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		Num ++;
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

定时中断完整工程:TIM定时中断之定时器定时中断应用案例

二、定时器外部时钟应用案例

2.1 应用案例简介

本案例实现了一个定时器外部时钟的功能。该程序使用了外部时钟来驱动定时器,我们可以在定时器指定的外部引脚上输入一个方波信号来提供定时器计数的时钟,在这里使用的是对射式红外传感器来手动模拟一个外部时钟。当我们用挡光片依次遮挡、移开、遮挡、移开这样的操作来提供一个方波信号时,可以看到OLED上这个CNT就是定时器中计数器的值。每遮挡移开一次,计数器加1,然后计数器记到9后自动清零,同时申请中断,执行Num++。(使用定时器的外部时钟,可以提供一个更加精准的时钟来计时,或者也可以把外部时钟当作一个计数器用来统计引脚上电平翻转的次数,毕竟定时器本质上就是一个计数器对吧)

2.2 电路接线图

在这里插入图片描述

2.3 应用案例代码

定时器头文件Timer.h:

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);
uint16_t Timer_GetCounter(void);

#endif

定时器实现文件Timer.c:

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

uint16_t Timer_GetCounter(void)
{
	return TIM_GetCounter(TIM2);
}

/*
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

主程序文件main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;

int main(void)
{
	OLED_Init();
	Timer_Init();
	
	OLED_ShowString(1, 1, "Num:");
	OLED_ShowString(2, 1, "CNT:");
	
	while (1)
	{
		OLED_ShowNum(1, 5, Num, 5);
		OLED_ShowNum(2, 5, Timer_GetCounter(), 5);
	}
}

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		Num ++;
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

2.4 应用案例分析

整体思路基本跟定时中断的案例一致,区别只是时钟源的选择以及多了一个GPIO的初始化,这里就不再累述了。

定时器外部时钟完整工程:TIM定时中断之定时器外部时钟应用案例


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2055630.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

详细分析 el-progress的基本知识以及用法(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 由于实战项目中有所引用,对此记录基本的知识点,并且以Demo的形式呈现 1. 基本知识 el-progress 是 Element Plus UI 库中的一个进度条组件,用于显示任务的完成情况 可以帮助用户了解某个操作或任…

企业迈向可持续发展的钥匙:ISO 50001能源管理体系认证的深远影响

在全球环保意识日益增强的背景下,企业如何在节能减排的道路上脱颖而出,成为行业标杆?ISO 50001能源管理体系认证无疑是企业迈向可持续发展的重要钥匙。这一认证不仅帮助企业优化能源管理,还在多个层面为企业带来了深远的积极影响。…

【JavaEE】Callable,Semaphore和CountDownLatch

🔥个人主页: 中草药 🔥专栏:【Java】登神长阶 史诗般的Java成神之路 一.Callable 在Java中,Callable接口是一个可以返回结果的异步任务执行方式。它与Runnable接口类似,都是描述一个“任务”,…

如何为列表元素分配序列号(Java Stream API 实战)

摘要: 本文将详细介绍如何在Java中使用Stream API对列表中的元素进行分组,并为每组元素分配一个唯一的序列号。通过一个具体的示例和测试案例,我们将演示这一技术如何简化数据处理,提高代码效率。 在Java编程中,我们经…

Linux-进程间关系与守护进程

一、进程组 1.1 什么是进程组 之前我们提到了进程的概念, 其实每一个进程除了有一个进程 ID(PID)之外 还属于一 个进程组。进程组是一个或者多个进程的集合, 一个进程组可以包含多个进程。 每一 个进程组也有一个唯一的进程组 ID(PGID), 并且…

阻抗控制中的dynamic movement primitives(DMP) model

在阻抗控制中,Dynamic Movement Primitives (DMP) 模型被用于实现一种高度灵活且可泛化的轨迹模仿学习方法。DMP模型由美国南加州大学(University of Southern California)的Stefan Schaal教授团队于2002年提出,它通过将动态系统建…

Linux命令之二

Linux命令之二 VI和VIM编辑器1、VIM的一般模式①、删除复制操作②、光标移动操作 2、VIM的编辑模式3、VIM的命令模式4、三种模式之间的切换关系 权限管理文件类型和权限的表示文件属性介绍chmod 改变文件权限chown 改变所有者 yum 安装软件命令 Linux命令速查平台 VI和VIM编辑器…

IOy系列BL196MQTT远程IO模块推动智能交通信号控制

在当今这个快速发展的时代,城市化进程加速推进,随之而来的是日益严重的交通拥堵问题。为了缓解这一状况,智能交通信号控制系统应运而生,成为解决城市交通问题的有效手段之一。 一、钡铼技术BL196模块简介 钡铼技术推出的IOy系列…

【SQL】商品销售

目录 题目 分析 代码 题目 表: Product ----------------------- | Column Name | Type | ----------------------- | product_id | int | | product_name | varchar | | unit_price | int | ----------------------- product_id 是该表的主键…

Redis与CAP定理—理解数据存储的权衡

一、概述 在分布式系统的设计中,理解CAP定理的重要性是不言而喻的。CAP定理指出,一个分布式系统不可能同时完全满足一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Toleran…

OCR识别行驶证(阿里云和百度云)

OCR识别行驶证(阿里云和百度云) 一、使用场景 1、通过识别行驶证,获取相关汽车信息,替代手输 二、效果图 三、代码部分: 1、阿里云OCR 1.1、控制层 PostMapping("/ocrCard") public JSONObject ocrCard(RequestPart("fi…

开放式耳机哪种好用?开放式种草测评!

现在很多人都很喜欢用开放式耳机了,因为这种耳机非常的舒服,而且不会压迫我们的耳道,从而给到我们更健康的体验,但是现在开放式耳机的品牌越来越多,我们也越来越难选择,所以我们应该怎么样才能选到一款适合…

ado.net 操作sqlite

新建控制台项目 安装nuget包Microsoft.Data.Sqlite 数据库名字和链接 string dbName "test.db"; SqliteConnection? connection null; try {//创建链接connection new SqliteConnection($"Data Source{dbName}");//打开链接connection.Open(); } ca…

RK33568 android12 背景图片替换

文章目录 前言一、直接文件替换二、使用属性替换1.属性获取图片的代码分析2.设置图片路径前言 项目中,需要替换背景图片,要求是黑色的背景图片 修改前: 修改后: 一、直接文件替换 文件路径 device/rockchip/rk356x/overlay/frameworks/base/core/res/res/drawable-nod…

为什么要用数字化营销管理平台?

数字化营销管理平台是一种利用数字技术来整合和优化营销流程的工具。它能够帮助企业更高效地进行市场推广、客户关系管理以及销售活动。 一、主要功能 1.数据整合与分析 整合多渠道数据,包括网站流量、社交媒体互动、电子邮件营销反馈等。通过数据分析&#xff0…

Docker 存储空间不足无法导入加载镜像

问题:在载入镜像时,发现docker没有空间了 解决办法: 更改docker的存储路径 1.添加新的硬盘 docker info #查看docker的存储位置 df -Th #查看占用以及挂载情况 发现没有可用的剩余空间,我们可以添加一个新的硬盘 在l

EMC学习之接地与平面

1 不同信号地的放置 下图为数字地和射频地的两种放置方式,第一个布局方式射频电流必须要经过数字地才能返回电源GND上,而且射频地与数字地之间的间隙很小,所以更容易通过寄生电容来耦合噪声。第二个布局数字地与射频地没有重叠,平…

iOS 18 Beta 5:苹果的细腻之笔,绘制用户体验新画卷

在苹果的世界里,每一次系统更新都是对用户体验进行的一次精心雕琢。 随着iOS 18 Beta 5的上线,苹果带来了一系列令人耳目一新的功能,同时也在系统的每个细微之处展现了对完美的追求。 Safari浏览器的“干扰控制”功能 在今天信息充斥的数字…

BF算法,KMP算法

前言:今天我们来学习两种算法,BF算法和KMP算法。相信会让许多小伙伴们打开新世界的大门。 1 BF算法 实践是检验真理的唯一标准。举一个例子说明BF算法。现在我们要在一个主串中找子串的位置。那我们该如何解决这个问题呢?最简单的办法自然是…

【数据结构-哈希前缀】力扣2845. 统计趣味子数组的数目

给你一个下标从 0 开始的整数数组 nums ,以及整数 modulo 和整数 k 。 请你找出并统计数组中 趣味子数组 的数目。 如果 子数组 nums[l…r] 满足下述条件,则称其为 趣味子数组 : 在范围 [l, r] 内,设 cnt 为满足 nums[i] % mod…