CC2640R2F之PWM添加篇(如何添加外设总章)

原创博客,如有转载,注明出处——在金华的电子民工林。

前面说过,CC2640R2F的外设驱动,协议栈都已经写好,非常方便调用,现在就来说说,怎么调用这些特殊的外设,而且,所有的外设添加思路一样,这篇博文,就介绍下添加的思路,以后的博客,只介绍添加的步骤,不讲思路。学会以后,大家可以更加方便的使用CC2640R2F进行项目的开发。

本人使用CCS,也强烈推荐同行们开发CC2640R2F也使用CCS,契合度最高。

首先,我们看底层文件CC2640R2_LAUNCHXL.C。很多人看到这个文件,一头雾水,那么多的数组,结构体等等,不知道做什么用。其实这些都是范例,给大家参考如何调用底层驱动的范例。比如PWM部分的代码如下:


/*
 *  =============================== PWM ===============================
 *  Remove unused entries to reduce flash usage both in Board.c and Board.h
 */
#include 
#include 

PWMTimerCC26XX_Object pwmtimerCC26xxObjects[CC2640R2_LAUNCHXL_PWMCOUNT];

const PWMTimerCC26XX_HwAttrs pwmtimerCC26xxHWAttrs[CC2640R2_LAUNCHXL_PWMCOUNT] = {
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN0, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER0A },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN1, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER0B },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN2, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER1A },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN3, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER1B },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN4, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER2A },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN5, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER2B },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN6, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER3A },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN7, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER3B },
};

const PWM_Config PWM_config[CC2640R2_LAUNCHXL_PWMCOUNT] = {
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[CC2640R2_LAUNCHXL_PWM0], &pwmtimerCC26xxHWAttrs[CC2640R2_LAUNCHXL_PWM0] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[CC2640R2_LAUNCHXL_PWM1], &pwmtimerCC26xxHWAttrs[CC2640R2_LAUNCHXL_PWM1] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[CC2640R2_LAUNCHXL_PWM2], &pwmtimerCC26xxHWAttrs[CC2640R2_LAUNCHXL_PWM2] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[CC2640R2_LAUNCHXL_PWM3], &pwmtimerCC26xxHWAttrs[CC2640R2_LAUNCHXL_PWM3] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[CC2640R2_LAUNCHXL_PWM4], &pwmtimerCC26xxHWAttrs[CC2640R2_LAUNCHXL_PWM4] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[CC2640R2_LAUNCHXL_PWM5], &pwmtimerCC26xxHWAttrs[CC2640R2_LAUNCHXL_PWM5] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[CC2640R2_LAUNCHXL_PWM6], &pwmtimerCC26xxHWAttrs[CC2640R2_LAUNCHXL_PWM6] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[CC2640R2_LAUNCHXL_PWM7], &pwmtimerCC26xxHWAttrs[CC2640R2_LAUNCHXL_PWM7] },
};

const uint_least8_t PWM_count = CC2640R2_LAUNCHXL_PWMCOUNT;

这些的数组定义有什么用呢?好吧,我们看看在那些地方,会使用到这些object,HwAttrs。我们打开PWMTimerCC26XX.c这个文件,有一个子程序:PWM_Handle PWMTimerCC26XX_open(PWM_Handle handle, PWM_Params *params)。这条是需要在初始化的时候调用。

作用是什么?就是我们申请一个操作PWM的HANDLE,我们初始化PWM的内容都存在内存的一个位置,而接下来我们对PWM的操作,只要传递这个地址,CPU就能知道我们是要对哪个外设进行操作,更加方便。所以,这个子函数的返回值,就是一个handle,当我们成功申请了一个外设使用(就是调用这个子程序返回一个不是0的数值,表示系统已经分配了一个handle),在接下来的使用中,我们只需要对这个handle进行操作即可。

然后看括号里的形参,第一个参数,也是handle?怎么回事?怎么有两个handle?和返回值是一样的属性? 我们来说明一下,形参这个handle,就是我们预想的要初始化的端口,就是我们前面PWM_Config PWM_config[CC2640R2_LAUNCHXL_PWMCOUNT] 这个数组里的成员,0-7,分别代表了8个端口。PWM最多可以分配8个端口。这样是不是对应起来了? 又有人要问,PWM_Config和PWM_Handle 的定义是不一样的啊?这个请朋友们自己去看这两者的定义,其实就是一个是数组形式,一个是指针形式,这个指针指向的内容格式是一样的。

然后看第二个形参,params,这个就是我们初始化的内容了。

再总结一下,这个子程序,返回一个handle,是我们后面操作的基础参数,输入的形参,一个是我们要配置的IO端口,一个是我们要配置的初始化参数。清晰明了。

下面贴上这个子程序的代码:

/* Open the specific PWM peripheral with the settings given in params.
   Will return a PWM handle if successfull, NULL if failed.
   PWM will output configured idle level when opened.
   Function sets a dependency on the underlying timer and adds the PWM pin to
   its internal PIN handle.
 */
PWM_Handle PWMTimerCC26XX_open(PWM_Handle handle, PWM_Params *params)
{
    PWMTimerCC26XX_HwAttrs const *hwAttrs = handle->hwAttrs;
    PWMTimerCC26XX_Object        *object  = handle->object;

    /* Check if PWM already open  */
    uint32_t key = Hwi_disable();
    if (object->isOpen)
    {
        Hwi_restore(key);
        Log_error1("PWM_open(%x):  Unit already in use.", (UArg) handle);
        return NULL;
    }
    …………
}

为什么我只撸这么一小段代码?因为我主要就是让大家看看这个子程序中的头两句,就是定义了两个参数,这两个参数的格式是什么样的?就是我们在上面看到过的两个数组的格式,是吧?就是说,协议栈,已经把底层驱动写好,把驱动需要的格式也给我们写好了,我们只要拿来用就行了。

怎么拿来用呢?

首先,先建议一个.c的文件,比如就叫GUA_PWM.c,然后我们把上面那段协议栈已经帮我们定义好的代码拷贝到这个.c里,如下面的代码:
注意,代码我把名称改过了,和底层的不同,避免冲突。

#include 
#include 
#include 

#include 

#include 
#include 

#include "ti/drivers/PWM.h"
#include "ti/drivers/pwm/PWMTimerCC26XX.h"
#include "ti/drivers/timer/GPTimerCC26XX.h"
#include "board.h"
#include "UserPwm.h"
PWMTimerCC26XX_Object pwmtimerCC2640Objects[CC2640R2_LAUNCHXL_PWMCOUNT];

const PWMTimerCC26XX_HwAttrs pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWMCOUNT] = {
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN0, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER0A },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN1, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER0B },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN2, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER1A },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN3, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER1B },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN4, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER2A },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN5, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER2B },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN6, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER3A },
    { .pwmPin = CC2640R2_LAUNCHXL_PWMPIN7, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER3B },
};

const PWM_Config UPWM_config[CC2640R2_LAUNCHXL_PWMCOUNT] = {
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC2640Objects[CC2640R2_LAUNCHXL_PWM0], &pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWM0] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC2640Objects[CC2640R2_LAUNCHXL_PWM1], &pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWM1] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC2640Objects[CC2640R2_LAUNCHXL_PWM2], &pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWM2] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC2640Objects[CC2640R2_LAUNCHXL_PWM3], &pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWM3] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC2640Objects[CC2640R2_LAUNCHXL_PWM4], &pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWM4] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC2640Objects[CC2640R2_LAUNCHXL_PWM5], &pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWM5] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC2640Objects[CC2640R2_LAUNCHXL_PWM6], &pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWM6] },
    { &PWMTimerCC26XX_fxnTable, &pwmtimerCC2640Objects[CC2640R2_LAUNCHXL_PWM7], &pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWM7] },
};

const uint_least8_t UPWM_count = CC2640R2_LAUNCHXL_PWMCOUNT;

这个拷贝出来我们就能使用了,所以说,底层的代码是供我们参考的,我们拿来用就行,不需要去修改他。

接下来,就是怎么初始化了,下面贴上初始化的代码:


PWM_Params userPwm[CC2640R2_LAUNCHXL_PWMCOUNT];         //params
PWM_Handle Huserp[CC2640R2_LAUNCHXL_PWMCOUNT];

uint8_t UserPwmInit(uint8_t channel,uint8_t initlevel)
{
    if(channel>=UPWM_count) return 0;                      //PWM最大就8个。
    if(initlevel)
    {
        userPwm[channel].idleLevel      = PWM_IDLE_HIGH;             //没有打开PWM时的状态,高或者低
        userPwm[channel].dutyValue     = 100 ;                        //设置一个初始值。
    }
    else
    {
        userPwm[channel].idleLevel      = PWM_IDLE_LOW;             //没有打开PWM时的状态,高或者低
        userPwm[channel].dutyValue     = 0 ;                        //设置一个初始值。
    }
/*    userPwm.periodUnits    = PWM_PERIOD_HZ;
    userPwm.periodValue   = 8e6;
    userPwm.dutyUnits      = PWM_DUTY_FRACTION;
    userPwm.dutyValue     = PWM_DUTY_FRACTION_MAX ;*/           //这里是定义HZ数

    userPwm[channel].periodUnits    = PWM_PERIOD_US;            //定义时间,单位是us
    userPwm[channel].periodValue   = 100;                       //最大是100us,频率就是10KHZ
    userPwm[channel].dutyUnits      = PWM_DUTY_US;              //单位US
//    userPwm[channel].dutyValue     = 0 ;                        //设置一个初始值。
    Huserp[channel] = UPWM_config[channel].fxnTablePtr->openFxn((PWM_Handle)&UPWM_config[channel],&userPwm[channel]);
    //定义一个HANDLE,因为后期对PWM的操作都是对HANDLE操作,先用CONFIG注册,获得一个HANDLE。具体程序在PWM。h.第一个PWM_configp[]里的数字,0-7都是一样的,指向同一个程序
    //这条指令指向了程序里的open程序,就是初始化用一次。

    if(Huserp[channel]==NULL) return 0;     //如果获得不到数据,说明开启失败。
    return 1;
}

以上就是初始化的程序,接下来的应用就简单了,来来回回就是开启,关闭,设置占空比等,我们也写一个子程序,轻松调用,代码如下:


void PWMSetDuty(uint32_t ddat,uint8_t channel)          //设置占空比,不能超过最大的占空比值。
{
    if(channel>=UPWM_count) return ;
    if(ddat>100) ddat = 100;
    UPWM_config[channel].fxnTablePtr->setDutyFxn(Huserp[channel], ddat);

}
void StartPwmOut(uint8_t channel)                       //开始PWM输出,没开启这条指令,不会输出。
{
    if(channel>=UPWM_count) return ;
    UPWM_config[channel].fxnTablePtr->startFxn(Huserp[channel]);
}
void StopPwmOut(uint8_t channel)                        //关闭PWM输出,关闭后的状态是初始状态。建议关闭输出前,先赋予PWM的初始值
{
    if(channel>=UPWM_count) return ;
    PWMSetDuty(100,channel);
    UPWM_config[channel].fxnTablePtr->stopFxn(Huserp[channel]);
}

最后,一个很重要的点,就是怎么把PWM输出口定义到我们想要的端口上呢?
参考我的IO口设置那一篇,就是把HwAttrs这个数组里的PIN口定义,给重新定义一下,

#define GUA_PWMPIN0  IOID_00
#define GUA_PWMPIN1  IOID_01
#define GUA_PWMPIN2  IOID_02
#define GUA_PWMPIN3  IOID_03
#define GUA_PWMPIN4  IOID_04
#define GUA_PWMPIN5  IOID_05
#define GUA_PWMPIN6  IOID_06
#define GUA_PWMPIN7  IOID_07


const PWMTimerCC26XX_HwAttrs pwmtimerCC2640HWAttrs[CC2640R2_LAUNCHXL_PWMCOUNT] = {
    { .pwmPin = GUA_PWMPIN0, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER0A },
    { .pwmPin = GUA_PWMPIN1, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER0B },
    { .pwmPin = GUA_PWMPIN2, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER1A },
    { .pwmPin = GUA_PWMPIN3, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER1B },
    { .pwmPin = GUA_PWMPIN4, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER2A },
    { .pwmPin = GUA_PWMPIN5, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER2B },
    { .pwmPin = GUA_PWMPIN6, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER3A },
    { .pwmPin = GUA_PWMPIN7, .gpTimerUnit = CC2640R2_LAUNCHXL_GPTIMER3B },
};

如上面的代码这样,那么你就可以将PWM输出映射到任意IO口啦,是不是很方便,很好用?如果觉得这篇博文对你有帮助,一起来群里探讨学习吧!

原创博客,如有转载,注明出处——在金华的电子民工林。

1)友情伙伴:甜甜的大香瓜
2)声明:喝水不忘挖井人,转载请注明出处。
3)纠错/业务合作:[email protected]
4)香瓜BLE之CC2640R2F群:557278427
5)本文出处:原创连载资料《简单粗暴学蓝牙5》
6)完整开源资料下载地址:
https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i

你可能感兴趣的:(CC2640R2F,CC2640R2F,PWM)