定时器PWM输出

news2024/11/26 17:44:25

目录

介绍

PWM占空比

框图

输出比较

通道 1 输出比较功能为例

PWM 输出模式

PWM 边沿对齐模式

hal库代码

标准库代码


介绍

       脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微
处理器的数字输出来对模拟电路进行控制的的技术。

        高级控制定时器 (TIM1 和 TIM8) 和通用定时器在基本定时器的基础上引入了外部引脚,可以实现输入捕获和输出比较功能。高级控制定时器比通用定时器增加了可编程死区互补输出、重复计
数器、带刹车 (断路) 功能,这些功能都是针对工业电机控制方面。主要常用的输入捕获和输出比较功能。

        如果把灯亮看作100%,灯灭看作0%,要实现50%的亮度,可以在某个单位时间里亮灯50%时间、灭灯50%时间,只要这个单位时间够小,由于人眼具有视觉暂留效应,就会感觉整个灯是一直亮着,且亮度只要原来的一半。PWM实质就是GPIO不断翻转输出高、低电平,这个效果可以写代码控制GPIO产生,但这样就会占用CPU, CPU就不方便做其它事情。此时可以利用定时器,设置好翻转时间,让其自动控制GPIO翻转,无需CPU再参与。

PWM占空比

        首先定时器从0开始计数,在0~t1时间段, TIMx_CNT<TIMx_CCR1,输出低电平;在t1~t2时间段, TIMx_CNT>TIMx_CCR1,输出高电平; t2时, TIMx_CNT=TIMx_ARR计数器溢出,重新从0开始,如此循环

        在一个周期内,高电平占整个信号周期的百分比,称之为占空比

框图

框图

输出比较

        输出比较就是通过定时器的外部引脚对外输出控制信号,有冻结、将通道 X(x=1,2,3,4)设置为匹配时输出有效电平、将通道 X 设置为匹配时输出无效电平、翻转、强制变为无效电平、强制变为有效电平、 PWM1 和 PWM2 这八种模式,具体使用哪种模式由寄存器 CCMRx 的位 OCxM[2:0]配置。其中 PWM 模式是输出比较中使用的最多。

通道 1 输出比较功能为例

        灰色阴影部分是输入捕获功能部分,右边没有阴影部分就是输出比较功能部分。首先程序员写 CCR1 寄存器,即写入比较值。这个比较值需要转移到对应的捕获/比较影子寄存器后才会真正生效。什么条件下才能转移?图阴影中可以看到 compare_transfer 旁边的与门,需要满足三个条件: CCR1 不在写入操作期间、 CC1S[1:0] = 0 配置为输出、 OC1PE 位置0(或者 OC1PE 位置 1,并且需要发生更新事件,这个更新事件可以软件产生或者硬件产生)。
        当 CCR1 寄存器的值转移到其影子寄存器后,新的值就会和计数器的值进行比较,它们的
比较结果将会通过输出控制部分影响定时器的输出。

PWM 输出模式

        PWM 输出就是对外输出脉宽(即占空比)可调的方波信号,信号频率由自动重装寄存器 ARR 的值决定,占空比由比较寄存器 CCR 的值决定

PWM 边沿对齐模式

        在递增计数模式下,计数器从 0 计数到自动重载值(TIMx_ARR 寄存器的内容),然后重新从 0开始计数并生成计数器上溢事件

        在边沿对齐模式下,计数器 CNT 只工作在一种模式,递增或者递减模式。这里我们以 CNT 工作在递增模式为例,在中, ARR=8, CCR=4, CNT 从 0 开始计数,当 CNT<CCR 的值时, OCxREF为有效的高电平,于此同时,比较中断寄存器 CCxIF 置位。当 CCR<=CNT<=ARR 时, OCxREF为无效的低电平。然后 CNT 又从 0 开始计数并生成计数器上溢事件,以此循环往复。
 

hal库代码

uint8_t led = 0;
TIM_HandleTypeDef g_pwm;
/*计数器CNT从0到CCR, 输出低电平,LED灯亮
 *计数器CNT从CCR到ARR,输出高电平, LED灯灭
 *CCR值越小, 占空比越大,灯越暗, CCR值越大,占空比越小,灯越亮, CCR值与亮度成正比
 *定时器时钟频率为72MHz / 360 = 200kHz。
 *计算得到每次计数的时间为1 / 200kHz = 5us。
 *计算定时器的溢出时间可以使用下面的公式:
 *Tout = ((ARR + 1) / TIM_CLK)= ((2000) / 200000) = 0.01秒 = 10毫秒
 */
/*工作参数*/
void tim_pwm_init(uint32_t prescaler, uint32_t period)
{
    TIM_OC_InitTypeDef pwm_tim;
    TIM_ClockConfigTypeDef tim_clk_conf;
    
    /*72MHz 经过 360 分频后,定时器时钟为 200KHz,即计数器每间隔 5us 计数一次,从 0 计数到 ARR,经历 10ms*/
    g_pwm.Instance = TIM2;
    g_pwm.Init.Prescaler = prescaler;/*分频系数  360-1*/
    g_pwm.Init.Period = period;/*自动重装载值   ARR=2000-1*/
    g_pwm.Init.CounterMode = TIM_COUNTERMODE_UP;/*向上计数*/
    
    g_pwm.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;/*定时器时钟不从 HCLK 分频*/
    g_pwm.Init.AutoReloadPreload = TIM_AUTOMATICOUTPUT_DISABLE;/*不自动重新装载*/
    HAL_TIM_PWM_Init(&g_pwm);/*初始化pwm*/
    
    tim_clk_conf.ClockSource = TIM_CLOCKSOURCE_INTERNAL;/*选用内部时钟作为定时器时钟源*/
    HAL_TIM_ConfigClockSource(&g_pwm ,&tim_clk_conf);
    /*pwm配置*/
    pwm_tim.OCMode = TIM_OCMODE_PWM1;/*模式1*/
    pwm_tim.Pulse = led; /*比较值.占空比一半,后面代码再修改*/
    pwm_tim.OCPolarity = TIM_OCPOLARITY_LOW;/*极性,LED灯对应低电平有效*/
    pwm_tim.OCFastMode = TIM_OCFAST_DISABLE;/*输出比较快速使能禁止(仅在 PWM1 和 PWM2 可设置*/
    HAL_TIM_PWM_ConfigChannel(&g_pwm, &pwm_tim, TIM_CHANNEL_2);/*通道2*/
    HAL_TIM_PWM_Start(&g_pwm, TIM_CHANNEL_2);
    /*启动中断*/
    HAL_TIM_PWM_Start_IT(&g_pwm ,TIM_CHANNEL_2 );
}
/*msp初始化*/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        GPIO_InitTypeDef g_led;
        __HAL_RCC_GPIOA_CLK_ENABLE();/*使能gpio时钟*/
        __HAL_RCC_TIM2_CLK_ENABLE();/*使能tim2时钟*/
        
        g_led.Pin = GPIO_PIN_1;
        g_led.Mode = GPIO_MODE_AF_PP;/*复用推挽*/
        g_led.Pull = GPIO_PULLUP;
        g_led.Speed = GPIO_SPEED_FREQ_HIGH;
        
        /*初始化灯*/
        HAL_GPIO_Init(GPIOA,&g_led);
        
        HAL_NVIC_SetPriority(TIM2_IRQn,0,0);/*配置定时器中断优先级*/
        HAL_NVIC_EnableIRQ(TIM2_IRQn);/*使能 TIM2 中断*/
    }
}

/*中断函数*/
void TIM2_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_pwm);
}

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        /*修改对应通道的比较寄存器 CCR 的值(占空比)*/
        __HAL_TIM_SET_COMPARE(&g_pwm , TIM_CHANNEL_2 , led*2000/255);
    }
}
/*led呼吸灯*/
void pwm_led(void)
{
    uint8_t flag = 0;
    
    /*波形0到255递增,255到0递减*/
    if(!flag)
    {
        led=led+1;/*递增*/
        if(led == 255)
            flag =1;
    }
    else{
        led=led-1;/*递减*/
        if(led == 0)
            flag =0;
    }
    HAL_Delay(4);
}

标准库代码

void pwm_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    /*开启时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;/*复用推挽*/
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    
    /*开启时钟*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    /*使用内部时钟*/
    TIM_InternalClockConfig(TIM2);
    /*初始化定时器*/
     /*PWM频率: frq = CK_PSC/(PSC + 1)/(ARR+1)= 72MHZ/ = 70Mhz/(720-1+1)/(100-1+1) = 1000
     *PWM占空比:duty = CCR/(ARR+1) = 50/(100-1+1) = 50%
     *PWM分辨率:reso = 1/(ARR + 1) = 1/(100-1+1)  = 1%
     */
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;/*向上计数*/
    TIM_TimeBaseInitStruct.TIM_Period = 100-1;/*arr自动重装值*/
    TIM_TimeBaseInitStruct.TIM_Prescaler = 720-1;/*psc预分频值*/
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;/*重复计数器,高级定时器才有,这里给0*/
    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
   
    TIM_OCInitTypeDef TIM_OCInitStruct;
    TIM_OCStructInit(&TIM_OCInitStruct);/*初始化TIM_OCInitStruct*/
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;              /*输出比较模式*/
    TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCPolarity_High;     /*输出比较的极性*/
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;/*使能输出*/
    TIM_OCInitStruct.TIM_Pulse = 0;/*ccr捕获比较寄存器值,后面设置*/
    TIM_OC2Init(TIM2, &TIM_OCInitStruct);/*使用通道*/
    
    /*开启Tim*/
    TIM_Cmd(TIM2,ENABLE);
}

void  pwm_set_ccr(void)
{
    uint8_t i;
    for(i=0;i<=100;i++)/*从0到100呼吸变亮*/
    {
        TIM_SetCompare2(TIM2,i);/*设置定时器通道的捕获比较寄存器(CCR1)的值*/
        Delay_ms(10);
    }
    
    for(i=0;i<=100;i++)/*从0到100呼吸变暗*/
    {
        TIM_SetCompare2(TIM2,100-i);/*设置ccr的值*/
        Delay_ms(10);
    }
}

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

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

相关文章

Android启动优化-全文详细

一个应用App的启动速度能够影响用户的首次体验&#xff0c;通常用户期望app响应和加载速度越快越好。一个启动速度慢的app很可能会给用户留下不好的印象&#xff0c;除了导致用户在应用市场上的打分低之外&#xff0c;很有可能导致致用户直接卸载。这种糟糕的体验可能会导致用户…

[译]CSS Animations 与 CSS Transitions

本文译者为 360 奇舞团前端开发工程师原文标题&#xff1a;CSS Animations Versus CSS Transitions原文作者&#xff1a;Kirupa Chinnathambi原文出处&#xff1a;《Creating Web Animations: Bringing Your UIs to Life》 在 CSS 中有两种设置动画的方式即 CSS animations 和 …

Modelsim 使用教程(3)——Projects

目录 一、概述 二、设计文件及tb 2.1 设计文件 counter.v 2.2 仿真文件 tcounter.v 三、操作流程 3.1 Create a New Project&#xff08;创建一个新的工程&#xff09; 3.2 Add Objects to the Project&#xff08;把代码加入项目&#xff09; 3.3 Compile the …

vue+vant图片压缩后上传

vuevant图片压缩后上传 vue文件写入 <template><div class"home"><van-field input-align"left"><template #input><van-uploaderv-model"fileList.file":after-read"afterRead":max-count"5":…

电子沙盘数字沙盘地理信息开发教程第17课

M3DGIS电子沙盘数字沙盘地理信息开发教程第17课新增加属性在MTGIS3d控件 public bool ShowFLGrid;//是否显 示方里网格。 public bool Atmosphere;//是否显示大气圈。&#xff08;因为WPF不支持shader功能&#xff0c;所以效果嘛。。。&#xff09; 在SDK中提供底层的模型访问接…

美观且可以很方便自定义的MATLAB绘图颜色

函数介绍 主函数是draw_test&#xff0c;用于测试函数。 draw_h是函数&#xff0c;用于给Matlab提供美观且可以很方便自定义的绘图颜色。 draw_h函数介绍 这是一个带输入输出的函数&#xff0c;输入1/2/3&#xff0c;输出下面三种颜色库的配色&#xff0c;每种库均有五种颜色…

开发板挂载 Ubuntu 的 NFS 目录

前言 使用的开发板为韦东山老师的 IMX6ULL 目录 什么是 NFS 协议&#xff1f; 为什么要挂载 Ubuntu 的 nfs 目录&#xff1f; 开发板挂载 Ubuntu 的 NFS 目录 步骤 1. 确定 ubuntu 的桥接网卡 IP 2. 判断是否开权限了 3. 判断是否安装并启动 NFS 服务 4. 在开发板上执…

C语言从入门到精通之【第一个程序hello world】

编程步骤 通常&#xff0c;我们按照以下步骤进行 确立目标设计程序编写代码编译程序运行程序测试&调试修改维护 输出hello world 每个学编程的人都会从最经典的【输出hello world】开始。 https://lightly.teamcode.com/ 我们可以使用这个在线IDE学习C语言。 代码很简…

1. PPT高效初始化设置

1. PPT高效初始化设置 软件安装&#xff1a;Office 2019 主题和颜色 颜色可以在白天与黑夜切换&#xff0c;护眼 切换成了黑色 撤回次数 撤回次数太少&#xff0c;只有20次怎么办 自动保存 有时忘记保存就突然关闭&#xff0c;很需要一个自动保存功能 图片压缩 图…

Android Studio打包AAR

注意 依赖的Android Studio版本为4.2.2 更高的Android Studio版本使用方法可能有所不同&#xff0c;gradle的版本和gradle plugins的版本都会影响使用方式。 基于此&#xff0c;本文只能作为参考&#xff0c;而不能作为唯一答案&#xff0c;如果要完全依赖本文&#xff0c;则…

Python接口自动化测试 —— Requests库学习

安装&#xff1a; pip install requests 例子&#xff1a; import requests r requests.get(http://www.baidu.com) print r.status_code print type(r) print r.cookies运行程序&#xff0c;得到结果&#xff1a; 运行程序&#xff0c;得到结果&#xff1a; 200 <cla…

sort的第三个参数与priority_queue的第三个模板参数

sort 在C的标准库中&#xff0c;std::sort是一个用于对容器的元素进行排序的算法。它接受三个参数&#xff1a; 1、需要排序的容器的起始迭代器&#xff08;包含&#xff09;。 2、需要排序的容器的结束迭代器&#xff08;不包含&#xff09;。 3、可选参数&#xff0c;用于指…

webgl入门-基础三角形绘制

背景 最近工作上频繁接触webgl&#xff0c;因为不熟悉每每看到shader中的语法总感觉脑袋大&#xff0c;所以打算开始从零学习一下webgl&#xff0c;文章只做记录学习历程&#xff0c;那就直接开始吧&#xff01; 开始 可以配合着这个文章食用。 我还是对webgl有一些概念的&…

【云备份|| 日志 day2】FileUtil JsonUtil

这几天感冒了&#xff0c;稍微落下了进度 util工具 fileUtil&#xff08;文件操作类&#xff09; 在客户端&#xff0c;又或者是在服务端&#xff0c;本质是都是对文件的读写和管理&#xff0c;所以有必要封装一个文件操作类。 class FileUtil{ private:std::string _name; …

Unity Editor工具,导出unitypackage可选择是否包含脚本

概述 Unity自带的Export Package...功能&#xff0c;如果选中资源中包含脚本&#xff0c;或者Prefab挂载了自定义的脚本。在之后弹出的选择框内&#xff0c;如果勾选了Include dependencies会将整个项目所有的脚本全部都包含在内。等于导入了很多不相关的代码。如果取消勾选In…

Express框架开发接口之req.params、req.query与req.body的区别

1.req.query 用于get请求 http://localhost:3000/user?usernameljz&age1 获取 URL 中携带的查询参数 通过req.query对象&#xff0c;可以访问到客户端通过查询字符串的形式&#xff0c;发送到服务器的参数&#xff1a; 2.req.params 用于get请求 url路径为&#xff…

Ubuntu MySQL客户端功能介绍(mysql-client)mysql命令(mysql客户端命令)数据库导出、数据库导入

文章目录 Ubuntu MySQL客户端(mysql-client)功能介绍MySQL客户端与服务端服务器端&#xff08;MySQL Server&#xff09;客户端&#xff08;MySQL Client&#xff09; 安装MySQL客户端连接到MySQL服务器&#xff08;mysql -h host -u user -p&#xff09;执行SQL查询批处理模式…

SpringCloud-Alibaba-Seata

SpringCloud-Alibaba-Seata 注意&#xff1a;最好使用JDK1.8&#xff0c;使用JDK17整合seata会出现一些问题&#xff01;&#xff01;&#xff01; Docker部署Seata1.5.2 1&#xff1a;拉取Seata1.5.2镜像&#xff1a; docker pull seataio/seata-server:1.5.22&#xff1a;在…

C/C++奇数求和 2021年3月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C奇数求和 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C奇数求和 2021年3月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 计算非负整数 m 到 n&#xff08;包括m 和 n &#xff…

驱动开发11-2 编写SPI驱动程序-点亮数码管

驱动程序 #include <linux/init.h> #include <linux/module.h> #include <linux/spi/spi.h>int m74hc595_probe(struct spi_device *spi) {printk("%s:%d\n",__FILE__,__LINE__);char buf[]{0XF,0X6D};spi_write(spi,buf,sizeof(buf));return 0; …