ZigBee——使用CC2530的定时器生成指定的PWM波

由于在使用zigbee网络时往往有对电机、灯具等执行器进行控制的需要,而出于成本和简化系统的需求,我们又往往希望在CC2530上实现相关的控制及ZigBee的组网工作,使用CC2530芯片实现PWM输出则是一项重要的控制能力。

首先,需要选定输出PWM的IO口,查阅CC2530的数据手册关于GPIO与外设的映射关系如下:

为了方便程序编写,选择TIMER1作为PWM的定时器,选择其备用位置1作为PWM比较输出的位置。注意到TIMER1与USART0存在默认位置的冲突,故这里仅选用TIMER1的2、3、4通道,1、2通道留给USART0串口使用,方便程序调试。

对于这样的复用功能,由于串口的优先级往往更高,因此还需要对寄存器进行进一步的配置。查阅数据手册中配置USART0与TIMER1的优先级顺序的寄存器,如下图:

可以看到,相关寄存器位于P2DIR中,这是由于P2的IO口数量较少(不足8个),因此与其它功能共用一个8bit寄存器地址。(需要注意的是,我们需要配置的是位于P0的IO口,因此不要错误使用P2SEL对其它备用位置的IO进行优先级配置)

而定时器的启动及相关配置较为简单,在此不再赘述,可参考数据手册与代码增进理解。

仿照TI官方的HAL库写法,添加两个文件,分别是hal_pwm.h和hal_pwm.c。

//hal_pwm.h
#ifndef HAL_PWM_H
#define HAL_PWM_H

#ifdef __cplusplus
extern "C"
{
#endif
    
#include "hal_board.h"

void PWM_Init(uint16 freq); // PWM初始化并设置频率
void PWM_Set(uint8 duty1, uint8 duty2, uint8 duty3); // 传入3个通道的比较值,0~255有效

#ifdef __cplusplus
}
#endif
    
#endif
//hal_pwm.c
#include "hal_pwm.h"

#define PWM_1 P0_4
#define PWM_2 P0_5
#define PWM_3 P0_6

void PWM_Init(uint16 freq)
{
    freq = 250000 / freq;
    PWM_1 = 1;
    PWM_2 = 1;
    PWM_3 = 1;
    CLKCONCMD &= ~0x40;       //设置系统时钟源为32MHZ晶振
    while(CLKCONSTA & 0x40);  //等待晶振稳定为32M
    CLKCONCMD &= ~0x07;       //设置系统主时钟频率为32MHZ
    CLKCONCMD |= 0x38;        //设置定时器所分频率为250KHZ
    PERCFG &= 0x00;           //选择定时器1映射的IO口位置,此处我们选择备用位置(Alt)1,它映射的IO口包括P0_4/5/6
    P2DIR |= 0xC0;            //当PERCFG分配给一些外设到相同引脚的时候,此时设定谁的优先级高,此处设定第1优先级为定时器1通道2-3
    P0DIR |= 0x70;            //设置P0_4/5/6为输出
    P0SEL |= 0x70;            //此时将P0_4/5/6配置为外设功能,即定时器1的输出比较
    T1CC0H = HI_UINT16(freq); //设定整个信号的周期时间(CC0即使不用也要设置)
    T1CC0L = LO_UINT16(freq) - 1;
    T1CC2H = 0x00;
    T1CC2L = 0x00;
    T1CCTL2 = 0x24;
    T1CC3H = 0x00;
    T1CC3L = 0x00;
    T1CCTL3 = 0x24;
    T1CC4H = 0x00;
    T1CC4L = 0x00;
    T1CCTL4 = 0x24;
    T1CTL = 0x02;            //设定定时器频率250KHz,1分频,设定运行模式为"模"模式,即从0x0000到T1CC0反复计数
}

void PWM_Set(uint8 duty1, uint8 duty2, uint8 duty3) //0~255
{
    uint16 duty_set;
    uint16 duty_comp = BUILD_UINT16(T1CC0L, T1CC0H);
    float duty_precent;
    
    duty_precent = duty1/255.0;   //取得PWM实际占空比
    duty_set = (uint16)(duty_precent * duty_comp); //计算比较值
    T1CC2H = HI_UINT16(duty_set); //高位赋值
    T1CC2L = LO_UINT16(duty_set); //低位赋值
    
    duty_precent = duty2/255.0;
    duty_set = (uint16)(duty_precent * duty_comp);
    T1CC3H = HI_UINT16(duty_set);
    T1CC3L = LO_UINT16(duty_set);
    
    duty_precent = duty3/255.0;
    duty_set = (uint16)(duty_precent * duty_comp);
    T1CC4H = HI_UINT16(duty_set);
    T1CC4L = LO_UINT16(duty_set);
}

#endif

代码中首先配置了系统时钟,若使用TI的Zstack协议栈则这些项目都是默认进行的配置(TI内部的OSAL系统使用TIMER4的1ms中断驱动操作系统工作)。

其次便是将定时器配置为1分频(不分频)模式,能够产生250KHz的最高频率,留给我们更多的配置空间。

通过对PERCFG寄存器的配置选择了TIMER0的位置选择,P2DIR则决定了这些IO上的优先等级。

P0DIR与P0SEL的功能与它们的名称一致,配置了IO映射到复用功能上,并设定方向为输出。

T1CC0则需要引起注意,由于我们选择的是输出的PWM频率与占空比皆可变化的“模”模式,因此T1CC0是PWM频率比较的基准,即使我们不使用TIMER1和通道0,也必须设置该寄存器。设置该寄存器即决定了整个PWM周期的频率,需要注意的是从0计算至T1CC0,因此设置值时需要注意减去1,对于频率敏感的场合尤其需要注意。

而T1CC2/T1CC3/T1CC4则默认设置为0,等待后续PWM配置函数进行计算和配置。

T1CCTL2/T1CCTL3/T1CCTL4则用于配置各个通道的输出模式与极性。在这里设置的值将使各个通道均工作在比较输出模式,并在计数值由0增加至T1CCx期间输出高电平,此后由T1CCx至T1CC0的过程中输出低电平,即输出正占空比,与一般的使用方式相符合。

在PWM_Set函数中,首先结合T1CC0的值,计算了PWM的实际占空比,并由此设置了3个输出通道的比较值。

在代码中,使用了TI公司提供的对数据结构进行转换的宏定义,摘录如下。

#define HI_UINT16(a) (((a) >> 8) & 0xFF)
#define LO_UINT16(a) ((a) & 0xFF)

#define BUILD_UINT16(loByte, hiByte) \
          ((uint16)(((loByte) & 0x00FF) + (((hiByte) & 0x00FF) << 8))) 

希望能够对大家学习CC2530及ZigBee有所启发~ 

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值