IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> M-Arch(9)第八个示例:定时器和PWM -> 正文阅读

[嵌入式]M-Arch(9)第八个示例:定时器和PWM

前言

回顾下之前的章节:

  • 第一章节中我们描述了整个框架的核心设计思路以及主要的文件架构

  • 第二章节中我们基于一个简单的定时器OS实现了串口的数据打印,并完成了通用crc模块的设计和测试

  • 第三章节中我们给出了真随机数和伪随机数的概念和代码示例,并在架构上对接口进行了重构

  • 第四章节中我们回顾了FMC的基本知识,并给出了示例,后面我们将在设计IAP的时候再次使用到FMC

  • 第五章节中我们使用ADC和DMA搭建了一个通用的采样框架,并通过串口给出了采样的数据示例

  • 第六章节中我们总结了DAC的基本使用方法,并通过DAC生成了任意频率的正弦波,三角波和方波

  • 第七章节中我们总结下时钟的概念,并给出了获取系统中各模块的时钟频率的代码

  • 第八章节中我们介绍了如何通过串口的DMA来实现串口数据的收发

本文我们再介绍下定时器的使用,以及如何产生普通占空比PWM以及互补带死区的PWM。

定时器

定时是一个很重要的功能,人类无法改变时间但可以想办法掌控利用时间。

人类最早使用的工具是沙漏或水漏,这一瓢水漏完就是一个时辰,该下课了;这一桶水漏完就是三个时辰,该下班了。

在沙漏或者水漏的例子中,时间是如何计算的呢?

t = count * △t

△t是一坨沙/一滴水落下的时间,count为计数,这就是定时器的基本原理。

在嵌入式软件领域,定时器是系统工作的基础,什么时候该做什么事情,要求的极其精确,它的工作原理就是时基+计数。

一般参数定义为prescaler和period,假设MCU的时钟频率为100MHz,prescaler=999,period=9,那么:

  • 定时器的频率为100MHz/(999+1) = 0.1MHz = 100KHz 即 10us

  • 定时器的周期为 10us *(9+1) = 100us

我们软件就可以基于这个100us的定时进行业务处理。

具体代码可以参见:串口和CRC那一篇。

PWM

PWM的全称是脉冲宽度调制(Pulse Width Modulation),从应用的角度来理解,就是频率可以控制,宽度也可以控制的方波信号;在工业控制领域,可以通过PWM来调节明暗,快慢,大小,使用范围极其广泛。

固定占空比的PWM

PWM的介绍比较多了,直接给代码:

GD32:

static?uint32_t?get_alt_func_num(uint32_t?TIMx)
{
????switch?(TIMx)
????{
????????case?TIMER0:
????????case?TIMER1:
????????????return?GPIO_AF_1;
????????case?TIMER2:
????????case?TIMER3:
????????case?TIMER4:
????????????return?GPIO_AF_2;
????????case?TIMER7:
????????case?TIMER8:
????????case?TIMER9:
????????case?TIMER10:
????????????return?GPIO_AF_3;
????????default:
????????????return?GPIO_AF_9;
????}
}

static?void?timerx_PWM_init(uint32_t?TIMx,?uint16_t?TIMCHx,?rcu_periph_enum?rcu,?uint32_t?gpio,?uint16_t?pin,?uint16_t?pulse)
{
????timer_oc_parameter_struct?timer_ocintpara;

????gpio_init(rcu,?gpio,?GPIO_MODE_AF,?get_alt_func_num(TIMx),?GPIO_OTYPE_PP,?GPIO_OSPEED_2MHZ,?pin);

????timer_ocintpara.outputstate??=?TIMER_CCX_ENABLE;
????timer_ocintpara.outputnstate?=?TIMER_CCXN_DISABLE;
????timer_ocintpara.ocpolarity???=?TIMER_OC_POLARITY_HIGH;
????timer_ocintpara.ocnpolarity??=?TIMER_OCN_POLARITY_HIGH;????
????timer_ocintpara.ocidlestate??=?TIMER_OC_IDLE_STATE_LOW;
????timer_ocintpara.ocnidlestate?=?TIMER_OCN_IDLE_STATE_LOW;
????timer_channel_output_config(TIMx,?TIMCHx,?&timer_ocintpara);
????timer_channel_output_pulse_value_config(TIMx,?TIMCHx,?pulse);
????timer_channel_output_mode_config(TIMx,?TIMCHx,?TIMER_OC_MODE_PWM0);
????timer_channel_output_shadow_config(TIMx,?TIMCHx,?TIMER_OC_SHADOW_DISABLE);

????timer_primary_output_config(TIMx,?ENABLE);
????timer_auto_reload_shadow_enable(TIMx);
????timer_enable(TIMx);
}

STM32:

static?void?get_oc_func(uint32_t?TIMCHx,?void?(**TIM_OCInit)(TIM_TypeDef*,?TIM_OCInitTypeDef*),?void?(**TIM_OCPreloadConfig)(TIM_TypeDef*,?uint16_t))
{
????switch?(TIMCHx)
????{
????????case?TIM_Channel_1:
????????????*TIM_OCInit?=?TIM_OC1Init;
????????????*TIM_OCPreloadConfig?=?TIM_OC1PreloadConfig;
????????break;
????????case?TIM_Channel_2:
????????????*TIM_OCInit?=?TIM_OC2Init;
????????????*TIM_OCPreloadConfig?=?TIM_OC2PreloadConfig;
????????break;
????????case?TIM_Channel_3:
????????????*TIM_OCInit?=?TIM_OC3Init;
????????????*TIM_OCPreloadConfig?=?TIM_OC3PreloadConfig;
????????break;
????????case?TIM_Channel_4:
????????????*TIM_OCInit?=?TIM_OC4Init;
????????????*TIM_OCPreloadConfig?=?TIM_OC4PreloadConfig;
????????break;
????}
}

static?void?timerx_PWM_init(TIM_TypeDef?*TIMx,?uint16_t?TIMCHx,?uint32_t?rcc,?GPIO_TypeDef?*gpio,?uint16_t?pin,?uint16_t?pulse)
{
????TIM_OCInitTypeDef?TIM_OCInitStructure;
????void?(*TIM_OCInit)(TIM_TypeDef*,?TIM_OCInitTypeDef*);
????void?(*TIM_OCPreloadConfig)(TIM_TypeDef*,?uint16_t);

????gpio_init(rcc,?gpio,?GPIO_Mode_AF_PP,?GPIO_Speed_2MHz,?pin);

????TIM_OCInitStructure.TIM_OutputState?=?TIM_OutputState_Enable;
????TIM_OCInitStructure.TIM_OutputNState?=?TIM_OutputNState_Disable;
????TIM_OCInitStructure.TIM_OCPolarity?=?TIM_OCPolarity_High;
????TIM_OCInitStructure.TIM_OCNPolarity?=?TIM_OCNPolarity_High;
????TIM_OCInitStructure.TIM_OCIdleState?=?TIM_OCIdleState_Reset;
????TIM_OCInitStructure.TIM_OCNIdleState?=?TIM_OCIdleState_Reset;
????TIM_OCInitStructure.TIM_OCMode?=?TIM_OCMode_PWM1;
????TIM_OCInitStructure.TIM_Pulse?=?pulse;
????get_oc_func(TIMCHx,?&TIM_OCInit,?&TIM_OCPreloadConfig);
????TIM_OCInit(TIMx,?&TIM_OCInitStructure);
????TIM_OCPreloadConfig(TIMx,?TIM_OCPreload_Enable);
????TIM_ARRPreloadConfig(TIMx,?ENABLE);
????TIM_CtrlPWMOutputs(TIMx,?ENABLE);
????TIM_Cmd(TIMx,?ENABLE);
}

占空比30%:e32b6761afe08cafac026844152b7f9f.png

占空比30%和50%的波形:

a8df3d8ca1eed417fdc7f132947509d8.gif
占空比30%和50%的波形

互补带死区的PWM

代码比较简单,这里说一下死区时间的配置。

芯片手册中的描述:

9648b247a496efac3a03accb17053354.png
死区芯片手册描述

推导下计算公式:83d43fb7fa1c79653aaf26f60fcb9924.png

画个图:

f962613bcaa809efad960f719f385400.png
死区公式
  • 死区配置为0xE0,死区时间为512*T = 512/100KHz = 5.12us

  • 死区配置为0xFF,死区时间为1008*T = 1008/100KHz = 10.08us

GD32代码:

static?void?timerx_PWM_ON_init(uint32_t?TIMx,?uint16_t?TIMCHx,?rcu_periph_enum?rcu,?uint32_t?gpio,?uint16_t?pin,?rcu_periph_enum?rcuN,?uint32_t?gpioN,?uint16_t?pinN,?uint16_t?pulse,?uint16_t?deadtime)
{
????timer_oc_parameter_struct?timer_ocintpara;
????timer_break_parameter_struct?breakpara;

????gpio_init(rcu,?gpio,?GPIO_MODE_AF,?get_alt_func_num(TIMx),?GPIO_OTYPE_PP,?GPIO_OSPEED_2MHZ,?pin);
????gpio_init(rcuN,?gpioN,?GPIO_MODE_AF,?get_alt_func_num(TIMx),?GPIO_OTYPE_PP,?GPIO_OSPEED_2MHZ,?pinN);

????timer_ocintpara.outputstate??=?TIMER_CCX_ENABLE;
????timer_ocintpara.outputnstate?=?TIMER_CCXN_ENABLE;
????timer_ocintpara.ocpolarity???=?TIMER_OC_POLARITY_HIGH;
????timer_ocintpara.ocnpolarity??=?TIMER_OCN_POLARITY_HIGH;??
????timer_ocintpara.ocidlestate??=?TIMER_OC_IDLE_STATE_LOW;
????timer_ocintpara.ocnidlestate?=?TIMER_OCN_IDLE_STATE_LOW;
????timer_channel_output_config(TIMx,?TIMCHx,?&timer_ocintpara);
????timer_channel_output_pulse_value_config(TIMx,?TIMCHx,?pulse);
????timer_channel_output_mode_config(TIMx,?TIMCHx,?TIMER_OC_MODE_PWM0);
????timer_channel_output_shadow_config(TIMx,?TIMCHx,?TIMER_OC_SHADOW_DISABLE);

????timer_break_struct_para_init(&breakpara);
????breakpara.deadtime?=?deadtime;
????breakpara.outputautostate?=?TIMER_OUTAUTO_ENABLE;
????timer_break_config(TIMx,?&breakpara);

????timer_primary_output_config(TIMx,?ENABLE);
????timer_auto_reload_shadow_enable(TIMx);
????timer_enable(TIMx);
}

STM32代码:

static?void?timerx_PWM_ON_init(TIM_TypeDef?*TIMx,?uint16_t?TIMCHx,?uint32_t?rcc,?GPIO_TypeDef?*gpio,?uint16_t?pin,?uint32_t?rccN,?GPIO_TypeDef?*gpioN,?uint16_t?pinN,?uint16_t?pulse,?uint16_t?deadtime)
{
????TIM_OCInitTypeDef?TIM_OCInitStructure;
????TIM_BDTRInitTypeDef?TIM_BDTRInitStructure;
????void?(*TIM_OCInit)(TIM_TypeDef*,?TIM_OCInitTypeDef*);
????void?(*TIM_OCPreloadConfig)(TIM_TypeDef*,?uint16_t);

????gpio_init(rcc,?gpio,?GPIO_Mode_AF_PP,?GPIO_Speed_2MHz,?pin);
????gpio_init(rccN,?gpioN,?GPIO_Mode_AF_PP,?GPIO_Speed_2MHz,?pinN);

????TIM_OCInitStructure.TIM_OutputState?=?TIM_OutputState_Enable;
????TIM_OCInitStructure.TIM_OutputNState?=?TIM_OutputNState_Enable;
????TIM_OCInitStructure.TIM_OCPolarity?=?TIM_OCPolarity_High;
????TIM_OCInitStructure.TIM_OCNPolarity?=?TIM_OCNPolarity_High;
????TIM_OCInitStructure.TIM_OCIdleState?=?TIM_OCIdleState_Reset;
????TIM_OCInitStructure.TIM_OCNIdleState?=?TIM_OCIdleState_Reset;
????TIM_OCInitStructure.TIM_OCMode?=?TIM_OCMode_PWM2;
????TIM_OCInitStructure.TIM_Pulse?=?pulse;
????get_oc_func(TIMCHx,?&TIM_OCInit,?&TIM_OCPreloadConfig);
????TIM_OCInit(TIMx,?&TIM_OCInitStructure);
????TIM_OCPreloadConfig(TIMx,?TIM_OCPreload_Enable);

????TIM_BDTRStructInit(&TIM_BDTRInitStructure);
????TIM_BDTRInitStructure.TIM_DeadTime?=?deadtime;
????TIM_BDTRInitStructure.TIM_AutomaticOutput?=?TIM_AutomaticOutput_Enable;
????TIM_BDTRConfig(TIMx,?&TIM_BDTRInitStructure);

????TIM_CtrlPWMOutputs(TIMx,?ENABLE);
????TIM_ARRPreloadConfig(TIMx,?ENABLE);
????TIM_Cmd(TIMx,?ENABLE);
}

在STM32中,复用的管脚需要配置,示例:

void?timer1_ch2_pwm_ON_init(void)
{
????RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,?ENABLE);
????GPIO_PinRemapConfig(GPIO_FullRemap_TIM1,?ENABLE);
????timerx_PWM_ON_init(TIM1,?TIM_Channel_2,?RCC_APB2Periph_GPIOE,?GPIOE,?GPIO_Pin_11,?RCC_APB2Periph_GPIOE,?GPIOE,?GPIO_Pin_10,?70,?0xFF);
}

互补PWM波形:

f0784b37847071f689d5953218f3b834.gif
互补PWM

死区时间5.12us:

9f17a6964c998b5a01315b4fb3e04298.png
死区时间5.12us

死区时间10.08us:

4e4235b017a7fdc9b1e6d43145de4024.png
死区时间10.08us

--EOF--

例行求粉,谢谢!

2adbd812641ff947499fbd9dbaf18373.png
求粉
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-04 13:00:11  更:2021-10-04 13:01:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:01:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码