MM32F0010 GPIO的配置
前言:
MM32F0010是基于ARM Cortex M0核的32位微控制器(MCU)即32位的单片机,使用库函数开发,每一个片上外设都有与之对应的外设xx.c和xx.h库函数,例如:hal_rcc.c和hal_rcc.h,hal_gpio.c和hal_gpio.h等,针对外设初的初始化工程师即可参考外设对应的库函数外xx.c和外设xx.h文件。不知道大家每接触一款新的ARM Cortex核心MCU是如何快速入门的?笔者简单说一下个人的学习方法,当笔者每接触一款不同厂家的ARM Cortex M0/M3/M4核心的MCU时,笔者是这样快速入门的:
学习方法简要说明:
(1)访问厂家的官网下载ARM Cortex 对应型号和版本的MCU的DS数据手册(手册一般描述了GPIO管脚定义、GPIO管脚复用功能、封装信息、电气特性、各个外设工作的电气特性、工作环境等);
下载ARM Cortex对应型号和版本的MCU的UM用户手册(手册一般对MCU的每一个片上外设资源的各个寄存器及其位定义做了详细的描述)单片机工程师做编程开发时可以参考以上DS和UM手册,有些厂家还会提供该MCU的库函数编程手册指南等。
(2)从官网下载对应MCU型号版本的Demo例程,Demo例程一般包含ARM Cortex 核心片上外设各个外设的Demo的使用(包含各个外设功能的初始化和实现简单功能的演示实例具有一定参考价值)。
(3)从官网下载对应MCU型号版本系列的Pack包,简单的说Pack包里面定义了该系列MCU型号的选型,开发时需要选择对应MCU型号,笔者习惯使用MDK Keil开发环境,MDK Keil从5.0以上版本官方就对各个厂家MCU设备型号使用Pack包形式进行管理。
(4)关于程序的仿真烧录工具,笔者习惯使用JLINK进行ARM Cotex核MCU的仿真烧录,很多同行会问各个厂家的ARM Cotex核心的MCU都支持JLINK仿真烧录程序吗?答案是肯定的,为什么呢?ARM Cortex核已经形成了一个世界标准,并且研发JLINK的SEGGER公司和ARM以及各个芯片半导体公司都有深入合作已经形成了标准的生态链,各家芯片设计公司只要设计的芯片使用的是ARM Cortex核都可以支持JLINK仿真烧录的,笔者目前学习单片机开发使用的JLINK版本为V9版本,因此大家可以放心的使用JLINK作为开发仿真、调试和烧录程序。
(5)搭建开发环境,笔者针对ARM Cotex核的MCU单片机开发习惯使用MDK Keil开发环境,安装好MDK Keil开发环境,然后安装厂家提供的Pack包,再配置一个JLINK v9仿真调试器,加上官网下载的DS和UM手册或编程手册以及官网的MCU例程就可以开始进入入门开发了。
一、本篇实例:MM32F0010 GPIO的配置驱动LED灯
要点提示:在实现本实例之前呢,笔者已经搭建好了基于MDK Keil5.30的MM32F0010的开发环境,请想学习和了解MM32F0010开发的读者或同行事先搭建好MDK Keil开发环境。
注意事项:基于ARM Cotex M0/M3/M4等核心的MCU都有共同的特点即:系统(System Clock)时钟和片上外设时钟是独立配置的,系统时钟配置一般在加载MCU启动文件xx.s时调用了System_xx.c文件里的SystemInit函数,用户只需在System_xx.c文件里开启对应的宏定义时钟项即可,然后就是外设时钟了,ARM Cortex 核的MCU每一个片上外设都有各自独立的时钟配置使能位,因此用户对片上外初始化外设时,首先要开启使能外设时钟,再配置外设的其它成员参数(注:基于ARM Cortex核心的MCU各个外设参数的配置都是以结构体成员、联合体、枚举参数进行配置的因此用户要有一定的C语言基础)。
1、MM32F0010配置系统时钟:
启动官网下载的LED工程例程,Keil左边栏Project工程目录下单击MM32Series前面的加号“+”展开工程目录,单击"STARTUP"文件夹的“+”号展开文件目录,可以看到2个文件分别是system_mm32f0010.c配置时钟文件和startup_mm32f0010_keil.s启动文件(基于keil环境)如下图1所示:用户在system_mm32f0010.c里开启宏定义时钟,即去掉双斜杠用于配置系统时钟(如果需要使用外部HSE 8M时钟去掉“#define SYSCLK_FREQ_HSE HSE_VALUE”前面的双斜杠,使用双斜杆屏蔽“#define SYSCLK_HSI_48MHz 48000000”即可)
图1
#define SYSCLK_HSI_48MHz 48000000时钟即可,程序运行启动文件时会对该宏定义的系统时钟进行初始化
2、MM32F0010的GPIO初始化:
不知道大家平时编写外设驱动代码是什么样的风格特点,笔者有自己的一套编程风格,本实例是使用MM32F0010的GPIO外设驱动LED灯,低电平点亮,那么笔者针对该驱动会编写一个外设xx.c和外设xx.h文件即:bsp_gpio_led.c和bsp_gpio_led.h文件。
初始化GPIO的步骤:
1>使能GPIOx端口时钟(这点很重要不要忘了)
2>定义GPIO结构体变量引用GPIO结构体成员使用库函数自定义值初始化GPIO 结构体成员参数
3>配置GPIO结构体成员管脚定义;
4>配置GPIO结构体成员管脚的输出速度;
5>配置GPIO结构体成员管脚为输入或输出模式;
6>根据给定的结构体成员变量配置的参数初始化GPIOx端口和引脚参数;
注意:关于GPIO管脚配置的工作模式有以下8种模式:
输入模式:
<1>GPIO_Mode_AIN 模拟输入输入模拟量
<2>GPIO_Mode_FLOATING 浮空输入(一般会在外部接上拉或下拉电阻,否则电平状态为不确定状态)
<3>GPIO_Mode_IPD 带内部下拉电阻的下拉输入(弱下拉)
<4>GPIO_Mode_IPU 带内部上拉电阻的上拉输入(弱上拉)
输出模式:
<5>GPIO_Mode_Out_OD 开漏输出(如需启动外部电路需外接适当的上拉或下拉电阻)
<6>GPIO_Mode_Out_PP 推挽输出(最大灌电流20mA)
<7>GPIO_Mode_AF_OD 复用功能开漏输出(如果IO有配置成复用功能需在外部接适当的上拉或下拉电阻)
<8>GPIO_Mode_AF_PP 复用功能推挽输出(如果IO有配置成复用功能,可使用该模式,最大灌电流20mA)
本实例是要用到普通的GPIO功能输出电平驱动LED1,因此无需复用,直接配置为推挽输出即可。
(1)在bsp_gpio_led.c文件里编辑MM32F0010的GPIO外设驱动LED灯的初始化代码函数如下所示:
1 #include "bsp_gpio_led.h" 2 3 /** 4 *********************************************************************************************************************** 5 *@函数名称:void Bsp_LED_Init(void) 6 *@功能描述:LED Init 7 *@输入参数:None 8 *@返回参数:None 9 *********************************************************************************************************************** 10 */ 11 void Bsp_LED_Init(void) 12 { 13 //定义GPIO 初始化结构体成员变量 14 GPIO_InitTypeDef GPIO_InitStructure; 15 //使能GPIO外设GPIOA端口时钟 16 RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE); 17 //使用库函数自定义值初始化GPIO 结构体成员参数 18 GPIO_StructInit(&GPIO_InitStructure); 19 //GPIOA PA7管脚配置 20 GPIO_InitStructure.GPIO_Pin = LED1_PIN; 21 //GPIOA PA7输出速度 22 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 23 //GPIOA PA7推挽输出 24 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 25 //根据以上配置的的参数初始化GPIOA PA7成员参数 26 GPIO_Init(LED1_PORT, &GPIO_InitStructure); 27 28 //初始化熄灭LED1 29 LED1_OFF(); 30 }
(2)在bsp_gpio_led.h文件里编辑MM32F0010的GPIO外设驱动LED灯的头文件代码如下所示,编辑头文件宏定义的好处是方便代码维护,当需要更换IO只需修改宏定义即可;
1 #ifndef __BSP_GPIO_LED_H__ 2 #define __BSP_GPIO_LED_H__ 3 4 #include "mm32_device.h" 5 #include "hal_conf.h" 6 7 //GPIOA LED1端口宏定义 8 #define LED1_PORT GPIOA 9 //GPIOA LED1管脚宏定义 10 #define LED1_PIN GPIO_Pin_7 11 12 //GPIOA PA7输出低电平 LED1 ON 13 #define LED1_ON() GPIO_ResetBits(LED1_PORT,LED1_PIN) 14 //GPIOA PA7输出高电平 LED1 OFF 15 #define LED1_OFF() GPIO_SetBits(LED1_PORT,LED1_PIN) 16 //根据读取的GPIOA PA7电平状态,按当前状态翻转 17 #define LED1_TOGGLE() (GPIO_ReadOutputDataBit(LED1_PORT,LED1_PIN))?(GPIO_ResetBits(LED1_PORT,LED1_PIN)):(GPIO_SetBits(LED1_PORT,LED1_PIN)) 18 19 #endif
3、MM32F0010 GPIOA PA7驱动LED:
(1)在main.c文件里包含bsp_gpio_led.h头文件,然后包含官方写好的“delay.h”头文件,调用bsp_gpio_led.h头文件里声明的void Bsp_LED_Init(void)函数初始化GPIOA PA7端口引脚,初始化为驱动LED1,main函数初始化时同时也调用“delay.h”头文件声明的SysTick函数初始化即void DELAY_Init(void);最后在while(1)主循环里调用bsp_gpio_led.h头文件里宏定义的LED1状态翻转代码并调用“delay.h”头文件声明的SysTick毫秒级别延时函数,实现LED1每隔一秒LED1灯状态翻转一次,以此循环,main函数代码如下所示:
1 #include "delay.h" 2 #include "bsp_gpio_led.h" 3 4 /** 5 *********************************************************************************************************************** 6 *@函数名称:int main(void) 7 *@功能描述:main函数,主函数入口代码在这里开始执行 8 *@输入参数:None 9 *@返回参数:int:0(和编译器有关) 10 *********************************************************************************************************************** 11 */ 12 int main(void) 13 { 14 //SysTick Init 15 DELAY_Init(); 16 //LED Init 17 Bsp_LED_Init(); 18 19 while(1) 20 { 21 //LED1 Toggle 22 LED1_TOGGLE(); 23 //Delay 1000ms 24 DELAY_Ms(1000); 25 } 26 }
(2)在MDK Keil IDE集成开发环境中编译代码,GPIOA PA7硬件连接LED1,可用MM32F0010开发板或核心板(PA7连接到LED负极)或用户自己设计的板子即可,MCU支持DC2~5.5V宽电压供电,一般选择VDD 3.3V或VDD5.0V给MCU供电,本实例使用VDD3.3V给MCU供电,最后使用JLINK v9把编译好的代码烧录到板子上,可看到板子实物LED1灯每隔1000ms即1s状态翻转一次,看到LED1周期2s点亮和熄灭一次,以此循环。
结束语:
有些读者可能会问我怎么知道GPIOA PA7是怎么初始化的呢?问的好,其实在博客开篇笔者已经提到关于ARM Cortex 核的MCU每一个片上外设都有与之对应的外设xx.c和xx.h库函数,例如:hal_rcc.c和hal_rcc.h,hal_gpio.c和hal_gpio.h等;
(1)头文件一般声明了可供main函数初始化调用的外设初始化函数以及参数,以及外设结构体成员,外设枚举参数等,C文件一般描述了使用该外设的功能配置可供初始化的函数;
(2)比如hal_rcc.c描述了系统时钟和总线时钟以及各个外设时钟的配置,hal_rcc.h描述了可被主函数调用的外设初始化的时钟参数等,hal_gpio.c和hal_gpio.h描述了GPIO口初始化函数,各端口、管脚宏定义,通过GPIO结构体成员、GPIO的枚举参数,以及有些以函数声明的形式给出,这样我们使用每个外设时一方面可参考官网例程,另一方面参考库函数外设的xx.c和外设xx.h文件结合UM手册寄存器的描述以及DS手册IO口复用,即可去初始化每一个需要使用的外设,因此掌握良好的方法规律对MCU外设底层初始化配置非常重要,尤其是在没有专门的库函数编程手册的情况下,该方法是最优的方法之一。