初学DSP(F28335芯片),玩了玩GPIO点了个灯,赶紧对GPIO相关知识进行一个梳理记录。(因为初学,肯定有很多地方理解的有偏差,如若各位大哥大姐们发现错误之处,还请不吝赐教,欢迎喷我,本人坚信会被喷才会有成长哈,感谢感谢!)
1、F28335芯片GPIO一共有88个GPIO口:GPIO0 - GPIO87。
2、分为ABC三组:
组名 | GPIO范围 | 写法 |
---|---|---|
A组 | GPIO0 - GPIO31 | GPA |
B组 | GPIO32 - GPIO63 | GPB |
C组 | GPIO64 - GPIO87 | GPC |
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 |
GPxDIR | IO方向寄存器 | 配置IO方向: 0:输入 1:输出 |
GPxDAT | 独立读写IO口寄存器 | 当IO方向为输入时为只读寄存器; 当IO方向为输出时,可以通过GPxSET设置该寄存器值 |
GPxSET | 置1寄存器 | 写1有效,写0无效 |
GPxCLEAR | 清0寄存器 | 写1有效,写0无效 |
GPxTOOGLE | 翻转寄存器 | 写1有效,写0无效 |
GPAPUD | 是否上拉寄存器 | 0:上拉 1:不上拉 |
要点亮led灯,需要关注以下几个方面:
代码如下(详细代码结构体解析见下章)
/* 设置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
示例语句: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
示例语句: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呢?留个疑问下一小节回答。
示例语句: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口。
示例语句:GpioCtrlRegs.GPCMUX1.bit.GPIO64 = 0;
所以整个语句需要表示GPIO64,则可以计算出来64是属于C组GPIO的前半部分,
(1)C组,则应该是GPC
(2)前半部分GPIO,则应该是MUX1
故合起来是GPCMUX1。其他各个成员则都一样。
同理,要表示GPIO82,则完整表达是应该是:GpioCtrlRegs.GPCMUX2.bit.GPIO82
所以一级一级追下来,可以发现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
};