【正点原子STM32连载】第二十一章 高级定时器输出指定个数PWM实验 摘自【正点原子】APM32F407最小系统板使用指南

2023-11-08

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#

第二十一章 高级定时器输出指定个数PWM实验

本章将介绍使用APM32F407高级定时器输出指定个数的PWM。通过本章的学习,读者将学习到高级定时器重复计数器的使用。
本章分为如下几个小节:
21.1 硬件设计
21.2 程序设计
21.3 下载验证

21.1 硬件设计
21.1.1 例程功能

  1. 按下KEY0按键,控制定时器8通过PC6引脚以2Hz的频率输出5个PWM脉冲
    21.1.2 硬件资源
  2. 按键
    KEY0 - PE4
  3. 定时器8
    通道1 - PC6
    21.1.3 原理图
    本章实验使用的定时器8为APM32F407的片上资源,因此没有对应的连接原理图。
    21.2 程序设计
    21.2.1 Geehy标准库的TMR驱动
    本章实验将使用KEY0按键控制TMR8通过通道1(PC6引脚)输出指定个数的PWM。对于高级定时器自动重装载值、预分频计数器数值等基本参数的配置,与基本定时器和通用定时器的配置基本一致;对于高级定时器输出PWM,则于通用定时器输出PWM的配置基本一致,不同于通用定时器不同的是,高级定时器使能输出PWM需要将“刹车和死区寄存器(TMRx_BDT)”的MOEN位置1。要做到输出指定个数的PWM,需要使用到高级定时器的重复计数寄存器,重复计数寄存器可以配置高级定时器产生多少次上溢或下溢(一次上溢或下溢对应一个PWM)后才产生一次更新事件,其具体的配置步骤如下:
    ①:配置TMR8的自动重装载值和预分频器数值等参数
    ②:配置输出比较通道1
    ③:使能TMR8的更新中断
    ④:使能TMR8中断,并配置其相关的中断优先级
    ⑤:使能TMR8
    ⑥:使能输出比较通道1输出
    ⑦:使能TMR8的PWM输出
    ⑧:配置TMR8的重复计数寄存器
    在Geehy标准库中对应的驱动函数如下:
    ①:配置TMR
    请见第16.2.1小节中配置TMR的相关内容。
    ②:配置输出比较通道
    请见第18.2.1小节中配置输出比较通道的相关内容。
    ③:使能TMR指定中断
    请见第16.2.1小节中使能TMR指定中断的相关内容。
    ④:配置TMR中断
    请见第12.2.3小节中配置中断的相关内容。
    ⑤:使能TMR
    请见第16.2.1小节中使能TMR的相关内容。
    ⑥:使能捕获比较通道
    请见第18.2.1小节中使能捕获比较通道的相关内容。
    ⑦:使能高级定时器PWM输出
    该函数用于使能高级定时器输出PWM,其函数原型如下所示:
    void TMR_EnablePWMOutputs(TMR_T* tmr);
    该函数的形参描述,如下表所示:
    形参 描述
    tmr 指向高级TMR外设结构体的指针
    例如:TMR1、TMR8(在apm32f4xx.h文件中有定义)
    表21.2.1.1 函数TMR_EnablePWMOutputs()形参描述
    该函数的返回值描述,如下表所示:
    返回值 描述
    无 无
    表21.2.1.2 函数TMR_EnablePWMOutputs()返回值描述
    该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_tmr.h"

void example_fun(void)
{
    /* 使能TMR8使出PWM */
    TMR_EnablePWMOutputs(TMR8);
}

⑧:配置重复计数寄存器
高级定时器的重复计数寄存器可以在配置TMR寄存器参数时,通过函数TMR_ConfigTimeBase()进行配置,也可以通过写寄存器的方式直接修改高级定时器重复计数寄存器的数值,示例如下所示:

#include "apm32f4xx.h"

void example_fun(void)
{
    /* 配置TMR8的重复计数寄存器为5 */
    TMR8->REPCNT = 5;
}

21.2.2 高级定时器驱动
本章实验的高级定时器驱动主要负责向应用层提供高级定时器的初始化和输出指定个数PWM的函数,并实现高级定时器的中断回调函数。本章实验中,高级定时器的驱动代码包括atmr.c和atmr.h两个文件。
高级定时器驱动中,对TMR、GPIO的相关宏定义,如下所示:

#define ATMR_TMRX_NPWM				TMR8
#define ATMR_TMRX_NPWM_IRQn			TMR8_UP_TMR13_IRQn
#define ATMR_TMRX_NPWM_IRQHandler	TMR8_UP_TMR13_IRQHandler
#define ATMR_TMRX_NPWM_CHY			TMR_CHANNEL_1
#define ATMR_TMRX_NPWM_CLK_ENABLE()						\
    do {														\
    		RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR8);	\
    } while (0)

#define ATMR_TMRX_NPWM_CHY_GPIO_PORT		GPIOC
#define ATMR_TMRX_NPWM_CHY_GPIO_PIN		GPIO_PIN_6
#define ATMR_TMRX_NPWM_CHY_GPIO_PIN_SOURCE	GPIO_PIN_SOURCE_6
#define ATMR_TMRX_NPWM_CHY_GPIO_AF			GPIO_AF_TMR8
#define ATMR_TMRX_NPWM_CHY_GPIO_CLK_ENABLE()				\
    do {														\
    		RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOC);	\
    } while (0)

高级定时器驱动中TMR8的初始化函数,如下所示:

/**
 * @brief	初始化高级定时器输出指定个数PWM
 * @note	当APB1PSC!=1时,定时器的时钟频率为APB2时钟的2倍
 * 			因此定时器的时钟频率为168MHz
 * 			定时器溢出时间计算方法:Tout = ((arr + 1) * (psc + 1)) / TMRxCLK
 * 			TMRxCLK=定时器时钟频率,单位MHz
 * @param	arr: 自动重装载值
 * @param	psc: 预分频器数值
 * @retval	无
 */
void atmr_tmrx_npwm_chy_init(uint16_t arr, uint16_t psc)
{
    GPIO_Config_T gpio_init_struct;
    TMR_BaseConfig_T tmr_init_struct;
    TMR_OCConfig_T tmr_oc_init_struct;
    
    /* 使能时钟 */
    ATMR_TMRX_NPWM_CLK_ENABLE();					/* 使能高级定时器时钟 */
    ATMR_TMRX_NPWM_CHY_GPIO_CLK_ENABLE();			/* 使能PWM输出引脚端口时钟 */
    
    /* 配置PWM输出引脚 */
    gpio_init_struct.pin	= ATMR_TMRX_NPWM_CHY_GPIO_PIN;	/* PWM输出引脚 */
    gpio_init_struct.mode	= GPIO_MODE_AF;					/* 复用功能模式 */
    gpio_init_struct.speed	= GPIO_SPEED_100MHz;			/* 高速 */
    gpio_init_struct.otype	= GPIO_OTYPE_PP;				/* 推挽输出 */
    gpio_init_struct.pupd	= GPIO_PUPD_DOWN;				/* 下拉 */
    GPIO_Config(ATMR_TMRX_NPWM_CHY_GPIO_PORT, &gpio_init_struct);
    GPIO_ConfigPinAF(	ATMR_TMRX_NPWM_CHY_GPIO_PORT,		/* 配置引脚复用功能 */
    						ATMR_TMRX_NPWM_CHY_GPIO_PIN_SOURCE,
    						ATMR_TMRX_NPWM_CHY_GPIO_AF);
    
    /* 配置高级定时器 */
    tmr_init_struct.countMode			= TMR_COUNTER_MODE_UP;	/* 向上计数 */
    tmr_init_struct.clockDivision		= TMR_CLOCK_DIV_1;		/* 时钟分频系数 */
    tmr_init_struct.period				= arr;					/* 自动重装载值 */
    tmr_init_struct.division			= psc;					/* 预分频器数值 */
    tmr_init_struct.repetitionCounter	= 0;					/* 重复计数数值 */
    TMR_ConfigTimeBase(ATMR_TMRX_NPWM, &tmr_init_struct);		/* 配置高级定时器 */
    
    /* 配置输出比较通道 */
    tmr_oc_init_struct.mode			= TMR_OC_MODE_PWM1;		/* PWM模式1 */
    tmr_oc_init_struct.outputState	= TMR_OC_STATE_ENABLE;	/* 使能PWM输出 */
    tmr_oc_init_struct.polarity		= TMR_OC_POLARITY_LOW;	/* 低电平有效 */
    tmr_oc_init_struct.pulse		= (arr + 1) >> 1;		/* PWM有效电平脉宽 */
    TMR_ConfigOC1(ATMR_TMRX_NPWM, &tmr_oc_init_struct);		/* 配置输出比较通道1 */
    
    /* 使能高级定时器及其相关中断 */
    NVIC_EnableIRQRequest(ATMR_TMRX_NPWM_IRQn, 1, 0);		/* 使能中断 */
    TMR_EnableInterrupt(ATMR_TMRX_NPWM, TMR_INT_UPDATE);	/* 使能更新中断 */
    TMR_EnablePWMOutputs(ATMR_TMRX_NPWM);					/* 使能PWM主输出 */
    /* 使能输出比较通道输出 */
    TMR_EnableCCxChannel(ATMR_TMRX_NPWM, ATMR_TMRX_NPWM_CHY);
}
从上面的代码中可以看出,本章实验中对高级定时器的初始化于前面章节中使用通用定时器输出PWM中对通用定时器的初始化配置是比较类似的,最大不同还是在高级定时器中断服务函数中对高级定时器特有的重复计数寄存器的相关处理中。
高级定时器驱动中,开启高级定时器输出指定个数PWM的函数,如下所示:
/**
 * @brief	设置高级定时器PWM个数
 * @param	npwm: PWM的个数,范围1~2^32
 * @retval	无
 */
void atmr_tmrx_npwm_chy_set(uint32_t npwm)
{
    if (npwm == 0)
    {
    		return;
    }
    
    /* 保存需要输出的脉冲个数 */
    g_npwm_remain = npwm;
    /* 产生一次更新事件,在更新中断中处理PWM脉冲输出 */
    TMR_GenerateEvent(ATMR_TMRX_NPWM, TMR_EVENT_UPDATE);
}

该函数记录下了需要产生的PWM个数,因为会在TMR8的更新中断中处理输出PWM,因此手动产生了一次更新事件,由于初始化函数中开启了TMR8的更新中断,因此随后会进入TMR的中断服务函数中。
高级定时器驱动中,TMR8的中断回调函数,如下所示:

/**
 * @brief	高级定时器中断服务函数
 * @param	无
 * @retval	无
 */
void ATMR_TMRX_NPWM_IRQHandler(void)
{
    uint16_t npwm;
    
    /* 判断更新中断标志 */
    if (TMR_ReadIntFlag(ATMR_TMRX_NPWM, TMR_INT_UPDATE) == SET)
    {
    		if (g_npwm_remain >= 256)	/* 剩余发送脉冲数量大于或等于256 */
    		{
    			g_npwm_remain -= 256;	/* 每次最多发送256个脉冲 */
    			npwm = 256;
    		}
    		else						/* 剩余发送脉冲数量不超过256 */
    		{
    			npwm = g_npwm_remain;	/* 发送剩余的全部脉冲数 */
    			g_npwm_remain = 0;
    		}
    		
    		if (npwm != 0)							/* 有脉冲数需要发送 */
    		{
    			ATMR_TMRX_NPWM->REPCNT = npwm - 1;	/* 设置重复计数数值 */
    			/* 产生一次更新事件,以更新REPCNT寄存器 */
    			TMR_GenerateEvent(ATMR_TMRX_NPWM, TMR_EVENT_UPDATE);
    			TMR_Enable(ATMR_TMRX_NPWM);			/* 使能高级定时器 */
    		}
    		else									/* 没有脉冲数需要发送 */
    		{
    			TMR_Disable(ATMR_TMRX_NPWM);		/* 关闭高级定时器 */
    		}
    		
    		TMR_ClearIntFlag(ATMR_TMRX_NPWM, TMR_INT_UPDATE);	/* 清除更新中断标志 */
    }
}

因为高级定时器的重复计数寄存器只有低八位有效,因此需要在需要产生大于255个PWM的情况下做特殊处理,随后将需要产生的PWM个数值减1写入TMR8的重复计数寄存器,并且由于写入重复计数寄存器的新值只有在下一次更新事件发生时才生效,因此需要手动产生一次更新事件,最后使能TMR8,这么一来,TMR8就会持续地输出PWM直到重复计数寄存器的数值为0后,产生一次更新中断,若此时已经输出完了指定个数的PWM,那么就会使用函数TMR_Disable()关闭高级定时器。
21.2.3 实验应用代码
本章实验的应用代码,如下所示:

int main(void)
{
    uint8_t key;
    
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3);	/* 设置中断优先级分组为组3 */
    sys_apm32_clock_init(336, 8, 2, 7);					/* 配置系统时钟 */
    delay_init(168);										/* 初始化延时功能 */
    usart_init(115200);									/* 初始化串口 */
    key_init();											/* 初始化按键 */
    /* 初始化高级定时器输出指定个数PWM,PWM频率为2Hz */
    atmr_tmrx_npwm_chy_init(5000 - 1, 16800 - 1);
    
    while (1)
    {
    		key = key_scan(10);
    		
    		if (key == KEY0_PRES)
    		{
    			atmr_tmrx_npwm_chy_set(5);	/* 输出5个PWM脉冲 */
    		}
    		
    		delay_ms(10);
    }
}

从上面的代码中可以看到,TMR8的自动重装载值配置为(5000-1),TMR8的预分频器数值配置为(16800-1),并且TMR8的时钟频率为168MHz,因此TMR8的计数频率为10KHz,且TMR8每计数5000次溢出一次,因此溢出频率为2Hz,也就是输出PWM的频率为2Hz。
初始化完TMR8后,就重复地扫描按键,若KEY0按键被按下,则输出5个PWM。
21.3 下载验证
在完成编译和烧录操作后,按下KEY0按键,此时可以通过示波器或外接LED的方式观察PC6引脚输出了5个频率为2Hz,占空比为50%的PWM。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【正点原子STM32连载】第二十一章 高级定时器输出指定个数PWM实验 摘自【正点原子】APM32F407最小系统板使用指南 的相关文章

  • kali linux子系统的网络配置

    kali linux子系统的网络配置 配置kali子系统时 踩了一些坑 主要是网络配置时卡了挺久 因为wsl2安装时默认为NAT模式 而在测试过程中 经常会需要反弹shell或开启服务让靶机下载文件之类的操作 因此最好使用桥接模式 kali
  • html鼠标背景特效,6种鼠标滑过按钮背景动画特效

    插件描述 这是一组效果非常酷的鼠标滑过按钮背景动画特效 该特效中 当鼠标滑过按钮时 使用CSS3 animation来动画backgroundsize和backgroundposition属性 来制作各种背景动画效果 简要教程 这是一组效果
  • 判断两个字符串是否相等的更好方法

    在Java中 判断两个字符串是否相等有几种常见的方法 以下是其中一些更好的方法 1 使用equals 方法 java String str1 Hello String str2 World if str1 equals str2 字符串相等
  • SpringMVC的请求(获取参数以及静态资源访问)

    SpringMVC的请求 SpringMVC的请求 1 SpringMVC的请求 获得请求参数 请求参数类型 理解 2 SpringMVC的请求 获得请求参数 获得基本类型参数 应用 3 SpringMVC的请求 获得请求参数 获得POJO

随机推荐

  • systemctl 命令详解及使用教程

    在本教程中 我们将向您介绍在运行systemd的linux系统上如何使用systemctl命令工具有效的控制系统和服务 Systemctl 介绍 Systemctl是一个systemd工具 主要负责控制systemd系统和服务管理器 Sys
  • VM虚拟机-安装Linux、windows、windows server超详细步骤演示

    本文章所展示的workstation为15 5版本 Linux为Redhat7 4版本 Windows为win10版本 windows server 2012版本 其他版本的安装过程都大致同理 目录 VMware Workstation安装
  • Linux编译FFmpeg

    Linux编译FFmpeg 1 下载FFmpeg源码 FFmpeg源码下载地址 http ffmpeg org download html 在下面选择版本 2 解压并创建生成目录 tar xvf ffmpeg snapshot tar bz
  • 单独编译使用WebRTC的音频处理模块

    单独编译使用WebRTC的音频处理模块 2016年12月08日 14 26 58 starRTC免费IM直播会议一对一视频 阅读数 8360 版权声明 starRTC免费im直播会议一对一视频 by elesos com starRTC c
  • Java中AWT、Swing与SWT三大GUI技术的原理与效率差异

    转自 http blog csdn net widegoose article details 333908 MineHe原创 看了一下对Java GUI讨论的帖子 所以写了一篇文章来阐述一下各种GUI技术的优劣 Java世界中 目前最知名
  • 黑马程序员Javaweb学习笔记02【request和response】

    该博客主要记录在学习黑马程序员Javaweb过程的一些笔记 方便复习以及加强记忆 系列文章 JavaWeb学习笔记01 BS架构 Maven Tomcat Servlet JavaWeb学习笔记02 request和response Jav
  • 模型训练——Loss函数 归纳汇总

    上一篇文章 语音降噪模型归纳汇总 很意外地收到了点赞收藏和关注 如此的反馈给了我极大的鼓舞 这里就再梳理了一下loss函数相关的知识 以求方便能作为一份工具性质的文章展现出来 能力一般 水平有限 欢迎大家指正 干货分享 欢迎收藏点赞加关注
  • (ESP32学习6)双核CPU的调用

    本来之前从来没有用过双核做东西 但是突然意识到他好像是双核的 所以找了一些资料更第一篇双核的调用 还是挺有趣的 博文会持续更FreeRTOS与双核相关内容 这一章先做一件最简单的事情 在两个核上创建简单任务让他们跑起来 先看代码再讲解 in
  • flink-cdc,clickhouse写入,多路输出

    1 场景 kafka日志数据从kafka读取 1 关联字典表 完善日志数据 2 判断日志内容级别 多路输出 低级 入clickhouse 高级 入clickhouse的同时推送到kafka供2次数据流程处理 2 实现 package com
  • 媒资知识图谱系统

    http wenku baidu com link url xEjnrOeeVaqCc5JNZfuFGewAFKWbPjZDI3FzopmFuDrxYIfXkU3fxaXnPDpI2 6 kvcbnwV 8irUddAmk2JpkZlD2B
  • 太赫兹技术的应用前景

    从振奋全国的神舟五号载人飞船开始 中国的航空进入了发展的快车道 有好奇的朋友问了 天上这么多的卫星 它们之间是怎么 交流 的 在太空中 两颗卫星在地球轨道上 运用传统卫星技术传数据需要一段时间才能完成传送 但太赫兹技术的出现 让卫星们可以不
  • vue3 Element UI使用自定义内容

    这里以Vue3版本的Element UI 举例 这是一个通知组件 通知的内容我们可以通过message参数来控制 例如
  • java解析json获取天气预报城市代码_利用json获取天气信息

    天气预报信息获取是利用json获取的 网上有非常多资源 源码 因为上面涉及到非常多天气信息 包含湿度 出行建议等 以及加入了全部城市代码的资源包 为了练手了解json的原理 我仅获取诚笃城市的最高温 最低温 城市名字 我的布局是通过一个bu
  • vue-cli3+ant-design-vue+typescript 注意事项

    项目参考vue cli3 web init ant design配置部分 1 实现ant disign vue的按需加载方案 1 引入所有的组件 然后加载到vue上面 components ant 2 对Ant Design Vue进行文件
  • unique_ptr的使用和陷阱

    转自 https blog csdn net qq 33266987 article details 78784286 unique ptr的使用 分配内存 与shared ptr不同 unique ptr没有定义类似make shared
  • JavaScript基础Day06:内置对象

    JavaScript基础Day06 内置对象 文章目录 JavaScript基础Day06 内置对象 如何学习一个方法 一 Math对象 二 Date对象 三 String对象 四 Array对象 五 基本包装类型 JavaScript中的
  • 【Docker】docker设置固定ip地址

    1 美图 2 概述 docker设置固定ip地址 Docker安装后 默认会创建下面三种网络类型 docker network ls NETWORK ID NAME DRIVER SCOPE 9781b1f585ae bridge brid
  • vue设置页面滚动高度_vue中获取滚动高度或指定滚动到某位置

    1 点击某个标签 滚动到某个具体位置 switchHeight num this selectNum num if num 0 document getElementById identifer one scrollIntoView els
  • Verilog测试(仿真)文件TestBench如何编写

    一 哪些步骤需要进行仿真 下图是FPGA开发的整个流程 先看一下仿真都出现在哪里 流程图中绿色的步骤是要进行测试仿真的 即有三个步骤是要进行仿真操作的 有时会省略 综合后仿真 这一步 1 RTL仿真 也称为综合前仿真 前仿真和功能仿真 这一
  • 【正点原子STM32连载】第二十一章 高级定时器输出指定个数PWM实验 摘自【正点原子】APM32F407最小系统板使用指南

    1 实验平台 正点原子stm32f103战舰开发板V4 2 平台购买地址 https detail tmall com item htm id 609294757420 3 全套实验源码 手册 视频下载地址 http www openedv