This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

【21ic原创】【MSP430i2xx教程第一讲】怎样使用库函数 & GPIO详解

Other Parts Discussed in Thread: CCSTUDIO, MSPDRIVERLIB, MSP430I2020

序言
1.1  综述

    德州仪器®MSP430®外设驱动程序库是一组基于访问MSP430i2xx家族微控制器的外设驱动程序。虽然它们不是纯粹的操作系统意义上的驱动程序,但他们提供一种容易使用外设的机制。
    驱动的功能和 组织都是遵循以下设计目标:
  • 他们都是完全由C语言编写(除了那些C绝对不可能实现的功能外)。
  • 他们演示如何在常见的操作模式下使用外设。
  • 他们很容易理解。
  • 他们在内存和处理器的使用上是合理高效的。
  • 他们尽可能独立(模块化)。
  • 在可能的情况下,计算指令在编译时候执行,而不是在运行时完成(代码编译的高效性)。
  • 他们可以适用于不只一个的工具链构建(目前提供三种:CSSGCCIAR)。

基于以上的设计目标可能有以下不好的影响:
  • 从一个代码大小和执行速度来考虑,驱动程序不一定是高效的代码。而最有效的操作外设的代码是在应用程序有特定需求下进行量身定制而编写的。更进一步的对驱动程序代码尺寸优化将会使得驱动代码更难以理解。
  • 驱动程序不能够实现硬件的全部功能。一些外设提供的复杂功能不能通过本库函数提供的驱动程序实现。但是你可以使用现有代码作为参考来添加额外的功能支持。
  • API清除了所有错误校验代码。因为错误校验通常只是用于最初的程序开发期间,它可以被删除用于改善代码的大小和速度。

    对于许多应用程序,可以使用驱动程序来开发。单是在某些情况下,驱动程序为了满足应用程序的功能,存储或处理的需求将会增强或重写(代码的冗余度提高了)。如果是这样的情况,可以把现有的驱动程序作为操作外设的参考程序。
    一些驱动库API 取相应的外设的基地址作为第一个参数。这个基地址是是从MSP430单片机特点的头文件(或单片机数据表)获取的。各种外设的例程演示如何使用这些基地址。

    驱动库支持以下工具链:
  • IAR Embedded Workbench®
  • Texas Instruments Code Composer Studio™
    使用断言语句调试
    默认情况下禁用断言语句。
    要启用断言语句需要编辑hw_regaccess.h ,该文件在 inc 文件夹。对#define NDEBUG进行添加注释符号。变成//#define NDEBUG            
    也就是把#define NDEBUG这句注释掉。断言只有在项目优化尺寸时候才在CCS中起作用。

MSP430i2xx命名法则
该系列命名法则如下图所示,
 

具体参见下表
处理器家族
MSP=混合信号处理器;XMS=实验硅
430MCU平台
德州仪器微控制器平台
设备类型
特殊应用,i=工业级Flash
系列
2系列=频率可达16.384MHz
特型
集成多种标准在一个系列上
集温度可选范围
T = -40°C到105 C°
封装类型
包装类型
T=小卷包装;R=大卷包装;没有标记:管或盘
附加特性
EP=增强型(-40℃到150℃);HT=极端温度(-55到150℃);Q1=汽车级
       MSP430i2xx系列是一款工业级430产品。
MSP430I2XX 教程第一讲.pdf
  • 第一章  怎样使用库函数(CCS版)1.1  开发环境介绍

    CCS6.1是目前CCStudio的最新版本,也是支持最全面的版本,本文档所有内容均在该版本下测试完成。经过作者测试CCS5系列还没有引入库函数概念,建议升级到最新版CCStudio6.1.
    下载地址:
    http://processors.wiki.ti.com/index.php/Download_CCS             或通过百度网盘下载
    ————————————————————————————————————————
    启动CCS6,单击菜单栏的 View-> Resource ExplorerExamples

    图1
    我们看到CCS菜单栏View下第一个Resource explorer(Examples),单击。弹出窗口就是TI Resource Explorer。如图2,我们可以看到我们安装的MSP430ware/Lirbrary
    如果你安装时候没有勾选安装MSP430ware,请通过Help/check for updatesApp Center升级安装
    图2

    点击 MSP430ware ,你将看到介绍页面通过左边的列表可以找到我们这里要介绍的MSP430i2xx系列的用户手册(Users Guide)、API编程手册(API Programmers Guide)以及创建空项目(emptyProject),还有例程(Example Projects)。看右侧的窗口有几个方块,我们单击第一个Driver Library。弹出如下窗口。单击蓝色字体的here,

    图3
    将会在CCS右侧的窗口弹出网页,如图4。在该页面可以下载到最新版的MSP430驱动库函数包。或登录http://www.ti.com/tool/mspdriverlib

    图4
    你可以在你的安装目录下找到本教程将要讲解的库函数源文件。
    C:\ti\msp430\MSP430ware_1_97_00_47\driverlib\driverlib\MSP430i2xx
    或者在CCSTI Resource Explorer里找到MSP430ware->Libraries->MSP430i2xx->API Programmers Guide
    这时右边窗口会出现如下图所示。左边分别有三个选项:Modules(模块)、Data Structures(数据结构)、Files(文件)。如图5所示。

    5
    通过Modules(模块)可以查看该系列单片机所有外设模块的库函数介绍,如图6所示为GPIO_clearInterruptFlag函数的相关说明。

    图6
    通过Files选项我们可以看到头文件列表,我们找到gpio.h单击,右边就弹出该文件的内容,我们翻页找到上面对应 那个函数,通过函数的注释,我们也可以看懂该函数包含了两个参数,以及参数的取值范围,该函数的作用。我们发现默认的注释字体是红色的,如果你觉得CCS给的配色十分的不愉快,可以使用Notepad++

    7

    笔者经常使用Notepad++阅读代码的,在CCS里你也可以通过修改配置来改变代码显示的配色,具体方法这里不作介绍。

    图8
    1.2  怎么使用Driverlib(驱动库)创建一个新的用户工程
    单击File->New->CCS Project,弹出新建工程窗口,如图9所示。
    9
    通过选项进行配置,如果图9所示,Target选择MSP430Ixxx Family,器件型号选择MSP430i2020。填入Project name(项目名字)。在Project templates and examples(项目模板和示例)选择MSP430 DriverLib下的Empty Project with DriverLib Source,之后单击Finish完成创建基于库函数的项目。并且弹出如下窗口。
    图10

    1.3  怎样给已经存在的项目添加库函数
    在已存在的应用项目单击右键,单击Source -> Apply Project Template,如图11打开向导窗口,如图12

    图11

    在图12选择Add Local Copy of DriverLib. 单击Finish完成向现有的项目添加一个DriverLib MSP430i2xx副本

    图12
  • 第二章  GPIO

        GPIOGeneral Purpose Input/Output(通用输入输出)的简称,是单片机最基本,最必须,最重要的外设,每个GPIO端口可通过软件分别配置成输入或输出。

        数字I/O(GPIO)APIApplication Programming Interface,应用程序编程接口)为使用MP430i2xx GPIO模块提供了一组函数。这些函数可以设置、使能使用I/O管脚(针脚、插脚),设置他们是否有中断,以及对管脚值的读和写。数字I / O功能包括:
    • 可对单独的I/O独立编程
    • 可对输入输出任意组合
    • 可单独配置P1P2端口中断,另外一些型号可能包含其他端口的中断。
    • 具有独立的输入和输出数据寄存器
    • 可单独配置上拉或下拉电阻


    2
    .1  本章引言

        该系列单片机具备的I/O端口数量最高可达12组(P1P11,外加PJ端口)。大部分呢端口每组包含8个管脚;然后,一些端口可用的管脚较少,不足8个(端口包含可用管脚的具体情况需要查看对应芯片的数据手册)。每个I/O管脚可用分别配置为输入或输出,并且每个可用单独进行读/写操作。

        端口P1P2都具备中断功能。P1P2两组端口的每一位管脚都可以单独配置成在输入一个上升沿或下降沿时候触发中断的功能。

        P1所有的管脚位中断源自一个单独的中断向量P1IV,P2所有的管脚中断院子另外一个单独的中断向量P2IV。在一些单片机中,可能具备额外中断功能的端口,且包含他们各自的中断向量(具体情况,需要从该型号单片机的数据手册中获取)。

        单个端口可以作为可以以字节宽度进行访问,也可以合二为一组合成字宽,以字的格式进行访问。P1/P2, P3/P4, P5/P6, P7/P8,四对端口分别对应PA、PB、PCPD。除了中断向量P1IVP2IV,所有的端口寄存器是按照这种命名约定方式处理的,也就是说不存在中断向量PAIV

        当以字为单位操作PA时候,所有的16位被写入端口。当用字节操作PA端口的低字节时候,高字节保持不变。同样,用字节指令写PA端口的高字节时候,低字节不变。当写端口时候,使用的位宽少于该端口的最大位宽时候,不用的位是不受影响的。端口PB、P、PD、PEPF也是类似的

        使用字操作读PA端口,可以读到16位管脚的全部状态。使用字节操作,读PA端口(P1P2)的低字节或高字节,分别只能读出低字节或高字节。使用字节操作,读PA端口,并存储到一个通用寄存器,本字节只被写入到寄存器的最低有效字节。目的寄存器的高位有效字节将自动被清除。端口PBPCPDPEPF类似。当读那些可能少于最大位宽的端口时候,未使用的位读作0(类似于端口PJ)。

        GPIO 管脚可使用函数GPIO_setAsOutputPin() 或GPIO_setAsInputPin()配置为一个I/O管脚。使用外设分配函数GPIO_setAsPeripheralModuleFunctionOutputPin()
    GPIO_setAsPeripheralModuleFunctionInputPin().可以把GPIO管脚替换成外围模块功能(即使用管脚I/O功能外的其他功能)。

        这个驱动程序包含在gpio.c文件里,gpio.h头文件包含该应用程序使用的API定义

    2.2  函数总览

    1
    void GPIO_setAsOutputPin (uint8_t selectedPort, uint16_t selectedPins)
    该函数可以配置所选管脚作为输出管脚
    2
    void GPIO_setAsInputPin (uint8_t selectedPort, uint16_t selectedPins)
    该函数可以配置所选管脚作为输入管脚
    3
    void GPIO_setAsPeripheralModuleFunctionOutputPin (uint8_t selectedPort, uint16_t selectedPins, uint8_t mode)
    该函数配置所选管脚上输出方向的外设
    4
    void GPIO_setAsPeripheralModuleFunctionInputPin (uint8_t selectedPort, uint16_t selectedPins, uint8_t mode)
    该函数配置所选管脚上输入方向的外设
    5
    void GPIO_setOutputHighOnPin (uint8_t selectedPort, uint16_t selectedPins)
    该函数在所选管脚上输出高电平
    6
    void GPIO_setOutputLowOnPin (uint8_t selectedPort, uint16_t selectedPins)
    该函数在所选管脚上输出低电平
    7
    void GPIO_toggleOutputOnPin (uint8_t selectedPort, uint16_t selectedPins)
    该函数翻转所选管脚的输出电平
    8
    uint8 t GPIO_getInputPinValue (uint8_t selectedPort, uint16_t selectedPins)
    该函数可以获取所选管脚的输入值
    9
    void GPIO_enableInterrupt (uint8_t selectedPort, uint16_t selectedPins)
    该函数使能所选管脚的端口中断功能
    10
    void GPIO_disableInterrupt (uint8_t selectedPort, uint16_t selectedPins)
    该函数禁用所选管脚端口的中断
    11
    uint16 t GPIO_getInterruptStatus (uint8_t selectedPort, uint16_t selectedPins)
    该函数可以获取被选中的端口的中断状态
    12
    void GPIO_clearInterruptFlag (uint8 t selectedPort, uint16 t selectedPins)
    该函数可以清除所选管脚的中断标志
    13
    void GPIO_interruptEdgeSelect (uint8_t selectedPort, uint16_t selectedPins, uint8_t edgeSelect)
    该函数可以对所选管脚的中断触发边沿类型进行选择(是上升沿触发还是下降沿触发)



    详细描述
        GPIO API函数分为三组:配置GPIO管脚的、处理中断的和访问管脚电平值的。
        配置GPIO管脚的有:
    • GPIO_setAsOutputPin()
    • GPIO_setAsInputPin()
    • GPIO_setAsInputPinWithPullDownresistor()
    • GPIO_setAsInputPinWithPullUpresistor()
    • GPIO_setAsPeripheralModuleFunctionOutputPin()
    • GPIO_setAsPeripheralModuleFunctionInputPin()
        处理GPIO中断的有:
    • GPIO_enableInterrupt()
    • GPIO_disableInterrupt()
    • GPIO_clearInterruptFlag()
    • GPIO_getInterruptStatus()
    • GPIO_interruptEdgeSelect()
    管脚状态访问的:
    • GPIO_setOutputHighOnPin()
    • GPIO_setOutputLowOnPin()
    • GPIO_toggleOutputOnPin()
    • GPIO_getInputPinValue()

    函数文档


    void GPIO_clearInterruptFlag (uint8 t selectedPort, uint16 t selectedPins)
      该函数可以清除所选管脚的中断标志。需要注意的是只针对端口12A有效。因为只有P1,P2,PA(前面说过P1P2组合后作为字标记为PA)有效
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有三个:GPIO_PORT_P1、GPIO_PORT_P2、GPIO_PORT_PA
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         该函数是通过修改寄存器PxIFG实现,返回值 None(空)


    void GPIO_disableInterrupt (uint8_t selectedPort, uint16_t selectedPins)
      该函数禁用所选管脚的端口中断。需要注意的是,只有端口P1,P2,PA具有该功能
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有三个:GPIO_PORT_P1、GPIO_PORT_P2、GPIO_PORT_PA
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         该函数是通过修改寄存器PxIE实现,返回值 None(空)


    void GPIO_enableInterrupt (uint8_t selectedPort, uint16_t selectedPins)
      该函数使能所选管脚的端口中断。需要注意的是,只有端口P1,P2,PA具有该功能
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有三个:GPIO_PORT_P1、GPIO_PORT_P2、GPIO_PORT_PA
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         该函数是通过修改寄存器PxIE实现,返回值 None(空)
  • uint8 t GPIO_getInputPinValue (uint8_t selectedPort, uint16_t selectedPins)
      该函数获取所选管脚的输入值。
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、         GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
        注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
        selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
        返回值:GPIO_INPUT_PIN_HIGHGPIO_INPUT_PIN_LOW二者中之一。即管脚的状态值。



    uint16 t GPIO_getInterruptStatus (uint8_t selectedPort, uint16_t selectedPins)
      该函数用于获取所选管脚的中断状态。需要注意的是只有P1、P2、PA具有该功能
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有三个:GPIO_PORT_P1、GPIO_PORT_P2、GPIO_PORT_PA
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0、GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         返回值: GPIO_PIN0、GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或。用于显示所选管脚的中断状态(默认值:0)。

    void GPIO_interruptEdgeSelect (uint8_t selectedPort, uint16_t selectedPins, uint8_t edgeSelect)
      该函数选择什么边沿触发中断,即选择是上升沿还是下降沿触发所选管脚中断。
         该函数具有三个参数:selectedPort(所选端口)、selectedPins(所选管脚)和edgeSelect(边沿选择)。
         selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、     GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
         注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         edgeSelect是对中断触发边沿的选择。其值有两个,GPIO_HIGH_TO_LOW_TRANSITION(下降沿触发)和GPIO_LOW_TO_HIGH_TRANSITION上升沿触发)。
         该函数是通过修改寄存器PxIES实现,返回值 None(空)

    void GPIO_setAsInputPin (uint8_t selectedPort, uint16_t selectedPins)
      该函数把所选管脚配置为输入管脚。
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
         注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         该函数是通过修改寄存器PxDIRPxREN、和PxSEL的位实现,返回值 None(空)

    void GPIO_setAsOutputPin (uint8_t selectedPort, uint16_t selectedPins)
      该函数把所选管脚配置为输出管脚。
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
         注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         该函数是通过修改寄存器PxDIR和PxSEL的位实现,返回值 None(空)

    void GPIO_setAsPeripheralModuleFunctionInputPin (uint8_t selectedPort, uint16_t selectedPins, uint8_t mode)
      该函数配置所选管脚上输入方向的外设功能,要么第一功能模块,要么第二功能模块,要么第三功能模块。需要注意的是MSP430F5xx/MSP4306xx家族不支持这个函数模式
          该函数具有三个参数:selectedPort(所选端口)、selectedPins(所选管脚)和mode(模式)。
          selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
          注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
          selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         mode 所选端口处的外设选择,一般端口提供了三种模式的外设模块,通过该参数可以进行选择使用。可选的值是GPIO_PRIMARY_MODULE_FUNCTION、GPIO_SECONDARY_MODULE_FUNCTION和GPIO_TERNARY_MODULE_FUNCTION。
         如果不知道这三种外设模式分别是什么,可以查看多选单片机的技术手册的管脚分布图。
         该函数是通过修改寄存器PxDIR和PxSEL的位实现,返回值 None(空)

    void GPIO_setAsPeripheralModuleFunctionOutputPin (uint8_t selectedPort, uint16_t selectedPins, uint8_t mode)
      该函数配置所选管脚上输出方向的外设功能,要么第一功能模块,要么第二功能模块,要么第三功能模块。需要注意的是MSP430F5xx/MSP4306xx家族不支持这个函数模式
         该函数具有三个参数:selectedPort(所选端口)、selectedPins(所选管脚)和mode(模式)。
          selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
         注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         mode 所选端口处的外设选择,一般端口提供了三种模式的外设模块,通过该参数可以进行选择使用。可选的值是GPIO_PRIMARY_MODULE_FUNCTION、GPIO_SECONDARY_MODULE_FUNCTION和GPIO_TERNARY_MODULE_FUNCTION。
         如果不知道这三种外设模式分别是什么,可以查看多选单片机的技术手册的管脚分布图。
         该函数是通过修改寄存器PxDIR和PxSEL的位实现,返回值 None(空)

    void GPIO_setOutputHighOnPin (uint8_t selectedPort, uint16_t selectedPins)
      该函数在所选端口的管脚配置为输出高电平。
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
         注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         该函数是通过修改寄存器PxOUT的位实现,返回值 None(空)

    void GPIO_setOutputLowOnPin (uint8_t selectedPort, uint16_t selectedPins)
      该函数在所选端口的管脚配置为输出低电平。
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
         注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         该函数是通过修改寄存器PxOUT的位实现,返回值 None(空)

    void GPIO_toggleOutputOnPin (uint8_t selectedPort, uint16_t selectedPins)
      该函数在所选端口的管脚进行电平的翻转,进行高低电平切换。
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
         注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
         selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
         该函数是通过修改寄存器PxOUT的位实现,返回值 None(空)
  • 2.3  例程

    下面简单的例程用于展示如何使用GPIO API
    // 把P1.0设置为输出端口
    GPIO_setAsOutputPin (GPIO_PORT_P1, GPIO_PIN0);
    // 把P1.4设置为输入端口
    GPIO_setAsInputPin (GPIO_PORT_P1, GPIO_PIN4);
    while (1)
    {
    // 测试管脚P1.4
    if (GPIO_INPUT_PIN_HIGH == GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN4))
    GPIO_setOutputHighOnPin (GPIO_PORT_P1, GPIOPIN0);     // 如果P1.4输入高电平,置位P1.0。
    else
    GPIO_setOutputLowOnPin (GPIO_PORT_P1, GPIO_PIN0);     //否则,管脚P1.0输清零
    }


    2.4  本章小结

    本章节介绍了GPIO相关的13个操作函数,这些函数的参数变量有:selectedPort、selectedPinmode。其中只有在使用IO管脚内的非IO功能的外设时候才使用第三个 参数mode进行选择功能。
    1和表2给出了只使用两个参变量selectedPort、selectedPins的函数,参变量所选范围的列表。
    表3给出了使用三个变量selectedPort、selectedPins、mode的函数,参变量的所选范围。
    表1
    [size=11.0000pt] GPIO_setAsOutputPin()
    [size=11.0000pt] GPIO_setAsInputPin()
    [size=11.0000pt] GPIO_setAsPeripheralModuleFunctionOutputPin()
    [size=11.0000pt] GPIO_setAsPeripheralModuleFunctionInputPin()
    [size=11.0000pt] GPIO_setOutputHighOnPin()
    [size=11.0000pt] GPIO_setOutputLowOnPin()
    [size=11.0000pt] GPIO_toggleOutputOnPin()
    [size=11.0000pt] GPIO_getInputPinValue()
    [size=11.0000pt] GPIO_interruptEdgeSelect()
    selectedPort
    selectedPins
    GPIO_PORT_P1
    GPIO_PORT_P2
    GPIO_PORT_P3
    GPIO_PORT_P4
    GPIO_PORT_P5
    GPIO_PORT_P6
    GPIO_PORT_P7
    GPIO_PORT_P8
    GPIO_PORT_P9
    GPIO_PORT_P10
    GPIO_PORT_P11
    GPIO_PORT_PA
    GPIO_PORT_PB
    GPIO_PORT_PC
    GPIO_PORT_PD
    GPIO_PORT_PE
    GPIO_PORT_PF
    GPIO_PORT_PJ
    GPIO_PIN0
    GPIO_PIN1
    GPIO_PIN2
    GPIO_PIN3
    GPIO_PIN4
    GPIO_PIN5
    GPIO_PIN6
    GPIO_PIN7
    GPIO_PIN8
    GPIO_PIN9
    GPIO_PIN10
    GPIO_PIN11
    GPIO_PIN12
    GPIO_PIN13
    GPIO_PIN14
    GPIO_PIN15

    表2
    GPIO_enableInterrupt()
    GPIO_disableInterrupt()
    GPIO_getInterruptStatus()
    GPIO_clearInterruptFlag()
    selectedPort
    selectedPins
    GPIO_PORT_P1
    GPIO_PORT_P2
    GPIO_PORT_PA
    GPIO_PIN0
    GPIO_PIN1
    GPIO_PIN2
    GPIO_PIN3
    GPIO_PIN4
    GPIO_PIN5
    GPIO_PIN6
    GPIO_PIN7
    GPIO_PIN8
    GPIO_PIN9
    GPIO_PIN10
    GPIO_PIN11
    GPIO_PIN12
    GPIO_PIN13
    GPIO_PIN14
    GPIO_PIN15

    表3
    [size=11.0000pt] GPIO_setAsPeripheralModuleFunctionOutputPin()
    [size=11.0000pt] GPIO_setAsPeripheralModuleFunctionInputPin()
    selectedPort
    selectedPins
    mode
    GPIO_PORT_P1
    GPIO_PORT_P2
    GPIO_PORT_P3
    GPIO_PORT_P4
    GPIO_PORT_P5
    GPIO_PORT_P6
    GPIO_PORT_P7
    GPIO_PORT_P8
    GPIO_PORT_P9
    GPIO_PORT_P10
    GPIO_PORT_P11
    GPIO_PORT_PA
    GPIO_PORT_PB
    GPIO_PORT_PC
    GPIO_PORT_PD
    GPIO_PORT_PE
    GPIO_PORT_PF
    GPIO_PORT_PJ
    GPIO_PIN0
    GPIO_PIN1
    GPIO_PIN2
    GPIO_PIN3
    GPIO_PIN4
    GPIO_PIN5
    GPIO_PIN6
    GPIO_PIN7
    GPIO_PIN8
    GPIO_PIN9
    GPIO_PIN10
    GPIO_PIN11
    GPIO_PIN12
    GPIO_PIN13
    GPIO_PIN14
    GPIO_PIN15







    GPIO_PRIMARY_MODULE_FUNCTION
    GPIO_SECONDARY_MODULE_FUNCTION
    GPIO_TERNARY_MODULE_FUNCTION

    我们注意到函数的命名规则是:
     以函数
    uint8_t GPIO_getInputPinValue        (uint8_t selectedPort,uint16_t selectedPins )        为例
        我们看到函数名以大些的GPIO开头,然后下划线,接着小写的get开头,后面的单子分别是大写字母打头。在引用库函数时候要注意到这些细节。另外看函数参变量类似,均告诉了你该值的类型与位宽。

        另外注意到跟中断有关的函数只有一个GPIO_interruptEdgeSelect()中断边沿选择的selectedPort是可以有18个值可以选择,其余只有3个值可以选择。

        另外要记住,对于管脚变量selectedPins输入时候是可以使用可选值的逻辑或运算的。

        我们观察函数名,发现按照字面意思就可以知道该函数的作用,也就是属于自然语言表达的函数名。
    例如:GPIO_setOutputLowOnPin (GPIO_PORT_P1, GPIO_PIN0);   
    GPIO_   表示这个函数属于GPIO外设族的,后面set(设置)Output(输出)Low(低电平)On()Pin(管脚)……,连起来读就是:设置输出低电平在管脚(xx,xx)只需要我们在参数里天上我们指定的管脚就行了,根本不用管关心需要操作什么寄存器,毕竟寄存器名字读起来没有自然语言表达更清楚明了。
  • 2.5  问题提出

    1为什么关于中断边沿选择的函数GPIO_interruptEdgeSelect()的参数selectedPort可以有18个量可以选择,而不是像其他4个关于中断设置的函数,只有GPIO_PORT_P1、
    GPIO_PORT_P2GPIO_PORT_PA 三个量可以选择

    2.在表3中给出的selectedPort所选的18个量,我用不同颜色做了标记,根据本章2.1引言部分介绍,PA就是P1P2的合,那么在位的对应关系上,是怎样对应的,是高位在前还是低位在前?请结合手册找出该对应关系。

    3.结合例程,使用库函数编写程序:P1.1配置为中断上升沿触发按钮,在P2上有8LED灯,初始时候灯是间隔点亮的,记为P2.0为亮。当按键触发时候全部灭掉,第二次中断触发的时候,轮流点亮。第三次触发的时候全部点亮。如此按键,将会循环上面的操作(即:全灭->轮流跑马灯->全亮)。

    4.谈谈你对本库函数的认识,什么情况下使用方便,什么情况下使用不方便。

    本科内容主要学习了解基于CCStudio6的库函数开发过程,学会阅读库函数,以及了解GPIO_相关的13个函数的功能和使用方法。另外请关注本帖,学习的同时跟帖与网友做好学习交流。
  • 资料下载:
    第一讲完整课件内容下载: 

    MSP430I2XX 教程第一讲.pdf
  • 官方的文件,库函数,应用手册,例程, 包括最新版2.0的下载地址和内容。

    MSP430i2xxDriverLib.rar
  • MSP430i2xx系列单片机用户手册: 

    MSP430i2xxFamilyUser's Guide.pdf
  • 21ic网友针对教程的学习讨论异常热烈,现将部分精彩问答及分享内容整理呈现:


    • 1、

    A提问:

    有这么一段话:“单个端口可以作为可以以字节宽度进行访问,也可以合二为一组合成字宽,以字的格式进行访问。P1/P2, P3/P4, P5/P6, P7/P8,四对端口分别对应PA、PB、PC、PD。除了中断向量P1IV和P2IV外,所有的端口寄存器是按照这种命名约定方式处理的,也就是说不存在中断向量PAIV。”后半句不理解:”除了中断向量P1IV和P2IV外,所有的端口寄存器是按照这种命名约定方式处理的,也就是说不存在中断向量PAIV“不太理解这块,还有那个”断言语句“第一次了解。

    B回复:

    灰常好,
           编程断言:编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真,可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。
           以上是百度百科里讲的。我是根据TI的英文版说明直接翻译的,但是我觉得这个断言应该是指的断点调试相关的东西吧。所以需要和网友互动,大家一起讨论其中的疑点。
    另外一个关于中断向量的问题,因为在IO端口使用上,我们可以使用P1,P……,也可以使用PA,PB,, 另外前面说PA是字的宽度(16bits)P1,P2分别是字节宽度(8bits),另外PA=P1和P2,而中断向量里只定义了对应P1,和P2宽度的,没有定义对应PA宽度的。

     

    • 2

    1.为什么关于中断边沿选择的函数GPIO_interruptEdgeSelect()的参数selectedPort可以有18个量可以选择,而不是像其他4个关于中断设置的函数,只有GPIO_PORT_P1GPIO_PORT_P2GPIO_PORT_PA三个量可以选择?
    答:该系列单片机具备的I/O端口数量最高可达12组(P1P11,外加PJ端口)。大部分呢端口每组包含8个管脚;然后,一些端口可用的管脚较少,不足8个(端口包含可用管脚的具体情况需要查看对应芯片的数据手册)。每个I/O管脚可用分别配置为输入或输出,并且每个可用单独进行读/写操作。
           selectedPort可选的有效值有18(11+7)
    :GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PB、GPIO_PORT_PC、GPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
    注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
    selectedPins
    2.在表3中给出的selectedPort所选的18个量,我用不同颜色做了标记,根据本章2.1引言部分介绍,PA就是P1P2的合,那么在位的对应关系上,是怎样对应的,是高位在前还是低位在前?请结合手册找出该对应关系。
    答:高位在前。
    3.结合例程,使用库函数编写程序:P1.1配置为中断上升沿触发按钮,在P2上有8LED灯,初始时候灯是间隔点亮的,记为P2.0为亮。当按键触发时候全部灭掉,第二次中断触发的时候,轮流点亮。第三次触发的时候全部点亮。如此按键,将会循环上面的操作(即:全灭->轮流跑马灯->全亮)。

    //定义标志位flag
    Uint8  flag=0;
    // 把P2设置为输出端口
    GPIO_setAsOutputPin(GPIO_PORT_P2,GPIO_PIN0);
    GPIO_setAsOutputPin(GPIO_PORT_P2,GPIO_PIN1);
    GPIO_setAsOutputPin(GPIO_PORT_P2,GPIO_PIN2);
    GPIO_setAsOutputPin(GPIO_PORT_P2,GPIO_PIN3);
    GPIO_setAsOutputPin(GPIO_PORT_P2,GPIO_PIN4);
    GPIO_setAsOutputPin(GPIO_PORT_P2,GPIO_PIN5);
    GPIO_setAsOutputPin(GPIO_PORT_P2,GPIO_PIN6);
    GPIO_setAsOutputPin(GPIO_PORT_P2,GPIO_PIN7);
    GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN0);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN1);
    GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN2);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN3);
    GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN4);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN5);
    GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN6);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN7);
    // 把P1.1设置为输入端口
    GPIO_setAsInputPin(GPIO_PORT_P1,GPIO_PIN1);
    while (1)
    {
    if(GPIO_INPUT_PIN_HIGH== GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN1))
    {
    //按键消抖
    asm("nop");
    asm("nop");
    asm("nop");
    // 第一次按键测试管脚P1.1
    if((GPIO_INPUT_PIN_HIGH== GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN1)&&flag==0))
    {
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN0);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN1);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN2);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN3);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN4);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN5);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN6);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN7);
    flag=1;
    }
    //第二次按键测试管脚P1.1
    else if ((GPIO_INPUT_PIN_HIGH== GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN1)&&flag==1))
    {
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN0);
    GPIO_ setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN0);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN1);
    GPIO_ setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN1);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN2);
    GPIO_ setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN2);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN3);
    GPIO_ setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN3);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN4);
    GPIO_ setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN4);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN5);
    GPIO_ setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN5);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN6);
    GPIO_ setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN6);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN7);
    GPIO_ setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN7);
    flag=2;
    }
    //第三次按键测试管脚P1.1
    else ((GPIO_INPUT_PIN_HIGH== GPIO_getInputPinValue(GPIO_PORT_P1, GPIO_PIN1)&&flag==2))
    {
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN0);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN1);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN2);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN3);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN4);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN5);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN6);
    GPIO_ setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN7);
    flag=0;
    }
    }
    4.谈谈你对本库函数的认识,什么情况下使用方便,什么情况下使用不方便。
    答:GPIO库函数比较实用,库函数中的每个函数的参数都是采用了单间易懂的英文大写,尤其是对C语言初学者和对MCU内部结构或者寄存去不是非常熟悉的情况下非常容易上手。
    但是对于一些特殊应用需求(比如运行效率要求比较高的场合)下显得有些麻烦,尤其是有些函数的参数非常长,书写起来不是很方便。

    • 3

    A提问:

    第一个问题中的函数名,在2.0的手册中,名字已经更改了:void GPIO selectInterruptEdge ( uint8 t selectedPort, uint16 t selectedPins, uint8 t
    edgeSelect )

    对于这样库函数,我感觉,在IO口操作时,选择寄存器,会来的更方便,在操作外设,比如定时器,ADC的时候,函数写起来就更方便了

    B回复:

    嗯,你很细心啊,那个问题就是他们制作库函数时候的一个失误,不过最新版的CCS给的是1.97,这几天刚更新了2.0,所以我赶紧传上来供大家找问题时候看看他们是否纠正,当时我做课件时候对这一点就很怀疑了,所以放在问题里了。

     

    • 4

    1、为什么关于中断边沿选择的函数GPIO_interruptEdgeSelect()的参数selectedPort可以有18个量可以选择,而不是像其他4个关于中断设置的函数,只有GPIO_PORT_P1、GPIO_PORT_P2、GPIO_PORT_PA 三个量可以选择?


    问题一:
    首先,要准备下基础知识:
    1、端口P1和P2都具备中断功能。P1和P2两组端口的每一位管脚都可以单独配置成在输入一个上升沿或下降沿时候触发中断的功能。


    2、单个端口可以作为可以以字节宽度进行访问,也可以合二为一组合成字宽,以字的格式进行访问。P1/P2, P3/P4, P5/P6, P7/P8,四对端口分别对应PA、PB、PC、PD。除了中断向量P1IV和P2IV外,所有的端口寄存器是按照这种命名约定方式处理的,也就是说不存在中断向量PAIV。
    从这里我们可以看出,为什么只有GPIO_PORT_P1、GPIO_PORT_P2、GPIO_PORT_PA这三个量,而不是P3或者PB,因为只有端口P1和P2都具备中断功能。
    那么下面我们继续来解释这个问题,解决这个问题前,依旧需要准备点知识,我们来看下这个五个中断函数以及功能:
    void GPIO_enableInterrupt (uint8_t selectedPort, uint16_t selectedPins)
    该函数使能所选管脚的端口中断功能
    void GPIO_disableInterrupt (uint8_t selectedPort, uint16_t selectedPins)
    该函数禁用所选管脚端口的中断
    uint16 t GPIO_getInterruptStatus (uint8_t selectedPort, uint16_t selectedPins)
    该函数可以获取被选中的端口的中断状态
    void GPIO_clearInterruptFlag (uint8 t selectedPort, uint16 t selectedPins)
    该函数可以清除所选管脚的中断标志
    void GPIO_interruptEdgeSelect (uint8_t selectedPort, uint16_t selectedPins, uint8_t edgeSelect)
    该函数可以对所选管脚的中断触发边沿类型进行选择(是上升沿触发还是下降沿触发)

    从这里可以看到这GPIO_enableInterrupt 、GPIO_disableInterrupt 、GPIO_getInterruptStatus 、GPIO_clearInterruptFlag 这几个中断函数都是针对具备中断功能的端口的,所有根据之前的介绍,很容易明白“其他4个关于中断设置的函数,只有GPIO_PORT_P1、GPIO_PORT_P2、GPIO_PORT_PA 三个量可以选择”。

    说了这么多,还有一个关键的没讲“为什么关于中断边沿选择的函数GPIO_interruptEdgeSelect()的参数selectedPort可以有18个量可以选择”,继续补充必要的知识:
    1、该系列单片机具备的I/O端口数量最高可达12组(P1至P11,外加PJ端口)。
    2、具体的18个值,selectedPort可选的有效值有18(11+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、GPIO_PORT_PB、GPIO_PORT_PC、GPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。

    从这里可以看出18个值是怎么来的,另外注意看下GPIO_interruptEdgeSelect的功能是“该函数可以对所选管脚的中断触发边沿类型进行选择(是上升沿触发还是下降沿触发)”也就是说所有的脚都是它可以选择的,也就是说所以的端口都是可以选择的,所有就有了18个量。

    • 5

    1.  2.0版本的库函数  好像并不存在 所提的 问题一   好像参量全部统一了   我对比了下头文件 ,还有那个函数的名称也改变了 。 看手册的 库函数的 寄存器手册 ,只有p1 ,p2端口存在独立的中断功能,独立的中断参数。所以,如果函数没封装好的话,会导致问题一的出现 。但是 我看了下 代码 ,发现其实除了1 2口 有定义地址之外   其它 的参数 应该都是返回0xffff的  ,好像没什么意义。
    2.   p1 ,p2 假如这两个按照字来操作的话 ,从库函数 selectedPins <<= 8;可以看出    加入是 p1 p2结合操作的话 就是P2 在前。位并没有进行变动 ,那么就还是高位在前。
    3.对于出东西来说  ,肯定是库函数 有优势,但是 就是相对于学习来说 ,从库函数入手  ,然后再到 底层 去看一下  学习一下 别人的做法 也未尝不是一件
    好事  ,可能库函数对于某些操作比较频繁的东西 有锁欠缺,但是可以说 是 学习的 好帮手,出产品的利刃啊 。

    • 6
    作者文中写到:
    uint8 t GPIO_getInputPinValue (uint8_t selectedPort, uint16_t selectedPins)
      该函数获取所选管脚的输入值。
         该函数具有两个参数:selectedPort(所选端口)和selectedPins(所选管脚)。
         selectedPort可选的有效值有1811+7)个:GPIO_PORT_P1、GPIO_PORT_P2……GPIO_PORT_P11,GPIO_PORT_PA、         GPIO_PORT_PBGPIO_PORT_PCGPIO_PORT_PD、GPIO_PORT_PE、GPIO_PORT_PF、GPIO_PORT_PJ。
        注意:数字标记的为以字节为操作位宽(8位)的,字母标记的为以字为位宽(16位)操作的。
        selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
        返回值:GPIO_INPUT_PIN_HIGHGPIO_INPUT_PIN_LOW二者中之一。即管脚的状态值。

    问题:因为返回值是真或否,总感觉selectPin不应该是所选引脚“或”关系???像是判断指定端口引脚的状态。。故查看库源代码。。。

    以下是gpio.c文件中函数:
    uint8_t GPIO_getInputPinValue(uint8_t selectedPort,
                                  uint16_t selectedPins) {
        uint16_t baseAddress = GPIO_PORT_TO_BASE[selectedPort];

        #ifndef NDEBUG
        if(baseAddress == 0xFFFF)
        {
            return;
        }
        #endif

        // Shift by 8 if port is even (upper 8-bits)
        if((selectedPort & 1) ^ 1)
        {
            selectedPins <<= 8;
        }

        uint16_t inputPinValue = HWREG16(baseAddress + OFS_PAIN) & (selectedPins);

        if(inputPinValue > 0)
        {
            return (GPIO_INPUT_PIN_HIGH);
        }
        return (GPIO_INPUT_PIN_LOW);
    }
    这里返回的是GPIO_INPUT_PIN_HIGH or   GPIO_INPUT_PIN_LOW  不是 inputPinValue。
    以下是gpio.h中函数声明及函数说明:
    //*****************************************************************************
    //
    //! \brief This function gets the input value on the selected pin
    //!
    //! This function gets the input value on the selected pin.
    //!
    //! \param selectedPort is the selected port.
    //!        Valid values are:
    //!        - \b GPIO_PORT_P1
    //!        - \b GPIO_PORT_P2
    //!        - \b GPIO_PORT_P3
    //!        - \b GPIO_PORT_P4
    //!        - \b GPIO_PORT_P5
    //!        - \b GPIO_PORT_P6
    //!        - \b GPIO_PORT_P7
    //!        - \b GPIO_PORT_P8
    //!        - \b GPIO_PORT_P9
    //!        - \b GPIO_PORT_P10
    //!        - \b GPIO_PORT_P11
    //!        - \b GPIO_PORT_PA
    //!        - \b GPIO_PORT_PB
    //!        - \b GPIO_PORT_PC
    //!        - \b GPIO_PORT_PD
    //!        - \b GPIO_PORT_PE
    //!        - \b GPIO_PORT_PF
    //!        - \b GPIO_PORT_PJ
    //! \param selectedPins is the specified pin in the selected port.
    //!        Valid values are:
    //!        - \b GPIO_PIN0
    //!        - \b GPIO_PIN1
    //!        - \b GPIO_PIN2
    //!        - \b GPIO_PIN3
    //!        - \b GPIO_PIN4
    //!        - \b GPIO_PIN5
    //!        - \b GPIO_PIN6
    //!        - \b GPIO_PIN7
    //!        - \b GPIO_PIN8
    //!        - \b GPIO_PIN9
    //!        - \b GPIO_PIN10
    //!        - \b GPIO_PIN11
    //!        - \b GPIO_PIN12
    //!        - \b GPIO_PIN13
    //!        - \b GPIO_PIN14
    //!        - \b GPIO_PIN15
    //!
    //! \return One of the following:
    //!         - \b GPIO_INPUT_PIN_HIGH
    //!         - \b GPIO_INPUT_PIN_LOW
    //!         \n indicating the status of the pin
    //
    //*****************************************************************************
    extern uint8_t GPIO_getInputPinValue(uint8_t selectedPort,
                                         uint16_t selectedPins);

    首先作者说了GPIO_getInputPinValue函数的selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或
    但是从头文件gpio.h函数中是这样的说的://! \param selectedPins is the specified pin in the selected port.   
    不像其他函数:例如extern void GPIO_clearInterrupt(uint8_t selectedPort,
                                    uint16_t selectedPins);
    //! \param selectedPins is the specified pin in the selected port.
    //!        Mask value is the logical OR of any of the following:
    • 7

    A提问:

    if((selectedPort & 1) ^ 1)。这个是库函数gpio.c中函数GPIO_interruptEdgeSelect()中的一句话,不太明白意图,难道只是判断奇偶?还有GPIO_interruptEdgeSelect()这个函数是如何区分传入GPIO_PORT_P1和 GPIO_PORT_PA 这两个参数的呢?

    下面是整个函数体
    void GPIO_interruptEdgeSelect(uint8_t selectedPort,
                                  uint16_t selectedPins,
                                  uint8_t edgeSelect) {
        uint16_t baseAddress = GPIO_PORT_TO_BASE[selectedPort];

        #ifndef NDEBUG
        if(baseAddress == 0xFFFF)
        {
            return;
        }
        #endif

        // Shift by 8 if port is even (upper 8-bits)
        if((selectedPort & 1) ^ 1)
        {
            selectedPins <<= 8;
        }

        if(GPIO_LOW_TO_HIGH_TRANSITION == edgeSelect)
        {
            HWREG16(baseAddress + OFS_PAIES) &= ~selectedPins;
        }
        else
        {
            HWREG16(baseAddress + OFS_PAIES) |= selectedPins;
        }
    }

    B回复:

    我刚开始和你一样困惑,不知道分析可对。

    #define HWREG16(x)   (*((volatile uint16_t *)((uint16_t)x)))


    if((selectedPort & 1) ^ 1)  这句话所有的GPIO函数都用到了,个人感觉是先判断端口是奇数端口还是偶数端口,
    如果是奇数,不区分是GPIO_PORT_P1或是GPIO_PORT_PA,所操作的寄存器直接对对应selectedPins进行操作,不执行  selectedPins <<= 8;
    如果是偶数端口需要移位执行selectedPins <<= 8;    GPIO_PORT_P1和GPIO_PORT_P2传递的基地址相同!!!!
    这里全部都按照16位地址操作的!!!!!
    主要原因是因为HWREG16(X)宏定义。
    HWREG16(baseAddress + OFS_PAIES) &= ~selectedPins;  //

    uint16_t baseAddress = GPIO_PORT_TO_BASE[selectedPort];  //我跟踪一下GPIO_PORT_P1和GPIO_PORT_P2传递的基地址相同
    static const uint16_t GPIO_PORT_TO_BASE[] = {
        0x00,
    #if defined(__MSP430_HAS_PORT1_R__)
        __MSP430_BASEADDRESS_PORT1_R__,
    #elif defined(__MSP430_HAS_PORT1__)
        __MSP430_BASEADDRESS_PORT1__,
    #else
        0xFFFF,
    #endif
    #if defined(__MSP430_HAS_PORT2_R__)
        __MSP430_BASEADDRESS_PORT2_R__,
    #elif defined(__MSP430_HAS_PORT2__)
        __MSP430_BASEADDRESS_PORT2__,
    #else
        0xFFFF,

    。。。。。}

    • 8

    版主的第一个问题“1.为什么关于中断边沿选择的函数GPIO_interruptEdgeSelect()的参数selectedPort可以有18个量可以选择,而不是像其他4个关于中断设置的函数,只有GPIO_PORT_P1、GPIO_PORT_P2、GPIO_PORT_PA 三个量可以选择?”


    第一:是因为在12个端口中只有端口P1和P2具备中断功能,其余端口都不具有中断功能,而P1/P2对端口对应PA,所以PA端口也具备中断功能;
    第二:处理GPIO中断的有:GPIO_enableInterrupt()、GPIO_disableInterrupt()、GPIO_clearInterruptFlag()、GPIO_getInterruptStatus()、GPIO_interruptEdgeSelect(),前四个(使能中断+禁用中断、清除中断标志+获取中断状态)从其名称上看,明显是只有具备中断功能的端口才能适用的,试想一个没有中断功能的端口怎么去使能其中断,怎么去清除其中断标志位?而第五个是中断沿翻转,每个端口都可以请求中断,这就决定了每个端口都可以选择其请求的中断是什么沿触发。

    个人理解,未涉及到寄存器,只从其字面含义去理解,如若有错欢迎指出。

    • 10

    A提问:

    在2.2小节中的“函数文档”部分很多函数都提到了“selectedPins是所选端口上的管脚。其掩码值可以是GPIO_PIN0、GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或。该函数是通过修改寄存器PxOUT的位实现,返回值 None(空)。”这里我有些不明白。
    我的理解是selectedPins应该是0~15个pin中的某一个,但是这里说的“掩码值”指什么呢?是“十六个值的逻辑或”?那我的理解是GPIO_PINn(n=0~15)就是16位的寄存器,selectedPins是几就在那一位上置1,最后将16个寄存器的16位值逻辑或,得到一个寄存器的16位表示,第几位是1就表示第几个pin被选。
    比如说selectedPins=pin0,那GPIO_PIN0=0x1000_0000_0000_0000,其余的GPIO_PIN=0x0000_0000_0000_0000.

    但是如果真是这样的话,感觉好浪费寄存器。我的理解是不是不对啊?

     

    B回复:

    "那GPIO_PIN0=0x1000_0000_0000_0000,其余的GPIO_PIN=0x0000_0000_0000_0000."这句话表述就有错了,0x代表16进制数。不会浪费寄存器,GPIO_PINn(n=0~15)在头文件中被宏定义,比如:0x0001、0x0002、0x0004、0x0008······,操作的时候只要运用逻辑运算将寄存器的某些位置1或清0就行。

     

    A回复:

    0x那是我表述错了。
    只说了我的理解,想看下我的理解是不是不对,因为按我的理解是相当浪费i寄存器了。只有16种情况,用1个4位的寄存器就可以表示清楚的,为什么用了16个16位的寄存器呢?不过按我所说的理解,只用1个4位的寄存器还需要提供些解码电路设计。

    期待正确的理解。

    B回复:

    不过还是用版主最后的话来解释比较好——“根本不用管关心需要操作什么寄存器,毕竟寄存器名字读起来没有自然语言表达更清楚明了。”

    也是,通过例程来看,使用这些GPIO驱动库函数时根本不需要去关注操纵什么寄存器那些的。

    A回复:

    原来你的“浪费寄存器”是这个意思啊,那只是1个16位的寄存器,不是16个。

     

    B回复:

    即使是一个16位的,我也不明白那段话,你能详细说一下吗?
    “其掩码值可以是GPIO_PIN0、GPIO_PIN1、GPIO_PIN2……GPIO_PIN15等十六个值的逻辑或。”这逻辑或之后怎么去表示是哪个pin被选中了呢?

    A回复:

    16位的寄存器,每一位控制一个引脚,GPIO_PIN0就是0000 0000 0000 0001,与寄存器进行逻辑或操作后,寄存器的0bit不就置1选中了吗,以此类推。

     

    • 11

    A

    一直对MSP430很好奇

    现在32位的单片机天下,这个16bit的单片机,Ti还在推

    看来真的学习学习了,想必有独到之处吧。。。

     

    B回复

    是的,MSP430凭借独特的低功耗特性完胜手持设备,电池供电设备以及太阳能供电设备的其他主控MCU应用,低功耗以其独有专利的分级系统休眠特性实现。所以MSP430单片机家族只会壮大不会被淘汰,先前推出的MSP430G2xx系列就凭借超高性价比受到了市场的高度认可,这次推出升级版MSP430i2xx系列,同时也不甘落后正式推出了相关库函数,库函数的推出就是为了适应MSP430外设功能越来越强大而带来了学习负担,这样我们就不用把精力关注在内部硬件结构的学习上了,从而可以更快的进入到产品的开发应用。另外这次以MSP430i2xx,为主线进行讲解,目的是为了让大家能够轻松学会,这些对于后续学习MSP430FRxxx系列奠定了基础,TI的铁电系列功能更加强大,性能更加优良,希望大家也能够有兴了解一下MSP430FRxx系列。如果有需要后续会推出相关教程,或者视频教程。

    • 12

    A提问:

    刚刚看了一下库函数,在2.0的库里没找到GPIO_interruptEdgeSelect().
    后来发现,DriverLib1.97版里面是GPIO_interruptEdgeSelect(),DriverLib2.0版里面是GPIO_selectInterruptEdge().怎么库升级把函数名都变了啊?这岂不是给程序的移植制造麻烦吗?你觉得有必要吗?

     

    B回复:

    字宽度和字节宽度不同,通过这个实现的,因为在内存中他们是连续的,且高位在前,低位在后。如果定义一个16bits宽度的字,那么就等于两个连续的8bits字节。

     

    A回复:

    程序中那个地方用来区分字寻址和字节寻址的呢?我没找到。能否指出来?谢了。

     

    B回复:

    #define GPIO_PORT_P1                                                          1
    #define GPIO_PORT_P2                                                          2
    #define GPIO_PORT_P3                                                          3
    #define GPIO_PORT_P4                                                          4
    #define GPIO_PORT_P5                                                          5
    #define GPIO_PORT_P6                                                          6
    #define GPIO_PORT_P7                                                          7
    #define GPIO_PORT_P8                                                          8
    #define GPIO_PORT_P9                                                          9
    #define GPIO_PORT_P10                                                        10
    #define GPIO_PORT_P11                                                        11
    #define GPIO_PORT_PA                                                          1
    #define GPIO_PORT_PB                                                          3
    #define GPIO_PORT_PC                                                          5
    #define GPIO_PORT_PD                                                          7
    #define GPIO_PORT_PE                                                          9
    #define GPIO_PORT_PF                                                         11
    #define GPIO_PORT_PJ                                                         13--------------------------------------------------------------------------------------
    看头文件的宏,在A开始后,是按照2字节宽度开始起名字,所以如果操作时候使用2字节宽度的操作就是2字节,如果还是使用1字节宽度的操作,那么就是只对PA的低8位操作

     

    A回复:

    不好意思还是不太明白,能不能在.c里面指出具体的地方,谢了!

     

    B回复:

    在头文件的.h里。

     

  • 很给力,感谢分享