..添加PWM Single模式回调示例
..添加PWM Grouped模式示例
..添加PWM WaveForm模式示例
..添加PWM 综合示例 2021/9/30
目录
nRF52xx PWM基础介绍
PWM模块特征和结构
与第六课的 PWM不同,这里是使用的硬件模块实现的PWM,第六课是使用的定时器软件实现的PWM,原理是相同的。
nrF52832 有3个PWM模块,每个模块有4个PWM通道,用于驱动分配的GPIO。所以nrF52832的PWM模块可提供12个PWM通道,单个频率控制最多可包含四个通道,每个模块的频率是相同的。
而且,内置解码器和EasyDMA功能使得可以在没有CPU干预的情况下操纵 PWM的占空比。 从数据 RAM 读取任意占空比序列。
根据官方手册列出的特征:
- 固定的PWM基频,带可编程时钟分频器
- 4个PWM通道,具有独立的极性和占空比
- PWM通道边沿或中心对齐脉冲
- 数据RAM中定义的多个占空比数组
- 通道EasyDMA直接从存储器中自动无干扰的更新占空比值
- 每个PWM周期可能改变极性,占空比和 基频
- 数据RAM序列可以重复或者连接成循环
nRF52832 PWM的内部结构:
PWM模块计数模式
向上计数模式:
向上向下计数模式:
EasyDMA解码器
Decoder(解码器)使用 EasyDMA 方式获取存储在数据 RAM 中的 PWM 参数。
并根据操作模式更新波形计数器的内部比较寄存器。
RAM中的 PWM 参数结构为 一个半字(16bit)的序列:
最高有效位 bit 15 表示 OUT 的极性,
而 bit 14 ~ 0 表示15位的比较值,如下图:
RAM 中的 PWM 参数,最终会通过读取 RAM 地址的方式赋值给 SEQ[n].PTR 寄存器。
PWM 模块的 DECODER 寄存器:
解码器的四种模式:
-
Common模式:
共用模式,四个PWM输出公用极性 和比较值。 -
Grouped模式:
分组模式,每组半个字,两个PWM公用极性和比较值设置。 -
Single模式:
单信号独立模式,每个通道半个字,每个PWM独立的极性和比较值。 -
WaveForm模式
波形模式。可使用特出操作模式,在此模式下面,最多只能使能3个PWM通道OUT[0] ~ OUT[2]。在RAM中,一次加载4个值,前面3个用于加载极性和比较值设置, 第四个 RAM 位置用于加载 COUNTERTOP 寄存器。 这样最多可以有3个PWM通道,而且( frequency base)基础频率在每个 PWM 周期的基础上发生变化。这种模式对于LED照明等应用中的任意波形生产非常有用。
关于RAM数据的定义方式和对应关系,还需要专门的说明一下
- 待补充
关于RAM数据的定义方式和对应关系,还需要专门的说明一下
寄存器SEQ[n].REFRESH:xxx (后面使用中遇到问题再来特殊说明)
寄存器SEQ[n].PTR 是用于从 RAM 获取 COMPARE 值的指针。如果 SEQ[n].PTR 未指向数据 RAM 区域, 则 EasyDMA 传输可能导致 HardFault 或 RAM 损坏。 在将 SEQ[n].PTR 设置为所需的 RAM 位置后,必须将 SEQ[n].CNT 寄存器设置为 序列中的 16 位半字的值。
PWM模块的寄存器
PWM的寄存器理解需要根据官方手册的 Descripton 多看看,如果后期使用中有特殊情况,再来记录
PWM使用程序样例
pwm Single模式计数示例(寄存器版)
/*
pwm波的数组,RAM存的数 低电平输出
但是这里得注意问题,最高位极性,最高位极性,0~ 14 位置都是占空比数值,
然后根据前面设置的NRF_PWM0->COUNTERTOP,不能超过这个值
8 7 9 B 1
1000 0001 1001 1011 0001
*/
uint16_t pwm_seq[4] = {0x8888,0x9888,0xB0FF,0xBBFF};
static void pwm_demo(void)
{
NRF_LOG_INFO("PWM Demo");
/*
NRF_PWM0,NRF_PWM1,NRF_PWM2 3个PWM模块,每个模块4个通道通过 PSEL.OUT[n](0~3)选择引脚
*/
//配置PWM输出,选择频道0输出:输出管教,输出的连接状态
NRF_PWM0->PSEL.OUT[0] = (17<< PWM_PSEL_OUT_PIN_Pos) |(PWM_PSEL_OUT_CONNECT_Connected <<PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->PSEL.OUT[1] = (18<< PWM_PSEL_OUT_PIN_Pos) |(PWM_PSEL_OUT_CONNECT_Connected <<PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->PSEL.OUT[2] = (19<< PWM_PSEL_OUT_PIN_Pos) |(PWM_PSEL_OUT_CONNECT_Connected <<PWM_PSEL_OUT_CONNECT_Pos);
NRF_PWM0->PSEL.OUT[3] = (20<< PWM_PSEL_OUT_PIN_Pos) |(PWM_PSEL_OUT_CONNECT_Connected <<PWM_PSEL_OUT_CONNECT_Pos);
//PWM模块的使能
NRF_PWM0->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
//设置为向上计数
NRF_PWM0->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
//NRF_PWM0->MODE = (PWM_MODE_UPDOWN_UpAndDown << PWM_MODE_UPDOWN_Pos); //向上向下计数 用这个
//设置分频数值
NRF_PWM0->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 <<PWM_PRESCALER_PRESCALER_Pos);
//设置顶点量 16k 16MHZ 1ms 如果是向上向下计数,一个周期2ms
NRF_PWM0->COUNTERTOP = (16000 << PWM_COUNTERTOP_COUNTERTOP_Pos);
//不循环,这里是什么意思还不太明白
NRF_PWM0->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);//
/*
PWM编码器配置:独立模式、刷新计数或者用下一步。
独立模式每一个16位对应一个通道
ex:0x0088 对应 PSEL.OUT[0] PWM channel 0
这里选择了 PWM_DECODER_MODE_RefreshCount 刷新计数,
所以下面的 NRF_PWM0->SEQ[0].REFRESH = 0;
*/
NRF_PWM0->DECODER = (PWM_DECODER_LOAD_Individual << PWM_DECODER_LOAD_Pos)|(PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
/*
设置PWM的占空比:比较寄存器的值,级性
SEQ[n].PTR 是用于从 RAM 获取 COMPARE 值的指针
将 SEQ[n].PTR 设置为所需的 RAM 位置后
将 SEQ[n].CNT 寄存器设置为 序列中的 16 位半字的值
*/
NRF_PWM0->SEQ[0].PTR = ((uint32_t)(pwm_seq) << PWM_SEQ_PTR_PTR_Pos);
//这个序列中的值的数量长度(占空比)
NRF_PWM0->SEQ[0].CNT = ((sizeof(pwm_seq) / sizeof(uint16_t)) <<PWM_SEQ_CNT_CNT_Pos);
//加载到比较寄存器的样本之间的额外PWM周期的数量
NRF_PWM0->SEQ[0].REFRESH = 0;
//在序列之后添加的时间
NRF_PWM0->SEQ[0].ENDDELAY = 0;
//从序列0开始在所有启用的通道上加载第一个PWM值,并以SEQ[0]REFRESH和/或解码器. mode中定义的速率开始播放该序列。
NRF_PWM0->TASKS_SEQSTART[0] = 1;
}
int main(void)
{
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
NRF_LOG_INFO("PWM example started.");
pwm_demo();
while(1){}
}
pwm Single模式非回调示例(库函数版)
库函数的使用解释,请参考下面的共同模式的例子里的注释
//...
static void pwm_demo_one(void)
{
NRF_LOG_INFO("pwm Demo one");
/*
独立模式,这个演示为各个通道回放一个具有不同值的序列。
它不使用事件处理程序。因此,PWM外设不使用中断,CPU可以保持休眠模式。
led(1-4)分别闪烁/125毫秒。
*/
nrf_drv_pwm_config_t const config0 =
{
.output_pins =
{
BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED, // channel 0
BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED, //
BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED, //
BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED //
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,//
.base_clock = NRF_PWM_CLK_125kHz,//PWM的频率
.count_mode = NRF_PWM_MODE_UP, //
.top_value = 15625, //
.load_mode = NRF_PWM_LOAD_INDIVIDUAL,//:独立模式
.step_mode = NRF_PWM_STEP_AUTO//自动
};
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
m_used |= USED_PWM(0);
// 存储在RAM内的极性和占空比的值
static nrf_pwm_values_individual_t /*const*/ seq_values[] =
{
{ 0x8000, 0, 0, 0 },
{ 0, 0x8000, 0, 0 },
{ 0, 0, 0x8000, 0 },
{ 0, 0, 0, 0x8000 }
};
nrf_pwm_sequence_t const seq =
{
.values.p_individual = seq_values,
.length = NRF_PWM_VALUES_LENGTH(seq_values),
.repeats = 0,//不重复
.end_delay = 0//不延迟
};
//重复回放
(void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1, NRF_DRV_PWM_FLAG_LOOP);
}
//...
pwm Single模式回调示例(库函数版)
需要使用回调函数,就需要在pwm初始化的函数中写上回调函数的名字:
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL))//NULL是没有回调,需要回调得命名;
会触发回调函数的事件有4种:
/**
* @brief PWM driver event type.
*/
typedef enum
{
NRFX_PWM_EVT_FINISHED, ///< Sequence playback finished.
NRFX_PWM_EVT_END_SEQ0, /**< End of sequence 0 reached. Its data can be
safely modified now. */
NRFX_PWM_EVT_END_SEQ1, /**< End of sequence 1 reached. Its data can be
safely modified now. */
NRFX_PWM_EVT_STOPPED, ///< The PWM peripheral has been stopped.
} nrfx_pwm_evt_type_t;
示例:
#include <stdio.h>
#include <string.h>
#include "nrf_drv_pwm.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "boards.h"
#include "bsp.h"
#include "app_timer.h"
#include "nrf_drv_clock.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
// This is for tracking PWM instances being used, so we can unintialize only
// the relevant ones when switching from one demo to another.
#define USED_PWM(idx) (1UL << idx)
static uint8_t m_used = 0;
static uint16_t const m_demo1_top = 10000;
static uint16_t const m_demo1_step = 200;
static uint8_t m_demo1_phase;
static nrf_pwm_values_individual_t m_demo1_seq_values;
static nrf_pwm_sequence_t const m_demo1_seq =
{
.values.p_individual = &m_demo1_seq_values,
.length = NRF_PWM_VALUES_LENGTH(m_demo1_seq_values),
.repeats = 0,
.end_delay = 0
};
/*
#define nrf_drv_pwm_evt_type_t nrfx_pwm_evt_type_t
typedef enum
{
NRFX_PWM_EVT_FINISHED, ///< Sequence playback finished.
NRFX_PWM_EVT_END_SEQ0, /**< End of sequence 0 reached. Its data can be
safely modified now.
NRFX_PWM_EVT_END_SEQ1, /**< End of sequence 1 reached. Its data can be
safely modified now.
NRFX_PWM_EVT_STOPPED, ///< The PWM peripheral has been stopped.
} nrfx_pwm_evt_type_t;
*/
static void pwmcallbackdemo_handler(nrf_drv_pwm_evt_type_t event_type)
{
if (event_type == NRF_DRV_PWM_EVT_FINISHED)
{
uint8_t channel = m_demo1_phase >> 1;// m_demo1_phase 每次+1 后进行右移00,01,10,11,100,101,110,111-->00,00,01,01,010,010,011,011
bool down = m_demo1_phase & 1;//0,1,0,1,0,1
bool next_phase = false;
uint16_t * p_channels = (uint16_t *)&m_demo1_seq_values;
uint16_t value = p_channels[channel];
if (down)
{
value -= m_demo1_step;//灯就越来越暗
if (value == 0)
{
next_phase = true;
}
}
else
{
value += m_demo1_step;
if (value >= m_demo1_top)//灯越来越亮,
{
next_phase = true;
}
}
p_channels[channel] = value;
if (next_phase)
{
if (++m_demo1_phase >= 2 * NRF_PWM_CHANNEL_COUNT)//设置范围
{
m_demo1_phase = 0;
}
}
}
}
static void pwmcallbackdemo(void)
{
NRF_LOG_INFO("pwm callback Demo ");
/*
* This demo plays back a sequence with different values for individual
* channels (LED 1 - LED 4). Only four values are used (one per channel).
* Every time the values are loaded into the compare registers, they are
* updated in the provided event handler. The values are updated in such
* a way that increase and decrease of the light intensity can be observed
* continuously on succeeding channels (one second per channel).
*/
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_2 | NRF_DRV_PWM_PIN_INVERTED, // channel 2
BSP_LED_3 | 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, pwmcallbackdemo_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);
}
static void bsp_evt_handler(bsp_event_t evt)
{
}
static void init_bsp()
{
APP_ERROR_CHECK(nrf_drv_clock_init());
nrf_drv_clock_lfclk_request(NULL);
APP_ERROR_CHECK(app_timer_init());
APP_ERROR_CHECK(bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler));
APP_ERROR_CHECK(bsp_buttons_enable());
}
void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
{
bsp_board_leds_on();
app_error_save_and_stop(id, pc, info);
}
int main(void)
{
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
init_bsp();
NRF_LOG_INFO("PWM example started.");
// Start with Demo , then switch to another one when the user presses
// button 1 or button 2 (see the 'bsp_evt_handler' function).
pwmcallbackdemo();
for (;;)
{
// Wait for an event.
__WFE();
// Clear the event register.
__SEV();
__WFE();
NRF_LOG_FLUSH();
}
}
pwm Common模式简单回放示例(库函数版 带函数解析)
/*
typedef struct
{
NRF_PWM_Type * p_registers; ///< Pointer to the structure with PWM peripheral instance registers.
uint8_t drv_inst_idx; ///< Driver instance index.
} nrfx_pwm_t;
*/
static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
// This is for tracking PWM instances being used, so we can unintialize only
// the relevant ones when switching from one demo to another.
#define USED_PWM(idx) (1UL << idx)
static uint8_t m_used = 0;
static void pwm_demo(void)
{
NRF_LOG_INFO("PWM Demo");
/*
typedef struct
{
uint8_t output_pins[NRF_PWM_CHANNEL_COUNT]; ///< Pin numbers for individual output channels (optional).
//< Use @ref NRFX_PWM_PIN_NOT_USED
// if a given output channel is not needed.
uint8_t irq_priority; ///< Interrupt priority.
nrf_pwm_clk_t base_clock; ///< Base clock frequency.
nrf_pwm_mode_t count_mode; ///< Operating mode of the pulse generator counter.
uint16_t top_value; ///< Value up to which the pulse generator counter counts.
nrf_pwm_dec_load_t load_mode; ///< Mode of loading sequence data from RAM.
nrf_pwm_dec_step_t step_mode; ///< Mode of advancing the active sequence.
} nrfx_pwm_config_t;
typedef enum
{
NRF_PWM_CLK_16MHz = PWM_PRESCALER_PRESCALER_DIV_1, ///< 16 MHz / 1 = 16 MHz.
NRF_PWM_CLK_8MHz = PWM_PRESCALER_PRESCALER_DIV_2, ///< 16 MHz / 2 = 8 MHz.
NRF_PWM_CLK_4MHz = PWM_PRESCALER_PRESCALER_DIV_4, ///< 16 MHz / 4 = 4 MHz.
NRF_PWM_CLK_2MHz = PWM_PRESCALER_PRESCALER_DIV_8, ///< 16 MHz / 8 = 2 MHz.
NRF_PWM_CLK_1MHz = PWM_PRESCALER_PRESCALER_DIV_16, ///< 16 MHz / 16 = 1 MHz.
NRF_PWM_CLK_500kHz = PWM_PRESCALER_PRESCALER_DIV_32, ///< 16 MHz / 32 = 500 kHz.
NRF_PWM_CLK_250kHz = PWM_PRESCALER_PRESCALER_DIV_64, ///< 16 MHz / 64 = 250 kHz.
NRF_PWM_CLK_125kHz = PWM_PRESCALER_PRESCALER_DIV_128 ///< 16 MHz / 128 = 125 kHz.
} nrf_pwm_clk_t;
typedef enum
{
NRF_PWM_MODE_UP = PWM_MODE_UPDOWN_Up, ///< Up counter (edge-aligned PWM duty cycle).
NRF_PWM_MODE_UP_AND_DOWN = PWM_MODE_UPDOWN_UpAndDown, ///< Up and down counter (center-aligned PWM duty cycle).
} nrf_pwm_mode_t;
typedef enum
{
NRF_PWM_LOAD_COMMON = PWM_DECODER_LOAD_Common, ///< 1st half word (16-bit) used in all PWM channels (0-3).
NRF_PWM_LOAD_GROUPED = PWM_DECODER_LOAD_Grouped, ///< 1st half word (16-bit) used in channels 0 and 1; 2nd word in channels 2 and 3.
NRF_PWM_LOAD_INDIVIDUAL = PWM_DECODER_LOAD_Individual, ///< 1st half word (16-bit) used in channel 0; 2nd in channel 1; 3rd in channel 2; 4th in channel 3.
NRF_PWM_LOAD_WAVE_FORM = PWM_DECODER_LOAD_WaveForm ///< 1st half word (16-bit) used in channel 0; 2nd in channel 1; ... ; 4th as the top value for the pulse generator counter.
} nrf_pwm_dec_load_t;
typedef enum
{
NRF_PWM_STEP_AUTO = PWM_DECODER_MODE_RefreshCount, ///< Automatically after the current value is played and repeated the requested number of times.在播放当前值并重复所请求的次数后自动执行。
NRF_PWM_STEP_TRIGGERED = PWM_DECODER_MODE_NextStep ///< When the @ref NRF_PWM_TASK_NEXTSTEP task is triggered.当@ref NRF_PWM_TASK_NEXTSTEP任务被触发时。
} nrf_pwm_dec_step_t;
*/
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,
BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED,
NRF_DRV_PWM_PIN_NOT_USED,
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
.base_clock = NRF_PWM_CLK_1MHz, // 1Us 25ms一个周期 1000/25 =40hz
.count_mode = NRF_PWM_MODE_UP, //计数模式
.top_value = 25000, //顶点值,周期
.load_mode = NRF_PWM_LOAD_COMMON,//PWM导入RAM模式:共同模式
.step_mode = NRF_PWM_STEP_AUTO //自动,重复次数后刷新
};
/*
#define nrf_drv_pwm_init nrfx_pwm_init
nrfx_err_t nrfx_pwm_init(nrfx_pwm_t const * const p_instance,
nrfx_pwm_config_t const * p_config,
nrfx_pwm_handler_t handler)
*/
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
m_used |= USED_PWM(0);//使用PWM0
static uint16_t /*const*/ seq_values[] =
{
// 0x8888,
// 0x9888,
// 0xB0FF,
0xBBFF, //这里设置一个,就是一个,如果设置4个,会在4个来回循环
};
nrf_pwm_sequence_t const seq =
{
.values.p_common = seq_values,//要回放的序列
.length = NRF_PWM_VALUES_LENGTH(seq_values),//长度
.repeats = 0,//重复次数为0
.end_delay = 0//结束的时候是否需要增加附带周期
};
/*
what this void effect? LOOP 相关寄存器?
#define nrf_drv_pwm_simple_playback nrfx_pwm_simple_playback
uint32_t nrfx_pwm_simple_playback(nrfx_pwm_t const * const p_instance,
nrf_pwm_sequence_t const * p_sequence,
uint16_t playback_count,
uint32_t flags)
*/
(void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1, NRF_DRV_PWM_FLAG_LOOP);
}
int main(void)
{
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
NRF_LOG_INFO("PWM example started.");
pwm_demo();
while(1)
{
NRF_LOG_FLUSH();
}
}
pwm Common模式复杂回放示例(库函数版)
.repeats = 1,//每个工作循环应重复的次数
每个RAM里面的数据重复执行的次数,执行完以后才会执行下一个地址的占空比
数字越大,当然时间会越长
是否回放,而且回放次数是由下面的函数配置(上面的是每个RAM的值执行的次数,下面是整个PWM大循环执行的次数):
(void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1, NRF_DRV_PWM_FLAG_LOOP);
如果使用 NRF_DRV_PWM_FLAG_LOOP,个人感觉 前面的 1没有什么意义;
注意这里,即便使用了NRF_DRV_PWM_FLAG_LOOP,次数也至少要大于0,如果写0,也只会执行一次,不会循环,所以是大于0的次数,多少无所谓
如果使用 NRF_DRV_PWM_FLAG_STOP,前面的 1表示执行一次,循环一次
//include ..
static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
#define USED_PWM(idx) (1UL << idx)
static uint8_t m_used = 0;
static void pwm_demo2(void)
{
NRF_LOG_INFO("PWM Demo 2");
/*
这个演示回放了两个串联的序列:-序列0:光强度在一秒内增加了25步。
-序列1:LED闪烁两次(100ms off, 100ms on),然后保持off为200ms *
注意这个时间是计算出来的,要学会计算这个时间的方法,我修改了例程
改成了等由暗变亮,再由亮变暗
循环播放。
*/
enum { // [local constants]
TOP = 10000,//设置计数器定点值
STEP_COUNT = 25//占空比变化次数
};
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, //
BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED, //
BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED //
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,//优先级
.base_clock = NRF_PWM_CLK_500kHz, //1秒计500k 记到10K 需要 10/500 s = 20ms 20ms一个周期 变化25次的话,就是1s
.count_mode = NRF_PWM_MODE_UP, //向上计数
.top_value = TOP, //脉冲计数的顶点值
.load_mode = NRF_PWM_LOAD_COMMON,//PWM导入模式:共同模式
.step_mode = NRF_PWM_STEP_AUTO //自动重复
};
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));//配置PWM
m_used |= USED_PWM(0);
/*
设置序列0的占空比 这个数组不能在堆栈上分配(因此是“静态的”),它必须在RAM中。
在Common模式和Frouped模式中
RAM数据可以放很长,注意说明中第三行的省略号
所以可以实现呼吸灯的操作
20ms一个周期 变化25次的话,就是1s实现 seq0_values 队列里的呼吸灯操作
*/
static nrf_pwm_values_common_t seq0_values[STEP_COUNT];
uint16_t value = 0;
uint16_t step = TOP / STEP_COUNT;//等分
uint8_t i;
for (i = 0; i < STEP_COUNT; ++i)
{
value += step;
seq0_values[i] = value;
}
//25步 需要1s
nrf_pwm_sequence_t const seq0 =
{
.values.p_common = seq0_values,
.length = NRF_PWM_VALUES_LENGTH(seq0_values),
.repeats = 1,//每个工作循环应重复的次数(在播放一次后) RAM再重复一次,1s
.end_delay = 0
};
//下面这个直接是打开灯,关闭灯的操作
// static nrf_pwm_values_common_t /*const*/ seq1_values[] =
// {
// 0,//20ms+80ms=100ms,20是周期,但是因为需要重复4次 .repeats = 4 ,所以是100ms
// 0x8000,//打开状态 20m一个周期
// 0,
// 0x8000,//
// 0,//关闭状态
// 0//关闭状态
// };
// nrf_pwm_sequence_t const seq1 =
// {
// .values.p_common = seq1_values,
// .length = NRF_PWM_VALUES_LENGTH(seq1_values),
// .repeats = 4,//播放完后的重复次数,如果变成50,就是实现1s一次的闪灯
// .end_delay = 0
// };
static nrf_pwm_values_common_t seq1_values[STEP_COUNT];
value = TOP;
for (i = 0; i < STEP_COUNT; ++i)
{
value -= step;
seq1_values[i] = value;
}
nrf_pwm_sequence_t const seq1 =
{
.values.p_common = seq1_values,
.length = NRF_PWM_VALUES_LENGTH(seq1_values),
.repeats = 1, //播放完后的重复次数
.end_delay = 0
};
//复杂回放,串联两个序列
(void)nrf_drv_pwm_complex_playback(&m_pwm0, &seq0, &seq1, 1,
NRF_DRV_PWM_FLAG_LOOP);
/* 注意,如果需要不回放,
使用 NRF_DRV_PWM_FLAG_STOP
下面的例子就是呼吸灯 由暗到亮,再由亮到暗,循环3次以后,结束任务
可以在某些时候用作指示
(void)nrf_drv_pwm_complex_playback(&m_pwm0, &seq0, &seq1, 3,
NRF_DRV_PWM_FLAG_STOP);
*/
}
int main(void)
{
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
NRF_LOG_INFO("PWM example started.");
pwm_demo2();
for (;;){
NRF_LOG_FLUSH();
}
}
pwm Grouped模式示例(库函数版 复习,带注释)
隔了几天来补充的示例,也添加了自己复习的一些注释,分组模式没有使用回调:
/*定义PWM模块,就3个*/
static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
// static nrf_drv_pwm_t m_pwm1 = NRF_DRV_PWM_INSTANCE(1);
// static nrf_drv_pwm_t m_pwm2 = NRF_DRV_PWM_INSTANCE(2);
// This is for tracking PWM instances being used, so we can unintialize only
// the relevant ones when switching from one demo to another.
#define USED_PWM(idx) (1UL << idx)
static uint8_t m_used = 0;
static void group_demo(void)
{
NRF_LOG_INFO("group_demo");
/*
这下面的config定义是分开定义的
config.output_pins 定义引脚的
这个config.output_pins 是对 NRF_PWM0->PSEL.OUT[0]寄存器赋值
group_demo还是只使用PWM0的
*/
nrf_drv_pwm_config_t config =
{
// These are the common configuration options we use for all PWM
// instances.
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
.count_mode = NRF_PWM_MODE_UP,
.step_mode = NRF_PWM_STEP_AUTO,
};
config.output_pins[0] = BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED;
config.output_pins[1] = BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED;
config.output_pins[2] = BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED;
config.output_pins[3] = BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED;
config.base_clock = NRF_PWM_CLK_125kHz;
config.top_value = 31250; // 250ms period
config.load_mode = NRF_PWM_LOAD_GROUPED;PWM导入模式:分组模式
/*
可以直接定义
*/
// nrf_drv_pwm_config_t const config =
// {
// .output_pins =
// {
// BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED, // channel 0
// BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED,
// BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED,
// BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED,
// },
// .irq_priority = APP_IRQ_PRIORITY_LOWEST,
// .base_clock = NRF_PWM_CLK_125kHz, //
// .count_mode = NRF_PWM_MODE_UP, //计数模式
// .top_value = 31250, //顶点值,周期
// .load_mode = NRF_PWM_LOAD_GROUPED,//
// .step_mode = NRF_PWM_STEP_AUTO //自动,重复次数后刷新
// };
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config, NULL));
m_used |= USED_PWM(0);
/*
This array cannot be allocated on stack (hence "static") and it must
be in RAM (hence no "const", though its content is not changed).
数据放置于RAM区域,所以必须用 static 修饰
分组模式:每组半个字
所以对于RAM区的第一组数据
{ 0 , 0 }
COMP0和COMP1 COMP2和COMP3
执行完第一组数据,会依次执行下面的数据,直到最后循环
{ 0x8000 , 0 }
COMP0和COMP1 COMP2和COMP3
*/
static nrf_pwm_values_grouped_t /*const*/ pwm0_seq_values[] =
{
{ 0, 0 },//灯全灭
{ 0x8000, 0 },//1、2亮,3、4灭
{ 0, 0x8000 },//
{ 0x8000, 0x8000 } //全亮
};
/*
.repeats 是每一个RAM区执行的重复次数
意思就是如果 = 0
{ 0, 0 }, 按照周期执行完了一次就是执行
{ 0x8000, 0 },
如果 = 5
{ 0, 0 },要执行5次,5个周期,所以每个状态的时间会比较长
*/
nrf_pwm_sequence_t const pwm0_seq =
{
.values.p_grouped = pwm0_seq_values,
.length = NRF_PWM_VALUES_LENGTH(pwm0_seq_values),
.repeats = 2,
.end_delay = 0
};
/*
因为是NRF_DRV_PWM_FLAG_LOOP,所以第三个参数无所谓
注意:不能使用0,0 的话就执行一次,也不会循环
只要大于0,1到其他数字都无所谓
*/
(void)nrf_drv_pwm_simple_playback(&m_pwm0, &pwm0_seq, 1,
NRF_DRV_PWM_FLAG_LOOP);
}
static void bsp_evt_handler(bsp_event_t evt)
{
}
static void init_bsp() //bsp init 需要找时间看一下
{
APP_ERROR_CHECK(nrf_drv_clock_init());
nrf_drv_clock_lfclk_request(NULL);
APP_ERROR_CHECK(app_timer_init());
APP_ERROR_CHECK(bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler));
APP_ERROR_CHECK(bsp_buttons_enable());
}
void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
{
bsp_board_leds_on();
app_error_save_and_stop(id, pc, info);
}
int main(void)
{
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
init_bsp();
NRF_LOG_INFO("PWM example started.");
group_demo();
for (;;)
{
// Wait for an event.
__WFE();
// Clear the event register.
__SEV();
__WFE();
NRF_LOG_FLUSH();
}
}
pwm WaveForm模式示例(库函数版)
其实使用库函数和前面的大部分都是类似的,只是设置的时候稍许不同,这里我们就只放pwm参数配置部分的代码:
//...
static void WaveFormdemo(void)
{
/*
使用了WaveForm模式, .top_value 不需要设置
只有3个通道能用
*/
nrf_drv_pwm_config_t const config0 =
{
.output_pins =
{
BEEP | NRF_DRV_PWM_PIN_INVERTED, // channel 0
BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED,
BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED,
BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED,//最后一个通道设置了没有效果
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
.base_clock = NRF_PWM_CLK_125kHz,
.count_mode = NRF_PWM_MODE_UP,
.load_mode = NRF_PWM_LOAD_WAVE_FORM,//PWM导入模式:波形模式
.step_mode = NRF_PWM_STEP_AUTO
};
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
m_used |= USED_PWM(0);
// 存储在RAM内的极性和占空比的值
// static nrf_pwm_values_wave_form_t /*const*/ seq_values[] =
// {
// { 0x8000, 0, 0, 0x3D09 },//灵活变动周期
// { 0x8500, 0x8000, 0, 0x3D09 },
// { 0x8A00, 0, 0x8000, 0x3D09 },
// { 0x8000, 0, 0, 0xF420 },
// { 0x8500, 0, 0, 0xF420 },
// { 0x8A00, 0, 0, 0xF420 },
// };
static nrf_pwm_values_wave_form_t /*const*/ seq_values[] =
{
{ 0x8000, 0, 0, 0x3D09 },//太吵了,变回来
{ 0x8000, 0x8000, 0, 0x3D09 },
{ 0x8000, 0, 0x8000, 0x3D09 },
{ 0x8000, 0, 0, 0xF420 },
{ 0x8000, 0, 0, 0xF420 },
{ 0x8000, 0, 0, 0xF420 },
};
/*
使用蜂鸣器能很好的感受一下.repeats这个参数 ~ ~!
*/
nrf_pwm_sequence_t const seq =
{
.values.p_wave_form = seq_values,
.length = NRF_PWM_VALUES_LENGTH(seq_values),
.repeats = 2,//不重复
.end_delay = 0 //不延迟
};
(void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1,
NRF_DRV_PWM_FLAG_LOOP);
}
//...
pwm 综合实验示例(库函数版)
综合实验就是把前面的所学的模式,3个PWM模块都使用上的综合示例,这里把整个main.c都放上:
#include <stdio.h>
#include <string.h>
#include "nrf_drv_pwm.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "boards.h"
#include "bsp.h"
#include "app_timer.h"
#include "nrf_drv_clock.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
static nrf_drv_pwm_t m_pwm1 = NRF_DRV_PWM_INSTANCE(1);
static nrf_drv_pwm_t m_pwm2 = NRF_DRV_PWM_INSTANCE(2);
// This is for tracking PWM instances being used, so we can unintialize only
// the relevant ones when switching from one demo to another.
#define USED_PWM(idx) (1UL << idx)
static uint8_t m_used = 0;
static void pwm_alldemo(void)
{
NRF_LOG_INFO("pwm_alldemo");
/*
* This demo uses all three PWM peripheral instances:
* - PWM0 drives LED 1 and LED 2: Subsequent 2-bit binary values are
* presented every 500 ms.
* - PWM1 drives LED 3: During 500 ms, the LED increases and decreases
* the light intensity, then it stays off for 1500 ms.
* - PWM2 drives LED 4: For 500 ms, the LED stays off, then during 1500 ms
* it increases and decreases the light intensity.
* Simple playback with grouped loading mode is used for PWM0, and complex
* playback with common loading mode is used for both PWM1 and PWM2.
*/
nrf_drv_pwm_config_t config =
{
/*
These are the common configuration options we use for all PWM
instances.
因为是所有公用的,所以直接定义
*/
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
.count_mode = NRF_PWM_MODE_UP,
.step_mode = NRF_PWM_STEP_AUTO,
};
// PWM0 initialization. PWM0的配置
config.output_pins[0] = BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED;
config.output_pins[1] = NRF_DRV_PWM_PIN_NOT_USED;
config.output_pins[2] = BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED;
config.output_pins[3] = NRF_DRV_PWM_PIN_NOT_USED;
config.base_clock = NRF_PWM_CLK_125kHz;
config.top_value = 31250; // 250ms period
config.load_mode = NRF_PWM_LOAD_GROUPED;PWM导入模式:分组模式
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config, NULL));
m_used |= USED_PWM(0);
// This array cannot be allocated on stack (hence "static") and it must
// be in RAM (hence no "const", though its content is not changed).
static nrf_pwm_values_grouped_t /*const*/ pwm0_seq_values[] =
{
{ 0, 0 },
{ 0x8000, 0 },
{ 0, 0x8000 },
{ 0x8000, 0x8000 }
};
nrf_pwm_sequence_t const pwm0_seq =
{
.values.p_grouped = pwm0_seq_values,
.length = NRF_PWM_VALUES_LENGTH(pwm0_seq_values),
.repeats = 2,
.end_delay = 0
};
// Common settings for PWM1 and PWM2.
enum { // [local constants]
TOP = 5000,
STEP_COUNT = 50
};
config.base_clock = NRF_PWM_CLK_1MHz;
config.top_value = TOP;
config.load_mode = NRF_PWM_LOAD_COMMON;PWM导入模式:共有模式
// This array cannot be allocated on stack (hence "static") and it must
// be in RAM.
/*
这里 2 * STEP_COUNT 是什么意思?
*/
static nrf_pwm_values_common_t fade_in_out_values[2 * STEP_COUNT];
uint16_t value = 0;
uint16_t step = TOP / STEP_COUNT;
uint8_t i;
for (i = 0; i < STEP_COUNT; ++i)
{
value += step;
fade_in_out_values[i] = value;
fade_in_out_values[STEP_COUNT + i] = TOP - value;
}
// This array cannot be allocated on stack (hence "static") and it must
// be in RAM (hence no "const", though its content is not changed).
static nrf_pwm_values_common_t /*const*/ stay_off_values[2] = { 0, 0 };
// PWM1 initialization.
/*
使用哪个通道无所谓
*/
config.output_pins[0] = NRF_DRV_PWM_PIN_NOT_USED;
// config.output_pins[1] = NRF_DRV_PWM_PIN_NOT_USED;
config.output_pins[1] = BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED;
config.output_pins[2] = NRF_DRV_PWM_PIN_NOT_USED;
// config.output_pins[2] = BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED;
config.output_pins[3] = NRF_DRV_PWM_PIN_NOT_USED;
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm1, &config, NULL));
m_used |= USED_PWM(1);
// Sequence 0 - fade-in/fade-out, duration: 500 ms.
nrf_pwm_sequence_t const pwm1_seq0 =
{
.values.p_common = fade_in_out_values,
.length = NRF_PWM_VALUES_LENGTH(fade_in_out_values),
.repeats = 0,
.end_delay = 0
};
// Sequence 1 - off, duration: 1500 ms.
nrf_pwm_sequence_t const pwm1_seq1 =
{
.values.p_common = stay_off_values,
.length = 2,
.repeats = 149,//通过这个次数设定灯灭的时间
.end_delay = 0
};
// PWM2 initialization.
config.output_pins[0] = NRF_DRV_PWM_PIN_NOT_USED;
config.output_pins[1] = NRF_DRV_PWM_PIN_NOT_USED;
config.output_pins[2] = NRF_DRV_PWM_PIN_NOT_USED;
config.output_pins[3] = BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED;
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm2, &config, NULL));
m_used |= USED_PWM(2);
// Sequence 0 - fade-in/fade-out, duration: 1500 ms.
nrf_pwm_sequence_t const pwm2_seq0 =
{
.values.p_common = stay_off_values,
.length = 2,
.repeats = 49,
.end_delay = 0
};
// Sequence 1 - off, duration: 500 ms.
nrf_pwm_sequence_t const pwm2_seq1 =
{
.values.p_common = fade_in_out_values,
.length = NRF_PWM_VALUES_LENGTH(fade_in_out_values),
.repeats = 5,//这个repeat放长,灯渐变的时间边长
.end_delay = 0
};
(void)nrf_drv_pwm_simple_playback(&m_pwm0, &pwm0_seq, 1,
NRF_DRV_PWM_FLAG_LOOP);
(void)nrf_drv_pwm_complex_playback(&m_pwm1, &pwm1_seq0, &pwm1_seq1, 1,
NRF_DRV_PWM_FLAG_LOOP);
(void)nrf_drv_pwm_complex_playback(&m_pwm2, &pwm2_seq0, &pwm2_seq1, 1,
NRF_DRV_PWM_FLAG_LOOP);
}
static void bsp_evt_handler(bsp_event_t evt){
}
static void init_bsp()
{
APP_ERROR_CHECK(nrf_drv_clock_init());
nrf_drv_clock_lfclk_request(NULL);
APP_ERROR_CHECK(app_timer_init());
APP_ERROR_CHECK(bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler));
APP_ERROR_CHECK(bsp_buttons_enable());
}
void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
{
bsp_board_leds_on();
app_error_save_and_stop(id, pc, info);
}
int main(void)
{
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
init_bsp();
NRF_LOG_INFO("PWM example started.");
// Start with Demo 1, then switch to another one when the user presses
// button 1 or button 2 (see the 'bsp_evt_handler' function).
pwm_alldemo();
for (;;)
{
// Wait for an event.
__WFE();
// Clear the event register.
__SEV();
__WFE();
NRF_LOG_FLUSH();
}
}