初学DSP(F28335芯片),玩了玩GPIO点了个灯,赶紧对GPIO相关知识进行一个梳理记录。(因为初学,肯定有很多地方理解的有偏差,如若各位大哥大姐们发现错误之处,还请不吝赐教,感谢感谢!)

一、GPIO知识点

1、F28335芯片GPIO一共有88个GPIO口:GPIO0 - GPIO87。

2、分为ABC三组:

组名GPIO范围写法
A组GPIO0 - GPIO31GPA
B组GPIO32 - GPIO63GPB
C组GPIO64 - GPIO87GPC

3、寄存器:(x为组名,可取值A、B、C

寄存器名功能含义
GPxMUXn复用功能寄存器决定GPIO口是否复用。每2bit表示一个GPIO,故可取值00/01/10/11,即:0/1/2/3。
0:关闭复用功能,当做普通IO口使用
1/2/3:打开复用功能,对应具体复用功能,请查看数据手册

注意:n可以取值1 or 2
原因:mux为32位寄存器,每2bit表示一个GPIO,故一个mux寄存器只能表示16个GPIO口。而每组GPIO有32个GPIO,故要全部表示需要2个MUX寄存器,即:MUX1和MUX2
MUX1:每组GPIO的前半部分,如:GPIO0 ~GPIO15
MUX2:每组GPIO的后半部分,如:GPIO16~GPIO31
GPxDIRIO方向寄存器配置IO方向:
0:输入
1:输出
GPxDAT独立读写IO口寄存器当IO方向为输入时为只读寄存器;
当IO方向为输出时,可以通过GPxSET设置该寄存器值
GPxSET置1寄存器写1有效,写0无效
GPxCLEAR清0寄存器写1有效,写0无效
GPxTOOGLE翻转寄存器写1有效,写0无效
GPAPUD是否上拉寄存器0:上拉
1:不上拉

二、代码

要点亮led灯,需要关注以下几个方面:

  • 1、电路图找到led所在GPIO,看是否需要上拉
  • 2、所对应GPIO口需要关闭复用功能,作为普通IO口使用
  • 3、配置GPIO口为输出口
  • 4、将该GPIO配置为低电平或高电平

代码如下(详细代码结构体解析见下章)

/* 设置GPIO口为输出口,并输出低电平 */
GpioCtrlRegs.GPCPUD.bit.GPIO64 = 0;    /* 上拉 */
GpioCtrlRegs.GPCMUX1.bit.GPIO64 = 0;   /* GPIO64属于C组前半部分GPIO,故为GPCMUX1 */
GpioCtrlRegs.GPCDIR.bit.GPIO64 = 1;    /* 1:表示方向为输出 */
GpioDataRegs.GPCCLEAR.bit.GPIO64 = 1;  /* 清零该bit */

三、代码结构体解析

关于上述代码赋值解析,也可以从手册中查看寄存器详细描述,也可以从代码中窥探一二。

如下,以上述代码第二行赋值语句【GpioCtrlRegs.GPCMUX1.bit.GPIO64 = 0;】为例进行解析,为什么GPIO64赋值就要这么表示?

将该结构体变量层层剥开:

变量名为:GpioCtrlRegs

第一级成员为:GPCMUX1

第二级成员为:bit

最后一级成员为:GPIO64

1、结构体变量:GpioCtrlRegs

示例语句:GpioCtrlRegs.GPCMUX1.bit.GPIO64 = 0;

在代码中可以找到变量GpioCtrlRegs的定义与结构体类型定义如下:

volatile struct GPIO_CTRL_REGS GpioCtrlRegs;
struct GPIO_CTRL_REGS {
   union  GPACTRL_REG  GPACTRL;   // GPIO A Control Register (GPIO0 to 31)
   union  GPA1_REG     GPAQSEL1;  // GPIO A Qualifier Select 1 Register (GPIO0 to 15)
   union  GPA2_REG     GPAQSEL2;  // GPIO A Qualifier Select 2 Register (GPIO16 to 31)
   union  GPA1_REG     GPAMUX1;   // GPIO A Mux 1 Register (GPIO0 to 15)
   union  GPA2_REG     GPAMUX2;   // GPIO A Mux 2 Register (GPIO16 to 31)
   union  GPADAT_REG   GPADIR;    // GPIO A Direction Register (GPIO0 to 31)
   union  GPADAT_REG   GPAPUD;    // GPIO A Pull Up Disable Register (GPIO0 to 31)
   Uint32              rsvd1;
   union  GPBCTRL_REG  GPBCTRL;   // GPIO B Control Register (GPIO32 to 63)
   union  GPB1_REG     GPBQSEL1;  // GPIO B Qualifier Select 1 Register (GPIO32 to 47)
   union  GPB2_REG     GPBQSEL2;  // GPIO B Qualifier Select 2 Register (GPIO48 to 63)
   union  GPB1_REG     GPBMUX1;   // GPIO B Mux 1 Register (GPIO32 to 47)
   union  GPB2_REG     GPBMUX2;   // GPIO B Mux 2 Register (GPIO48 to 63)
   union  GPBDAT_REG   GPBDIR;    // GPIO B Direction Register (GPIO32 to 63)
   union  GPBDAT_REG   GPBPUD;    // GPIO B Pull Up Disable Register (GPIO32 to 63)
   Uint16              rsvd2[8];
   union  GPC1_REG     GPCMUX1;   // GPIO C Mux 1 Register (GPIO64 to 79)
   union  GPC2_REG     GPCMUX2;   // GPIO C Mux 2 Register (GPIO80 to 95)
   union  GPCDAT_REG   GPCDIR;    // GPIO C Direction Register (GPIO64 to 95)
   union  GPCDAT_REG   GPCPUD;    // GPIO C Pull Up Disable Register (GPIO64 to 95)
};

可以看到定义中将所有的寄存器都封装到该结构体中,并且将所有的寄存器封装成ABC三组:

​ 前三分之一为A组,中间的为B组,后面的为C组,寄存器名字风格大都一致。

深追下一级成员:GPCMUX1

2、第一级成员:GPCMUX1

示例语句:GpioCtrlRegs.GPCMUX1.bit.GPIO64 = 0;

上面结构体中倒数第四行可以看到该成员定义:union GPC1_REG GPCMUX1; ,是一个union类型GPC1_REG,追进去可以看到定义如下:

union GPC1_REG {
   Uint32              all;
   struct GPC1_BITS    bit;
};

可以看到有两个成员,一个all,一个bit,bit也是一个结构体GPC1_BITS,为第二级成员

疑问:为什么要用union呢?留个疑问下一小节回答。

3、第二级成员:bit

示例语句:GpioCtrlRegs.GPCMUX1.bit.GPIO64 = 0;

第二级成员bit定义如下:struct GPC1_BITS bit;是一个结构体类型,追进去如下:

struct GPC1_BITS {            // bits   description
   Uint16 GPIO64:2;           // 1:0    GPIO64   
   Uint16 GPIO65:2;           // 3:2    GPIO65
   Uint16 GPIO66:2;           // 5:4    GPIO66
   Uint16 GPIO67:2;           // 7:6    GPIO67   
   Uint16 GPIO68:2;           // 9:8    GPIO68
   Uint16 GPIO69:2;           // 11:10  GPIO69
   Uint16 GPIO70:2;           // 13:12  GPIO70   
   Uint16 GPIO71:2;           // 15:14  GPIO71 
   Uint16 GPIO72:2;           // 17:16  GPIO72 
   Uint16 GPIO73:2;           // 19:18  GPIO73   
   Uint16 GPIO74:2;           // 21:20  GPIO74 
   Uint16 GPIO75:2;           // 23:22  GPIO75       
   Uint16 GPIO76:2;           // 25:24  GPIO76 
   Uint16 GPIO77:2;           // 27:26  GPIO77 
   Uint16 GPIO78:2;           // 29:28  GPIO78 
   Uint16 GPIO79:2;           // 31:30  GPIO79 
};

可以看到,该结构体表示了GPIO64~GPIO79,且内部用了C语言中【位域】的语法,每2bit表示一个GPIO,所以一个32bit寄存器可以表示16个GPIO口。

所以不难发现,上一级成员中,有两个子成员(【32位的all】和【结构体GPC1_BITS】)。

(1)当代码使用第一个成员all的时候,表示所有的bit即:结构体中所有的GPIO口(GPIO64~GPIO79),

(2)当使用子成员bit的时候,可以继续具体到内部成员,具体到每一个GPIO口。

4、最后一级成员:GPIO64

示例语句:GpioCtrlRegs.GPCMUX1.bit.GPIO64 = 0;

所以整个语句需要表示GPIO64,则可以计算出来64是属于C组GPIO的前半部分,

(1)C组,则应该是GPC

(2)前半部分GPIO,则应该是MUX1

故合起来是GPCMUX1。其他各个成员则都一样。

同理,要表示GPIO82,则完整表达是应该是:GpioCtrlRegs.GPCMUX2.bit.GPIO82

5、GPCMUX1与GPCMUX2区别

所以一级一级追下来,可以发现GPIO64,就属于C组的GPIO,因为2bit表示一个GPIO,所以一个32bit的寄存器只能表示16个GPIO,所以只能表示到GPIO64~GPIO79,

那么猜想后16个GPIO应该也有一个寄存器表示,回到最开始的结构体变量定义中,可以找到相邻的两个语句:

struct GPIO_CTRL_REGS {
   ...
   union  GPC1_REG     GPCMUX1;   // GPIO C Mux 1 Register (GPIO64 to 79)
   union  GPC2_REG     GPCMUX2;   // GPIO C Mux 2 Register (GPIO80 to 95)
   ...
};

其实GPCMUX1与GPCMUX2,是一模一样的定义,只是GPCMUX2恰好表示后面16个GPIO口,应该是GPIO80~GPIO95,可以看到官方代码中注释也是(GPIO80 to 95),但是,由于F28335芯片总共只有88个GPIO,所以GPIO最大应该是GPIO87。

追到代码中可以验证,代码如下:

struct GPC2_BITS {            // bits   description
   Uint16 GPIO80:2;           // 1:0    GPIO80   
   Uint16 GPIO81:2;           // 3:2    GPIO81 
   Uint16 GPIO82:2;           // 5:4    GPIO82 
   Uint16 GPIO83:2;           // 7:6    GPIO83   
   Uint16 GPIO84:2;           // 9:8    GPIO84
   Uint16 GPIO85:2;           // 11:10  GPIO85 
   Uint16 GPIO86:2;           // 13:12  GPIO86    
   Uint16 GPIO87:2;           // 15:14  GPIO87
   Uint16 rsvd:16;            // 31:16  reserved
};
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐