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

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] =3XMAX_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的原理。
先解析相关代码:
这是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波。

粽子糖果 发表于11-30 11:24 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

粽子糖果
粽子糖果(总统)
金币:41624个|学分:51977个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号