文章目录
什么是pwm波?pwm就是脉冲宽度调制,就是占空比可变的脉冲波形,通过改变占空比,输出不同的pwm波,就能实现许多有趣的功能,比如说我们生活中常见的呼吸灯就是通过这样实现的。接下来我们就以呼吸灯为例,学习stm32通过定时器输出pwm波和io口模拟输出pwm波。
控制方式就是对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替正弦波或所需要的波形。也就是在输出波形的半个周期产生多个脉冲,使各个脉冲的等值电压为正弦波形,所获得的输出平滑且低次谐波少,按一定的规则对各脉冲的宽度进行调制,既可改变逆变电路输出电压的大小,也可以改变输出频率。
定时器,顾名思义,就是拿来定时的机器。在stm32单片中,定时器是存在于stm32中的一个外设。分别有高级定时器、通用定时器、基本定时器,也有些型号的单片机没有高级定时器,具体要去查看芯片手册。
| 定时器种类 | 位数 | 计数模式 | 捕获/比较 | 互补输出 | 应用场景 |
|---|---|---|---|---|---|
高级定时器 | 16 | 向上,向下,向上/下 | 4 | 有 | 带死区用来紧急刹车或者产生pwm控制 |
通用定时器 | 16 | 向上,向下,向上/下 | 4 | 无 | pwm输出,输入捕获,输出比较 |
基本定时器 | 16 | 向上,向下,向上/下 | 4 | 无 | 主要应用于驱动DAC |
向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。向下计数模式:计数器自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并且产生一个计数器向下溢出事件。中央对齐模式:计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1,并且产生一个计数器溢出事件;然后再从0开始重新计数。

内部时钟(CK_INT)
外部时钟模式:外部触发输入(ETR)
内部触发输入(ITRx):使用一个定时器作为另一个定时器的与分屏器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。
外部时钟模式:外部输入脚(TIx)
计数寄存器(TIMx_CNT)
向上计数、向下计数或者中心对齐计数;
预分频寄存器(TIMx_PSC)
可将时钟频率按1到65535之间的任意值进行分频,可在运行的时候改变其设置值;
自动重装载寄存器(TIMx_ARR)
如果TIMx_CR1寄存器中的ARPE位为0,ARR寄存器的内容将直接写入影子寄存器;如果ARPE为1,ARR寄存器的内容将在每次的更新时间发生时传送到影子寄存器;
如果TIMx_CR1中的UDIS位为0,当计数器产生溢出条件时,产生更新事件。
IC1、2和IC3、4可以分别通过软件设置将其映射到TI1、TI2和TI3、TI4;
4个16位捕捉比较寄存器可以编程用于存放检测到对应的每一次输入捕捉时计数器的值;
当产生一次捕捉,相应的CCxIF标志位被置1;同时如果中断或DMA请求使能,则产生中断或DMA请求。
如果当CCxIF标志位已经为1,当又产生一个捕捉,则捕捉溢出标志位CCxOF将被置1。
void PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_DeInit(TIM3);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCNPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
//TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);//使能ccr2上的预装载寄存器
//TIM_OC4PreloadConfig(TIM8,TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3,ENABLE);
TIM_CtrlPWMOutputs(TIM3,ENABLE);
TIM_Cmd(TIM3,ENABLE);
}
定时器的初始化需要开启许多时钟,配置许多寄存器,虽然标库已经很简便,但还是略微的有点麻烦,大家可以通过STM32CubeMX进行相关配置。
int main(void)
{
uint8_t dir = 1;
uint16_t pwm = 0;
Delay_Init();
Motor_init();
PWM_Init(1999,719);
My_Uart_Init(115200);
while(1)
{
if(dir)
{
Delay_ms(1);
pwm++;
TIM_SetCompare1(TIM3,pwm);
if(pwm == 2000) dir = 0;
}
if(!dir)
{
Delay_ms(1);
pwm--;
TIM_SetCompare1(TIM3,pwm);
if(pwm == 0) dir = 1;
}
}
}

pwm实质上就是在相同的周期输出不同时长的高低电平,用io口也可以模拟出来,因为有时候定时器资源可能不是很够。用io口模拟也是一个好方法。
void TIM3_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
NVIC_InitTypeDef NVIC_InitStructure; //中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = 10; //自动重装载寄存器周期的值1/CK_CNT=1us,1000x1us=1ms
TIM_TimeBaseStructure.TIM_Prescaler =71; //设置用来作为TIMx时钟频率除数的预分频值CK_CNT=CK_INT/(71+1)=1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //TIM_CKD_DIV1是.h文件中已经定义好的,TIM_CKD_DIV1=0,也就是时钟分频因子为0
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
TIM_Cmd(TIM3, ENABLE); //使能TIMx
}
void TIM3_IRQHandler()
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)
{
pwm_val++;
if(pwm_val<pwm_set) GPIO_ResetBits(GPIOC,GPIO_Pin_13);
if(pwm_val>pwm_set) GPIO_SetBits(GPIOC,GPIO_Pin_13);
if(pwm_val == PWM_MAX) pwm_val = 0;
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
}
u16 pwm_set = 0;
int main(void)
{
uint8_t dir = 1;
Delay_Init();//延时函数初始化
led_init();//led灯的初始化
TIM3_Init();//定时器初始化
while(1)
{
if(dir)
{
Delay_ms(1);
pwm_set++;
if(pwm_set == 2000) dir = 0;
}
if(!dir)
{
Delay_ms(1);
pwm_set--;
if(pwm_set == 0) dir = 1;
}
}
}

PWM技术在生活中很多领域都有运用到,大多是对伺服电机的控制,如电机、舵机等等,通过pwm波可以使电机有不同的转速,可以使舵机转动不同的角度,这样在一些特定的场合起着不一样的作用。在学习pwm的过程中还是比较简单的,配置stm32定时器就能输出pwm波,但是运用在不同的场景往往并不是单单的pwm的调制,还要与许多算法的结合,还需要我们不断的去学习,去扩充自己的知识。
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳