一般PPI实现PWM方法 1.使能time1,分配2个比较事件。注意只有周期时间是清除定时器,产生中断。
2.使能一个IO的GPIOTE任务。
3.分配2个PPI通道
4.绑定PPI通道1,定时比较事件1,IO翻转任务;
绑定PPI通道2,定时比较事件2,IO翻转任务;
5.中断里改变CC[0]和CC[1]的值。
达到的效果是,可改变周期,和占空比。
实现注意事项 【Nordic51822用PPI方式产生PWM注意事项】1.跑裸机不带协议栈,PWM可以完美实现。SDK包里的示例就可以实现。使用我上面的方式则能达到周期可变的目的。
2.使用带协议栈,需要与手机保持连接。不能产生绝对准确的PWM。下面解释原因。
(1)因为我使用的是中断周期溢出中断,原本是想可以准确控制脉冲数。但是因为有协议栈,所以中断会被打断,PPI没有停止,所以脉冲数会变多。
(2)直接改变CC[0]和CC[1]的值导致可能反相。
文章图片
出现这个的原因如下:
因为中断周期性改变比较值,可能造成需要翻转那一次被跳过。
举例:占空比较值是100,当前跑到95,然后需要比较值减10变90,则这一次占空翻转跳过
周期比较值是200,当前跑到95,然后需要比较值减10变190,则这一次周期翻转没跳过
是递增也会有问题,可能出现,触发了翻转,因为改变了又多触发一次。
反正是用CPU干预改变比较值就会有问题。要么是协议栈打断导致脉冲数变多,要么是改变比较值,导致多触发一次或者少触发一次。
只要是多触发一次或者少触发一次,就会造成反相的问题。
SDK包里为什么没这个问题 因为SDK的示例程序都没有带协议栈,只要是在改变比较值的时候没有当前值跨过比较值得现象就不会有问题。因为协议栈的存在time的值在累加,但是改变比较值却没有及时跑完,导致time当前值跨过比较值得现象。
怎么解决这个问题 1.在改变比较值cc[0],cc[1]之前如下操作:
nrf_drv_gpiote_out_task_disable(IO_buzzer);
//禁止task,恢复默认IO
nrf_drv_timer_disable(&TIMER_BiBi);
//关闭time,这样就不会有event
NRF_TIMER1->CC[0] = timetick;
//改周期
NRF_TIMER1->CC[1] = timetick-V_Voice_level;
//改占空
nrf_drv_timer_enable(&TIMER_BiBi);
nrf_drv_gpiote_out_task_enable(IO_buzzer);
2.这样做等同于重新初始化PPI功能了。就不会有反相的波形出现。
3.这样做有如下影响,导致无效电平变长,因为这个中间如果发生协议栈占用,事件没办法产生。
附PPI实现PWM的核心代码
// 使能time1,在扩展比较模式下,选择通道0,比较值是timetick,匹配0事件定时器清除,开启中断.
nrf_drv_timer_extended_compare(&TIMER_BiBi, (nrf_timer_cc_channel_t)0, timetick,\
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
// 使能time1,在扩展比较模式下,选择通道1,比较值是timetick-V_Voice_level,不清除定时器,不开中断.
nrf_drv_timer_extended_compare(&TIMER_BiBi, (nrf_timer_cc_channel_t)1, timetick-V_Voice_level, NULL, false);
//创建GPIOTE 任务
nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
err_code = nrf_drv_gpiote_out_init(IO_buzzer, &config);
//创建PPI通道0,关联匹配0事件 和 GPIOTE 任务
compare_evt_addr = nrf_drv_timer_event_address_get(&TIMER_BiBi, NRF_TIMER_EVENT_COMPARE0);
gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(IO_buzzer);
err_code = nrf_drv_ppi_channel_assign(ppi_channel, compare_evt_addr, gpiote_task_addr);
APP_ERROR_CHECK(err_code);
//关联事件和任务,通过PPI// 创建PPI通道1,关联匹配1事件 和 GPIOTE 任务
compare_evt_addr_1 = nrf_drv_timer_event_address_get(&TIMER_BiBi, NRF_TIMER_EVENT_COMPARE1);
err_code = nrf_drv_ppi_channel_assign(ppi_channel_1, compare_evt_addr_1, gpiote_task_addr);
APP_ERROR_CHECK(err_code);
//关联事件和任务,通过PPI
我使用的是SDK10.