CC3220学习笔记---点亮LED PinMux版

来源:互联网 发布:淘宝上哪家玉石店靠谱 编辑:程序博客网 时间:2024/05/16 10:40
上节课使用了driverlib API来实现了LED灯的点亮,在实际开发中,不可能这样写程序,效率太低。这时就需要把硬件的各种行为抽象出来,帮助开发人员更容易地,更符合人类思维的方式进行操作。

首先理解CC3220 SDK源码的架构,下面是目录结构:


一共这五个文件夹,展开后:


  • 【devices】目录下的是最底层驱动,它直接跟硬件打交道。ROM内存放的API就是这一块,上篇日志我们使用的就是这里的函数。
  • 【drivers】目录则是在最底层API的基础上进行包装,抽象出MCU的各种行为,方便开发人员使用。这里打个比方吧。比如接电话这个行为可分解为如下步骤:电话铃响-->接通电话-->通话-->挂电话。电话铃响、接通电话、通话、挂电话这些单独的动作组合在一起才成为“接电话”这个行为。drivers层的作用就是把devices层的单独动作组合成具体行为,供开发者使用。
  • 【boards】从文件命名来看drivers层针对的是CC32XX系列的MCU,应该就是现有的CC3220、CC3220S、CC3220SF三款。三款功能绝大部分相同,只有少数差异,那么drivers层包含的仅为这几款MCU的相同部分的功能。另外,我们知道,大部分引脚是可以复用的。每个人拿到相同的芯片做的事情不一样,从而设计的开发板并不相同,甚至天差地别。所以这时就需要boards这一层,专门针对特定的开发板,在drivers层的基础上做进一步抽象,以方便开发人员使用。所以这一层文件的命名都以开发板为前缀,它也仅针对特定开发板使用。

今天,我们使用【drivers】层库函数来实现LED灯点亮。首先得分析GPIO相关部分源码。

一、GPIO_PinConfig
首先打开【drivers】根目录下的【GPIO.h】文件,首先找到这个声明:
/*! *  @brief  GPIO pin configuration settings * *  The upper 16 bits of the 32 bit PinConfig is reserved *  for pin configuration settings. * *  The lower 16 bits are reserved for device-specific *  port/pin identifications */typedef uint32_t GPIO_PinConfig;
一个引脚的名称、分组、GPIO配置信息都被包含在了这个32位整数GPIO_PinConfig里面了。它的高16位用于存放GPIO配置信息,而低16位用于存放引脚标识。下面分别讲解:

1、引脚标识
打开【drivers】-->【gpio】下的GPIOCC32XX.h文件,找到下述代码:
//引脚为空#define GPIOCC32XX_EMPTY_PIN 0x0000 //GPIO A0 port下的8个引脚#define GPIOCC32XX_GPIO_00 0x0001 #define GPIOCC32XX_GPIO_01 0x0002 #define GPIOCC32XX_GPIO_02 0x0004 #define GPIOCC32XX_GPIO_03 0x0008 #define GPIOCC32XX_GPIO_04 0x0010 #define GPIOCC32XX_GPIO_05 0x0020 #define GPIOCC32XX_GPIO_06 0x0040 #define GPIOCC32XX_GPIO_07 0x0080 //GPIO A1 port下的8个引脚#define GPIOCC32XX_GPIO_08 0x0101 #define GPIOCC32XX_GPIO_09 0x0102 #define GPIOCC32XX_GPIO_10 0x0104 #define GPIOCC32XX_GPIO_11 0x0108 #define GPIOCC32XX_GPIO_12 0x0110 #define GPIOCC32XX_GPIO_13 0x0120 #define GPIOCC32XX_GPIO_14 0x0140 #define GPIOCC32XX_GPIO_15 0x0180 //GPIO A2 port下的8个引脚#define GPIOCC32XX_GPIO_16 0x0201 #define GPIOCC32XX_GPIO_17 0x0202 #define GPIOCC32XX_GPIO_18 0x0204 #define GPIOCC32XX_GPIO_19 0x0208 #define GPIOCC32XX_GPIO_20 0x0210 #define GPIOCC32XX_GPIO_21 0x0220 #define GPIOCC32XX_GPIO_22 0x0240 #define GPIOCC32XX_GPIO_23 0x0280 //GPIO A3 port下的8个引脚#define GPIOCC32XX_GPIO_24 0x0301 #define GPIOCC32XX_GPIO_25 0x0302 #define GPIOCC32XX_GPIO_26 0x0304 #define GPIOCC32XX_GPIO_27 0x0308 #define GPIOCC32XX_GPIO_28 0x0310 #define GPIOCC32XX_GPIO_29 0x0320 #define GPIOCC32XX_GPIO_30 0x0340 #define GPIOCC32XX_GPIO_31 0x0380 
可以看到32个GPIO引脚都对应有一个数字,这个数字的低8位中的每一位正好表示了这个引脚的编号。第9、10位则表示引脚所在端口。GPIO_PinConfig的低16位存放的就是此信息。

2、GPIO配置信息
回到【GPIO.h】文件,找到GPIO_PinConfig声明之后的代码:
/*! *  Internally used configuration bit access macros. */#define GPIO_CFG_IO_MASK           0x00ff0000#define GPIO_CFG_IO_LSB            16#define GPIO_CFG_OUT_TYPE_MASK     0x00060000#define GPIO_CFG_OUT_TYPE_LSB      17#define GPIO_CFG_IN_TYPE_MASK      0x00060000#define GPIO_CFG_IN_TYPE_LSB       17#define GPIO_CFG_OUT_STRENGTH_MASK 0x00f00000#define GPIO_CFG_OUT_STRENGTH_LSB  20#define GPIO_CFG_INT_MASK          0x07000000#define GPIO_CFG_INT_LSB           24#define GPIO_CFG_OUT_BIT           19/*! *  \defgroup GPIO_PinConfigSettings 宏,用于配置GPIO引脚 *  @{ *//*GPIO_PinConfig 输出配置宏*/#define GPIO_CFG_OUTPUT            (((uint32_t) 0) << GPIO_CFG_IO_LSB) /* 输出. */#define GPIO_CFG_OUT_STD           (((uint32_t) 0) << GPIO_CFG_IO_LSB) #define GPIO_CFG_OUT_OD_NOPULL     (((uint32_t) 2) << GPIO_CFG_IO_LSB) /* 开漏 */#define GPIO_CFG_OUT_OD_PU         (((uint32_t) 4) << GPIO_CFG_IO_LSB) /* 开漏/上拉 */#define GPIO_CFG_OUT_OD_PD         (((uint32_t) 6) << GPIO_CFG_IO_LSB) /* 开漏/下拉 */#define GPIO_CFG_OUT_STR_LOW       (((uint32_t) 0) << GPIO_CFG_OUT_STRENGTH_LSB) /* 驱动强度:低 */#define GPIO_CFG_OUT_STR_MED       (((uint32_t) 1) << GPIO_CFG_OUT_STRENGTH_LSB) /* 驱动强度:中 */#define GPIO_CFG_OUT_STR_HIGH      (((uint32_t) 2) << GPIO_CFG_OUT_STRENGTH_LSB) /* 驱动强度:高 */#define GPIO_CFG_OUT_HIGH          (((uint32_t) 1) << GPIO_CFG_OUT_BIT) /* 输出 1 */#define GPIO_CFG_OUT_LOW           (((uint32_t) 0) << GPIO_CFG_OUT_BIT) /* 输出 0 *//* GPIO_PinConfig 输入配置宏*/#define GPIO_CFG_INPUT             (((uint32_t) 1) << GPIO_CFG_IO_LSB) /* 输入 */#define GPIO_CFG_IN_NOPULL         (((uint32_t) 1) << GPIO_CFG_IO_LSB) /* 无内部上拉/下拉 */#define GPIO_CFG_IN_PU             (((uint32_t) 3) << GPIO_CFG_IO_LSB) /* 内部上拉 */#define GPIO_CFG_IN_PD             (((uint32_t) 5) << GPIO_CFG_IO_LSB) /* 内部下拉 *//* GPIO_PinConfig 中断配置宏*/#define GPIO_CFG_IN_INT_NONE       (((uint32_t) 0) << GPIO_CFG_INT_LSB)    /* 无中断 */#define GPIO_CFG_IN_INT_FALLING    (((uint32_t) 1) << GPIO_CFG_INT_LSB)    /* 下降沿触发 */#define GPIO_CFG_IN_INT_RISING     (((uint32_t) 2) << GPIO_CFG_INT_LSB)    /* 上升沿触发 */#define GPIO_CFG_IN_INT_BOTH_EDGES (((uint32_t) 3) << GPIO_CFG_INT_LSB)    /* 上升下降沿触发 */#define GPIO_CFG_IN_INT_LOW        (((uint32_t) 4) << GPIO_CFG_INT_LSB)    /* 低电平触发 */#define GPIO_CFG_IN_INT_HIGH       (((uint32_t) 5) << GPIO_CFG_INT_LSB)    /* 高电平触发 */
高16位存放的就是以上信息,每个位都对应有一个功能,位功能图我就不画了,了解大概原理即可。

二、GPIOCC32XX_Config
继续GPIOCC32XX.h文件:
typedef struct GPIOCC32XX_Config {    GPIO_PinConfig  *pinConfigs; //引脚GPIO配置    GPIO_CallbackFxn  *callbacks;    uint32_t numberOfPinConfigs;    uint32_t numberOfCallbacks;    uint32_t intPriority;} GPIOCC32XX_Config;
GPIOCC32XX_Config是更高一层的GPIO配置,注意加粗那一行,就是上面讲的32位引脚配置整数。这里再加上回调函数和中断优先级。

翻到GPIOCC32XX.h文件头部注释,上面有详细介绍如何使用gpio。简而言之,就是需要在Border.c文件中提供三个数据:
  1. GPIO_PinConfig数组
  2. GPIO_CallbackFxn数组
  3. GPIOCC32XX_Config结构体
这里我就不细说了,因为有神器自动生成。下面还需要介绍两个方法。

三、GPIO_init()
上面讲的三个数据提供完成后,就可以使用GPIO_init()方法对gpio进行初始化了。看看它的源码,GPIOCC32XX.c文件中:
void GPIO_init(){    unsigned int i, j;#if DebugP_ASSERT_ENABLED    initCalled = true;#endif    for (i = 0; i < NUM_PORTS; i++)    {        for (j = 0; j < NUM_PINS_PER_PORT; j++)        {            gpioCallbackInfo[i].pinIndex[j] = CALLBACK_INDEX_NOT_CONFIGURED;        }    }    /*     * Configure pins and create Hwis per static array content     */    for (i = 0; i < GPIOCC32XX_config.numberOfPinConfigs; i++)    {        if (!(GPIOCC32XX_config.pinConfigs[i] & GPIO_DO_NOT_CONFIG))        {            GPIO_setConfig(i, GPIOCC32XX_config.pinConfigs[i]);        }        if (i < GPIOCC32XX_config.numberOfCallbacks)        {            if (GPIOCC32XX_config.callbacks[i] != NULL)            {                /* create Hwi as necessary */                GPIO_setCallback(i, GPIOCC32XX_config.callbacks[i]);            }        }    }    Power_registerNotify(&powerNotifyObj,                         PowerCC32XX_ENTERING_LPDS | PowerCC32XX_AWAKE_LPDS,                         powerNotifyFxn, (uintptr_t) NULL);}注意加粗的GPIO_setConfig()方法,追踪下去,代码太长,我就不贴完了,这里只贴关键几句:/* Configure the GPIO pin */MAP_GPIODirModeSet(portBase, pinMask, direction);MAP_PinConfigSet(pin, strength, pinType);/* Set output value */if (direction == GPIO_DIR_MODE_OUT){    MAP_GPIOPinWrite(portBase, pinMask,                     ((pinConfig & GPIO_CFG_OUT_HIGH) ? 0xFF : 0));}
加粗部分都是上篇日志我们使用过的ROM API。

四、GPIO_write()
最终点亮LED灯还得写GPIO。所以还需介绍GPIO_write()函数,还是在GPIOCC32XX.c文件中。
void GPIO_write(uint_least8_t index, unsigned int value){    uintptr_t  key;    uint32_t   output;    PinConfig *config = (PinConfig *) &GPIOCC32XX_config.pinConfigs[index];    DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfPinConfigs);    DebugP_assert((GPIOCC32XX_config.pinConfigs[index] & GPIO_CFG_INPUT) ==                  GPIO_CFG_OUTPUT);    key = HwiP_disable();    /* Clear output from pinConfig */    GPIOCC32XX_config.pinConfigs[index] &= ~GPIO_CFG_OUT_HIGH;    if (value)    {        output = config->pin;        /* Set the pinConfig output bit to high */        GPIOCC32XX_config.pinConfigs[index] |= GPIO_CFG_OUT_HIGH;    }    else    {        output = value;    }    MAP_GPIOPinWrite(getPortBase(config->port), config->pin, output);    HwiP_restore(key);    DebugP_log3("GPIO: port 0x%x, pin 0x%x wrote 0x%x",                getPort(config->port), config->pin, value);}
真正写GPIO那句代码我加粗了,上篇日志调用过。另外需要注意的是看源码可知,index参数表示的是对应gpio引脚在数组中的索引号。等下在写程序的时候这里需要格外注意。

TI Pin Mux Tool
下面来介绍一个编程神器:Pin Mux Tool,它用于配置开发板功能。根据我们所选的功能自动生成主板配置代码。安装略。

打开Pin Mux Tool,在Device栏选择CC3220SF,如下图所示:

然后点击【Start】按钮。在窗体左边【GPIO】栏点击3次【+】号,添加三个GPIO。由于我们只操作三盏灯,所以只使用三个GPIO。添加完成后效果如下图所示:

 将MyGPIO3的【GPIO Pins】改为64,并将每个GPIO的Active改为Output,效果如下图所示:

窗体右边,单击【Download File】按钮,下载CC3220SF_LAUNCHXL.c文件和CC3220SF_LAUNCHXL.h文件。

接下来打开CC3220SF_LAUNCHXL.c文件,得手动修改一下。找到

前面说了,这个顺序很重要,关系到后面的调用,把这个顺序调整一下,改为:

GPIO_PinConfig gpioPinConfigs[] ={    /* output pins with callbacks */    GPIOCC32XX_GPIO_09 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_LOW,    GPIOCC32XX_GPIO_10 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_LOW,    GPIOCC32XX_GPIO_11 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_LOW,};

接下来打开CC3220SF_LAUNCHXL.h文件,找到以下代码


我们看到,有两个按钮,没有我们加进去的LED灯。这里需要自己手动修改,但现在不改也不会影响到等下写的程序。

PinMux版跑马灯
终于可以开始写程序了。
1、复制上篇日志中做好的【demo_gpio_lib】程序,粘贴为【demo_gpio_PinMux】项目。在新项目上右键选中【Add Files】菜单,将刚才生成的CC3220SF_LAUNCHXL.c文件和CC3220SF_LAUNCHXL.h文件加进项目。

2、将main_tirtos.c文件中的代码替换为:
#include <ti/drivers/GPIO.h>#include <ti/drivers/gpio/GPIOCC32XX.h>void delay(int temp){    int i = 0;    for (i = 0; i < temp; i++);}int main(void){    GPIO_init();    while(1)    {        GPIO_write(2, 0); //灭绿灯        GPIO_write(0, 1); //亮红灯        delay(0xfffff);        GPIO_write(0, 0); //灭红灯        GPIO_write(1, 1); //亮黄灯        delay(0xfffff);        GPIO_write(1, 0); //灭黄灯        GPIO_write(2, 1); //亮绿灯        delay(0xfffff);    }}
Debug程序,跑马灯亮,有了PinMux神器,写程序还是挺方便的。
这里需要注意,GPIO_write()函数中的第一个参数表示gpio口在gpioPinConfigs[]数组中的索引号;第二个参数0表示灭灯,1表示亮灯。

程序是写完了,也能运行了。不过代码看上去不太有范啊!GPIO_write()函数中的两个冰冷的数字无任何意义,让人无法读懂。继续改造!

PinMux版跑马灯程序改进
1、在项目上右键选中【New】-->【File】菜单,新建一个【Board.h】文件。也可从之前导入的timerled项目中拷贝一个过来。
2、输入如下代码:
#ifndef __BOARD_H#define __BOARD_H#ifdef __cplusplusextern "C" {#endif#include "CC3220SF_LAUNCHXL.h"#define LED_ON            CC3220SF_LAUNCHXL_GPIO_LED_ON#define LED_OFF           CC3220SF_LAUNCHXL_GPIO_LED_OFF#define RED_LED           (0)#define YELLOW_LED        (1)#define GREEN_LED         (2)#ifdef __cplusplus}#endif#endif /* __BOARD_H */
保存。

3、将main_tirtos.c文件的代码更改为:
#include <ti/drivers/GPIO.h>#include <ti/drivers/gpio/GPIOCC32XX.h>#include "Board.h"void delay(int temp){    int i = 0;    for (i = 0; i < temp; i++);}int main(void){    GPIO_init();    while(1)    {        GPIO_write(GREEN_LED, LED_OFF); //灭绿灯        GPIO_write(RED_LED, LED_ON); //亮红灯        delay(0xfffff);        GPIO_write(RED_LED, LED_OFF); //灭红灯        GPIO_write(YELLOW_LED, LED_ON); //亮黄灯        delay(0xfffff);        GPIO_write(YELLOW_LED, LED_OFF); //灭黄灯        GPIO_write(GREEN_LED, LED_ON); //亮绿灯        delay(0xfffff);    }}
4、现在这代码看上去就很舒服了。编译,运行,大功告成。
Board.h文件的作用为针对特定主板声明各种宏,增加程序可读性。

跑马灯系列总算写完,不容易,庆祝一下。弄完这一系列,对CC3220的整个驱动框架应该有一个大至的了解了。

0 0
原创粉丝点击