鄙人才疏学浅,如有看不入眼的代码和见解,烦请各位指教。
PWM原理介绍
PWM的原理也算是老生常谈了,在此就不多说什么了。我们看一下有关PWM的介绍:
波形计数器负责以占空比产生脉冲。而占空比取决于比较值,并且取决于COUNTERTOP的频率。一个 PWM模块有一个共用的15位计数器,带有四个比较通道。因此,所有四个通道将共享相同的周期(PWM频率),但可以具有单独的占空比和极性。其中极性和比较值由从RAM读取的值设置。定时器最高值由COUNTERTOP寄存器控制。当COUNTERTOP值小于比较值的设置时,将导致不生成PWM边沿的状态,如果极性设置为FallingEdge,那么输出 OUT [n]n=0~3分别保持高电平。
向上计数时,计数到 COMP0 时候,触发 OUT[0]的边沿变化。计数到 COMP1时候,触发 OUT[1]的边沿变化。定时器最高值由 COUNTERTOP 寄存器控制,计算器计数到COUNTERTOP 的值后清零,同时 OUTn]发生电平跳变,结束一个周期。所以PWM 周期由COUNTERTOP的值和计数频率决定,计数频率由PWM CLK 的所选 PRESCALER 来决定。占空比取决于比较值 COMP 的值,所有比较寄存器 COMP 都是内部的 RAM 进行设置的,只能通过后面介绍的解码器进行配置。
PWM 模块可以在 GPIO 上生成脉冲宽度调制信号。该模块实现了一个向上或向下计数器,带有四个PWM 通道,用于驱动分配的 GPIO。三个PWM 模块可提供多达 12个PWM 通道,单个频率控制,最多可包含四个通道。每个模块的频率是相同的。此外,内置解码器和 EasyDMA 功能使得可以在没有 CPU 干预的情况下操纵PWM 占空比。 从数据 RAM 读取任意占空比序列,可以链接以实现乒乓缓冲或重复进入复杂循环。
这里列出的是一个 PWM 模块的主要特征:
·固定 PWM 基频,带可编程时钟分频器
·最多四个 PWM 通道,具有独立的极性和占空比值
·PWM 通道上的边沿或中心对齐脉冲
数据 RAM 中定义的多个占空比数组(序列)
通过 EasyDMA 直接从存储器中自主且无干扰地更新占空比值
每个PWM 周期可能会改变极性,占空比和基频事
数据 RAM 序列可以重复或连接成循环
我们可以得知,在nrf内,我们需要对PWM的分频,通道,极性,占空比值,脉冲选择,占空数列,周期和循环模式进行设置。
我们找到坐标:nRF5_SDK_17.0.2_d674dde\examples\peripheral\pwm_driver\pca10040\blank\arm5_no_packs
这个例程里pwm的配置就包含了上述部分。我们一条条来看。
PWM配置
nrf_drv_pwm_config_t const config0 =
{
.output_pins =
{
BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED, // channel 0
BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED, // channel 1
BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED, // channel 2
BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED // channel 3
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
.base_clock = NRF_PWM_CLK_1MHz,
.count_mode = NRF_PWM_MODE_UP,
.top_value = m_demo1_top,
.load_mode = NRF_PWM_LOAD_INDIVIDUAL,
.step_mode = NRF_PWM_STEP_AUTO
};
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, demo1_handler));
m_used |= USED_PWM(0);
m_demo1_seq_values.channel_0 = 0;
m_demo1_seq_values.channel_1 = 0;
m_demo1_seq_values.channel_2 = 0;
m_demo1_seq_values.channel_3 = 0;
m_demo1_phase = 0;
(void)nrf_drv_pwm_simple_playback(&m_pwm0, &m_demo1_seq, 1,
NRF_DRV_PWM_FLAG_LOOP);
}
首先命名了一个config0,来包含所有配置部分,照抄。
Output pin,很明显,输出的IO口,这里是例程自带的IO,我们把通道1前面的改为我们自己的IO,例如P6。
irq_priority 也就是优先级,没什么可说的,按照自己的需求去配置即可,这里就按照历程的来。
base_clock 这个就是时钟配置了,我们点进去看一下,可以看到,从125k到16Mhz共计八个档位,如何选择?这个时候我们需要了解一下需求,根据实际需求使用公式换算。例如我们需要按照25ms为一个周期,那么1秒内能产生40个完整的周期,那么频率就是40hz,这符合你的需求了,那么我们就看怎么达到这个频率。按最高的档位,16Mhz,用一秒除,1/16M=0.0625us,这也就是一个脉冲触发所需的时间,那么我们要达到40hz,25ms,就用2500us/0.0625us=40000,也就是说我们只需要触发40000个脉冲即可达到所需要的频率。这也就是我们所需的脉冲顶点,到达这个顶点就意味着满载清零了一次。
count_mode计数模式,没什么好说的,按自己需求选择,这边只需要简单的波形即可,因此选择向上计数;向上/向下,和双边计数,可以产生对称的波形。
top_value 这个就是前面计算的脉冲顶点
load_mode这个值是装载模式,点进去可以看到后面有英文注释。如果你只需要简单的设计,单个PWM输出,因此选择第一个即可。如果你需要多个通道同时具有不同装载值,就需要看个人需求了,第二个模式指0,1共用第一个装载值,2,3用第二个,第三个模式则需要为每个通道设置一个装载值。最后一个和第三个一样,不过最后一个装载值作为脉冲计数顶点。
step_mode就是停止的模式,和定时器类似,一个会自动重复,一个完成就停止,我们选择自动重复。
PWM的配置,还需要一个handler来作为触发后的动作。看个人需要进行配置。
下方这五条是装载值,因为我们选择的装载模式是普通,并且装载值的数据正是电机转速的关键,因此也不需要处理,直接注释即可,后续实际处理装载值时在填写。
static nrf_pwm_values_common_t /*const*/ seq1_values[] =
{
0,
0x8000,
0,
0x8000,
0,
0
};
回放重启也可以后面再处理,那么我们就简单配置好了。
配置结果
.irq_priority = APP_IRQ_PRIORITY_LOWEST,//优先级
.base_clock = NRF_PWM_CLK_16MHz,// 1Us 25ms一个周期 1000/25 =40hz 16Mhz=0.0625US*40000 = 25ms =40hz
.count_mode = NRF_PWM_MODE_UP,//向上计数
.top_value = 40000,//脉冲计数的顶点值
.load_mode = NRF_PWM_LOAD_COMMON,//PWM导入模式:共同模式
.step_mode = NRF_PWM_STEP_AUTO//自动重复
如果没有配置回放重启,那么PWM是不能正常工作的,因此我们在其基础上写一条可以嵌入数值让其工作的函数。下一篇文章中将会讲到。
研发类型的需求和讨论,如有需要请联系鄙人。公司邮箱:zhongweixiong@kyohoon.com,联系电话:15507589165 钟先生