73202|114

6366

帖子

4920

TA的资源

版主

楼主
 

LPC1343学习笔记(连载中)--6月21日新增第十二篇 [复制链接]

 

有幸拿到了EEWORLD论坛的LPC1343评估板,实在是一件意外而激励人心的事情。为感谢EEWORLD和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的用户手册翻译了一下,大家可以作为参考。具体请看链接:

https://bbs.eeworld.com.cn/thread-102972-1-1.html

 

 

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

 

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

 

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

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

3、 接着首先要导入LPC1343的初始化文件,找到第一步下载的lpc1343.examples.zip,复制粘贴到:你安装LPCXpresso的盘符\lpcxpresso_3.3\Examples\LPC3000\LPC31xx目录下,接着点击左边project区下方的”Import Example project”

 

出现如下对话框:

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

Next之后,完成导入,出现以下对话框:

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

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

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

弹出以下对话框:

填入Project Name->Next

勾选Use CMSIS->Next

可以在此填上你的名字,Next

y

[ 本帖最后由 tiankai001 于 2010-6-21 14:58 编辑 ]

7.jpg (57.03 KB, 下载次数: 3)

7.jpg

11.jpg (38.59 KB, 下载次数: 2)

11.jpg
此帖出自NXP MCU论坛

最新回复

好帖子,赞一个   详情 回复 发表于 2019-9-4 17:30

点评

LPCX用的仿真器是什么呀?能用JLINK吗?  详情 回复 发表于 2013-12-5 15:38

赞赏

1

查看全部赞赏

点赞 关注(3)
 

回复
举报

6366

帖子

4920

TA的资源

版主

沙发
 

Dubug是“调试”的意思,Release是“发布”的意思,无关紧要,Next

当然选择LPC1343了,Finish

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

如此第一个工程的框架已经建立起来了,我们可以看到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

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

然后双击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,确保硬件连接好之后,点击工具栏里一个绿色的昆虫模样的图标:

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

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

[ 本帖最后由 tiankai001 于 2010-5-2 23:32 编辑 ]

12.jpg (33.15 KB, 下载次数: 2)

12.jpg

15.jpg (28.78 KB, 下载次数: 3)

15.jpg
此帖出自NXP MCU论坛
 
 
 

回复

6366

帖子

4920

TA的资源

版主

板凳
 

上一篇讲述了LPCXpresso For LPC1343的第一个工程的建立以及程序的运行,点亮了LPC1343评估板上唯一的一个LED(寒酸了点儿)。本文来讲讲这个LED是怎么被点亮的,也就是我们操控LPC1343IO口的方法。

 

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”,即“初始化GPIOGeneral 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配置为外部中断方式的时候设置NVICNVICM3器件的中断控制器。

以上是第一个函数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的方向,即作为输出口还是输入口。

 

函数内部还是很简单,如果dir1则将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;

 

同时在gpio.h里面还可以找到如下定义

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

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

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

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

 

 

讲讲这两天使用LPC1343LPCXpresso的感受:

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

 

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

 

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

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

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

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

 

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

 

[ 本帖最后由 tiankai001 于 2010-5-2 23:46 编辑 ]
此帖出自NXP MCU论坛
 
 
 

回复

4997

帖子

19

TA的资源

裸片初长成(初级)

4
 
写的不错啊,学习了!!!!!!!!!!!!!!
此帖出自NXP MCU论坛
 
 
 

回复

2955

帖子

0

TA的资源

纯净的硅(初级)

5
 
很好啊 谢谢楼主了......
此帖出自NXP MCU论坛
 
个人签名不断地学习,才会有创新!
淘宝小店:手机、qq点卡、游戏点卡自动充值 http://shop63727265.taobao.com/
 
 

回复

6366

帖子

4920

TA的资源

版主

6
 

这课我们来看看如何用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其他部件的一个时钟,可以利用独立电源供电,可以在整体系统出现故障的情况下仍然正常工作,正因如此,它也往往用来作为记录系统各种故障和错误的时间基准。下面了解一下它的内部结构:

 

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

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

该寄存器的第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(系统定时器值重装寄存器):

该寄存器很简单,前面的24位装载着Systick的重装值,当24-bit down counter计数到0时,该重装值自动载入24-bit down counter重新计数;

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

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

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

该寄存器包含一些校验信息,本文不需要用到Systick的校验功能,略过不表。

所以,SysTIck的工作过程是:24-bit down counterSystem 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);

}

重点是,这个初值要怎么算,首先我们肯定知道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就有节奏的闪烁起来了~

 

 

 

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

此帖出自NXP MCU论坛
 
 
 

回复

4997

帖子

19

TA的资源

裸片初长成(初级)

7
 
不错,继续学习!!!!!!!!!!!!!!!!!!!!!!!!
此帖出自NXP MCU论坛
 
 
 

回复

6366

帖子

4920

TA的资源

版主

8
 

本节讲述如何用外部IO的中断介入一个LED的点亮状态。

各位一定都使用过各种单片机的中断功能,其作用,意义以及工作过程我想都不用特别说明了。LPC1343IO中断相对于低端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);

我们在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;

}

首先看文件头的说明中的

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中有关中断的寄存器:

现在来分析程序:

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

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

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

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

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

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

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

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

继续看程序,下一句是: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;

}

笔者在此就不多阐述了,这个函数就是对GPIO寄存器中的IE寄存器操作,打开对应IO的中断功能(即取消中断屏蔽)。

最后两句是点灯程序,这个大家都知道了的。

我们继续看看中断的另外一半,中断服务函数:

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

** Function name:              PIOINT2_IRQHandler

**

** Descriptions:           Use one GPIO pin(port2 pin1) as interrupt source

**

** parameters:                    None

** Returned value:              None

**

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

void PIOINT2_IRQHandler(void)

{

  uint32_t regVal;

gpio2_counter++;

  regVal = GPIOIntStatus( PORT2, 1 );//读取IO中断状态,若触发返回1

  if ( regVal )

  {

       p2_1_counter++;

       GPIOIntClear( PORT2, 1 );//这里很重要,一定要软件清除IO的中断触发标志!!!

  }

// CodeRed - extend original interrupt handler so as to toggle the LED state

  if (LEDvalue == LED_OFF)

  {

         LEDvalue = LED_ON;

  }

  else

  {

         LEDvalue = LED_OFF;

  }

  GPIOSetValue( LED_PORT, LED_BIT, LEDvalue );

return;

}

这个服务函数的流程也很清晰,首先判断是否是我们想要的IO触发的中断,为什么要判断呢?那是因为每个PORT共用同1个中断,即共用同一个中断服务函数。判断完之后,清除对应中断标志,否则中断不会被清除,再三强调一下。之后就是程序员想要在中断里面处理的事情了。特别提一下注意中断服务函数的函数名的写法~

编译,运行,灯亮,用杜邦线将P21接到GND上,灯灭,拔出再接上,灯亮……

当然这个现象是理想状态,你的手不可能那么稳那么准~呵呵,Enjoy

 

下一节,我们来看看如何使用中断的嵌套功能~

[ 本帖最后由 tiankai001 于 2010-5-6 10:55 编辑 ]
此帖出自NXP MCU论坛
 
 
 

回复

4997

帖子

19

TA的资源

裸片初长成(初级)

9
 
跟上!!!!!!!!!!!!!!!!!!!;P
此帖出自NXP MCU论坛
 
 
 

回复

6366

帖子

4920

TA的资源

版主

10
 

LPC1343中断嵌套程序实验设计

本节的内容是对LPC1343进行中断嵌套程序实验设计。

实验具体内容是,使用下载附件  保存到相册

2010-5-11 15:29 上传

明显它的取值范围是0~7,正好表示2^8=256个中断优先级,具体含义如下:

[10:8] PRIGROUP Interrupt priority grouping field:

PRIGROUP         Split      of pre-emption priority from subpriority

0                   7.1       indicates seven bits of pre-emption priority, one bit of subpriority

1                   6.2       indicates six bits of pre-emption priority, two bits of subpriority

2                   5.3       indicates five bits of pre-emption priority, three bits of subpriority

3                   4.4       indicates four bits of pre-emption priority, four bits of subpriority

4                   3.5       indicates three bits of pre-emption priority, five bits of subpriority

5                   2.6       indicates two bits of pre-emption priority, six bits of subpriority

6                   1.7       indicates one bit of pre-emption priority, seven bits of subpriority

7                   0.8       indicates no pre-emption priority, eight bits of subpriority.

 

Split是分割的意思,分割什么呢,分割实际支配M3器件中表述中断优先级的那个8位字节,控制256个优先级。

上面字段表示,当PRIGROUP0时,分割点位于控制字节右起第1位(7.1),表示有7位数据用于先占优先级,同时1位用于次占优先级。所以使用PRIGROUP=0,则一共有128个先占优先级以及2个次占优先级。

同样,当PRIGROUP1时,分割点位于控制字节右起第2位(6.2),表示有6位数据用于先占优先级,同时2位用于次占优先级。所以使用PRIGROUP=1,则一共有64个先占优先级以及2个次占优先级。

同样,当PRIGROUP2时,分割点位于控制字节右起第2位(5.3),表示有5位数据用于先占优先级,同时3位用于次占优先级。所以使用PRIGROUP=2,则一共有32个先占优先级以及8个次占优先级。

………………一直类推到PRIGROUP=7

同样,当PRIGROUP7时,分割点位于控制字节右起第8位(0.8),表示有0位数据用于先占优先级,同时8位用于次占优先级。所以使用PRIGROUP=7,则一共有0个先占优先级以及256个次占优先级。

 

这就是中断优先组的概念,先占优先组拥有比后占优先组更高的优先权限。需要提到的是,Cortex-M3并没有要求所有的控制器都要使用8位数据来管理优先级,毕竟不是什么场合都需要多达256个中断优先级,所以可以使用低于7个优先组的分级机制,比如LPC13437组,而STM32F1034……

所以两个中断的优先级关系可以如下判断:

先占优先级高的优先执行,先占优先级一样的按后占优先级执行,两个优先级一样的按照M3指定的默认优先级执行。

以上是笔者对中断优先组的一些见解,下面看回源程序:

NVIC_SetPriorityGrouping(1);这里使用优先级分组1,函数原型如下:

static __INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)

{

  uint32_t reg_value;

  uint32_t PriorityGroupTmp = (PriorityGroup & 0x07);          /* only values 0..7 are used */

 

  reg_value  =  SCB->AIRCR;                            /* read old register configuration*/

  reg_value &= ~(SCB_AIRCR_VECTKEY_Msk|SCB_AIRCR_PRIGROUP_Msk);/*clear bits to change  

 

 reg_value  =  (reg_value|(0x5FA << SCB_AIRCR_VECTKEY_Pos)|

(PriorityGroupTmp << 8));              /* Insert write key and priorty group */

  SCB->AIRCR =  reg_value;

}

 

这个函数里,最重要的一个信息是“only values 0..7 are used”,只能填入0-7之间。说明LPC1343使用了7位数据来管理中断优先级,最多可以设置256个中断优先级(而广为流传的STM32是使用4个优先组,只能设置16个优先级)。这里设置优先级分组为1,即使用“64个先占优先级以及2个次占优先级

接着是设置具体中断的优先级:

NVIC_SetPriority(EINT2_IRQn, 0);//设置外部中断20级中断(最高级)

NVIC_SetPriority(EINT3_IRQn, 1); //设置外部中断31级中断

也找到其原型如下:

/**

 * @brief  Set the priority for an interrupt

* @param  IRQn      The number of the interrupt for set priority

 * @param  priority  The priority to set

 * Set the priority for the specified interrupt. The interrupt

 * number can be positive to specify an external (device specific)

 * interrupt, or negative to specify an internal (core) interrupt.

* Note: The priority cannot be set for every core interrupt.

 */

static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

{

  if(IRQn < 0) {

SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); }

 /* set Priority for Cortex-M3 System Interrupts */

  else {

NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }       

/* set Priority for device specific Interrupts  */

}

从函数头部的注释就可以知道很多内容:

该函数的作用是设置中断优先级;

第一个参数填入具体中断号,第二个参数填入优先级;

优先级参数是负数的时候,表示指定Cortex-M3内部中断,是正数则指定外部中断;

并不是每一个内核中断都可以被指定;

我设置外部中断0为最高级中断,而外部中断31级中断。

至此,中断设置部分结束,以下是具体的中断服务函数:

void PIOINT2_IRQHandler(void)

{

    if (GPIOIntStatus( PORT2, 0 )==1)

    {

        GPIOIntClear( PORT2, 0 );

        for(n=0;n<10;n++)

        {

            LPC_GPIO0->DATA|=0x0080;

            for(k=0;k<1000000;k++);

            LPC_GPIO0->DATA&=~0x0080;

            for(k=0;k<1000000;k++);

        }

    }

 

  return;

}

void PIOINT3_IRQHandler(void)

{

    if (GPIOIntStatus( PORT3, 0 )==1)

    {

        GPIOIntClear( PORT3, 0 );

        for(n=0;n<100;n++)

          {

                LPC_GPIO0->DATA|=0x0080;

                for(k=0;k<100000;k++);

                LPC_GPIO0->DATA&=~0x0080;

                for(k=0;k<100000;k++);

        }

    }

}

这个就没什么特别的了,上一节特别提示过,要注意中断函数的命名,在这里就用到了,如果是P1口的中断函数呢?对~ 举一反三,就是void PIOINT1_IRQHandler(void)P2口呢?void PIOINT2_IRQHandler(void)

编译,DebugF8全速运行,此时

PORT3.0一个下降沿,LED开始快速闪烁,在LED快速闪烁期间给PORT2.0一个下降沿,LED的快速闪烁状态被打断,开始慢速闪烁,说明低级中断被高级中断打断了,正在执行高级中断PORT2.0的服务程序,慢速闪烁结束之后,返回快速闪烁状态,说明高级中断执行完毕,返回被打断的低级中断继续执行至完结……

 

下一节,我们来看看Cortex-M3的看门狗~

 

 

 

 

 

 

此帖出自NXP MCU论坛
 
 
 

回复

2955

帖子

0

TA的资源

纯净的硅(初级)

11
 
学习了.............
此帖出自NXP MCU论坛
 
个人签名不断地学习,才会有创新!
淘宝小店:手机、qq点卡、游戏点卡自动充值 http://shop63727265.taobao.com/
 
 

回复

190

帖子

0

TA的资源

五彩晶圆(初级)

12
 
比较奇怪的是,似乎这样的好帖子人气都不太好啊
此帖出自NXP MCU论坛
 
 
 

回复

1248

帖子

0

TA的资源

一粒金砂(高级)

13
 

很高的资料!

很高的资料!
此帖出自NXP MCU论坛
 
个人签名James Wang
 
 

回复

247

帖子

0

TA的资源

纯净的硅(初级)

14
 

马哥,我又一次看到你了

马哥,看了你的帖子,我很佩服你,敬业
此帖出自NXP MCU论坛
 
 
 

回复

6366

帖子

4920

TA的资源

版主

15
 

本节内容,我们来会会LPC1343内部的看门狗。

看门狗,又叫 watchdog timer,是一个定时器电路, 一般有一个输入,喂狗”(kicking the dog or service the dog),一个输出到MCURST,MCU正常工作的时候,每隔一端时间输出一个信号到喂狗端, WDT 清零,如果超过规定的时间不喂狗,(一般在程序跑飞时),WDT 定时超过,就会给出一个复位信号到MCU,使MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。

我们的LPC1343 内部自带了看门狗,以下是它的特性,来自《user.manual.lpc13xx》:

     在看门狗没有定期重载的情况下从内部复位芯片

     支持调试模式

     使用软件使能,但是只有在硬件复位或者发生看狗复位/中断时候才能失能

     错误或者不完整的喂狗结果会导致看门狗/中断的发生

     拥有表面发生看门狗复位的标志

     拥有内部预分频器的可编程32位定时器

     可选的定时时间,从(TWDCLKX256X4)到(TWDCLKX2^32X4)可选,它们都是TWCLKX4的倍数

     看门狗的时钟源可以从系统控制模块从选择,包括内部RC振荡器,主时钟以及看门狗振荡器,这样就看门狗就有一个较为宽广的定时时间选择,在方便在不同的低功耗场合使用。为了提高可靠性,看门狗也可以只在独立的内部时钟源驱动下运行,这个内部时钟与外部晶振,与它连接的部件和线路都保持独立状态,既是说看门狗可以在不受干扰的内部时钟源驱动下运行。

如何使用看门狗呢?同样来自来自《user.manual.lpc13xx》:

1、 WDTC寄存器中设置看门狗的固定重装值;

2、 WDMOD寄存器中设置看门狗的运行模式;

3、 通过向WDFFD寄存器写0XAA接着写0X55启动看门狗;

4、 在看门狗计数器向下溢之前应该再次喂狗以阻止看门狗复位/中断;

凭借以上信息,我们来分析一下LPC1343自带的wdt例程

int main (void)

{

  WDT_CLK_Setup(WDTCLK_SRC_MAIN_CLK);

  init_timer16( 0, TIME_INTERVALmS * 10 );

  GPIO_Configuration();               //作者自添函数,作用在后面解释

  WDTInit();

  enable_timer16( 0 );

while( 1 )

  {

       if ( timer16_0_counter > 0 ) // Every 10ms

       {

         /* Feed the watchdog timer */

         feed_counter++;

         WDTFeed();

         timer16_0_counter = 0;

       }

  }

}

此主函数的执行功能是,开始进行看门狗和16位定时器0的初始化,使定时器0实现10ms间隔,并以此间隔进行喂狗操作,比较简单。下面就是比较重要的剖析部分了,首先看看

WDT_CLK_Setup(WDTCLK_SRC_MAIN_CLK);这个函数,看门狗时钟源的选择,原型如下:

void WDT_CLK_Setup ( uint32_t clksrc )

{

  /* Watchdog configuration. */

  /* Freq = 0.5Mhz, div_sel is 0, divided by 2. WDT_OSC should be 250khz */

  LPC_SYSCON->WDTOSCCTRL = (0x1<<5)|0x00;

  LPC_SYSCON->WDTCLKSEL = clksrc;        /* Select clock source */

  LPC_SYSCON->WDTCLKUEN = 0x01;          /* Update clock */

  LPC_SYSCON->WDTCLKUEN = 0x00;          /* Toggle update register once */

  LPC_SYSCON->WDTCLKUEN = 0x01;

  while ( !(LPC_SYSCON->WDTCLKUEN & 0x01) );  /* Wait until updated */

  LPC_SYSCON->WDTCLKDIV = 1;            /* Divided by 1 */ 

  LPC_SYSCON->PDRUNCFG &= ~(0x1<<6);    /* Let WDT clock run */

  return;

}

这个函数的作用是,WDT_CLK_Setup,即看门狗时钟设置,其结果是Freq = 0.5Mhz, div_sel is 0, divided by 2. WDT_OSC should be 250khz,即看门狗振荡器的频率是250KHz,但是这句话带有相当的误导性,笔者一开始也以为这个函数的功能就是将看门狗时钟设置为250KHz频率。导致后面计算喂狗间隔时间错误!我们来看看这段来自user.manual.lpc13xx》的关键REMARK

 

Remark: The frequency of the watchdog oscillator is undefined after reset. The

watchdog oscillator frequency must be programmed by writing to the WDTOSCCTRL

register before using the watchdog oscillator for the WDT.

 

注意:在复位之后看门狗振荡器并未定义,在使用看门狗振荡器之前必须通过WDTOSCCTRL寄存器定义看门狗振荡器频率。

这句话包含了一个重要的信息,使用看门狗振荡器之前必须通过WDTOSCCTRL寄存器定义看门狗振荡器频率,但是在我们的主函数中使用的是如下语句:WDT_CLK_Setup(WDTCLK_SRC_MAIN_CLK)   

选择主时钟作为看门狗时钟而不是看门狗振荡器。所以只有在使用看门狗振荡器的情况下

Freq = 0.5Mhz, div_sel is 0, divided by 2. WDT_OSC should be 250khz才是正确的。那么在使用主时钟的情况下,这个函数的结果是什么呢?

这样问题已经转变为Cortex-M3时钟系统的问题了,下面是LPC1343的内部时钟分支结构:

这个图很清晰地表现出LPC1343内部的时钟分配路线,而圈起来的部分就是提供给看门狗的部分。

从左边开始看起,可以看到前文已经提到的irc_osc_clk(内部RC振荡器)wdt_osc_clk(看门狗振荡器)sys_osc_clk(系统时钟,即外部晶振),从圈1处我们可以得知对于上文的信息:看门狗时钟可以从内部RC振荡器,主时钟以及看门狗振荡器,那么我们的程序中使用了主时钟,所以看门狗时钟要来自圈2。其中

MAINCLKSEL:主时钟源选择;

SYSPLLCLKSEL:系统锁相环时钟源选择;

SYSPLL:系统锁相环,可以将输入以倍数放大,LPC1343所谓的最大72MHz工作频率就是12MHz从这里产生6倍放大得到的;

我们可以按下主时钟的来源有五条路径:

1、 irc_osc_clk-> SYSPLLCLKSE-> MAINCLKSEL,既使用内部RC振荡器经过系统锁相环时钟源选择作为主时钟;

2、 sys_osc_clk-> SYSPLLCLKSE-> MAINCLKSEL,既使用外部晶振经过系统锁相环时钟源选择作为主时钟;

3、 irc_osc_clk-> MAINCLKSEL,使用内部RC直接作为主时钟源;

4、 sys_osc_clk> MAINCLKSEL,使用外部晶振直接作为时钟源;

5、 sys_osc_clk/ irc_osc_clk-> SYSPLLCLKSE ->SYSPLL-> MAINCLKSEL ,既使用外部晶振/内部RC振荡器经过系统锁相环作为主时钟;

那么在本节看门狗程序中,我们的主时钟来自哪条路径呢?先在user.manual.lpc13xx》中找到MAINCLKSEL寄存器:

得知这个寄存器的前两位值决定了当前主时钟的来源。现在查看这两个关键的位!

这里笔者通过LPCXpresso的寄存器查看功能尝试查看到这个信息,将来NXP自己解释这些例程的混乱原因。编译运行… …

查看到:

所以确认主时钟源来自PLL倍频器的输出,即上述第5条路径,而PLL倍频的输入呢?

查看到:

所以PLL输入来自System oscillator。由此我们最终确定了看门狗的时钟来源:

sys_osc_clk-> SYSPLLCLKSE ->SYSPLL-> MAINCLKSEL ,既使用外部晶振过系统锁相环作为主时钟;

既然通过了倍频器,自然要来看看倍频器是个什么东西:

 

此帖出自NXP MCU论坛
 
 
 

回复

6366

帖子

4920

TA的资源

版主

16
 

这个是它的说明:

The block diagram of this PLL is shown in Figure 4. The input frequency range is 10 MHz to 25 MHz. The input clock is fed directly to the Phase-Frequency Detector (PFD). This block compares the phase and frequency of its inputs, and generates a control signal when phase and/ or frequency do not match. The loop filter filters these control signals and drives the current controlled oscillator (CCO), which generates the main clock and optionally two additional phases. The CCO frequency range is 156 MHz to320 MHz.These clocks are either divided by 2×P by the programmable post divider to create the output clock(s), or are sent directly to the output(s). The main output clock is then divided by M by the programmable feedback divider to generate the feedback clock. The output signal of the phase-frequency detector is also monitored by the lock detector, to signal when the PLL has locked on to the input clock.

在此笔者就不翻译啦,这段话里面最重要的红字部分:时钟在输出之前可以经过一个“post divider”进行“2XP”倍数的放大,或者不放大之间输出。所以我们还是得查看一下本次看门狗程序中的PLL放大了几倍:

先是寄存器:

 

接着查看到:

得知PSEL01,则P=2,则放大倍数为2XP=2X2=4倍。所以从SYS_PLL出来的时钟频率是12MHzX4=48MHz!!这个就是我们苦苦追寻的看门狗主时钟!

时钟选择好之后,来到看门狗初始化部分(定时器初始化部分属于定时器的内容,暂且跳过):

WDTInit()

原型(笔者已经添加详细注释):

void WDTInit( void )

{

  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<15);//开启看门狗AHB时钟,即启用看门狗

wdt_counter = 0;

  NVIC_EnableIRQ(WDT_IRQn);//允许看门狗中断

LPC_WDT->TC = WDT_FEED_VALUE;/* 重装值,once WDEN is set, the WDT will start after*/ /*feeding                                  */ 

LPC_WDT->MOD = WDEN;     /*使能看门狗*/

LPC_WDT->FEED = 0xAA;           /*喂狗*/

  LPC_WDT->FEED = 0x55;   

  return;

}

这部分完全是按照前文提到的步骤来编写的:

1、在WDTC寄存器中设置看门狗的固定重装值;

2、在WDMOD寄存器中设置看门狗的运行模式;

3、通过向WDFFD寄存器写0XAA接着写0X55启动看门狗;

其中有一个很有用的信息,便是once WDEN is set, the WDT will start afte feeding,当WDEN设置后,执行完LPC_WDT->FEED = 0xAA; LPC_WDT->FEED = 0x55; 看门狗就开始启动计数了。

我们通过LPCXpresso的查找功能查找到:

#define WDT_FEED_VALUE           0x003FFFFF        //0x003fffff= 4194303,备待会计算用

前文同样提到过,LPC1343的看门狗拥有内部预分频器的可编程32位定时器,同样翻阅到:

The Watchdog consists of a divide by 4 fixed pre-scaler and a 32-bit counter. The clock is

fed to the timer via a pre-scaler. The timer decrements when clocked. The minimum value

from which the counter decrements is 0xFF. Setting a value lower than 0xFF causes 0xFF

to be loaded in the counter. Hence the minimum Watchdog interval is (TWDCLK × 256 × 4)

and the maximum Watchdog interval is (TWDCLK × 232 × 4) in multiples of (TWDCLK × 4).

由以上信息可知,该分频器是一个4分频分频器。同时还有一个重要信息是,看门狗的重装值不能低于0XFF,即便你写入的数小于0XFF还是会得到0XFF的写入结果。所以看门狗的最小溢出时间是(TWDCLK × 256 × 4),最大是(TWDCLK × 2^32 × 4)

现在我们要尝试计算一下WDT_FEED_VALUE=0x003FFFF的情况下看门狗的溢出时间:

Tunderflow = (TWDCLK ×WDT_FEED_VALUE  × 4)

=1/48MHz X 0x003FFFF X 4

          =1/48MHz X 4194303 X 4=0.34952525s

所以溢出时间大概是350ms,而在该看门狗程序中,定时器设定了10ms的喂狗间隔,所以在程序不跑飞的情况下,看门狗永远都不会溢出,不会产生看门狗复位以及中断。

最后来看看看门狗的中断服务函数:

void WDT_IRQHandler(void)

{

  NVIC_DisableIRQ(WDT_IRQn);   // Disable the watchdog- interrupt flag cannot

                                                       // be cleared except by reset

LPC_WDT->MOD &= ~WDTOF;          /* clear the time-out interrupt flag */

  wdt_counter++;

}

 

 

 

 

 

配合以下寄存器:

 

 

从其中对WDINT的描述,我们知道看门狗中断标志位无法像往常一样用软件清除。所以在该看门狗中断服务程序中,只能使用NVIX控制器NVIC_DisableIRQ(WDT_IRQn);来禁止看门狗中断来达到清除看门狗中断的效果。

在此送上笔者修改之后的程序,其功能是,定时器每10ms进行一次喂狗,当PORT2.0中断触发,进入void PIOINT2_IRQHandler(void){}服务程序之后关闭定时器,此后停止喂狗,看门狗经过大概350ms的时间之后触发看门狗中断,进入其中断服务函数,LED闪烁10次之后静止,因为此时看门狗中断已经禁止,不会再触发看门狗中断,不应该有任何现象发生。

 

 

下一节来看看LPC1343的定时器~

 

LPCXpresso_WatchDog.rar (572.29 KB, 下载次数: 122)
[ 本帖最后由 tiankai001 于 2010-5-17 11:43 编辑 ]
此帖出自NXP MCU论坛
 
 
 

回复

18

帖子

0

TA的资源

一粒金砂(初级)

17
 
虽然现在用不了,不过还是要顶,好帖!
此帖出自NXP MCU论坛
 
 
 

回复

19

帖子

0

TA的资源

一粒金砂(初级)

18
 
学习中。。。
感谢版主!
此帖出自NXP MCU论坛
 
 
 

回复

2955

帖子

0

TA的资源

纯净的硅(初级)

19
 
楼主依然是那么的辛苦啊 谢谢了
此帖出自NXP MCU论坛
 
个人签名不断地学习,才会有创新!
淘宝小店:手机、qq点卡、游戏点卡自动充值 http://shop63727265.taobao.com/
 
 

回复

4997

帖子

19

TA的资源

裸片初长成(初级)

20
 
继续学习!!!!!!!!!!!!
此帖出自NXP MCU论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表