前面我们已经对敏矽微电子的基于cortex m0内核的ME32F030R8T6的基本功能做了介绍,然后详细讲解了开发环境MDK的安装,pack包的安装,工程的建立及程序的仿真,紧接着讲解了ME32F030R8T6的时钟系统。
如果说前面都是基础知识介绍和理论讲解,那么从本篇开始,我们不但会有理论介绍,还会结合敏矽微电子提供的ME32F030R8T6开发板进行实例验证,让大家看到实例运行的结果。
1、敏矽微电子Cortex-M0学习笔记04-GPIO详解及实例
首先我们对ME32F030R8T6芯片的I/O端口做一个大致的了解。
1.1. ME32F030R8T6的GPIO 概述
ME32F030R8T6提供多达 57个 GPIO 管脚。主要的特点有:
• 数字管脚可以用软件定义为输入或输出
• 管脚读写可以被屏蔽
• 多个管脚的置位、清零位用一条指令实现
• 管脚的输出取反
• 每一个管脚可作为外部中断信号
• 可编程的中断触发条件及中断优先级
• 所有GPIO管脚在复位后被配置成带上拉电阻的输入管脚
MCU的端口除了灵活易用的特点之外,其外设功能也十分强大。管脚功能由IO控制寄存器配置,除电源管脚,其余管脚均可复用。
系统复位后,管脚功能将被设置成默认值。
GPIO可以复用的功能如下:LED/LCD 驱动、触摸按键、ADC、定时器6输出、定时器7输出、PWM输出、UART0、UART1、SPI、I2C。
在使用端口复用功能时,通过 I/O 配置寄存器IOCON对端口进行功能设置,每个引脚对应的复用功能,请参照官方数据手册的详细说明。
1.2. ME32F030R8T6的GPIO 寄存器详解
对于使用过单片机开发产品的人来说,功能繁多、名称各异的寄存器应该是最重要也是最令人头疼的地方,但是没办法,想要开发出功能稳定、性能可靠的产品,就必须来硬着头皮查看各个寄存器的功能和配置方法。
当然,对于ME32F030R8T6来说,官方推出了库函数,利用这些库函数可以不必关心具体的寄存器就能编写出合适的程序。但是对于想要深入了解的话,还是要看看寄存器的。
1.2.1. GPIO功能配置寄存器
MCU有一系列的GPIO 控制寄存器来实现对I/O口的灵活控制。
首先我们对 I/O 配置寄存器IOCON做一个介绍,因为它决定着I/O端口的功能选择和电气特性。所以需要拿出来单独进行介绍,该寄存器的每一位的功能如下:
①、管脚功能:
IOCON 寄存器中的 FUNC 位可设为 GPIO (FUNC = 000) 或外设功能。如果将管脚配置为 GPIO 管脚,则 DIR 寄存器决定管脚是配置为输入还是输出。对于任何外设功能,会根据管脚的功能自动控制管脚方向。GPIO 的 DIR 寄存器此时对外设功能无效。
②、管脚模式:
IOCON 寄存器的 MODE 位允许为每个管脚使能或禁止片内上拉电阻。默认情况下,所有管脚的上拉电阻都被使能。
③、管脚驱动
对于每个正常驱动管脚,可以选择两种电流的输出驱动,即低电流模式和高电流模式。用户可以根据自己的实际需求进行选择。
④、开漏模式
所有数字 I/O 管脚都可为开漏模式。但是请注意,该模式并不是真正的开漏模式。输入上拉至不能超过VDD。
⑤、模式功能
I/O 管脚可以配置为模拟功能,作为模拟信号的输出和输入管脚,例如复用为AD口的时候,可以作为检测电压的模拟信号输入口。
⑤、电压转换速率模式
在作用AD转换口的时候,可以设置端口转换速率为快速模式或慢速模式,默认为快速模式。相对于快速模式,慢速模式转满时间会变长,随之电流功耗也增大,但时精度会更加精确,根据应用场景和自身需求,选择适合的模式。
1.2.2. GPIO控制寄存器总览
MCU所有的GPIO被分布到4个端口: PA,、PB、PC、PD。 每个端口都拥有自己独立的控制寄存器去管理 GPIO 的功能。下面的表列出了所有的寄存器以供参考。接下来会对每个寄存器做出详解。
1.2.3. GPIO屏蔽寄存器
GPIO屏蔽寄存器可屏蔽下列寄存器的读和写操作: PIN、OUT、SET、CLR和 NOT,相当于给这些寄存器上了个“锁”,只有当MASK寄存器相应的BIT位被置 0,被屏蔽的寄存器才能进行读和写操作。该寄存器上电后默认为0,即不进行GPIO屏蔽。当被置1启动屏蔽功能后,对处于输出功能的端口进行任何写操作都无效,不会改变其当前的输出状态。对处于输入功能的端口进行读操作,无论此时端口处于何种电平,都会返回0.
1.2.4. GPIO管脚值寄存器
对配置为数字功能的端口,对该寄存器进行读操作将返回管脚的当前逻辑值,不管该管脚是配置为输入还是输出,也不管它是配置为 GPIO 还是任何其它适用的备用数字功能,都可以直接进行读取。但也有例外,在以下两种情况中, PIN 寄存器中读出的管脚值无效:①、如果选择了管脚的模拟功能(如适用),则不能读取管脚状态,例如将管脚选作 ADC 输入会断开管脚的数字功能。②、该引脚被GPIO屏蔽寄存器MASK给屏蔽了。
1.2.5. GPIO管脚输出寄存器
在没有被GPIO屏蔽寄存器MASK屏蔽的情况下,向该寄存器写0或1将在相应端口管脚产生低电平或高电平。但是对于所有其他配置(输入、非GPIO功能),OUT 寄存器位的值对管脚输出电平无效。读取该寄存器将返回 GPIO 输出寄存器的内容,不管数字管脚配置和方向如何。
通过SET、CLR和NOT寄存器可以对OUT 寄存器执行写操作,允许按位对单个端口管脚进行置位、清除、取反操作。以此来控制OUT 寄存器的输出内容。
1.2.6. GPIO管脚输出置位寄存器
在没有被GPIO屏蔽寄存器MASK屏蔽,端口DIR为输出方向且端口功能为数字GPIO功能的情况下,写1会将相应端口管脚设为高电平。写0对GPIO输出电平无效。 另外该寄存器为只写寄存器,对其进行读操作是无效的。
1.2.7. GPIO管脚输出清除寄存器
在没有被GPIO屏蔽寄存器MASK屏蔽,端口DIR为输出方向且端口功能为数字GPIO功能的情况下,写1会将相应端口管脚设为低电平。写0对GPIO输出电平无效。 另外该寄存器为只写寄存器,对其进行读操作是无效的。
1.2.8. GPIO管脚输出取反寄存器
在没有被GPIO屏蔽寄存器MASK屏蔽,端口DIR为输出方向且端口功能为数字GPIO功能的情况下,写1会将相应端口的输出状态进行反转。写0对GPIO输出电平无效。 另外该寄存器为只写寄存器,对其进行读操作是无效的。
1.2.9. GPIO数据方向寄存器
在使用数字GPIO前,首先要确定的就是端口的数据方向。向该寄存器写1会将端口设置为输出模式,写0设置为输入模式。上电后端口默认为输入状态。
1.2.10. GPIO中断感应寄存器
在文章开头的介绍中,我们就说过MCU的每一个管脚是可以作为外部中断信号。因此会有一系列与之对应的中断管理寄存器,来对每个端口的中断进行管理。这就是接下来要介绍的中断寄存器。在使用端口中断前,需要明确需要触发中断的条件。向中断感应寄存器ISENSEx写入1,端口中断的触发方式为电平中断。向寄存器写入0,端口中断的触发方式为沿中断触发,具体需要什么样的沿来触发,这个还需要下面的中断配置寄存器来设置。
1.2.11. GPIO中断配置寄存器
紧接上文,在明确使用端口触发方式为沿中断触发后,我们通过中断配置寄存器IBEx来选择沿触发条件。向寄存器写入1,管脚的上升沿和下降沿都触发中断。向寄存器写入0,管脚只能由上升沿或下降沿中的一种来触发中断,具体由哪种来触发,需要下面的中断事件寄存器IEVx来决定。
1.2.12. GPIO中断事件寄存器
当中断配置寄存器IBEx值为0时,中断事件寄存器就决定着中断触发的条件,向寄存器写1,上升沿触发中断。写入0,则下降沿产生中断。
1.2.13. GPIO中断屏蔽寄存器
在实际的开发过程中,使用到中断功能的端口毕竟是少数,因此MCU在上电后就默认屏蔽了端口的中断功能。如果要开启端口的中断功能,向对应的寄存器位写1即可。
1.2.14. GPIO原始中断状态寄存器
该寄存器的位读出为高时反映了对应管脚上的原始(屏蔽之前)中断状态,表示在触发 IE 之前所有的要求都满足。位读出为0时表示对应的输入管脚还未启动中断。该寄存器为只读。
1.2.15. GPIO中断状态寄存器
该寄存器中的位读为高反映了输入触发中断的状态。读出为低则表示对应的输入管脚没有中断产生,或者中断被屏蔽。读出为高则表示对应的输入管脚有中断产生。该寄存器为只读。
1.2.16. GPIO中断清除寄存器
在中断发生后,程序会进入中断服务子程序。在中断服务子程序中处理完中断程序后,需要向中断清除寄存器CLRx写1来清除中断标志。
1.3. ME32F030R8T6的GPIO 库函数函数
为了便于开发者快速上手,敏矽微电子官方例程中提供了gpio.c文件,其中包含了设置端口方向、读取端口状态、配置端口中断等函数,供开发者直接使用。
1、设置GPIO为输入方向
void GPIO_ConfigPinsAsInput(PA_Type *port, uint16_t pins){port->DIR &= ~pins;return;}
2、设置GPIO位输出方向
void GPIO_ConfigPinsAsOutput(PA_Type *port, uint16_t pins){port->DIR|=pins;return;}
3、设置GPIO输出高
void GPIO_SetPin(PA_Type *port, uint16_t pin){port->SET |= pin;return;}
4、设置GPIO输出低
void GPIO_ResetPin (PA_Type *port, uint16_t pin){port->CLR |= pin;return;}
5、设置GPIO输出反转
void GPIO_InvertOutPin (PA_Type *port, uint16_t pin){port->NOT |= pin;return;}
6、读取GPIO某个引脚的输入状态
uint8_t GPIO_GetPinState(PA_Type *port, uint16_t pin){if (port->PIN & pin)return 1;elsereturn 0;}
7、读取GPIO整个引脚的输入状态
uint16_t GPIO_GetPortState(PA_Type *port){return (uint16_t)port->PIN;}
8、屏蔽GPIO引脚
void GPIO_SetPortMask(PA_Type *port, uint16_t pins){port->MASK |= pins;return;}
9、使能GPIO引脚
void GPIO_SetPortMask(PA_Type *port, uint16_t pins){port->MASK |= pins;return;}
10、配置GPIO引脚的中断功能
void GPIO_EnableInt(PA_Type *port, uint16_t pin, uint8_t triggeredge){port->IS &= ~pin;port->IE |= pin;switch(triggeredge){case RISE_EDGE:port->IBE &= ~pin;port->IEV |= pin;break;case FALL_EDGE:port->IBE &= ~pin;port->IEV &= ~pin;break;case BOTH_EDGE:port->IBE |= pin;break;default:break;}return;}
11、清除GPIO引脚的中断标志
void GPIO_ClrInt(PA_Type *port, uint16_t pins){port->IC =pins;return;}
1.4. ME32F030R8T6的GPIO 开发实例
介绍完GPIO的原理和函数,接下来就用最经典的LED小灯试验来进行示例。
本例使用敏矽微电子专门为ME32F030R8T6提供的库函数编写程序。
实例程序的代码如下:
int main(void){WDT->MOD=0; //关闭看门狗 PB_9_INIT(PB_9_GPIO); //PB9(LED)设置为GPIO功能GPIO_ConfigPinsAsOutput(PB, IO_PIN9); //PB9(LED)设置为输出方向while (1){GPIO_InvertOutPin(PB, IO_PIN9); //PB9(LED)端口输出反转SYS_DelaymS(500); //延时500ms}}
程序下载成功后,先在端口反转这句话处打上一个断点,然后全速运行程序(快捷键F5)。随后程序会停在断点处,
此时先暂停观察下LED小灯的状态,发现红圈标注的小灯并没有被点亮。
接下来单步运行程序,观察执行完端口反转这段程序后的状态。这时我们发现小灯已经被点亮了。
继续单步运行,当再次执行端口反转这段程序后,端口输出就会反转,接下来小灯就会熄灭。最后取消程序中的所有断点,让程序全速运行起来,小灯便开始周期性的闪烁。