文章目录
最近因为从标准库过渡到hal库,又重新温习了一遍定时器相关的功能,在stm32中定时器经常被用作输出PWM波,在此对定时器常用来输出PWM的两种模式:输出比较模式和PWM模式做一个总结
本次实验使用stm32G431,使用cubemx生成底层代码。
输出比较模式和PWM模式都可以用来输出PWM波,在功能上两者有相同之处,对于一个定时器这两种方式都可以做到四路输出PWM,每一路PWM占空比都可调,也有不同之处,输出比较模式可以方便的调节每一路PWM波的频率,可以输出四路频率不同,占空比不同的PWM。但是PWM模式如果想要调节PWM波的频率,那么就只能重新设置预分频系数或者自动重装载寄存器ARR,并且输出的四路PWM频率必定一致。
PWM模式是输出比较模式的子集。
在预分频系数确定的条件下,PWM模式的信号频率是由自动重装载寄存器ARR来确定的,占空比则由比较寄存器CCR来确定。
PWM模式分为两种,PWM1和PWM2,都可以设置计数器递增计数或者递减计数。具体如下表

在使用PWM模式时,我们只需要设置预分频系数PSC,自动重装载值ARR,即可确定PWM具体频率,即 PWM频率 =(系统时钟频率)/((ARR+1)*(PSC+1))(单位:Hz)
这里以PWM1的递增计数方式为例,假定系统时钟频率设置为80MHz,那么当ARR取999,PSC取79的时候,PWM的频率即为1kHz。配置完成后对时钟进行初始化,此时时钟开始以预分频之后的频率即1MHz开始计数,当CNT的值小于设置的CCR的值时,PWM输出引脚输出为高电平,当CNT的值大于CCR的值时,PWM输出引脚输出为低电平,当CNT计数至ARR时,计数器产生上溢事件,计数器的值更新为0,以此往复。
1、首先需要对时钟频率进行设置,这里设置系统时钟频率为80MHz

2、此处选用TIM3通用定时器,配置时钟来源为内部时钟,选择Channel1和Channel2的PWM模式

3、配置PSC为79,ARR为999,向上计数模式,使能自动重载预装载,Channel1选择PWM1模式,pulse值取200,这里的pulse就是指比较寄存器CCR,理论上计算可得PWM频率为1KHz,占空比为20%
Channel2的pulse值取300,其余和Channel1一致

4、最后,配置project相关参数,生成代码。
cubemx配置生成完成后只需要再启动一下定时器即可完成PWM的输出
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
__HAL_TIM_CLEAR_IT(&htim3,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
__HAL_TIM_CLEAR_IT(&htim3,TIM_CHANNEL_2);
此处实验使用的是TIM3的Channel1和Channel2,相应的引脚为PA6、PA7,下载到开发板上用逻辑分析仪可以得到如下波形

可以看到波形符合预期,当然在代码中也可以通过
__HAL_TIM_SET_COMPARE(HANDLE, CHANNEL, COMPARE)
来重新设置CCR更改PWM的占空比
或者通过__HAL_TIM_SET_AUTORELOAD(HANDLE, AUTORELOAD)
来重新设置ARR来改变PWM的频率
在上面的PWM模式下我们看一发现Channel1和Channel2的占空比可以不一样,但是其频率是一致的,那么如果需要在一个定时器下的多个通道分别输出频率不同,占空比不同的PWM那么就需要使用输出比较模式了。
输出比较模式和PWM模式的原理很相似,在输出比较模式输出PWM的实验中,有一点不同的是PWM1递增模式下CNT与CCR作比较,若CNT小于CCR则输出为高电平,若CNT大于CCR则输出为低电平,并且在CNT计数至ARR时,CNT会更新至0并产生上溢事件。但是输出比较模式在CNT与CCR不断做比较的过程中,若CNT等于CCR,产生的则是电平翻转,并且会产生中断,通过对中断回调函数的编写,就能够实现多路不同频率信号的输出。
例如,我们定义duty作为占空比,定义pulse作为周期计数数目,若duty为10%,pulse为10000,取ARR为最大值65535(在输出比较模式中ARR没啥用,定义成最大值就不用经常更新CNT为0了)系统时钟频率为80MHz,预分频系数为79,则预分频之后的时钟频率为1MHz,那么当计pulse个数目,则时间为10ms,占空比为10%,则所需高电平时间为1ms。
当第一次CNT等于CCR时,进入中断回调函数,让CCR加上(pulse-pulse*duty),即9ms后再次进行反转电平,此时PWM持续了9ms的低电平;
当第二次CNT等于CCR是,进入中断回调函数,让CCR加上(pulse*duty),即1ms后再进行反转电平,此时PWM持续了1ms的高电平;
这样即可完成输出比较PWM的配置。
有人可能会问,为什么CNT可以加这么多数,那是因为当CNT从65535溢出后,仍然会从头开始计数,对程序没有影响,具体如下图。
1、依然配置系统时钟频率为80MHz,此处选用TIM3通用定时器,配置时钟来源为内部时钟,选择Channel1和Channel2的输出比较模式

2、如图配置参数,需要注意的是自动重载预装载和输出比较预装载都需要关闭,
模式需要选择toogle on match

3、最后配置project相关参数,生成代码
cubemx配置生成完成后,首先需要启动一下定时器,清除中断标志位
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
__HAL_TIM_CLEAR_IT(&htim3,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
__HAL_TIM_CLEAR_IT(&htim3,TIM_CHANNEL_2);
然后对中断回调函数进行编写
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
uint32_t OC_Count = 0;
OC_Count = __HAL_TIM_GET_COUNTER(htim);
if(htim->Instance == TIM3)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_6))
{
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,OC_Count + OC_Channel1_Pulse - OC_Channel1_Duty*OC_Channel1_Pulse/100);
}
else
{
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,OC_Count + OC_Channel1_Duty*OC_Channel1_Pulse/100);
}
}
else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_7))
{
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,OC_Count + OC_Channel2_Pulse - OC_Channel2_Duty*OC_Channel2_Pulse/100);
}
else
{
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,OC_Count + OC_Channel2_Duty*OC_Channel2_Pulse/100);
}
}
}
}
此处OC_Channel1_Pulse和OC_Channel2_Pulse 我就是前面“原理”中的pulse,OC_Channel1_Duty和OC_Channel2_Duty就是“原理”中的duty,有定义了两个标志位,通过判断引脚电平,使之能够间次调用。
其中duty和pulse的值可以自己设置,这里由于我duty定义的是整形,所以在代码中对其相应的除以了100,相当于转化为了小数。
若取OC_Channel1_Duty为10%,OC_Channel1_Pulse为10000,则Channel1频率为100Hz,占空比为10%
若取OC_Channel2_Duty为30%,OC_Channel1_Pulse为5000,则Channel1频率为200Hz,占空比为30%
最后下载到开发板中,使用逻辑分析仪观察波形如下


符合实验预期
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
我想使用spawn(针对多个并发子进程)在Ruby中执行一个外部进程,并将标准输出或标准错误收集到一个字符串中,其方式类似于使用Python的子进程Popen.communicate()可以完成的操作。我尝试将:out/:err重定向到一个新的StringIO对象,但这会生成一个ArgumentError,并且临时重新定义$stdxxx会混淆子进程的输出。 最佳答案 如果你不喜欢popen,这是我的方法:r,w=IO.pipepid=Process.spawn(command,:out=>w,:err=>[:child,:out])
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,