本文主要是介绍CC3220学习笔记---点亮LED PinMux版,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
上节课使用了driverlib API来实现了LED灯的点亮,在实际开发中,不可能这样写程序,效率太低。这时就需要把硬件的各种行为抽象出来,帮助开发人员更容易地,更符合人类思维的方式进行操作。
然后点击【Start】按钮。在窗体左边【GPIO】栏点击3次【+】号,添加三个GPIO。由于我们只操作三盏灯,所以只使用三个GPIO。添加完成后效果如下图所示:
将MyGPIO3的【GPIO Pins】改为64,并将每个GPIO的Active改为Output,效果如下图所示:
前面说了,这个顺序很重要,关系到后面的调用,把这个顺序调整一下,改为:
接下来打开 CC3220SF_LAUNCHXL.h文件,找到以下代码
首先理解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文件中提供三个数据:
- GPIO_PinConfig数组
- GPIO_CallbackFxn数组
- GPIOCC32XX_Config结构体
这里我就不细说了,因为有神器自动生成。下面还需要介绍两个方法。
三、GPIO_init()
上面讲的三个数据提供完成后,就可以使用GPIO_init()方法对gpio进行初始化了。看看它的源码,GPIOCC32XX.c文件中:
void GPIO_init()
{unsigned int i, j;#if DebugP_ASSERT_ENABLEDinitCalled = true;
#endiffor (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 __cplusplus
extern "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的整个驱动框架应该有一个大至的了解了。
这篇关于CC3220学习笔记---点亮LED PinMux版的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!