完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本文摘录于本人博客:http://blog.csdn.net/chengdong1314/article/details/51473546
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波 下面上传该例程: http://download.csdn.net/detail/chengdong1314/9527769 |
|
相关推荐
|
|
只有小组成员才能发言,加入小组>>
物联网工程师必备:怎么选择不同的无线连接技术,本指南帮你忙!
3259 浏览 1 评论
【DFRobot TinkerNode NB-IoT 物联网开发板试用连载】WIFI功能测试
3908 浏览 0 评论
【DFRobot TinkerNode NB-IoT 物联网开发板试用连载】Arduino的替代SublimeText3+STino
3416 浏览 0 评论
使用端口扩展器轻松高效地向IIoT端点添加具有成本效益的子节点
3966 浏览 1 评论
20607 浏览 11 评论
模组有时候复位重启后输出日志为“REBOOT_CAUSE_SECURITY_PMU_POWER_ON_RESET”的原因?
747浏览 2评论
936浏览 2评论
962浏览 1评论
1085浏览 1评论
361浏览 1评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-27 07:36 , Processed in 0.741158 second(s), Total 69, Slave 52 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号