nrf51822学习之定时器PWM例程的笔记

SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。SYD8801片上集成了Balun无需阻抗匹配网络、高效率DCDC降压转换器,适合用于可穿戴、物联网设备等。具体可咨询:http://www.sydtek.com/

 

nrf51822并没有PWM模块,但是如果巧妙的结合PPI模块,并加上一个定时器中断就可以轻松的实现了PWM,思路是这样的:

定时器使用三个比较器 cc0、cc1和cc2,当三个比较器任何一产生比较事件的时候都会通过PPI去翻转GPIO的引脚,在初始化的时候这样设置这三个比较器:
    NRF_TIMER2->CC[0] = MAX_SAMPLE_LEVELS + next_sample_get();
    NRF_TIMER2->CC[1] = MAX_SAMPLE_LEVELS;
    // CC2 will be set on the first CC1 interrupt.
    NRF_TIMER2->CC[2] = 0;

这是初始化的配置,到这里会有一个思考,这样的话计数器技术到cc0的时候依然会继续的往下计数,那这样的话他的再溢出的值就将回到cc2的时候也就是归零的时候,那这样的波形就分为了三段了,这不是我们所需要的,那这样要实现PWM就要把cc2的比较值往后挪,让他超过cc0,并且cc2到之前的一个比较值是固定的,这样就需要从新设置cc2的值,还有一个办法就是当计数器到cc0的时候请求中断重置计数器,但是这样做有一个问题就是进入中断是需要时间的,而当计数器到达cc0的时候就需要重置,同时计数器的下一个值就是cc2,这样就会造成冲突,所以我们使用了第一种方案。

具体实现是这样的,使能cc1比较中断,在第一次中断中重新设置cc1,让他的值变成了两倍,同时从新设置cc2,让他的值变成了cc1+N,N就是占空比参数,在第二次中断中,也是从新设置cc1,但是和上一次中断不同的是这时候设置的是cc0,而不是cc2

这样造成的计数器溢出值是这样的:

刚开始:cc2=0

然后 :cc1=MAX_SAMPLE_LEVELS                同时进入中断设置                cc1=2X MAX_SAMPLE_LEVELS,CC[2] = cc1+next_sample

然后:CC[0] = MAX_SAMPLE_LEVELS + next_sample_get();也即是CC[0] = MAX_SAMPLE_LEVELS + next_sample

然后:cc1=2X MAX_SAMPLE_LEVELS            同时进入中断设置                cc1=3X MAX_SAMPLE_LEVELS,CC[0] = cc1+next_sample

然后 :,CC[2] =2X MAX_SAMPLE_LEVELS+next_sample

然后:cc1=3X MAX_SAMPLE_LEVELS            同时进入中断设置                cc1=4X MAX_SAMPLE_LEVELS,CC[2] = cc1+next_sample

然后:CC[0] =3X MAX_SAMPLE_LEVELS +next_sample

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

下面做如下简化:

M= MAX_SAMPLE_LEVELS

N=next_sample

那么从这里了总结出比较溢出值得顺序是这样的,

0->M->M+N->2M->2M+N->3M->3M+N->.....................

那么我们可以看到他们的差值一次是

M->N->M-N->N->M-N->N->...................................

我们可以看到除了第一次特殊外,奇数项的差值是一样的但和偶数项的差值不一样,同时偶数项的差值又是一样的,这也就是PWM波形,那么M也就是MAX_SAMPLE_LEVELS就是周期,N也就是next_sample也就是占空比参数,这就构成了PWM的原理,为了更形象的体现出这样的关系,下面上传这张图片

nrf51822学习之定时器PWM例程的笔记_第1张图片

先解析相关代码:

这是PPI初始化函数:

void ppi_init(void)
{
   // ppi通道一开启time2定时器.通过定时器2
    NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[0];
    NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];

   // ppi通道一开启time2定时器.通过定时器2
    NRF_PPI->CH[1].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[1];
    NRF_PPI->CH[1].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];
    
   // ppi通道一开启time2定时器.通过定时器2
    NRF_PPI->CH[2].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[2];
    NRF_PPI->CH[2].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];
    
   
    //开启通道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);
}

使用了三个通道,都通向了GPIOTE的输出功能

 

这是LED口配置函数:

void gpiote_init(void)  //io输出
{
    // Connect GPIO input buffers and configure PWM_OUTPUT_PIN_NUMBER as an output.
    nrf_gpio_cfg_output(19);

    // Configure GPIOTE channel 0 to toggle the PWM pin state
    // @note Only one GPIOTE task can be connected to an output pin.
    nrf_gpiote_task_config(0, 19,NRF_GPIOTE_POLARITY_TOGGLE, NRF_GPIOTE_INITIAL_VALUE_LOW);
}

使用了GPIOTE模块

 

这是定时器初始化:

void timer2_init(void)
{
    // 设置16m时钟
    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;

    // 清除timer
    NRF_TIMER2->TASKS_CLEAR = 1;

    //设置timer  cc
    NRF_TIMER2->CC[0] = MAX_SAMPLE_LEVELS + next_sample_get();
    NRF_TIMER2->CC[1] = MAX_SAMPLE_LEVELS;

    // CC2 will be set on the first CC1 interrupt.
    NRF_TIMER2->CC[2] = 0;

    // Interrupt setup.
    NRF_TIMER2->INTENSET = (TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENSET_COMPARE1_Pos);
}

初始化了定时器和三个比较器并打开比较器1的中断功能

 

这是定时器中断函数:

void TIMER2_IRQHandler(void) //timer2中断函数
{
    static bool cc0_turn = false; /**< Keeps track of which CC register to be used. */
     uint32_t next_sample = next_sample_get();
    if ((NRF_TIMER2->EVENTS_COMPARE[1] != 0) &&
       ((NRF_TIMER2->INTENSET & TIMER_INTENSET_COMPARE1_Msk) != 0))
    {
        // Sets the next CC1 value
        NRF_TIMER2->EVENTS_COMPARE[1] = 0;
        NRF_TIMER2->CC[1]             = (NRF_TIMER2->CC[1] + MAX_SAMPLE_LEVELS);
    
        // Every other interrupt CC0 and CC2 will be set to their next values.
        if (cc0_turn)
        {
            NRF_TIMER2->CC[0] = NRF_TIMER2->CC[1] + next_sample;
        }
        else
        {
            NRF_TIMER2->CC[2] = NRF_TIMER2->CC[1] + next_sample;
        }
        // Next turn the other CC will get its value.
        cc0_turn = !cc0_turn;
    }
}

这里改变三个比较器的值以实现PWM波

 

 

下面上传该例程:

http://download.csdn.net/detail/chengdong1314/9527769

你可能感兴趣的:(NORDIC,TI蓝牙)