nRF52832学习记录(十、PWM 脉冲调制)

..添加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();
    }
}
  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

矜辰所致

您们的鼓励是我奋斗的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值