본문 바로가기
Embedded/nRF52 BLE 개발 안내서

nRF52 BLE 개발하기 - pwm_driver

by 큐찡 2021. 2. 7.
728x90
반응형

시작하기

PWM 드라이버를 사용해서 다양한 유형의 펄스 신호를 만드는 예제를 살펴보도록 하자.

예제는 총 5가지 유형의 PWM 신호를 출력하도록 되어 있으며 버튼 1과 버튼 2를 사용해 동작을 전환할 수 있다.

\examples\peripheral\pwm_driver\pca10040\blank\ses 폴더에서 프로젝트를 실행한다.

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 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
};

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());
}

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).
    demo1();

    for (;;)
    {
        // Wait for an event.
        __WFE();

        // Clear the event register.
        __SEV();
        __WFE();

        NRF_LOG_FLUSH();
    }
}

main 함수는 크게 init_bsp 함수와 demo1 함수를 호출하는 단순한 구조를 하고 있다.

그럼 바로 PWM 드라이버에 관련된 demo1 함수에 대해서 알아보도록 하자.

/** @brief Number of channels in each PWM instance. */
#define NRF_PWM_CHANNEL_COUNT  4

/** @brief This value can be added to a pin number to invert its polarity (set idle state = 1). */
#define NRFX_PWM_PIN_INVERTED    0x80

/** @brief PWM driver configuration structure. */
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;

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
};

static void demo1(void)
{
    NRF_LOG_INFO("Demo 1");

    /*
     * 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_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 변수에는 PWM 드라이버 설정 값들을 넣어줘야 한다.

output_pins 값에는 PWM 신호를 출력할 GPIO를 넣어주며 PWM 모듈 하나는 최대 4개를 사용할 수 있다.

사용할 GPIO에 NRF_DRV_PWM_PIN_INVERTED 값을 비트 마스크로 OR 연산해서 넣어주면 사용 설정이 된다.

irq_priority는 인터럽트 우선순위를 설정하는 변수로 여기서는 APP_IRQ_PRIORITY_LOWEST로 설정한다.

base_clock은 PWM 신호를 출력할 때 사용되는 클럭 속도로 여기서는 NRF_PWM_CLK_1MHz로 설정한다.

count_mode는 PWM 동작이 Up counter 또는 Up and down counter로 할지 정하는 변수로 여기서는 NRF_PWM_MODE_UP로 설정한다.

top_value는 펄스 카운트되는 최댓값으로 3~32767의 범위를 가지며 WaveForm 모드인 경우엔 무시된다. 여기서는 10000으로 설정한다.

load_mode는 비교기를 통해 시퀀스를 읽어오는 방식을 설정하는 것으로 여기서는 Single 모드로 설정한다.

step_mode는 시퀀스의 다음 값을 읽어오는 방식을 설정하는 것으로 여기서는 RefreshCount로 설정한다.

nrf_drv_pwm_init 함수의 전달 인자로 PWM 인스턴스, PWM 설정, 이벤트 핸들러로 demo1_handler 함수를 전달함으로 PWM 설정이 끝난다.

nrf_drv_pwm_simple_playback 함수의 전달 인자로 PWM 인스턴스, 시퀀스, 시퀀스 횟수, 옵션을 전달해 PWM 시퀀스를 동작시킨다.

이렇게 하면 PWM 드라이버를 이용해서 설정한 GPIO를 통해 PWM 신호를 시퀀스 값에 맞춰 출력하기 시작한다.

예제에서는 초기 시퀀스 값이 0이고 시퀀스 횟수는 1, 옵션으로 NRF_DRV_PWM_FLAG_LOOP로 설정되었으니 초기엔 PWM 신호 출력이 없고 시퀀스 재생이 끝나면 이벤트 핸들러를 호출한다.

PWM flags that provide additional playback options.
NRFX_PWM_FLAG_STOP 

When the requested playback is finished, the peripheral will be stopped.

NoteThe STOP task is triggered when the last value of the final sequence is loaded from RAM, and the peripheral stops at the end of the current PWM period. For sequences with configured repeating of duty cycle values, this might result in less than the requested number of repeats of the last value.
NRFX_PWM_FLAG_LOOP 

When the requested playback is finished, it will be started from the beginning. This flag is ignored if used together with NRFX_PWM_FLAG_STOP.

NoteThe playback restart is done via a shortcut configured in the PWM peripheral. This shortcut triggers the proper starting task when the final value of previous playback is read from RAM and applied to the pulse generator counter. When this mechanism is used together with the NRF_PWM_STEP_TRIGGERED mode, the playback restart will occur right after switching to the final value (this final value will be played only once).
NRFX_PWM_FLAG_SIGNAL_END_SEQ0 

The event handler is to be called when the last value from sequence 0 is loaded.

NRFX_PWM_FLAG_SIGNAL_END_SEQ1 

The event handler is to be called when the last value from sequence 1 is loaded.

NRFX_PWM_FLAG_NO_EVT_FINISHED 

The playback finished event (enabled by default) is to be suppressed.

NRFX_PWM_FLAG_START_VIA_TASK 

The playback must not be started directly by the called function. Instead, the function must only prepare it and return the address of the task to be triggered to start the playback.

static void demo1_handler(nrf_drv_pwm_evt_type_t event_type)
{
    if (event_type == NRF_DRV_PWM_EVT_FINISHED)
    {
        uint8_t channel    = m_demo1_phase >> 1;
        bool    down       = m_demo1_phase & 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;
            }
        }
    }
}

이벤트 핸들러인 demo1_handler 함수를 살펴보도록 하자.

첫 번째 PWM 채널부터 200씩 시퀀스 값을 증가시키다가 시퀀스 최댓값에 도달하면 다시 200씩 시퀀스 값을 감소시킨다.

이런 식으로 4개의 PWM 채널을 순차적으로 반복하게 된다.


static void demo2(void)
{
    NRF_LOG_INFO("Demo 2");

    /*
     * This demo plays back two concatenated sequences:
     * - Sequence 0: Light intensity is increased in 25 steps during one second.
     * - Sequence 1: LED blinks twice (100 ms off, 100 ms on), then stays off
     *   for 200 ms.
     * The same output is generated on all 4 channels (LED 1 - LED 4).
     * The playback is repeated in a loop.
     */

    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, // 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_500kHz,
        .count_mode   = NRF_PWM_MODE_UP,
        .top_value    = TOP,
        .load_mode    = NRF_PWM_LOAD_COMMON,
        .step_mode    = NRF_PWM_STEP_AUTO
    };
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
    m_used |= USED_PWM(0);

    // This array cannot be allocated on stack (hence "static") and it must
    // be in RAM.
    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;
    }

    nrf_pwm_sequence_t const seq0 =
    {
        .values.p_common = seq0_values,
        .length          = NRF_PWM_VALUES_LENGTH(seq0_values),
        .repeats         = 1,
        .end_delay       = 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_common_t /*const*/ seq1_values[] =
    {
             0,
        0x8000,
             0,
        0x8000,
             0,
             0
    };
    nrf_pwm_sequence_t const seq1 =
    {
        .values.p_common = seq1_values,
        .length          = NRF_PWM_VALUES_LENGTH(seq1_values),
        .repeats         = 4,
        .end_delay       = 0
    };

    (void)nrf_drv_pwm_complex_playback(&m_pwm0, &seq0, &seq1, 1,
                                       NRF_DRV_PWM_FLAG_LOOP);
}

demo2 함수를 살펴보도록 하자.

base_clock을 500 kHz, count_mode는 up counter, top_valuesms 10000, load_mode를 common, step_mode는 auto로 PWM 설정을 한다.

seq0, seq1 시퀀스 변수를 다음과 같이 정의하였다.

seq0 시퀀스는 25단계로 400씩 증가하는 값을 가지고 듀티 사이클 반복 횟수는 1번이다.

seq1 시퀀스는 6단계로 0, 32768, 0, 32768, 0, 0의 값을 가지고 듀티 사이클 반복 횟수는 4번이다.

이를 그래프로 표기하면 다음 그림과 같다.

nrf_drv_pwm_complex_playback 함수의 전달 인자로 seq0, seq1 시퀀스를 넣어주면 LED가 서서히 밝아지다가 깜빡이는 동작을 하게 된다.


static void demo3(void)
{
    NRF_LOG_INFO("Demo 3");

    /*
     * This demo uses only one channel, which is reflected on LED 1.
     * The LED blinks three times (200 ms on, 200 ms off), then it stays off
     * for one second.
     * This scheme is performed three times before the peripheral is stopped.
     */

    nrf_drv_pwm_config_t const config0 =
    {
        .output_pins =
        {
            BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED, // channel 0
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 1
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 2
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 3
        },
        .irq_priority = APP_IRQ_PRIORITY_LOWEST,
        .base_clock   = NRF_PWM_CLK_125kHz,
        .count_mode   = NRF_PWM_MODE_UP,
        .top_value    = 25000,
        .load_mode    = NRF_PWM_LOAD_COMMON,
        .step_mode    = NRF_PWM_STEP_AUTO
    };
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, 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 uint16_t /*const*/ seq_values[] =
    {
        0x8000,
             0,
        0x8000,
             0,
        0x8000,
             0
    };
    nrf_pwm_sequence_t const seq =
    {
        .values.p_common = seq_values,
        .length          = NRF_PWM_VALUES_LENGTH(seq_values),
        .repeats         = 0,
        .end_delay       = 4
    };

    (void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 3, NRF_DRV_PWM_FLAG_STOP);
}

demo3 함수를 살펴보도록 하자.

base_clock을 125 kHz, count_mode는 up counter, top_value는 25000, load_mode를 common, step_mode는 auto로 PWM 설정을 한다.

seq 시퀀스 변수를 다음과 같이 정의하였다.

seq1 시퀀스는 6단계로 32768, 0, 32768, 0, 32768, 0의 값을 가지고 시퀀스 재생이 끝나고 마지막 듀티 사이클 유지 시간은 4이다.

이를 그래프로 표기하면 다음 그림과 같다.

nrf_drv_pwm_simple_playback 함수의 전달 인자로 시퀀스 횟수는 3번, 재생 옵션은 stop이므로 LED1이 3번 깜빡임을 3번 반복하고 종료한다.


 

static void demo4(void)
{
    NRF_LOG_INFO("Demo 4");

    /*
     * 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.

    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;
    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          = 1,
        .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;

    // This array cannot be allocated on stack (hence "static") and it must
    // be in RAM.
    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[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         = 2,
        .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);
}

demo4 함수는 PWM 모듈을 총 3개 사용하는 예제로 모듈별로 살펴보도록 하자.

공통 설정으로 count_mode는 up counter, step_mode는 auto로 정했다.

첫 번째 PWM은 base_clock은 125 kHz, top_value는 31250, load_mode는 Grouped로 1, 2 채널이 그룹이 되고 3, 4 채널이 그룹이 되는 모드로 그룹 1은 LED1, 그룹 2는 LED2로 설정했다.

두 번째 PWM은 base_clock은 1 MHz, top_value는 5000, load_mode는 Common으로 채널 3에 LED3을 설정했다.

pwm1_seq0 시퀀스 값은 5000까지 100씩 증가하다가 다시 100씩 감소해 0이 된다.

pwm1_seq1 시퀀스 값은 0, 0 2단계로 듀티 사이클 반복 횟수가 149번으로 결과적으로 LED3이 꺼져있는 시간이 1500ms가 된다.

세 번째 PWM은 base_clock은 1 MHz, top_value는 5000, load_mode는 Common으로 채널 4에 LED4을 설정했다.

pwm2_seq0 시퀀스 값은 0, 0 2단계로 듀티 사이클 반복 횟수가 49번으로 결과적으로 LED3이 꺼져있는 시간이 500ms가 된다.

pwm2_seq1 시퀀스 값은 5000까지 100씩 증가하다가 다시 100씩 감소하는데 듀티 사이클 반복 횟수는 2번이다.


static void demo5(void)
{
    NRF_LOG_INFO("Demo 5");

    /*
     * This demo, similarly to demo1, plays back a sequence with different
     * values for individual channels. Unlike demo 1, however, it does not use
     * an event handler. Therefore, the PWM peripheral does not use interrupts
     * and the CPU can stay in sleep mode.
     * The LEDs (1-4) blink separately. They are turned on for 125 ms each,
     * in counterclockwise order (looking at the board).
     */

    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, // channel 1
            BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED, // channel 2
            BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED  // channel 3
        },
        .irq_priority = APP_IRQ_PRIORITY_LOWEST,
        .base_clock   = NRF_PWM_CLK_125kHz,
        .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);

    // 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_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);
}

마지막으로 demo5 함수를 살펴보도록 하자.

base_clock은 125 kHz, top_value는 15625, load_mode는 Single로 채널 1에 LED1, 채널 2에 LED3, 채널 3에 LED4, 채널 4에 LED2을 설정해 LED1, LED3, LED4, LED2 순으로 켜는 동작을 한다.

 

728x90
반응형

댓글