打印

LPC1343学习笔记(连载中)

[复制链接]
25362|71
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
LPC300|  楼主 | 2010-6-28 22:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
有幸拿到了LPC1343评估板,实在是一件意外而激励人心的事情。为感谢NXP,特将学习过程与大家共同分享。也算是为NXPLPC系列的推广,尽一份绵薄之力吧~



如今基于ARM Cortex-M3核心的微控制器的宣传是铺天盖地,已经越来越引起大家的注意,并且也以极低的功耗、优秀的低成本以及突出的性能,向传统低端单片机诸如51AVR等单片机发起了强有力的挑战。NXP LPC1343正是NXP公司LPC13xx系列中基于Cortex-M3内核的性能最高的一款,希望能在众多Cortex-M3器件中占据一席之地。




这个是为此而写的第一篇应用**,见解粗浅,经不起推敲,希望大家批评,希望能成系列。已经面世的Cortex-M3器件比如ST公司的STM32Luminary Micro公司Stellaris,它们多数是在MDK或者是IAR开发环境进行开发的。MDK或者是IAR已经广泛地被广大电子爱好者所使用,不过这次学习LPC系列,却不得不采用一个新的开发环境了,那就是NXP推出的LPCXpresso。(其实MDK和IAR也是支持LPC系列的).




今天,说说LPCXpresso for LPC1343:第一个工程的建立。

坛子里已经有大侠提供了关于LPCXpresso IDE的下载,安装以及激活的方法。英文不好的朋友也许会稍显吃力,但是相信不会难住各位。感谢前辈们的指导,这里就将这部分省略。

我自不量力,将LPCXpresso的用户手册翻译了一下,大家可以作为参考。具体请看链接:

http://bbs.**/thread-102972-1-1.html


相关帖子

沙发
LPC300|  楼主 | 2010-6-28 22:35 | 只看该作者
1、
首先在论坛里找到文件并下载:lpc1343.examples.zip,这个文件里包含了NXP公司提供给我们的一系列开发板例子程序以及最为重要的“CMSISv1p30_LPC13xx”文件夹,这个文件夹里面包含了LPC1343器件的一些最底层函数诸如启动函数和LPC1343的诸多外设的定义。




2、
打开LPCXpresso,弹出以下对话框,填上你建立工程的路径,默认放在C盘“我的文档”里:





1.jpg (33.44 KB)
2010-5-2 23:21






在此提醒大家注意:不要使用带有中文的路径!笔者曾使用中文路径,编译的时候总是也会出现错误。(我的系统是Windows 7 Ultimate 英文版)

点击OK后启动LPCXpresso,看到如下界面:





2.jpg (179.84 KB)
2010-5-2 23:21

使用特权

评论回复
板凳
LPC300|  楼主 | 2010-6-28 22:35 | 只看该作者
3、
接着首先要导入LPC1343的初始化文件,找到第一步下载的lpc1343.examples.zip,复制粘贴到:你安装LPCXpresso的盘符\lpcxpresso_3.3\Examples\LPC3000\LPC31xx目录下,接着点击左边project区下方的”Import Example project”







3.jpg (29.81 KB)
2010-5-2 23:21



出现如下对话框:

4.jpg (62.58 KB)
2010-5-2 23:21



点击Browse加载你刚才放入\lpcxpresso_3.3\Examples\LPC3000\LPC31xx目录中的lpc1343.examples.zip(这里可以说明刚才为什么说不要解压,这里加载的是压缩文件,解压了就无法加载了):


使用特权

评论回复
地板
LPC300|  楼主 | 2010-6-28 22:35 | 只看该作者
这些就是NXP提供给我们的所有例子程序以及CMSIS文件,我们在此只加载CMSIS文件,所以其他都去掉后,点击Finish。完成CMSIS文件的导入,注意它是一个文件夹,不是一个单一的文件。看到以下结果(双击可以打开CMSIS文件夹,查看里面的内容,但是如果你不是非常熟悉ARM核心的器件,基本上看不懂那些东西,事实上,在入门阶段也不需要懂):


8.jpg (10.95 KB)
2010-5-2 23:21






4、
加载完毕启动文件之后,我们继续建立我们的第一个工程,接下来建立一个新的Project

点击Project区下方的Project and File wizards,依次:MCU project winzards->NXP LPC1300 project templates->Create NXP C Project


9.jpg (27.61 KB)
2010-5-2 23:21






弹出以下对话框:


10.jpg (27.78 KB)
2010-5-2 23:21

使用特权

评论回复
5
LPC300|  楼主 | 2010-6-28 22:36 | 只看该作者
填入Project Name->Next


15.jpg (28.78 KB)
2010-5-2 23:31









勾选Use CMSIS->Next

11.jpg (38.59 KB)
2010-5-2 23:31

使用特权

评论回复
6
LPC300|  楼主 | 2010-6-28 22:36 | 只看该作者
可以在此填上你的名字,Next

12.jpg (33.15 KB)
2010-5-2 23:31

y



[ 本帖最后由 tiankai001 于 2010-6-21 14:58 编辑 ] 附件 EEWORLD提示:为减少服务器的压力,请尽量不要使用迅雷等下载软件。 7.jpg (57.03 KB) 2010-5-2 23:21

11.jpg (38.59 KB) 2010-5-2 23:21

使用特权

评论回复
7
LPC300|  楼主 | 2010-6-28 22:36 | 只看该作者
Dubug是“调试”的意思,Release是“发布”的意思,无关紧要,Next

13.jpg (52.74 KB)
2010-5-2 23:26






当然选择LPC1343了,Finish


14.jpg (65.35 KB)
2010-5-2 23:26






如此就完成了一个新工程的建立了:


16.jpg (16.29 KB)
2010-5-2 23:26

使用特权

评论回复
8
LPC300|  楼主 | 2010-6-28 22:36 | 只看该作者
如此第一个工程的框架已经建立起来了,我们可以看到LPCXpresso已经自动生成了一个main.c文件,而且是置于src文件夹内。接下来是添加工程的代码以及相关文件,笔者在这里以添加一个最简单的点亮LED的程序为例。这时把lpc1343.examples.zip解压,因为我们需要里面的某些头文件已经.c文件。(感觉NXP应该提供一个固件库,把所有提供的函数集合起来并做个说明),解压之后找到……\lpc1343.examples\gpio\src中的gpio.cgpio.h这两个文件。接下来就比较特别了,以往我们熟悉的keil可以在工程里面点击右键添加文件,但是LPCXpresso却不是这样操作的,LPCXpresso添加文件如下:

1、 1、复制gpio.hgpio.c

2、 2、LPCXpresso工程区的src文件夹上右键选择paste



17.jpg (80.44 KB)
2010-5-2 23:26






3、
添加成功后看到如下结果:



18.jpg (18.1 KB)
2010-5-2 23:26

使用特权

评论回复
9
LPC300|  楼主 | 2010-6-28 22:37 | 只看该作者
然后双击main.c文件,在编辑区打开它,清空里面所有内容,并添加以下代码:



#include "LPC13xx.h"

#include "gpio.h"



int main(void)

{


GPIOInit();



GPIOSetDir(PORT0,7,1);



GPIOIntEnable(PORT0,7);



while(1)



{



LPC_GPIO0->DATA|=0x0080;



}


}

依次点击:Project-Build All,软件开始编译,速度还算可以……

编译完成之后,我们就要把编译生成的文件烧写进主控器件里面,也就是LPC1343,确保硬件连接好之后,点击工具栏里一个绿色的昆虫模样的图标:



19.jpg (54.04 KB)
2010-5-2 23:26






稍后就可以进入调试界面,同时也就完成软件的烧写了,在这点上,跟我们以往熟悉的单片机烧写过程就不太一样,至少笔者使用过的M3器件都是用Debug的办法来烧写的。下为调试界面:



20.jpg (156.54 KB)
2010-5-2 23:26






按下F8或者点击上图红圈处的Resume图标,就可以运行程序,此时第一个LED点亮,下一篇,讲讲这个LED是怎么点亮的。


[ 本帖最后由 tiankai001 于 2010-5-2 23:32 编辑 ] 附件 EEWORLD提示:为减少服务器的压力,请尽量不要使用迅雷等下载软件。 12.jpg (33.15 KB) 2010-5-2 23:26

使用特权

评论回复
10
LPC300|  楼主 | 2010-6-28 22:37 | 只看该作者

使用特权

评论回复
11
LPC300|  楼主 | 2010-6-28 22:37 | 只看该作者
上一篇讲述了LPCXpresso For LPC1343的第一个工程的建立以及程序的运行,点亮了LPC1343评估板上唯一的一个LED(寒酸了点儿)。本文来讲讲这个LED是怎么被点亮的,也就是我们操控LPC1343的IO口的方法。







从NXP提供的《lpcxpresso.getting.started》文档中可以查看到,LPC1343上的LED接在了PORT0.7上,另一端通过一个限流电阻接地。从《LPC13xx preliminary user manual》可以查到LPC13xx系列器件IO口可以输出最高为20ma的电流,也就是说,I/O口可以直接驱动LED。所以点亮这颗LED的思路很简单,在PORT0.7上输出高电平。下面是上一篇**的程序,很短:



#include "LPC13xx.h"



#include "gpio.h"



int main(void)



{



       GPIOInit();



       GPIOSetDir(PORT0,7,1);



       GPIOIntEnable(PORT0,7);//这句是设置IO的中断功能,其实是在此不需要的



       while(1)



       {



              LPC_GPIO0->DATA|=0x0080;



       }



}



希望深入浅出的把它呈现给大家。



我们从主函数看起,首先是函数调用GPIOInit();,很显然在gpio.h中声明了这个函数,我们可以在gpio.h查看到该函数的声明以及在gpio.c文件查看到该函数的原型,原型如下:



/*****************************************************************************



** Function name:              GPIOInit



**



** Descriptions:           Initialize GPIO, install the GPIO interrupt handler



**                                                      



**



** parameters:                    None



** Returned value:              true or false, return false if the VIC table



**                                        is full and GPIO interrupt handler can be



**                                        installed.



**



*****************************************************************************/



void GPIOInit( void )



{



  /* Enable AHB clock to the GPIO domain. */



  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);







#ifdef __JTAG_DISABLED  



  LPC_IOCON->JTAG_TDO_PIO1_1  &= ~0x07;



  LPC_IOCON->JTAG_TDO_PIO1_1  |= 0x01;



#endif







  /* Set up NVIC when I/O pins are configured as external interrupts. */



  NVIC_EnableIRQ(EINT0_IRQn);



  NVIC_EnableIRQ(EINT1_IRQn);



  NVIC_EnableIRQ(EINT2_IRQn);



  NVIC_EnableIRQ(EINT3_IRQn);



  return;



}



在该函数头的注释中,我们可以了解到该函数的作用是“Initialize GPIO, install the GPIO interrupt handler”,即“初始化GPIO(General Purpose I/O,即通用IO),并且设置GPIO的中断功能。”函数内部的第一句:



LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6); /* Enable AHB clock to the GPIO domain. */该句的作用是使能GPIO的时钟,M3器件为了达到低功耗,内部的各个设备的时钟都是可以配置的,在不需要使用的时候,可以将其对应的时钟关闭,这样就节省了功耗。



接下来:



#ifdef __JTAG_DISABLED  



  LPC_IOCON->JTAG_TDO_PIO1_1  &= ~0x07;



  LPC_IOCON->JTAG_TDO_PIO1_1  |= 0x01;



#endif



这是个预编译内容,有关JTAG仿真器的内容,这里就先略过吧。



最后是:



  /* Set up NVIC when I/O pins are configured as external interrupts. */



  NVIC_EnableIRQ(EINT0_IRQn);



  NVIC_EnableIRQ(EINT1_IRQn);



  NVIC_EnableIRQ(EINT2_IRQn);



  NVIC_EnableIRQ(EINT3_IRQn);



这个注释说的很明白,当IO配置为外部中断方式的时候设置NVIC,NVIC是M3器件的中断控制器。



以上是第一个函数GPIOInit();的内部解析,只是一些初始化的东西,搬过来用就行了。来看第二个GPIOSetDir(PORT0,7,1);原型在gpio.h找到:



static __INLINE void GPIOSetDir( uint32_t portNum, uint32_t bitPosi, uint32_t dir )



{



       if(dir)



              LPC_GPIO[portNum]->DIR |= 1<<bitPosi;



       else



              LPC_GPIO[portNum]->DIR &= ~(1<<bitPosi);



}



可以看出,该函数的3个参数分别是“portNum/端口号”、“bitPosi/位”还有“dir/方向”,那么该函数的作用非常明显了,设置某端口的某位IO的方向,即作为输出口还是输入口。







函数内部还是很简单,如果dir为1则将LPC_GPIO[portNum]-结构体中的Dir对应的IO位置1,否则置0。这个是C语言里面标准的位操作,大家都很熟悉了。也许有人会疑问,这个结构体在哪定义的呢?那么我们来找一下吧,在这个程序里,包含两个头文件。我们搜索一下gpio.h,没有找到。那应该在"LPC13xx.h"里,经过搜索后,找到了这个结构体的定义:







typedef struct



{



  union {



    __IO uint32_t MASKED_ACCESS[4096];



    struct {



         uint32_t RESERVED0[4095];



    __IO uint32_t DATA;



    };



  };



       uint32_t RESERVED1[4096];



  __IO uint32_t DIR;



  __IO uint32_t IS;



  __IO uint32_t IBE;



  __IO uint32_t IEV;



  __IO uint32_t IE;



  __IO uint32_t RIS;



  __IO uint32_t MIS;



  __IO uint32_t IC;



} LPC_GPIO_TypeDef;

使用特权

评论回复
12
LPC300|  楼主 | 2010-6-28 22:37 | 只看该作者
同时在gpio.h里面还可以找到如下定义



static LPC_GPIO_TypeDef (* const LPC_GPIO[4]) = { LPC_GPIO0, LPC_GPIO1, LPC_GPIO2, LPC_GPIO3 };



这样,整个GPIO的定义就比较容易理解了?这种结构体定义+结构体指针的形式应该会在我们使用M3器件进行开发的时候会大量大量的使用。



LPC_GPIO[portNum]->DIR指向的是LPC1343中GPIO的方向寄存器了,当对应位置1表示该IO为输出口,反之则为输入口。所以GPIOSetDir(PORT0,7,1);的意思是,将PORT0口的第7位IO设置为输出口。以上是第二个函数的分析。



最后是while(1)大循环,对IO口写值,这个大家肯定都了解,就不多说了。











讲讲这两天使用LPC1343和LPCXpresso的感受:



1、 目前就我了解到的信息来说,LPC13xx系列的资料还比较少,官方出的也只有一个user.manual.lpc13xx和附带的例程包,里面提供的调用函数也没有一个系统的说明。不知道是我没找到,还是NXP还在逐步完整这些资料呢。不知道NXP希望开发者以一种什么样的方式去使用LCP13xx器件。(比如STM32和LM都事无巨细地提供了非常完整的函数固件库)。







2、 LPCXpresso IDE,对大家来说都是个新鲜玩意。首先软件的界面做的很漂亮,功能齐全的前提下保持了清爽的布局,反面教材就是IAR,功能众多图标众多设置复杂,新手难以上手。而且我发现LPCXpresso IDE居然可以设置Code Style,哈哈,我很喜欢里面的BSD和GNU风格,赞一个。同时可以LPCXpresso IDE随意拖动的各个控制面板可以最小化,只在需要的时候弹出,这个对计算机屏幕比较小的人而言是一个非常非常加分的细节。







3、 但是还是发现LPCXpresso IDE的一些问题,比如常在仿真的时候会提示设备没有连接好,需要重新拔插才行;



比如有的时候编译完成,仿真的图标是灰色的无法点击,必须要再编译一次才行;



比如不能使用带有中文的路径;



比如退出的时候明明勾选了Always exit without prompt,下一次退出时还会提示……这些相信有一部分是我本身的操作系统或者是硬件的问题,但是相信没有一个软件没有Bug吧~







下一篇,我们来看如何利用系统时钟产生1S的准确定时。

使用特权

评论回复
13
LPC300|  楼主 | 2010-6-28 22:38 | 只看该作者
这课我们来看看如何用LPC1343内部的Systick产生1S的定时。

各位以前肯定都用8位单片机做过软件延时的流水灯,几乎无一例外的是两个for循环。当然在LPC1343这个平台上肯定也是可以的,不过它是一个32位的处理器,利用for循环来达到大概1S的延时,调试起来要花费相当多的时间和精力,况且这么高的主频(最大72MHz)用来空转,实在也是一种不小的浪费。如何达到延时呢?我们可以利用其内部自带的Systick来做到这点。看看什么是Systick
The System Tick Timer is an integral part of the Cortex-M3. The System Tick Timer is

intended to generate a fixed 10 millisecond interrupt for use by an operating system or

other system management software.

我们可以从中了解到以下信息:

1、
Systick,全称为“System Tick Timer”,系统滴答定时器;

2、
Systick可以产生10ms间隔的中断(笔者按:其实未必就只能是10ms);

3、
Systick在操作系统或系统管理软件中用来提供时间基准;

大家所用的计算机,右下角都有一个时间,这个时间就是内部CPUSystick产生的(可不要认为是DS1302这类的时钟芯片哦),即便你的电脑死机,断电甚至是系统崩溃,这个时间都保持与现实时间的一致性。所以它还有一个特点,就是它是独立于Cortex-M3其他部件的一个时钟,可以利用独立电源供电,可以在整体系统出现故障的情况下仍然正常工作,正因如此,它也往往用来作为记录系统各种故障和错误的时间基准。下面了解一下它的内部结构:






1.jpg (26.93 KB)
2010-5-4 21:28





可以看到它的核心是一个24-bit down counter,从它的寄存器的定义可以比较直观的看到它的工作方式:

System Timer Control and status register(系统定时控制和状态寄存器):



2.jpg (102.45 KB)
2010-5-4 21:28






该寄存器的第0ENABLE,当该位为1是,24-bit down counter启动,反之关闭;

该寄存器的第1TICKINTSystick中断使能位,为1时开启中断,反之屏蔽;

该寄存器的第2CLKSOURCE,时钟源选择,为1时选择系统时钟源,反之选择经过系统滴答时钟分频器分频后的时钟作为时钟源,值得注意的是后者,当选择分频后的时钟,则分频数必须大于2.5,否则“Count values are unpredictable”。(NXP提供的例程里进行了8分频)

该寄存器的第16COUNTFLAG,当24-bit down counter计数到0时,该位硬件置1,读取System Timer Control and status register可以将改位清0

System Timer Reload value register(系统定时器值重装寄存器):



未命名.jpg (34.08 KB)
2010-5-4 21:28

使用特权

评论回复
14
LPC300|  楼主 | 2010-6-28 22:38 | 只看该作者
该寄存器很简单,前面的24位装载着Systick的重装值,当24-bit down counter计数到0时,该重装值自动载入24-bit down counter重新计数;

System Timer Current value register(系统定时器当前值寄存器):



3.jpg (40.86 KB)
2010-5-4 21:28






同样,该寄存器的前24Systick当前计数值,向其写入任意值会将其清0

System Timer Calibration value register(系统定时器值校验寄存器):



4.jpg (49.86 KB)
2010-5-4 21:28

使用特权

评论回复
15
LPC300|  楼主 | 2010-6-28 22:38 | 只看该作者
该寄存器包含一些校验信息,本文不需要用到Systick的校验功能,略过不表。



所以,SysTIck的工作过程是:24-bit down counter从System Timer Reload value register取得定时初值后在选定时钟源的驱动下开始减1计数,当减至0时产生一个中断,并且重新载入初值继续新一轮计数,很简单。



源程序(部分源自NXP提供的例程):



#include "LPC13xx.h"



#include "gpio.h"







#define DELAY_LEN 1000



volatile uint32_t msTicks;



void SysTick_Handler(void);



static void Delay (uint32_t dlyTicks);







int main (void)



{



  GPIOInit();



  GPIOSetDir(0,7,1);







/*LPC1343默认使用IRC,即12MHz内部时钟*/



  SysTick_Config(SystemCoreClock/1000);







  while (1)



  {



      GPIOSetValue(0,7,1);



      Delay (DELAY_LEN);



      GPIOSetValue(0,7,0);



      Delay (DELAY_LEN);



  }



}



void SysTick_Handler(void)



{



  msTicks++;



}



static void Delay (uint32_t dlyTicks)



{



  uint32_t curTicks;







  curTicks = msTicks;



  while ((msTicks - curTicks) < dlyTicks);



}

使用特权

评论回复
16
LPC300|  楼主 | 2010-6-28 22:39 | 只看该作者
重点是,这个初值要怎么算,首先我们肯定知道LPC默认使用内部12MHz晶振(要知道如何达到最大的72MHz,要参看用户手册),那么Systick计数一次的时间是1/12MHz(单位s),那么定时1ms便需要计数1x1000ms/(1/12MHz)=12Mhz/1000,所以程序中的SysTick_Config(SystemCoreClock/1000);这个“1000”就是这样来的。至于SystemCoreClock这个在system_LPC13xx.c进行了宏定义,意为当前系统时钟频率。



为此可以查找到如下定义:



#define __XTAL (12000000UL)/* Oscillator frequency             */



#define __SYSTEM_CLOCK (__XTAL)



uint32_t SystemCoreClock = __SYSTEM_CLOCK;/*!< System Clock Frequency (Core Clock)*/



再看看SysTick_Config(uint32_t ticks);这个函数的原型:



static __INLINE uint32_t SysTick_Config(uint32_t ticks)



{



  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);



  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) -1;



NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);



  SysTick->VAL   = 0;



  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk|



                      SysTick_CTRL_TICKINT_Msk|



                      SysTick_CTRL_ENABLE_Msk;



  return (0);



}



这个函数,首先检查传递的参数是否超出重装最大值,超出则返回1表示出错;若不超出,则向SysTick->LOAD 写入初值:



SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) -1;为什么要减1呢,是因为24-bit down counter减到0触发中断,所以其实计数范围是“0~Max-1”而不是“1~Max”。



写入初值之后,分别设置中断优先级,清空当前计数值还有使能Systick、打开中断和选择时钟源。对3个寄存器的操作就这么几句。



编译,运行,LED就有节奏的闪烁起来了~

使用特权

评论回复
17
LPC300|  楼主 | 2010-6-28 22:39 | 只看该作者
PS:感觉NXP提供的函数库相当的凌乱,有点找不着头脑的感觉,不过也成就了LPCXpresso的一个亮点,就是它的Open Element功能,在工具栏右半部分一个小文件夹模样的图标。只要在程序中鼠标蓝底高亮选中某个关键字,点击Open Element就可以在整个工程(是整个工程哦)中找出符合这个关键字的条目,这对查找一些宏定义和函数原型非常有用。在此提出,希望大家一起来找出LPCXresso更多的优点与缺点。

使用特权

评论回复
18
LPC300|  楼主 | 2010-6-28 22:39 | 只看该作者
本节讲述如何用外部IO的中断介入一个LED的点亮状态。



各位一定都使用过各种单片机的中断功能,其作用,意义以及工作过程我想都不用特别说明了。LPC1343的IO中断相对于低端8位单片机来说,有以下不一样的地方:



1、 每一个IO都可以随意定制成中断IO;



2、 可以设置为跳变沿触发和电平触发方式,其中的跳变沿模式中,可以设置成双跳变沿触发方式,原文如下:Interrupts can be configured on single falling or rising edges and on both edges.笔者理解,触发电平应该是一个脉冲的形式。



3、 这个不算IO中断的特点,但是比较重要,在此提出:IO中断的中断标志并不会硬件清除,需要在中断服务中软件清除,否则会一直在触发IO中断(似乎M3器件都是这样子的);



分析一下NXP给我们附带的例子程序“extint”,先是主程序:



#include "LPC13xx.h"



#include "gpio.h"



#include "config.h"







int main(void)



{



       GPIOInit();



       GPIOSetDir(PORT2, 1, 0);



GPIOSetInterrupt(PORT2, 1, 0, 0, 0);//第一个0表示边沿触发,第二个0表示单边沿触发, //第三个0表示负跳变触发







       GPIOIntEnable(PORT2, 1);//取消中断屏蔽,即打开IO中断功能







       GPIOSetDir(LED_PORT, LED_BIT, 1);



       GPIOSetValue(LED_PORT, LED_BIT, LED_OFF );







       while (1);



}



首先头两句句是GPIO的初始化,以及方向设置,这里它设置了P2口的第1位(注意是第1位不是第0位)作为输入口,明显将要用该口作为中断IO。



下面一句是本文的重点了,IO中断方式的设置:



GPIOSetInterrupt(PORT2, 1, 0, 0, 0);

使用特权

评论回复
19
LPC300|  楼主 | 2010-6-28 22:39 | 只看该作者
我们在gpio.c中找到这个函数的原型:



/*****************************************************************************



** Function name:              GPIOSetInterrupt



** Descriptions:           Set interrupt sense, event, etc.



**                                        edge or level, 0 is edge, 1 is level



**                                        single or double edge, 0 is single, 1 is double



**                                        active high or low, etc.



** parameters:                    port num, bit position, sense, single/doube, polarity



** Returned value:              None



**



*****************************************************************************/



void GPIOSetInterrupt( uint32_t portNum, uint32_t bitPosi, uint32_t sense,



                     uint32_t single, uint32_t event )



{



  switch ( portNum )



  {



       case PORT0:



         if ( sense == 0 )//如果设置边沿触发



         {



              LPC_GPIO0->IS &= ~(0x1<<bitPosi);



              /* single or double only applies when sense is 0(edge trigger). */



              if ( single == 0 )



                LPC_GPIO0->IBE &= ~(0x1<<bitPosi);//单跳变沿



              else



                LPC_GPIO0->IBE |= (0x1<<bitPosi);//双跳变沿



         }



         else   //如果设置电平触发



             LPC_GPIO0->IS |= (0x1<<bitPosi);



         if ( event == 0 )



              LPC_GPIO0->IEV &= ~(0x1<<bitPosi);//低电平或负跳变触发,取决于IBE



         else



              LPC_GPIO0->IEV |= (0x1<<bitPosi);//高电平或正跳变触发,取决于IBE



       break;



      case PORT1:



         if ( sense == 0 )



         {



              LPC_GPIO1->IS &= ~(0x1<<bitPosi);



              /* single or double only applies when sense is 0(edge trigger). */



              if ( single == 0 )



                LPC_GPIO1->IBE &= ~(0x1<<bitPosi);



              else



                LPC_GPIO1->IBE |= (0x1<<bitPosi);



         }



         else



             LPC_GPIO1->IS |= (0x1<<bitPosi);



         if ( event == 0 )



              LPC_GPIO1->IEV &= ~(0x1<<bitPosi);



         else



              LPC_GPIO1->IEV |= (0x1<<bitPosi);  



       break;



       case PORT2:



         if ( sense == 0 )



         {



              LPC_GPIO2->IS &= ~(0x1<<bitPosi);



              /* single or double only applies when sense is 0(edge trigger). */



              if ( single == 0 )



                LPC_GPIO2->IBE &= ~(0x1<<bitPosi);



              else



                LPC_GPIO2->IBE |= (0x1<<bitPosi);



         }



         else



             LPC_GPIO2->IS |= (0x1<<bitPosi);



         if ( event == 0 )



              LPC_GPIO2->IEV &= ~(0x1<<bitPosi);



         else



              LPC_GPIO2->IEV |= (0x1<<bitPosi);  



       break;



       case PORT3:



         if ( sense == 0 )



         {



              LPC_GPIO3->IS &= ~(0x1<<bitPosi);



              /* single or double only applies when sense is 0(edge trigger). */



              if ( single == 0 )



                LPC_GPIO3->IBE &= ~(0x1<<bitPosi);



              else



                LPC_GPIO3->IBE |= (0x1<<bitPosi);



         }



         else



             LPC_GPIO3->IS |= (0x1<<bitPosi);



         if ( event == 0 )



              LPC_GPIO3->IEV &= ~(0x1<<bitPosi);



         else



              LPC_GPIO3->IEV |= (0x1<<bitPosi);        



       break;



       default:



         break;



  }



  return;



}

使用特权

评论回复
20
LPC300|  楼主 | 2010-6-28 22:39 | 只看该作者
首先看文件头的说明中的



Descriptions:        Set interrupt sense, event, etc.



**                          edge or level, 0 is edge, 1 is level



**                          single or double edge, 0 is single, 1 is double



**                          active high or low, etc.



说明了该函数的作用是:设置中断的各种方式(跳变沿触发,电平触发,跳变沿书目,还有电平触发方式),都是一些很常见的概念了。看看内部,大家先仔细看看这个函数,可以发现,这个函数作为一个switch分支结构,它的每一个case的内容都是很相似的,明显四个case分别对应四个PORT的设置,那么我们只要把某一个看懂,其他的也触类旁通了,我们看第一个,



case PORT0:



         if ( sense == 0 )//如果设置边沿触发



         {



              LPC_GPIO0->IS &= ~(0x1<<bitPosi);//设置为边沿触发方式



              /* single or double only applies when sense is 0(edge trigger). */



              if ( single == 0 )



                LPC_GPIO0->IBE &= ~(0x1<<bitPosi);//单跳变沿



              else



                LPC_GPIO0->IBE |= (0x1<<bitPosi);//双跳变沿



         }



         else   //如果设置电平触发



             LPC_GPIO0->IS |= (0x1<<bitPosi);



         if ( event == 0 )



              LPC_GPIO0->IEV &= ~(0x1<<bitPosi);//低电平或负跳变触发,取决于IBE



         else



              LPC_GPIO0->IEV |= (0x1<<bitPosi);//高电平或正跳变触发,取决于IBE



       break;



再配上GPIO中有关中断的寄存器:



  

1.jpg (48.14 KB)

2010-5-5 22:49





现在来分析程序:



首先进入case,判断sense的值,如果是0,则表示程序员要将IO设置为“跳变沿触发方式”



,接着将IO设置为“跳变沿触发方式”(详见GPIOnIS,因为接下去CM3的各个设备的寄存器会越来越多,笔者这里就无法将每一个寄存器详细描述了,希望NXP能出本书~),根据参数single来设置是使用单跳变沿还是双跳变沿(详见GPIOnIBE)。而在进入case之后,判断sense的值,如果是1,则设置IO设置为“电平触发方式”,接着判断是低电平触发还是高电平触发(详见GPIOnIBE),至此设置完毕,break。



这里有一点绕圈的地方关键在于IS,IBE和IEV三个寄存器:



1、 IS寄存器是设置触发方式的,跳变沿触发或者电平触发;



2、 IBE是设置跳变沿数目的,所以如果IS已经设置为电平触发,那IBE就没有意义了;



3、 如果IS设置为跳变沿触发,则IBE设置跳变沿书目,而IEV设置是上升沿触发还是下降沿触发;



4、 如果IS设置为电平触发,则IBE无用,IEV来设置是高电平触发还是低电平触发;



总结一下,GPIOSetInterrupt(PORT2, 1, 0, 0, 0);的作用是,设置P2口的第1位IO为跳变沿触发方式,单跳变沿,负跳变触发。



继续看程序,下一句是:GPIOIntEnable(PORT2, 1);这句很简单,原型如下:



/*****************************************************************************



** Function name:              GPIOIntEnable



**



** Descriptions:           Enable Interrupt Mask for a port pin.



**



** parameters:                    port num, bit position



** Returned value:              None



**



*****************************************************************************/



void GPIOIntEnable( uint32_t portNum, uint32_t bitPosi )



{



  switch ( portNum )



  {



       case PORT0:



         LPC_GPIO0->IE |= (0x1<<bitPosi);



       break;



      case PORT1:



         LPC_GPIO1->IE |= (0x1<<bitPosi);     



       break;



       case PORT2:



         LPC_GPIO2->IE |= (0x1<<bitPosi);         



       break;



       case PORT3:



         LPC_GPIO3->IE |= (0x1<<bitPosi);         



       break;



       default:



         break;



  }



  return;



}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:30——驱动高手、流利的英语

107

主题

525

帖子

0

粉丝