본문 바로가기
Study/nRF51xxx(BLE)

nRF51 DK 예제 10 - PWM

by Answer Choi 2015. 3. 4.
반응형



이번 예제는 PWM입니다.



Timer를 이용하여 LED를 toggle하는데, 버튼을 누를때마다 



토글되는 간격이 변하는 PWM예제입니다.




main.c


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main(void)
{
    gpiote_init();
    bsp_configuration();
    ppi_init();
    timer2_init();
 
    NRF_POWER->TASKS_CONSTLAT = 1;
 
    // Enable interrupt on Timer 2.
    NVIC_EnableIRQ(TIMER2_IRQn);
    __enable_irq();
 
    *(uint32_t *)0x4000AC0C = 1;    
       
    // Start the timer.
    NRF_TIMER2->TASKS_START = 1;
 
    while (true)
    {
            // Do nothing.
    }
}
cs


Line 3 : GPIOTE 초기화


1
2
3
4
5
6
static void gpiote_init(void)
{
    nrf_gpio_cfg_output(PWM_OUTPUT_PIN_NUMBER);
    nrf_gpiote_task_config(0, PWM_OUTPUT_PIN_NUMBER, \
                           NRF_GPIOTE_POLARITY_TOGGLE, NRF_GPIOTE_INITIAL_VALUE_LOW);
}
cs


여기서 PWM_OUTPUT_PIN_NUMBER은 LED1을 뜻합니다.


LED1을 출력으로 설정하고, GPIOTE Task에서 LED1을 toggle방식으로 설정합니다.



Line 4 : bsp 설정


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static void bsp_configuration()
{
    // Start oscillator for app_timer
    NRF_CLOCK->LFCLKSRC            = (CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos);
    NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_LFCLKSTART    = 1;
 
    while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0)
    {
        // Do nothing.
    }
 
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);
    APP_GPIOTE_INIT(1);
 
    uint32_t err_code;
    err_code = bsp_init(BSP_INIT_BUTTONS, APP_TIMER_TICKS(100, APP_TIMER_PRESCALER), bsp_evt_handler);
    err_code = bsp_buttons_enable( (1 << BUTTON_PREV_ID) | (1 << BUTTON_NEXT_ID) );
    APP_ERROR_CHECK(err_code);
}
cs


4~11 : APP_Timer를 위해 LFCLK을 설정하는 부분이고, 


13 : APP_Timer 초기화.


16~19 : button을 사용하기 위한 bsp setting입니다. BUTTON_PREV_ID는 0, BUTTON_NEXT_ID는 1입니다.


버튼을 눌렀을때 Callback되는 함수는 bsp_evt_handler()입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void bsp_evt_handler(bsp_event_t evt)
{
    switch (evt)
    {
        case BSP_EVENT_KEY_0:
            duty_cycle -= INCREMENT_VALUE;
 
            if ( duty_cycle <= 0 )
                duty_cycle = 1;
            break;
 
        case BSP_EVENT_KEY_1:
            duty_cycle += INCREMENT_VALUE;
 
            if ( duty_cycle >= MAX_SAMPLE_LEVELS )
                duty_cycle = MAX_SAMPLE_LEVELS - 1;
            break;
 
        default:
            break// no implementation needed
    }
}
cs



5~10 : 버튼 0을 눌렀을 때이며, 버튼이 눌릴때마다 duty_cycle이 줄어듦니다.


duty_cycle 초기값은 1, INCREMENT_VALUE는 16입니다.


12~17 : 버튼 1이 눌려졌을 경우이며, 버튼이 눌려질때마다 duty_cycle이 증가합니다.


다시 main함수에서


Line 5 : PPI 초기화부분입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void ppi_init(void)
{
    NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[0];
    NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];
 
    NRF_PPI->CH[1].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[1];
    NRF_PPI->CH[1].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];
 
    NRF_PPI->CH[2].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[2];
    NRF_PPI->CH[2].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];
 
    NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos)
                    | (PPI_CHEN_CH1_Enabled << PPI_CHEN_CH1_Pos)
                    | (PPI_CHEN_CH2_Enabled << PPI_CHEN_CH2_Pos);
}
cs


3~4 : PPI 채널0설정이며, TIMER2의 COMPARE[0]이 event되면 GPIOTE에서 세팅한 LED1이 토글됩니다.


6~7 : PPI 채널1이며, TIMER2의 COMPARE[1]이 event되면 역시 LED1을 토글합니다.


9~10 : PPI 채널2이며, TIMER2의 COMPARE[2]가 event되면 LED1을 토글합니다.


12~14 : PPI 채널 0~2를 모두 enable시킵니다.


Timer2의 compare0~2가 event발생하면 LED1을 토글 시키겠다는 거네요.


Line 6 : Timer2 초기화입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void timer2_init(void)
{
    NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_HFCLKSTART    = 1;
 
    while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0)
    {
        // Do nothing.
    }
 
    NRF_TIMER2->MODE      = TIMER_MODE_MODE_Timer;
    NRF_TIMER2->BITMODE   = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos;
    NRF_TIMER2->PRESCALER = TIMER_PRESCALERS;
 
    NRF_TIMER2->TASKS_CLEAR = 1;
 
    NRF_TIMER2->CC[0= MAX_SAMPLE_LEVELS + duty_cycle;
    NRF_TIMER2->CC[1= MAX_SAMPLE_LEVELS;
    NRF_TIMER2->CC[2= 0;
 
    NRF_TIMER2->INTENSET = (TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENSET_COMPARE1_Pos);
}
cs


3~9 : HFCLK를 시작시키는 거고


11~13 : 보면 타이머를 설정합니다. 모드는 타이머이며, 비트는 16bit, prescalers는 6입니다.


계산해보면 f=16M/₂6=25kHz가 나옵니다. 즉 4us마다 Timer 카운트가 1씩 증가됩니다.


15 : Timer2값을 0으로 초기화하고


17~19 : CC(Capture/Compare)값을 설정합니다.


CC[0]은 MAX_SAMPLE_LEVELS+duty_cycle로 설정합니다. 즉 256+1=257


CC[1]은 MAX_SAMPLE_LEVELS=256


CC[2]는 0


21 : 타이머2의 COMPARE1만 인터럽트를 enable 합니다.


다시 main으로와서


Line 11~17 : Timer2 인터럽트를 enable한 후 Timer를 동작시킵니다.


초기 세팅대로라면, Timer2의 COMPARE[1]값이 256이므로 4us*256=약 1ms쯤에 인터럽트가 걸릴 것입니다. 


그리고 LED는 꺼지고, Timer2가 257이 되는 4us 후에 LED는 다시 켜지게 됩니다.


타이머 인터럽트를 보시면


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void TIMER2_IRQHandler(void)
{
    static bool cc0_turn = false/**< Keeps track of which CC register to be used. */
 
    if ((NRF_TIMER2->EVENTS_COMPARE[1!= 0) &&
        ((NRF_TIMER2->INTENSET & TIMER_INTENSET_COMPARE1_Msk) != 0))
    {
   
        NRF_TIMER2->EVENTS_COMPARE[1= 0;
        NRF_TIMER2->CC[1]             = (NRF_TIMER2->CC[1+ MAX_SAMPLE_LEVELS);
 
        if (cc0_turn)
        {
            NRF_TIMER2->CC[0= NRF_TIMER2->CC[1+ duty_cycle;
        }
        else
        {
            NRF_TIMER2->CC[2= NRF_TIMER2->CC[1+ duty_cycle;
        }
        cc0_turn = !cc0_turn;
    }
}
cs


Timer2가 256이되어 인터럽트에 걸리게 되면 위 TIMER2_IRQHandler로 들어오게 됩니다.


3 : cc0_turn은 static 변수라 전역변수의 역할을 하게됩니다.


5~21 : if문으로 들어오게되고, 


9 : COMPARE[1]레지스터를 초기화하고, 


10 : CC[1]에 CC[1]+MAX_SAMPLE_LEVELS를 넣게됩니다. 즉 256+256=512가 됩니다.


18 : 초기 cc0_turn이 false이므로 실행되고, CC[2]은 CC[1]+duty_cycle=512+1=513이 됩니다.


20 : cc0_turn은 true가 되고, 다음 인터럽트 때는 


14 : CC[0]값이 CC[1]보다 1큰수가 들어가게 됩니다.


즉, Timer2가 256[LED off]->257[LED on]->512[LED off]->513[LED on]->768[LED off]->769[LED on]....


==>> LED on(1ms)->LED off(4us) 이런식이 되는거죠.


이건 어디까지나 초기상태일때 얘깁니다. duty_cycle이 1일때!!!


버튼2를 누르면 어떻게 될까요??


버튼2를 누를때마다 duty_cycle이 증가하게되고, 최고 255까지 증가합니다.


duty_cycle이 255라고 가정하게되면


Timer2가 256[LED off]->257[LED on]->512[LED off]->767[LED on]->768[LED off]->1023[LED on]....


==>> LED on(4us)->LED off(1ms) 로 duty_cycle이 1일때와 반대로 되어버립니다.


버튼1을 누르면 duty_cycle은 다시 줄어들며 최소 1까지 줄어들게 됩니다.


이 pwm출력은 LED1와 맞물려있어 LED1의 밝기가 달라지게 됩니다.


결과는 아래의 영상을 참고하세요^^




반응형

'Study > nRF51xxx(BLE)' 카테고리의 다른 글

nRF51 DK 예제 11 - radiotest (2)  (2) 2015.03.06
nRF51 DK 예제 11 - radiotest (1)  (0) 2015.03.05
nRF51 DK 예제 9 - PPI  (0) 2015.03.04
nRF51 DK 예제 8 - Pin change interrupt  (0) 2015.03.03
nRF51 DK 예제 7 -Radio Receiver  (0) 2015.03.03

인기글