S3C2440无操作系统实验——LED部分
硬件:飞凌FL2440开发板
软件:ADS1.2
拿到ARM开发板,应该从哪儿开始学习呢?困扰了很多初学者,当然也正在困扰我,因为我也是初学者。所以我想与其困惑,还不如先做些自己能做的,将会做的做熟悉了,也许就找到学习的方向了吧。那么从哪儿入手呢,想来想去,那就从点亮一个LED灯作为学习的起点。虽然很多人,不推荐用学习单片机的模式来学习ARM,但是我感觉基础还是重要的,再说高级的学习暂时也不会呀,呵呵。虽然这样学习会慢点,但是总比开发板落厚厚一层灰要划得来吧(开发板买了快2年了,拿来玩的时间不超过2个月,也许很多朋友都有这样的经历吧)。所以,把它拿出来,开始学习吧。
不管是单片机还是ARM,对其进行控制其实就是对相应的寄存器进行控制,如果能够对常用寄存器进行熟练的操作,那么基本也就掌握了这种芯片的使用方法。所以就先从寄存器介绍开始。
1、相应寄存器介绍:
LED灯是接在某一个I/O上的,点亮或者熄灭LED灯其实就是对I/O口寄存器的操作。ARM的I/O口寄存器主要包括端口配置寄存器GPXCON、端口数据寄存器GPXDAT、端口上拉电阻使能寄存器GPXUP、MISCELLANEOUS控制寄存器和外部中断寄存器五种(其中X为芯片的I/O口字母)。在这个试验中我们只使用前三个寄存器,下面就对这三个寄存器进行简单的介绍。
举个例子说明可能更容易理解,比如快递员要送包裹给张三。当货物送到以后,如何来确定收货人就是张三呢,这个时候快递员就要查看张三的身份证。因为它上面的号码是唯一的。通过身份证号码,就可以准确无误地确定张三的身份。
对于ARM芯片来说,也有这样的问题,因为它其中的寄存器很多,那么根据什么来区分寄存器呢?为了能让ARM找到相应的寄存器,系统给每个寄存器都分配一个固定地址,地址就像一个人的身份证号码一样,是唯一的。那么当你使用这个地址的时候,ARM就知道你要操作哪个寄存器了。所以在编程的时候,首先要声明待操作的寄存器的地址。下表就是我们要使用的三种寄存器地址和位定义:
当快递员找到张三后,就通知张三有个包裹需要签收。同样的道理,ARM芯片找到相应寄存器后,也先要设置端口配置寄存器GPBCON,也就是告诉寄存器做好接收数据或者输出数据或者响应外部中断的准备,下表为它的功能描述:
当张三签收后,就可以得到包裹了。ARM设置完I/O口状态后,就准备读/写数据了。这个功能可以通过设置数据寄存器GPBDAT来实现。下表即为它的功能描述:
设置完前两个寄存器后,还要设置上拉电阻使能寄存器GPBUP,顾名思义,它的作用就是告诉ARM这个端口要不要配置上拉电阻。上拉电阻、下拉电阻的作用在于,当IO引脚处于第三态(即不是高电平,也不是低电平,而是高阻态,相当于没接芯片)时,它的电平状态由上拉电阻、下拉电阻确定。下表为它的功能描述:当为0时,上拉电阻是允许的;反之,则上拉电阻是被禁止的。
到此,需要的寄存器就配置完成了,那么接下来介绍下LED在开发板上的电路原理图。
2、电路原理图介绍:
电路如下图所示,这个电路采用灌电流方式驱动LED,只需加一限流电阻即可。根据基本电路知识可以计算出限流电阻R的值:
R={U(电源)-U(LED端电压)}/IO口推荐电流
由LED的手册参数可知,当LED内流过10mA的电流时,两端压降约为1.7V,即U(LED压降)=1.7V;S3C2440普通I/O口推荐电流为4mA。据此就可以计算出R的值为400Ω,一般会留有余量,比如取到470Ω。因为LED灯的亮度由流过的电流决定,FL2440电路板采用10K的电阻,就是为了减弱LED灯的亮度,要不就会很刺眼。所以在设计电路的时候,可以根据实际需要做些调整。
LED的工作原理很简单,要让LED发光,首先要将I/O口设置为输出模式,然后控制I/O口输出不同的电平。当输出为低电平时,LED灯发光;反之当输出为高电平时,LED灯熄灭。
3、例程
下面就通过几个例子来练习下这几种寄存器的用法。如果不会使用ADS,可以参考下我写的《ADS使用方法》一文,可以方便快速上手。
实验一:点亮一个LED灯。
首先要编写init.s汇编源文件,这段程序的目的是进行一些初始化操作,并跳转到主函数中继续执行后面的程序。
Init.s程序:
1 AREA |DATA|,CODE,READONLY
2 ENTRY
3 ldr r13,=0x1000
4 IMPORT led0
5 b led0
6 END
注意:前面必须要有空格,不能顶格写;前面的数字为了方便分析程序才加的,编写程序的时候不需要。init.s程序只是在跳转的目标函数上有区别,所以以后的程序只需要改变第四句和第五句中的函数名就行了,所以,以后省略对此函数的介绍。
程序分析:
第一句:
AREA |DATA|,CODE,READONLY
这句是用AREA伪指令定义一个段名为|DATA|的代码段,属性为只读。再给段命名的时候要注意,如果是以非字母开头的话,那么要用“|”将段名括起来。所以本句中的段名取掉“|”也可以正常执行。
第二句:
ENTRY也是伪指令,用来指定汇编程序的入口点。
第三句:
ldr r13,=0x1000
LDR指令用于从寄存器中将一个32位的字数据传送到目的寄存器中。因为2440处理器的内部有4K的RAM,0x1000是放到了4K的最高端,也就是堆栈所对应的地址。
第四句:
IMPORT led0
IMPORT伪指令用于通知标号led0在其它源文件中定义,但要在当前源文件中引用。
第五句:
b led0
B为最简单的跳转指令,当遇到B指令,ARM处理器就立即跳转到给定的目标地址,并从那里继续执行。
第六句:
END
END伪指令用于通知编译器已经到了源程序的结尾。
Led0.c主程序
程序控制流程:
将程序加载到开发板上,就可以看实验的结果了,其它LED灯的控制和LED0一样,只是寄存器的设置值不同罢了。
实验二、流水灯
终于能点亮一个LED灯了,那么它有什么用呢?它的主要用途在于一些状态的显示上,比如可以应用在开发板的电源指示灯上。但是这样会很单调,所以接下来我想让LED灯动起来,也就是4个灯顺序点亮,但是每次只有一个灯保持点亮状态,也就是所谓的流水灯或者跑马灯,那么怎样来实现呢?流水灯的原理就是点亮一个LED灯延迟一段时间后熄灭,然后点亮另外一个灯,延迟后熄灭,反复循环,就实现了流水的功能。知道原理了,程序编写就方便多了。
程序控制流程:
流水灯程序:
/******************************************
程序的目的:流水灯
I/O口与LED的对应关系:
GPB5 LED0
GPB6 LED1
GPB8 LED2
GPB10 LED3
********************************************/
/*---------地址声明----------*/
#include "2440addr.h"
注释:在本程序的开头,引入一个2440addr.h的头文件,该文件中定义了我们要常用的寄存器的地址,以后的程序中,只需引入即可。引入的方法是将这个头文件直接放在ADS安装文件的include中就行了。
/*---------变量声明---------*/
#define uint unsigned int
/*---------函数声明----------*/
void delay(uint); //延时函数
#define LED0_ON() (rGPBDAT&=~(1<<5)) //LED0点亮
#define LED1_ON() (rGPBDAT&=~(1<<6)) //LED1点亮
#define LED2_ON() (rGPBDAT&=~(1<<8)) //LED2点亮
#define LED3_ON() (rGPBDAT&=~(1<<10)) //LED3点亮
#define LED0_OFF() (rGPBDAT|=(1<<5)) //LED0熄灭
#define LED1_OFF() (rGPBDAT|=(1<<6)) //LED1熄灭
#define LED2_OFF() (rGPBDAT|=(1<<8)) //LED2熄灭
#define LED3_OFF() (rGPBDAT|=(1<<10)) //LED3熄灭
注释:这种对LED的控制方式非常方便。
/*-----------主函数----------*/
int LSD_Main(void)
{
rGPBCON=0x1dd7fc; //将GPB5、GPB6、GPB8、GPB10设置为输出
rGPBUP=0xfff; //禁止GPB端口上拉
rGPBDAT=((1<<5)|(1<<6)|(1<<8)|(1<<10)); //让LED灯全灭
while(1) //程序进入流水灯死循环
{
LED0_ON();
delay(30);
LED0_OFF();
LED1_ON();
delay(30);
LED1_OFF();
LED2_ON();
delay(30);
LED2_OFF();
LED3_ON();
delay(30);
LED3_OFF();
delay(30);
}
return 0;
}
/*---------延时函数---------*/
void delay(uint x)
{
uint i,j,k;
for(i=0;i<x;i++) </x;i++)
for(j=0;j<=0xff;j++)
for(k=0;k<=0xff;k++);
}
到现在,流水灯的实验就做完了。但是这种流水灯太过于单调,我想让LED灯按照我要求的方式流动,那应该怎么实现呢?那么就是下面要实现的花样灯实验的内容了。
实验三 花样灯
花样灯的目的就是让LED灯可以随意流动。
/******************************************
程序的目的:花样灯
I/O口与LED的对应关系:
GPB5 LED0
GPB6 LED1
GPB8 LED2
GPB10 LED3
********************************************/
/*---------地址声明----------*/
#include "2440addr.h"
/*---------变量声明---------*/
#define uint unsigned int
/*---------函数声明----------*/
void Delay(uint); //延时函数
void BoardInit(void);
uint tab[]={
0x29E,0xffe,0xfde,0xfbe,
0xefe,0xbfe,0xefe,0xfbe,
0xfde,0xebe,0xbde,0xffe};
注释:用数组方式来实现花样灯。
/*-----------主函数----------*/
int HYD_Main(void)
{
BoardInit(); //板子初始化
注释:将初始化放在一个函数中,这样使程序看起来清晰明了。
while(1) //程序进入花样灯死循环
{
int i;
for(i=0;i<13;i++)
{
rGPBDAT=tab;
Delay(20);
}
}
return 0;
}
/*---------延时函数---------*/
void Delay(uint x)
{
uint i,j,k;
for(i=0;i<x;i++) </x;i++)
for(j=0;j<=0xff;j++)
for(k=0;k<=0xff;k++);
}
/*---------初始化函数-------*/
void BoardInit( )
{
rGPBCON=0x1dd7fc; //将GPB5、GPB6、GPB8、GPB10设置为输出
rGPBDAT=((1<<5)|(1<<6)|(1<<8)|(1<<10)); //让LED灯全灭
rGPBUP=0xfff; //禁止GPB端口上拉
}
到此,LED灯的实验基本就做完了。
赞一个
赞一个小编 今天在看led的时候发现好多点亮led的程序中没有说明有关上拉电阻GPBUP的设置问题,GPBUP的初始化是0x00,根据这个原理图,个人认为还是没有必要打开上拉电阻的,所以应该是设置GPBUP为0xfff。
学习一下,谢谢分享
很不错啊是
好东西,谢谢分享
感谢小编分享~
谢谢分享
谢谢分享