Chinaunix首页 | 论坛 | 博客
  • 博客访问: 585339
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-23 22:57
个人简介

我本仁慈,奈何苍天不许

文章分类

全部博文(165)

文章存档

2018年(1)

2016年(33)

2015年(5)

2014年(34)

2013年(92)

分类: 嵌入式

2016-07-21 14:13:29

原文地址:nrf51822裸机教程-PWM 作者:ifndef


先简单介绍一下PWM的原理。




原理很简单。 假设COUNTER是个从0开始递增的计数器。  我们设置两个值 counter0 counter1 COUNTER 计数到counter0的值时候翻转输出的电平,然后COUNTER继续计数,在计数到counter1的值的时候再翻转输出电平。 同时清零COUNTER计数器。让其从0开始重新计数,这样就可以产生一个方波。


从上面的图可以看出这个方波的一个周期T的时间是由 counter1来决定的。所以周期的调节就是通过counter1的值来调节。   counter0的值则影响着方波的占空比。


综上,PWM的实现就是通过调节counter1counter0的两个值来实现周期和占空比可调。

 

 

51822硬件没有PWM模块,所以如果需要使用PWM,从上面的原理介绍可以知道使用timer定时器就可以实现上述功能。


我们可以使用timer定时器中的寄存器cc[1],和cc[0]来设置上面说的counter1值和counter0值。并分别设置当计数器计数到指定值是产生中断。 在中断里面 将电平翻转就可以了。

但是这中方法因为中断的处理需要CPU参数,会影响PWM的周期和占空比。更多的影响是如果timer会频繁产生中断。导致正常的程序执行流程会被频繁打断。


所以这里需要用到 51822的 可编程外围互联系统(PPI), 该系统可以使51822的外围模块在无CPU参与的情况下相互协作。(详见PPI教程)

同时因为使用PPI 让timer模块和GPIO模块来协作产生PWM,所以这里不能使用普通的GPIO,而需要使用针对PPI的GPIOTE模块。(详见GPIOTE教程)





如上图所示。 我们使用timer模块 让其 计数到 counter0 和counter1时分别产生event0,和event1。这两个event通过PPI然后触发同一个task,这个task就是翻转电平。


下面是main.c代码细节。


#include "nrf51.h"

#include "stdio.h"

#include "nrf_gpio.h"


#define PWM_OUT          22


void timer0_init(void){

    NRF_TIMER0->PRESCALER  = 4;     //2^4   16分频成1M时钟源

    NRF_TIMER0->MODE = 0;           //timer模式

    NRF_TIMER0->BITMODE = 3;    //32bit

   

    NRF_TIMER0->CC[1] = 1000000;    //cc[1]的值等于是1s,这里相当于方波的周期为1s

    NRF_TIMER0->CC[0] =500000;      //调节占空比,这里设置为0.5

   

    NRF_TIMER0->SHORTS = 1<<1;      //设置到计数到cc1中的值时 自动清0 重新开始计数

   

    NRF_TIMER0->TASKS_START = 1;    //启动timer

}



void gpiote_init(void){

    NRF_GPIOTE->CONFIG[0] = ( 3 << 0 )        //作为task模式

                         | ( PWM_OUT << 8) //设置PWM输出引脚

                         | ( 3 << 16 )     //设置task为翻转PWM引脚的电平

                         | ( 1 << 20);     //初始输出电平为高

}



//使用了两个PPI通道。 通道0 用来将 timer event0 (计数到cc0的值产生的事件) 与 上面设置的GPIOTE task绑定在一起

//通道1 用来将timerevent1(计数到cc1的值产生的事件) 也与上面的GPIOTE task事件绑定在一起。

//这样到计数到cc0cc1时都会自动翻转 PWM_OUT引脚的电平。

void ppi_set(void){

    NRF_PPI->CH[0].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[0]);   //注意,这里赋值要取地址   

    NRF_PPI->CH[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]);           

   

    NRF_PPI->CH[1].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[1]);

    NRF_PPI->CH[1].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]);

   

    //两个通道的task端绑定的都是翻转电平的task

    //使能PPI通道 0 和 通道1

    NRF_PPI->CHENSET = 0x03;

}


int main(void){

   

    gpiote_init();

    ppi_set();

    timer0_init();

    while(1);

return 0; 

}


通过调节cc0cc1的值就可以分别控制占空比和周期了。这里只是个简单的示例。实际使用简单封装下就可以当做自己的PWM来使用了


阅读(2911) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

我本痴情2018-02-11 10:04:27

ynynleslie:楼主,这个代码直接复制到工程的main.cpp里面吗,还需要什么才能编译、下载和使用。。我用的软件是keil,硬件是nrf51822

不好意思,很久没上博客了,这个直接复制进去就行,Keil也可以的

回复 | 举报

ynynleslie2017-03-14 16:18:21

楼主,这个代码直接复制到工程的main.cpp里面吗,还需要什么才能编译、下载和使用。。我用的软件是keil,硬件是nrf51822