合宙AIR105(三): 定时器, 定时器中断和PWM输出
目录
Air105 的 Timer
定时器
- 1 个 Timer 单元,包含 8 个独立定时器: Timer0 - Time7
- 8 个定时器中断源独立,每个定时器单独占 1 个中断源
- 使用 PCLK 时钟频率作为定时器计时钟源
- 定时器采用向下计数方式
定时器的两种运行模式
- user-defined: 定时器计数值载入TimerNLoadCount寄存器设定值, 使用用户模式可以产生固定时间的定时器中断
- free-runing: 定时器计数值会载入其允许的最大值, 即0xFFFFFFFF. 在定时器产生中断(计数到0)前, 用户可以再编程或禁止定时器中断. 使用这个模式, 定时器只产生1次中断, 中断产生后计数重置为 0xFFFFFFFF 并向下计数, 但不会再产生中断.
PWM
- 每个 Timer 单元定时器都支持 PWM 模式
- PWM 模式最高频率 PCLK/2
- PWM 单次触发(one shot)功能
定时器相关代码
以下代码基于 air105_project https://gitee.com/iosetting/air105_project 的库函数
定时器模块结构
在Air105中, 全局只有一个定时器模块, TIMM0
typedef struct
{
TIM_TypeDef TIM[TIM_NUM];
__I uint32_t TIM_IntStatus;
__I uint32_t TIM_EOI;
__I uint32_t TIM_RawIntStatus;
__I uint32_t TIM_Comp;
__IO uint32_t TIM_ReloadCount[TIM_NUM];
} TIM_Module_TypeDef;
这个 TIMM0 的地址定义在 air105.h 中
#define TIMM0 ((TIM_Module_TypeDef *)TIMM0_BASE)
#define AIR105_PERIPH_BASE (0x40000000UL) /*!< (Peripheral) Base Address */
#define AIR105_APB0_BASE (AIR105_PERIPH_BASE + 0x10000)
#define TIMM0_BASE (AIR105_APB0_BASE + 0x3000)
- 地址 = 0x40000000UL + 0x10000 + 0x3000 = 0x4001 3000
- 范围 [0x4001_3000, 0x4001_3FFF]
定时器初始化
定时器的初始化只需要两个参数: TIMx, 周期(时钟数), 为配合定时器使用, 还需要定义中断
void Timer_Init(void)
{
TIM_InitTypeDef TIM_InitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
// 开启定时器的外设时钟
SYSCTRL_APBPeriphClockCmd(SYSCTRL_APBPeriph_TIMM0, ENABLE);
SYSCTRL_APBPeriphResetCmd(SYSCTRL_APBPeriph_TIMM0, ENABLE);
// 定时器的时钟是 PCLK, 计数间隔为 1ms 对应的时钟数
TIM_InitStruct.TIM_Period = SYSCTRL->PCLK_1MS_VAL;
// 使用 定时器0
TIM_InitStruct.TIMx = TIM_0;
// 初始化
TIM_Init(TIMM0, &TIM_InitStruct);
// 开启定时器0的中断
TIM_ITConfig(TIMM0, TIM_InitStruct.TIMx, ENABLE);
//NVIC
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = TIM0_0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
// 启动定时器0
TIM_Cmd(TIMM0, (TIM_NumTypeDef)TIM_0, ENABLE);
}
在库函数中, 会将模式设为 user-defined, 即自动循环, 重复载入周期并产生中断.
/**
* @brief Initializes the TIMx Unit peripheral according to the specified parameters.
* @param TIMMx: x can be 0 to select the TIM peripheral
* @param TIM_InitStruct: pointer to a TIM_InitTypeDef structor that contains the configuration information
* @retval None
*/
void TIM_Init(TIM_Module_TypeDef *TIMMx, TIM_InitTypeDef *TIM_InitStruct)
{
TIM_Cmd(TIMMx, TIM_InitStruct->TIMx, DISABLE);
TIMMx->TIM[TIM_InitStruct->TIMx].ControlReg = 0;
TIMMx->TIM[TIM_InitStruct->TIMx].ControlReg |= TIMER_CONTROL_REG_TIMER_MODE;
TIMMx->TIM[TIM_InitStruct->TIMx].ControlReg &= ~TIMER_CONTROL_REG_TIMER_PWM;
TIMMx->TIM[TIM_InitStruct->TIMx].LoadCount = TIM_InitStruct->TIM_Period;
}
定时器中断处理
Air105对应每个定时器, 各有一个中断处理函数, 可以查看 startup.air105.s 中的中断向量定义
TIM0_0_IRQHandler
TIM0_1_IRQHandler
TIM0_2_IRQHandler
TIM0_3_IRQHandler
TIM0_4_IRQHandler
TIM0_5_IRQHandler
TIM0_6_IRQHandler
TIM0_7_IRQHandler
对应 Timer0 的中断处理, 写在 air105_it.c. TIM_ClearITPendingBit 和 NVIC_ClearPendingIRQ 是必须调用的, 用于清除中断
void TIM0_0_IRQHandler(void)
{
TIM_ClearITPendingBit(TIMM0, TIM_0);
NVIC_ClearPendingIRQ(TIM0_0_IRQn);
}
下面加入处理逻辑的例子, 每秒调用一次 timer_handler(), 注意不要在中断处理中使用耗时的工作
extern uint32_t timer_count;
extern void timer_handler(void);
void TIM0_0_IRQHandler(void)
{
timer_count++;
if (timer_count >= 1000)
{
timer_count = 0;
timer_handler();
}
TIM_ClearITPendingBit(TIMM0, TIM_0);
NVIC_ClearPendingIRQ(TIM0_0_IRQn);
}
定时器示例代码
使用Timer0控制板载LED每隔一秒闪烁
https://gitee.com/iosetting/air105_project/tree/master/Demos/Timer/Timer_Blink
Air105 的 PWM
Air105 的8个独立定时器均可编程产生PWM信号. 当用户设定TimerNControlReg中PWM比特位为1后,定时器进入PWM工作模式. 此时 PWM 由 TimerNLoadCount2 和 TimerNLoadCount 寄存器分别控制高电平及低电平周期翻转输出.
频率和占空比设置
- 高电平周期 = (TimerNLoadCount2 + 1) * PCLK_Period
- 低电平周期 = (TimerNLoadCount + 1) * PCLK_Period
PWM 相关代码
PWM初始化也只需要三个参数 TIMx 和高低电平两个周期, 两者之和就是一个PWM周期
typedef struct
{
TIM_NumTypeDef TIMx;
uint32_t TIM_LowLevelPeriod;
uint32_t TIM_HighLevelPeriod;
}TIM_PWMInitTypeDef;
用Timer5初始化
void TimerPWM_Init(void)
{
TIM_PWMInitTypeDef TIM_PWMInitStruct;
SYSCTRL_APBPeriphClockCmd(SYSCTRL_APBPeriph_TIMM0, ENABLE);
SYSCTRL_APBPeriphResetCmd(SYSCTRL_APBPeriph_TIMM0, ENABLE);
//Timer5 -> PWM5
TIM_PWMInitStruct.TIM_HighLevelPeriod = SYSCTRL->PCLK_1MS_VAL;
TIM_PWMInitStruct.TIM_HighLevelPeriod = 0;
TIM_PWMInitStruct.TIMx = TIM_5;
TIM_PWMInit(TIMM0, &TIM_PWMInitStruct);
TIM_Cmd(TIMM0, TIM_5, ENABLE);
}
在初始化PWM的库函数中, 默认将模式设为 user-defined, 自动循环载入周期, 并屏蔽中断
/**
* @brief Initializes the TIMx PWM Unit peripheral according to the specified parameters.
* @param TIMMx: x can be 0 to select the TIM peripheral
* @param TIM_PWMInitStruct: pointer to a TIM_PWMInitTypeDef structor that contains the configuration information
* @retval None
*/
void TIM_PWMInit(TIM_Module_TypeDef *TIMMx, TIM_PWMInitTypeDef *TIM_PWMInitStruct)
{
TIM_Cmd(TIMMx, TIM_PWMInitStruct->TIMx, DISABLE);
TIMMx->TIM[TIM_PWMInitStruct->TIMx].ControlReg = 0;
TIMMx->TIM[TIM_PWMInitStruct->TIMx].ControlReg |= TIMER_CONTROL_REG_TIMER_MODE;
TIMMx->TIM[TIM_PWMInitStruct->TIMx].ControlReg |= TIMER_CONTROL_REG_TIMER_PWM;
TIMMx->TIM[TIM_PWMInitStruct->TIMx].ControlReg |= TIMER_CONTROL_REG_TIMER_INTERRUPT;
TIMMx->TIM[TIM_PWMInitStruct->TIMx].LoadCount = TIM_PWMInitStruct->TIM_LowLevelPeriod;
TIMMx->TIM_ReloadCount[TIM_PWMInitStruct->TIMx] = TIM_PWMInitStruct->TIM_HighLevelPeriod;
}
将 PB5 功能复用为 PWM5
GPIO_InitTypeDef gpio;
gpio.GPIO_Pin = GPIO_Pin_5;
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Remap = GPIO_Remap_2;
GPIO_Init(GPIOB, &gpio);
printf("GPIO Init\r\n");
实时调节占空比, 后两个参数代表PCLK时钟周期个数
TIM_SetPWMPeriod(TIMM0, TIM_5, period - high_period, high_period);
PWM示例代码
使用PWM5(Timer5)控制LED产生呼吸灯效果
https://gitee.com/iosetting/air105_project/tree/master/Demos/PWM/PWM_FadeLED
示例接线:
根据 开发板的BOM PCB 查看 https://wiki.luatos.com/_static/bom/Air105.html
示例中使用Timer4, Timer5对应的PWM4和PWM5输出, 使用的是PB4和PB5, 对应开发板的SP2_MO和SP2_MI, 开发板上的PWM5对应的是PC7, 要注意, 别接错了.
运行示例, 将两个LED各自串接一个1-5K的电阻, 分别接GND后接在SP2_MO和SP2_MI上, 就能看到呼吸灯的效果了
合宙AIR105(三): 定时器, 定时器中断和PWM输出的更多相关文章
- 合宙AIR105(四): SPI, MAX7219 8x8LED驱动
目录 合宙AIR105(一): Keil MDK开发环境, DAP-Link 烧录和调试 合宙AIR105(二): 时钟设置和延迟函数 合宙AIR105(三): 定时器, 定时器中断和PWM输出 合宙 ...
- 合宙AIR105(二): 时钟设置和延迟函数
目录 合宙AIR105(一): Keil MDK开发环境, DAP-Link 烧录和调试 合宙AIR105(二): 时钟设置和延迟函数 Air105 的时钟 高频振荡源 芯片支持使用内部振荡源, 或使 ...
- 打打基础,回头看看avr单片机的定时器、中断和PWM(转)
以前小看了定时器,发现这东西还真的很讲究,那先复习复习吧. 先提提中断:我的理解就是cpu执行时,遇到中断——根据对应的中断源(硬件或软件)——pc定位中断入口地址,然后根据这里的函数指针——跳转到相 ...
- 合宙AIR105使用Keil MDK + DAP-Link 烧录和调试
关于AIR105 AIR105是合宙LuatOS生态下的一款芯片, 1月初上市, 开发板与摄像头一起搭售(赠送). 从配置信息看, 芯片性能相当不错: Cortex-M4F内核, 最高频率204Mhz ...
- (五)转载:通用定时器PWM输出
1. TIMER输出PWM基本概念 脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有 ...
- STM32 HAL库学习系列第4篇 定时器TIM----- 开始定时器与PWM输出配置
基本流程: 1.配置定时器 2.开启定时器 3.动态改变pwm输出,改变值 HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); 函数总结: __HAL_TIM ...
- STM32(7)——通用定时器PWM输出
1.TIMER输出PWM基本概念 脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种 ...
- zigbee学习之路(七):定时器3(中断方式)
一.前言 上次我们学习了了用定时器3进行查询方式来进行溢出判断,今天我们来换一种方式,用中断方式来检测和查询定时器3的溢出. 二.原理与分析 要使用定时器3,我们必须先要配置的是T3CTL,来把定时器 ...
- Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用
OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...
随机推荐
- 如何在 Java 中实现无向环和有向环的检测
无向环 一个含有环的无向图如下所示,其中有两个环,分别是 0-2-1-0 和 2-3-4-2: 要检测无向图中的环,可以使用深度优先搜索.假设从顶点 0 出发,再走到相邻的顶点 2,接着走到顶点 2 ...
- numpy教程06---ndarray的进阶操作
欢迎关注公众号[Python开发实战], 获取更多内容! 工具-numpy numpy是使用Python进行数据科学的基础库.numpy以一个强大的N维数组对象为中心,它还包含有用的线性代数,傅里叶变 ...
- 基于LAMP离线部署zabbix3.2.11
zabbix是个什么东西这里不再赘述,先安装lamp再安装zabbix. 1. 安装依赖插件(把下面PHP那些依赖库全部都装了) #yum install -y gcc gcc-c++ opens ...
- Visual Studio 打包和安装 exe
# Visual Studio 打包和安装 exe > **小型项目(无复杂的库)** //VS2022 作为演示平台 > 1.解决方案配置 = Release > 2.解决 ...
- toggleClass() 把本来的有的类名去掉 本来没有的 加上
连续点击按钮可以交替颜色,就是改变class
- TemplatesImpl利用链
FastJson利用链 Fastjson的版本在1.2.22-1.2.24主要有两条链利用TemplatsImpl和JdbcRowSetImpl利用链先来学习TemplatsImpl利用链,这个与前面 ...
- DevOps转型到底值不值?
摘要:企业进行DevOps转型是否有价值?是否能计算出明确的投资回报率呢?本文将为您解惑. 本文分享自华为云社区<DevOps转型到底值不值?>,作者:敏捷小智 . 引言 企业都是以盈利为 ...
- Infrastructure 知识: DNS 命令: dig, host
dig 基本用法: dig @server name type 或者用-t type来指定(更常见) dig @server -t type name 例子详解 # 最简单的使用 $ dig www. ...
- MySQL 回表
MySQL 回表 五花马,千金裘,呼儿将出换美酒,与尔同销万古愁. 一.简述 回表,顾名思义就是回到表中,也就是先通过普通索引扫描出数据所在的行,再通过行主键ID 取出索引中未包含的数据.所以回表的产 ...
- openstack PCI透传(GPU)
描述 kolla-ansible部署openstack的GPU透传方法 一.gpu物理服务器配置 在gpu服务器上主启用IOMMU 确认内核⽀支持iommu $ cat /proc/cmdline | ...