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

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

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

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

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  = ;     //2^4   16分频成1M时钟源
    NRF_TIMER0->MODE = ;           //timer模式
    NRF_TIMER0->BITMODE = ;    //32bit

    NRF_TIMER0->CC[] = ;    //cc[1]的值等于是1s,这里相当于方波的周期为1s
    NRF_TIMER0->CC[] =;      //调节占空比,这里设置为0.5

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

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

void gpiote_init(void){
    NRF_GPIOTE->CONFIG[] = (  <<  )        //作为task模式
                         | ( PWM_OUT << ) //设置PWM输出引脚
                         | (  <<  )     //设置task为翻转PWM引脚的电平
                         | (  << );     //初始输出电平为高
}

//使用了两个PPI通道。 通道0 用来将 timer的 event0 (计数到cc0的值产生的事件) 与 上面设置的GPIOTE task绑定在一起
//通道1 用来将timer的event1(计数到cc1的值产生的事件) 也与上面的GPIOTE task事件绑定在一起。
//这样到计数到cc0和cc1时都会自动翻转 PWM_OUT引脚的电平。
void ppi_set(void){
    NRF_PPI->CH[].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[]);   //注意,这里赋值要取地址
    NRF_PPI->CH[].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[]);           

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

    //两个通道的task端绑定的都是翻转电平的task
    //使能PPI通道 0 和 通道1
    NRF_PPI->CHENSET = 0x03;
}

int main(void){
    gpiote_init();
    ppi_set();
    timer0_init();
    );
;
} 

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

nrf51822裸机教程-PWM的更多相关文章

  1. nrf51822裸机教程-IIC

    关于IIC总线的核心有以下几点: :时钟线高电平期间必须保持数据线不变. :时钟线低电平期间可以改变数据. :时钟线和数据线上都要接上拉电阻,以使总线不工作时,两根线的电平都处于高电平状态. :每个传 ...

  2. nrf51822裸机教程-UART

    art硬件模块通常都有内置的硬件接收buff,比如51822的硬件uart模块图如下 因为通常接收到uart数据时都会做一些处理.比如保存到数据,或者对数据做一些判断之类的. 如果uart的波特率设置 ...

  3. nrf51822裸机教程-RTC

    RTC0被协议栈使用了.所以在跑蓝牙程序的情况下.RTC0不能使用. RTC相关寄存器如下: EVTEN,EVTENSET,EVTENCLR. 这三个寄存器用来设置是否使能某个事件.(TICK,OVR ...

  4. nrf51822裸机教程-PPI

    Programmable Peripheral Interconnect即可编程外设互联 系统,该模块是51822 提供的一个特性. 目的是为了让51822 的外围模块可以不通过处理器而自动相互作用. ...

  5. nrf51822裸机教程-GPIOTE

    GPIO通常都会具有中断功能,上一讲的GPIO中并没有涉及到中断的相关寄存器. 51822将GPIO的中断相关做成了一个单独的模块GPIOTE,这个模块不仅提供了GPIO的中断功能,同时提供了 通过t ...

  6. nrf51822裸机教程-SPI(主)

    关于SPI总线的介绍这里就不细说了,网上有很多介绍SPI总线时序的. SPI总线的本质就是一个环形总线结构,在时钟驱动下两个双向移位寄存器进行数据交换. 所以SPI总线的特色就是:传输一字节数据的同时 ...

  7. nrf51822裸机教程-硬件timer

    该讲介绍51822的Timer/Counter模块工作在timer模式下(定时器模式,还可以工作为计数器模式) 如何操作 51822的Timer/Counter结构如下图所示 Timer模块从PCLK ...

  8. nrf51822裸机教程-GPIO

    首先看看一下相关的寄存器说明 Out寄存器 输出设置寄存器 每个比特按顺序对应每个引脚,bit0对应的就是 引脚0 该寄存器用来设置 引脚作为输出的时候的 输出电平为高还是低. 与输出设置相关的 还有 ...

  9. AVR单片机教程——PWM调光

    本文隶属于AVR单片机教程系列.   PWM 两位数码管的驱动方式是动态扫描,每一位都只有50%的时间是亮的,我们称这个数值为其占空比.让引脚输出高电平点亮LED,占空比就是100%. 在驱动数码管时 ...

随机推荐

  1. Python 中文Key 报错问题

    例如: # -*- coding=UTF-8 -*- import json data = [] with open('data.json') as f: for line in f: data.ap ...

  2. http://blog.csdn.net/jiazimo/article/details/17265061

    http://blog.csdn.net/jiazimo/article/details/17265061

  3. java中的String设计原理

    首先,必须强调一点:String Pool不是在堆区,也不是在栈区,而是存在于方法区(Method Area) 解析: String Pool是常量池(Constant  Pool)中的一块. 我们知 ...

  4. 如何在 .Net Framework 4.0 项目上使用 OData?

    最新的 Microsoft ASP.NET Web API 2.1 OData 5.1.0 已只能在 .Net Framework 4.5 的安装了,如果要在 VS2010的 .Net Framewo ...

  5. BZOJ3217 : ALOEXT

    替罪羊树套Trie,Trie合并用线段树合并,注意常数优化. 顺便AC800题纪念~~~ #include<cstdio> #include<cmath> #include&l ...

  6. BZOJ 1001 & SPFA

    1001: [BeiJing2006]狼抓兔子 Time Limit: 15 Sec  Memory Limit: 162 MB Description 现在小朋友们最喜欢的"喜羊羊与灰太狼 ...

  7. 【BZOJ】3319: 黑白树(并查集+特殊的技巧/-树链剖分+线段树)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3319 以为是模板题就复习了下hld............................. 然后n ...

  8. 【BZOJ】1221: [HNOI2001] 软件开发(最小费用最大流)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1221 先吐槽一下,数组依旧开小了RE:在spfa中用了memset和<queue>的版本 ...

  9. iOS应用程序本地化

    一.简介 * 使用本地化功能,可以轻松地将应用程序翻译成多种语言,甚至可以翻译成同一语言的多种方言 * 如果要添加本地化功能,需要为每种支持的语言创建一个子目录,称为”本地化文件夹”,通常使用.lpr ...

  10. QCheckBox 的按钮响应

    Qt中QCheckBox的按键响应如下,其中checkbox为对象名: void YourClass::on_checkbox_toggled(bool state) { // Do somethin ...