普冉(PUYA)单片机开发笔记(5): 配置定时器PWM输出

概述

定时器的输出通道作为 PWM 驱动是 MCU 的常用功能。

PY32F003 有一个高级定时器 TIM1 和一个通用定时器 TIM3,这两个定时器都可以驱动4个输出通道。现在我们就利用 TIM1 的某一个通道实现可控占空比的 PWM 输出。

原理简介

看数据手册,简单摘录如下。

关于 TIM1 的描述,厂家的《Reference_manual v1.1.pdf》中很详细了,内容很多,这里不再详述了。不说废话,上代码吧。

实现代码

使用 SystemClock_Config 函数选择 HSE

/********************************************************************************************************
**函数信息 :void SystemClock_Config(void)
**功能描述 :系统时钟配置
**输入参数 :
**输出参数 :
**    备注 :
********************************************************************************************************/
HAL_StatusTypeDef SystemClock_Config(void)
{HAL_StatusTypeDef conf_res= HAL_OK;RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE |RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_LSI;              // 配置时钟源HSE/HSI/LSE/LSIRCC_OscInitStruct.HSIState = RCC_HSI_ON;                                // 开启HSIRCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;                                // 不分频//RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_8MHz;      // 配置HSI输出时钟为8MHz//RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_4MHz;      // 配置HSI输出时钟为4MHz//RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_16MHz;     // 配置HSI输出时钟为16MHz//RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_22p12MHz;  // 配置HSI输出时钟为22.12MHzRCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_24MHz;       // 配置HSI输出时钟为24MHzRCC_OscInitStruct.HSEState = RCC_HSE_ON;                                // 开启 HSERCC_OscInitStruct.HSEFreq = RCC_HSE_16_32MHz;                           // HSE工作频率范围16M~32MRCC_OscInitStruct.LSIState = RCC_LSI_OFF;                               // 关闭 LSIconf_res = HAL_RCC_OscConfig(&RCC_OscInitStruct);                       // 初始化RCC振荡器if (conf_res != HAL_OK)                                                 return conf_res;//初始化CPU,AHB,APB总线时钟RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;      // RCC系统时钟类型RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;  // SYSCLK的源选择为HSERCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;      // APH时钟不分频RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;       // APB时钟不分频conf_res = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1); // 初始化RCC系统时钟// (FLASH_LATENCY_0=24M以下;// FLASH_LATENCY_1=48M)if (conf_res != HAL_OK)  return conf_res;return HAL_OK;
}

和先前开发笔记中的相同,选用 HSE 作为 TIM 子系统的时钟。重要的语句是

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE; 
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

这三句,设置TIM 子系统的总线频率和 HSE 相同(24MHz)。

SystemClock_Config 函数存放在 Application/User 组的 app.c 文件中。

main.h 声明 TIM1_PWM 相关的几个函数

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H#ifdef __cplusplus
extern "C"
{
#endif/* Includes ------------------------------------------------------------------*/
#include "py32f0xx_hal.h"
#include "py32f003xx_Start_Kit.h"
#include <stdbool.h>/* Exported functions prototypes ---------------------------------------------*/
HAL_StatusTypeDef SystemClock_Config(void);
HAL_StatusTypeDef GPIO_Config(void);
HAL_StatusTypeDef USART_Config(void);
HAL_StatusTypeDef DBG_UART_Start(void);
HAL_StatusTypeDef TIM16_Config(void);
HAL_StatusTypeDef TIM16_Start(void);/*** ------------------------------------------------------* Added for TIM1 and PWM output.* Marked by HardCoder_LLuo@2023-12-07*/
HAL_StatusTypeDef TIM1_PWM_Config(void);
HAL_StatusTypeDef TIM1_PWM_Start(uint32_t duty);
HAL_StatusTypeDef TIM1_PWM_Stop(void);
void TIM1_PWM_Output(const uint8_t duty_percent);
/* ------------------------------------------------------
*/void Debug_Info(const char* msg);......
......

在 app_pwm.c 中编写代码

在 Application/User 组加入 app_pwm.c 文件,编写在 main.h 中声明的函数。

/********************************************************************************* @file    app_pwm.c* @brief   Application level PWM codes.******************************************************************************* @attention** Copyright (c) 2023 CuteModem Intelligence.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/#include "main.h"#define PWM_PERIOD 1200TIM_HandleTypeDef  htim1;
TIM_OC_InitTypeDef cfg_tim1;HAL_StatusTypeDef TIM1_PWM_Config(void)
{HAL_StatusTypeDef cfg_res = HAL_OK;htim1.Instance = TIM1;                                                  // 选择TIM1htim1.Init.Period            = PWM_PERIOD - 1;                                // 自动重装载值,和Prescaler可确定PWM的频率htim1.Init.Prescaler         = 1 - 1;                                   // 预分频为1-1htim1.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;                  // 时钟不分频htim1.Init.CounterMode       = TIM_COUNTERMODE_UP;                      // 向上计数htim1.Init.RepetitionCounter = 1 - 1;                                   // 不重复计数htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;          // 自动重装载寄存器无缓冲cfg_res = HAL_TIM_Base_Init(&htim1);                                    // 初始化if (cfg_res != HAL_OK)              {return cfg_res;}cfg_tim1.OCMode       = TIM_OCMODE_PWM1;                                // 输出配置为模式1cfg_tim1.OCPolarity   = TIM_OCPOLARITY_HIGH;                            // OC通道输出高电平有效cfg_tim1.OCFastMode   = TIM_OCFAST_DISABLE;                             // 输出快速使能关闭cfg_tim1.OCNPolarity  = TIM_OCNPOLARITY_HIGH;                           // OCN通道输出高电平有效cfg_tim1.OCNIdleState = TIM_OCNIDLESTATE_RESET;                         // 空闲状态OC1N输出低电平cfg_tim1.OCIdleState  = TIM_OCIDLESTATE_RESET;                          // 空闲状态OC1输出低电平cfg_tim1.Pulse        = 0;                                              // CC1值为0,相当于复位PWM输出cfg_res = HAL_TIM_OC_ConfigChannel(&htim1, &cfg_tim1, TIM_CHANNEL_1);   // OC1配置通道if (cfg_res != HAL_OK) return cfg_res;cfg_res = HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1);                      // OC1通道开始输出if ( cfg_res != HAL_OK) return cfg_res;return cfg_res;
}HAL_StatusTypeDef TIM1_PWM_Start(uint32_t duty)
{HAL_StatusTypeDef cfg_res = HAL_OK;cfg_tim1.Pulse = duty - 1;                                              // CC1值为duty,PWM平均=duty/periodcfg_res = HAL_TIM_OC_ConfigChannel(&htim1, &cfg_tim1, TIM_CHANNEL_1);   // OC1配置通道if (cfg_res != HAL_OK) return cfg_res;cfg_res = HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1);                      // OC1通道开始输出if ( cfg_res != HAL_OK) return cfg_res;return cfg_res;
}HAL_StatusTypeDef TIM1_PWM_Stop(void)
{return HAL_TIM_OC_Stop(&htim1, TIM_CHANNEL_1);
}void TIM1_PWM_Output(const uint8_t duty_percent)
{uint8_t  tmp_duty = 0;uint32_t duty     = 0;tmp_duty = duty_percent;if(duty_percent > 100) tmp_duty = 100;duty = (uint32_t)(tmp_duty * PWM_PERIOD / 100.0F + 0.5F) + 1;TIM1_PWM_Start(duty);
}

在 py32_f0xx_hal_msp.c 中增加对复用管脚的初始化

/********************************************************************************* @file    py32f0xx_hal_msp.c* @author  MCU Application Team* @Version V1.0.0* @Date    2020-10-19* @brief   This file provides code for the MSP Initialization*          and de-Initialization codes.*******************************************************************************//* Includes ------------------------------------------------------------------*/
#include "main.h"/*** Initializes the Global MSP.*/void HAL_MspInit(void)
{BSP_LED_Init(LED_GREEN);BSP_PB_Init(BUTTON_USER,BUTTON_MODE_GPIO);
}/*** -----------------------------------------------------------------------* @name   : void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)* @brief  : 功能描述 :初始化TIM相关MSP* @param  : [in] htim, TIM handler pointer* @retval : void* @remark :* -----------------------------------------------------------------------
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM16){__HAL_RCC_TIM16_CLK_ENABLE();           //使能TIM16时钟HAL_NVIC_SetPriority(TIM16_IRQn, 0, 8); //设置中断优先级HAL_NVIC_EnableIRQ(TIM16_IRQn);         //使能TIM1中断return;}if(htim->Instance == TIM1){GPIO_InitTypeDef GPIO_InitStruct;__HAL_RCC_TIM1_CLK_ENABLE();                  // TIM1 时钟使能__HAL_RCC_GPIOA_CLK_ENABLE();                 // GPIOA 时钟使能GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;       // 复用GPIO_InitStruct.Pull = GPIO_PULLDOWN;         // 下拉GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速GPIO_InitStruct.Pin = GPIO_PIN_3;             // PA3 初始化成 TIM1_CH1GPIO_InitStruct.Alternate = GPIO_AF13_TIM1;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);}
}

在 py32_f0xx_hal_it.c 中增加对中断服务程序的重定向

extern TIM_HandleTypeDef  htim16;
extern TIM_HandleTypeDef  htim1;...
...// Override. For TIM1_PWM service
void TIM1_BRK_UP_TRG_COM_IRQHandler(void)
{HAL_TIM_IRQHandler(&htim1);
}...
...void TIM16_IRQHandler(void)
{HAL_TIM_IRQHandler(&htim16);
}

在 main.c 中添加对这些功能的调用

/**
* -------------------------------------------------------------------------
* @file   : int main(void)
* @brief  : main函数
* @param  : 无
* @retval : 无限循环,无返回值
* @remark : 
* -------------------------------------------------------------------------
*/
int main(void)
{HAL_Init();             // systick初始化SystemClock_Config();   // 配置系统时钟if(USART_Config() != HAL_OK) Error_Handler();         printf("[SYS_INIT] Debug port initilaized.\r\n");if(GPIO_Config() != HAL_OK) Error_Handler();          printf("[SYS_INIT] Board LED initilaized.\r\n");if(TIM16_Config() != HAL_OK) Error_Handler();printf("[SYS_INIT] Timer initialized.\r\n");if (TIM16_Start() != HAL_OK) Error_Handler();printf("[SYS_INIT] Timer started.\r\n");/***   Added for TIM1 PWM output*   By HardCoder_LLuo@2023-12-07*/if(TIM1_PWM_Config() != HAL_OK) Error_Handler();printf("[SYS_INIT] PWM initialized.\r\n");printf("\r\n+---------------------------------------+""\r\n|        PY32F003 MCU is ready.         |""\r\n+---------------------------------------+""\r\n");if (DBG_UART_Start() != HAL_OK) Error_Handler();TIM1_PWM_Output(0);//    while(BSP_PB_GetState(BUTTON_USER) != 0){}
//    TIM1_PWM_Output(20);while(BSP_PB_GetState(BUTTON_USER) != 0){}TIM1_PWM_Output(50);
//        
//    while(BSP_PB_GetState(BUTTON_USER) != 0){}
//    TIM1_PWM_Output(60);
//        
//    while(BSP_PB_GetState(BUTTON_USER) != 0){}
//    TIM1_PWM_Output(80);
//        
//    while(BSP_PB_GetState(BUTTON_USER) != 0){}
//    TIM1_PWM_Output(100);while (1){/***  For testing GPIO output*  2023-11-24*  Hard coder Luoyuan*/
#if(0)// Toggle LED3 in TIM16 IT service procedure instead.HAL_Delay(1000);HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
#endif}
}

代码新增部分实现对 TIM1_PWM 的初始化,然后将 TIM1 的 CHANNEL_1 拉低,等待按压一下开发板上的按钮,设置不同的 PWM 占空比,在示波器上观察这个输出通道的波形。

先前开发的功能仍然在使用,设置不同的中断优先级,避免中断之间的冲突。

实验结果

  • 启动后,在 XCOM 的显示如下图。

  • 启动信息显示正常,连续发送字符串,MCU 回显正常。
  • 板载的 LED 以 1Hz 的频率在翻转,说明原有的 TIM16 中断运行正常。
  • 查看按钮按下后,测量 GND 和 PA3 的波形如下,按顺序分别是占空比为 20%, 50%, 60%, 80% 和 100% 时的输出波形。

总结

利用 PY32F003 实现PWM输出的步骤:

  1. 选择时钟
  2. 设置端口复用
  3. 初始化高级定时器 TIM1
  4. 为 TIM1 的 OCx(x取值0,1,2,3) 通道设定占空比的值
  5. 启动(重启)这个通道

注意事项

  • 在 HAL_TIM_Base_MspInit 函数中,定义输出通道对应的 IO 管脚时,应根据外部硬件电路是否有上拉或者下拉来设置 GPIO_InitStructure.Pull 属性的值。在这里的实验当中,PWM输出的初始状态是全低电平(PWM关断的状态),并且这个 IO 管脚外部没有上拉电阻,那么这个 GPIO_InitStructure.Pull 属性就应该设置成 PULLDOWN,如果设置成了 PULLUP(默认值)就会在 PWM 通道进行初始化前产生一个高电平脉冲。在示波器上观察,这个高电平脉冲的宽度大约有 50us,如果是驱动一个灵敏的外部设备,例如一盏 LED 灯的话,足以让这盏灯突然闪亮一下了(有网友取名为“零点爆闪”现象,如果做到产品中,那是很 LOW 的哦)。相反,如果外部有上拉电阻,并且是使用 PWM2 模式输出的话,就应该将其初始化成 PULLUP。
  • 想要使 PWM 输出变成零,设置 PWM CCx 输出为0 和 PWM_PERIOD,都可以。这是因为比较器比较 0 和比较 PWM_PERIOD,两者之差一拍,就都能产生 Elapsed 的结果。
  • 和通用定时器相同,TIM1 的Period 和 Prescaler 决定了 PWM 波形的频率。零输出和满输出时,PWM 频率体现不出来。
  • TIM1 可以同时驱动 4 个通道的 PWM 输出,这里只列出了一个。

谬误之处,欢迎指正。

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

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

相关文章

DM8/达梦 数据库管理员使用手册详解

1.1DM客户端存放位置 Windows&#xff1a;DM数据库安装目录中tool文件夹和bin文件夹中。 Linux&#xff1a;DM数据库安装目录中tool目录和bin目录中。 1.2DM数据库配置助手 1.2.1Windows创建数据库 打开数据库配置助手dbca 点击创建数据库实例 选择一般用途 浏览选择数据库…

图中的最长环

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是处于什么原因&#xff0c;算法学习需要持续保持&#xff0c;今天让我们一起来看看这一道题目————图中的最长环&#xff0c;图论题目中比较常见的环路…

C语言笔试例题_指针专练30题(附答案解析)

C语言笔试例题_指针专练30题(附答案解析) 指针一直是C语言的灵魂所在&#xff0c;是掌握C语言的必经之路&#xff0c;收集30道C语言指针题目分享给大家&#xff0c;测试环境位64位ubuntu18.04环境&#xff0c;如有错误&#xff0c;恳请指出&#xff0c;文明讨论&#xff01;&am…

基于SSM+JSP网上订餐管理系统(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

蓝桥杯2021年5月青少组Python程序设计国赛真题

30 个人在一条船上,超载&#xff0c;需要 15 人下船于是人们排成一队&#xff0c;排队的位置即为他们的编号。报数,从1开始,数到9的人下船。如此循环,直到船上仅剩15 人为止&#xff0c;问都有哪些编号的人下船了呢? 2】判断101-200之间有多少个素数&#xff0c;并输出所有素数…

Leetcode每日一题

https://leetcode.cn/problems/binary-tree-preorder-traversal/ 这道题目需要我们自行进行创建一个数组&#xff0c;题目也给出我们需要自己malloc一个数组来存放&#xff0c;这样能达到我们遍历的效果&#xff0c;我们来看看他的接口函数给的是什么。 可以看到的是这个接口函…

5_CSS三大特性盒子模型

第5章-盒子模型【比屋教育】 本课目标&#xff08;Objective&#xff09; 掌握CSS三大特性理解什么是盒子模型掌握内边距padding的用法掌握外边距margin的用法 1. CSS的层叠&#xff0c;继承&#xff0c;优先级 1.1 CSS层叠 层叠&#xff1a;是指多个CSS样式叠加到同一个元…

Web(8)SQL注入

Web网站&#xff08;对外门户&#xff09; 原理&#xff1a;not>and>or(优先级) 一.低级注入 order by的作用是对字段进行排序&#xff0c;如order by 5&#xff0c;根据第五个字段 进行排序&#xff0c;如果一共有4个字段&#xff0c;输入order by 5系统就会报错不 …

详细介绍开源固件-TF-A

什么是TF-A&#xff1f; TF-A&#xff08;Trusted Firmware-A&#xff09;是一种用于嵌入式系统的开源固件&#xff0c;而不是Linux的一部分。TF-A主要用于ARM架构的处理器和设备&#xff0c;它提供了一组安全和可信任的软件组件&#xff0c;用于引导和初始化系统。 如下是其…

GD32F30X-RT-Thread学习-线程管理

1. 软硬件平台 GD32F307E-START Board开发板MDK-ARM Keil 2.RT-Thread Nano 3.RT-Thread 内核学习-线程管理 ​ 在多线程操作系统中&#xff0c;可以把一个复杂的应用分解成多个小的、可调度的、序列化的程序单元&#xff0c;当合理地划分任务并正确地执行时&#xff0c;这…

qt可以详细写的项目或技术

1.QT 图形视图框架 2.QT 模型视图结构 3.QT列表显示大量信息 4.QT播放器 5.QT 编解码 6.QT opencv

Bypass open_basedir

讲解 open_basedir是php.ini中的一个配置选项&#xff0c;可用于将用户访问文件的活动范围限制在指定的区域。 假设open_basedir/var/www/html/web1/:/tmp/&#xff0c;那么通过web1访问服务器的用户就无法获取服务器上除了/var/www/html/web1/和/tmp/这两个目录以外的文件。…

洛谷 P8674 [蓝桥杯 2018 国 B] 调手表

文章目录 [蓝桥杯 2018 国 B] 调手表题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 题意解析CODE分析一下复杂度 [蓝桥杯 2018 国 B] 调手表 题目描述 小明买了块高端大气上档次的电子手表&#xff0c;他正准备调时间呢。 在 M78 星云&#xff0c;时间的计量…

JVM虚拟机:命令行查看JVM垃圾回收器的执行信息

在eclipse中打开命令行窗口 window->show view->Terminal 这样就打开了Terminal窗口&#xff0c;效果如下所示&#xff1a; java -XX:PrintCommandLineFlags -version 这个命令可以查看一些配置信息&#xff0c;其中最重要的配置信息就是&#xff0c;当前使用的G1回收器…

键盘打字盲打练习系列之成为大师——5

一.欢迎来到我的酒馆 盲打&#xff0c;成为大师&#xff01; 目录 一.欢迎来到我的酒馆二.关于盲打你需要知道三.值得收藏的练习打字网站 二.关于盲打你需要知道 盲打系列教程&#xff0c;终于写到终章了。。。一开始在看网上视频&#xff0c;看到up主熟练的打字技巧&#xff…

LabVIEW与Tektronix示波器实现电源测试自动化

LabVIEW与Tektronix示波器实现电源测试自动化 在现代电子测试与测量领域&#xff0c;自动化测试系统的构建是提高效率和精确度的关键。本案例介绍了如何利用LabVIEW软件结合Tektronix MDO MSO DPO2000/3000/4000系列示波器&#xff0c;开发一个自动化测试项目。该项目旨在自动…

MYSQL练题笔记-高级查询和连接-指定日期的产品价格

这依旧是中等题&#xff0c;想了好久&#xff0c;终于理解了很开心&#xff01; 一、题目相关内容 1&#xff09;相关的表和题目 2&#xff09;帮助理解题目的示例&#xff0c;提供返回结果的格式 二、自己初步的理解 题目是找出2019-08-16 时全部产品的价格&#xff0c;所以…

使用最小花费爬楼梯

1.状态表示 2.状态转移方程 3.初始化 保证填表时&#xff0c; 不越界 4.填表顺序 从左往右 5.返回值 解法2&#xff1a; 1.状态表示 2.状态转移方程 3.初始化 4.填表 从右往左 5.返回值 min( dp[0] , dp[1] ) ----------------------------------------------------…

java+springboot+ssm学生社团管理系统76c2e

本系统包括前台和后台两个部分。前台主要是展示社团列表、社团风采、社团活动、新闻列表等&#xff0c;前台登录后进入个人中心&#xff0c;在个人中心能申请加入社团、查看参加的社团活动等&#xff1b;后台为管理员与社团负责人使用&#xff0c;应用于对社团的管理及内容发布…

常见统计学习方法特点总结

1. 概述 方法适用问题模型特点模型类型学习策略损失函数学习算法1感知机二分类分离超平面判别模型极小化误分点到超平面距离误分点到超平面距离SGD2KNN多分类&#xff0c;回归特征空间&#xff0c;样本点判别模型---3朴素贝叶斯多分类特征与类别的联合概率分布&#xff0c;条件…