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的原理,为了更形象的体现出这样的关系,下面上传这张图片
先解析相关代码:
这是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