本帖最后由 洋葱圈 于 2013-10-19 19:20 编辑
似乎学习一款单片机如何使用,首先学习它的IO引脚使用已经成了大家的共识。因为使用IO引脚来控制外围器件比如LED等亮灭是检测单片机是否正常运行的最简单标准。那么作为OSKinetis的编程实战,我们也首先从K60的GPIO模块开刀吧。 接下来的课程我们都将针对OSKinetis编程使用方法来讲,换个方式说就是教大家如何通过固件库来使用K60的各个模块,重点并非Kinetis单片机模块的讲解。当然为了使大家快速掌握模块的编程方法,我们会简单介绍该模块的原理等信息。如果读者想要深入学习Kinetis K60单片机的各个模块,可以阅读我们出的书籍《Cortex-M4自学笔记-基于Kinetis K60》
GPIO模块讲解 说起GPIO,其实就是通用输入输出的英文缩写(General purpose input/output),模块本身没有什么复杂的功能,就是字面意思——输入输出功能。只要在程序中设定某个引脚为逻辑1,那么相应的引脚就会变成高电平,反之为低电平。但是针对于Kinetis单片机来说,他的GPIO要比51单片机来说发杂了一点,这一点体现在寄存器上。
相信大部分朋友都接触过51单片机,这个单片机你在程序中置某一引脚为1,那么他就直接输出高电平,要读取某一引脚的逻辑电平状态,只要先置1再读取就可以了,简单方便。但是对于Kinetis来说,他的GPIO包含了比较多的寄存器,含有:端口数据寄存器、端口置逻辑1寄存器、端口清逻辑1寄存器、端口状态翻转寄存器、端口数据输入寄存器以及最重要的端口数据方向寄存器。使用Kinetis的GPIO引脚读取或输出数据之前,你要先配置他是输入还是输出功能,这就涉及到端口数据方向寄存器,接下来的输出还是输入操作,只要使用剩下的寄存器就可以了。
读者可以详细参考GPIO模块的技术文档,例如RUSH K60开发板采用的是K60DN系列的100MHz 144引脚的单片机,你需要参考文档K60P144M100SF2RM.pdf的“Chapter 54 General purpose input/output (GPIO)”一节,你会发现这节是所有章节中最简单的章节之一。
但实际上,Kinetis在物理上的每个引脚,大多是复用的,也就是说这个引脚既有GPIO,模块的输入输出功能也可能是ADC模块的模拟电压输入引脚、也可能是FTM模块的输入输出引脚。那么K60是如何区分不同引脚的复用功能是什么呢,下面一节我们会讲解。
Kinetis的引脚复用 还是要扯到51单片机,为什么51单片机(基础型号)没有引脚复用这个概念呢,说白了就是它的功能太简单了,只有GPIO功能……。但是Kinetis系列单片机的引脚大部分都是功能复用的,那么我们怎么控制他选择什么功能,答案就是PORT模块。
PORT字面上市端口的意思,然而在参考手册中全程是端口控制和中断模块(Port control and interrupts),还是字面上的意思很容易理解,PORT模块负责端口的复用和其他控制、以及端口的外部中断功能。PORT模块主要用到的寄存器是它的引脚控制寄存器以及中断状态标志寄存器。引脚控制寄存器主要负责引脚复用功能的选择、中断\DMA触发模式的配置、内部上下拉、是否为开漏等功能。中断状态标志寄存器主要用来判断到底是哪个引脚产生了外部中断。
说了半天,不知道大家是不是已经把GPIO和PORT这两个概念给混淆了呢?因为他们都含有端口、引脚、输入输出等含义。但是区别在于,GPIO仅代表普通IO口模块,他负责输出或输入逻辑电平;而PORT是掌管单片机所有外部引脚功能和配置的模块。举个简单的例子,单片机的所有功能引脚相当于一个公司中的所有职工,GPIO相当于某一个部门比如采购部吧,PORT相当于公司的人事部,那么人事部(PORT)就要运行自己的权利(功能配置),把不同的职工(功能引脚)划分到不同的部门(比如GPIO、ADC等)。
GPIO固件库编程思路 前面提及的所有概念都是单片机相关的,但也仅仅是概念,我们并没有说的特别细,因为那些在参考手册里已经写的很详细了。下面我们将讲讲如何通过OSKinetis V3固件库来使用GPIO模块了。
前面我们知道了GPIO具有输入、输出逻辑电平的功能,配合PORT模块的使用,还可以作为外部中断使用。所以根据功能来推理编程思路,就十分简单了: 输入功能:初始化GPIO相关引脚为输入—>读取引脚电平状态 输出功能:初始化GPIO相关引脚为输出—>输出电平 外部中断功能:初始化GPIO相关引脚为输入并配置中断—>中断函数
具体到固件库的编程上,顺序是一样的,首先我们要调用GPIO模块的初始化库函数,对GPIO进行初始化,然后就可以根据不同功能进行使用了。
GPIO例程讲解 下面我们通过两个例程来理解GPIO固件库的编程方法,分别是例程包中的“02-(GPIO)LPLD_LedLight”和“03-(GPIOint)LPLD_ButtonPress”工程。
LED流水灯-LPLD_LedLight 该例程中通过CARD宏定义区分了K60 Card和K60 Nano两款K60核心板,这里我们用默认的K60 Card核心板做讲解。 程序首先调用函数init_gpio()对GPIO模块进行初始化,其实现代码如下:
01 | GPIO_InitTypeDef gpio_init_struct; | 02 | void init_gpio() | 03 | { | 04 | gpio_init_struct.GPIO_PTx = PTA; | 05 | gpio_init_struct.GPIO_Pins = GPIO_Pin4|GPIO_Pin6|GPIO_Pin8|GPIO_Pin10; | 06 | gpio_init_struct.GPIO_Dir = DIR_OUTPUT; | 07 | gpio_init_struct.GPIO_Output = OUTPUT_H; | 08 | gpio_init_struct.GPIO_PinControl = IRQC_DIS; | 09 | LPLD_GPIO_Init(gpio_init_struct); | 10 | } |
Line 1:声明一个GPIO初始化结构体变量,用于配置GPIO的各项参数。该结构体内部含有多个成员变量,你完全可以通过浏览这些成员变量的注释来理解GPIO都有哪些功能。
Line 4~8:行开始配置这个结构体变量的各个成员变量。我们之所以推荐各位使用IAR 6.4或以上的版本,就是因为新版的IAR加入了对成员变量自动补全的功能,非常适合快速编写代码,例如你先打了“gpio_init_struct”这个结构体,再打一个点“.”后,开发环境会列出所有该结构体的成员变量,你只要选择其一即可。又由于我们对成员变量书写的格式非常规范,你只要光看名字就基本知道这个变量是干什么用的了,在下面的开发中你会充分体现到这一点。
Line 4:配置结构体的GPIO_PTx变量,选择使用PTD组的GPIO引脚。
Line 5:配置结构体的GPIO_Pins变量,选择使用编号为D8~D15的引脚。
Line 6:配置结构体的GPIO_Dir变量,设置PTD的相关引脚方向为输出。
Line 7:配置结构体的GPIO_Output变量,设置PTD的相关引脚初始化输出为高电平。
Line 8:配置结构体的GPIO_PinControl变量,配置端口的控制模式为禁止中断。
Line 9:调用GPIO初始化的库函数,并将初始化结构体变量传入其中,完成初始化。
注意,这里我们定义的GPIO初始化结构体为全局变量,全局结构体的成员变量在你不初始化它的时候,它默认是0的,因此有些成员变量我们不用初始化,使用默认值就行。但是如果你的结构体变量为局部变量,即定义在函数内部的变量,那么它的所有成员变量的默认值是随机的,因此需要对所有成员变量进行赋值,如果是可以不必须初始化的变量,那么赋NULL就可以了。如果不对局部结构体变量的全部成员变量赋值,那么会导致意想不到的结果。 对GPIO初始化完毕后,PTD的第8~15引脚就已经输出高电平,此时K60 Card的8个LED灯的负极连接的是这8个引脚,因此所有LED均为熄灭状态。
接下来请看main函数中while循环内的程序代码:
01 | i=8; | 02 | while(1) | 03 | { | 04 | //D1至D8依次触发点亮、熄灭 | 05 | LPLD_GPIO_Toggle_b(PTD, i); | 06 | i+=1; | 07 | if(i==16) | 08 | i=8; | 09 | delay(); | 10 | } |
Line 1:在进入while之前,先对变量i赋初值8,变量i负责记录8个PTD的引脚哪个需要进行电平翻转。
Line 5:调用LPLD_GPIO_Toggle_b()单个引脚翻转库函数对标号为i的PTD引脚进行电平翻转操作。如果是第1次运行到这里,那么i=8,即对PTD8引脚的电平做逻辑翻转操作。之前对所有已经的初始化逻辑电平为高,那么运行之后PTD8的引脚就变为了低电平,此时链接PTD8的LED灯就被点亮了。
Line 6~8:对i进行自加操作,目的是依次翻转8~15号的引脚,当i加到16时调回到8。
Line 9:延时一小段时间。
通过这个例程,相信大家都对GPIO的库函数使用有了初步的认识,至此我们没有涉及任何寄存器的编写。今后也希望大家形成一个良好的学习模式,就是通过学习理不同解模块的功能和原理,来使用固件库进行编程。这就相当于只要学会怎样开车,而无需学会怎样造车一样。怎么样,是不是相当easy呢!当然没有一个库或者函数是万能,但是希望大家能够以学会使用现在的库为基础,将来自己写出更好的库函数为目标!
(帖子太长,一个楼发布下,下面还有2层)
|