从51到ARM裸机开发实验(005)LPC2138 GPIO实验

        仿照“从51到ARM裸机开发实验(003) AT89C51 GPIO实验”,同样实现这样一种场景:四个按键作为开关、四个LED作为响应,每个开关控制一盏灯,按一次开灯,再按一次关灯。再接入一个蜂鸣和另外两个按键作为音量加和音量减,音量加每按一次蜂鸣器音量增加5%,音量减每按一次音量减少5%。这次将MCU替换成LPC2138芯片,LPC2138是飞利浦公司推出的基于ARM7TDMI-S内核的微控制器,属ARMv4架构。与STM32同属ARM系列,其编程方式也与STM32类似。

一、仿真电路图

P0口:P0口是一个32 位双向I/O口,每位的方向可单独控制。P0口共有31个管脚可用作通用双向数字I/O口,但 P0.31 是一个仅为输出口。P0 口的功能取决于管脚连接模块的管脚功能选择。P0.24脚未使用,在仿真图中也未显示。

P1口:P1口是一个32位双向I/O口,每位的方向可单独控制。P1口的功能取决于管脚连接模块的管脚功能选择。P1.0~P1.15不可用,P1.16~P1.31可用。

从芯片手册得知,LPC2138的P0口是没有内置上拉/下拉电阻的,也没有相关寄存器可以配置上拉/下拉。所以按键检测电路中需要外接上拉电阻,将端口上拉成为默认高电平(逻辑1),否则按键检测程序无法读取正确的按键状态。

二、使用Keil5开发LPC2138

1、地址映射

1.1、管脚控制模块寄存器

① 、管脚功能选择寄存器 0 (PINSEL0 - 0xE002C000)

从图中可以看出寄存器PINSEL0共有32位,每两位控制一个引脚的功能,32位控制P0.0~P0.15共16个引脚的功能。当两位为00时,此引脚为基本GPIO功能,当两位值为01、10、11时则对应着引脚的其他功能。

②、管脚功能选择寄存器 1(PINSEL1 - 0xE002C004)

同样,寄存器PINSEL1也是32位,每两位控制一个引脚的功能,32位控制P0.16~P0.31共16个引脚的功能。当两位为00时,此引脚为基本GPIO功能,当两位值为01、10、11时则对应着引脚的其他功能。

③、管脚功能选择寄存器2(PINSEL2 - 0xE002C014)

寄存器PINSEL2用于控制P1端口,它有些特殊,0~1位和4~31位都是保留位。仅使用2、3位,使用方式如上图所示。本场景中没有使用到P1端口。

1.2、管脚作为GPIO时相关寄存器

P0口共32个管脚,其中有30个输入/输出管脚,1个管脚仅用作输出(P0.31),1个未使用(P0.24)。P1口有多达16个管脚可用作GPIO功能。PORT0和PORT1由2组(4个)寄存器控制。

①、GPIO 管脚值寄存器(IO0PIN - 0xE0028000, IO1PIN – 0xE0028010)
该寄存器提供GPIO管脚的值。它反映了外部环境仅对GPIO管脚的影响。IOPIN寄存器不能监测非GPIO的管脚配置,因此不能使用IOPIN寄存器来指示非GPIO配置管脚的活动。

②、GPIO 输出置位寄存器(IO0SET - 0xE0028004, IO1SET – 0xE0028014)
当管脚配置为GPIO输出模式时,可使用该寄存器从管脚输出高电平。写入1使对应管脚输出高电平,写入0无效。如果一个管脚被配置为输入或第二功能,写IOSET无效。读IOSET寄存器返回GPIO输出寄存器中的值。该值由前一次对IOSET和IOCLR(或前面提到的IOPIN)的写操作决定。该值不反映任何外部环境对 I/O 管脚的影响。

③、GPIO 输出清零寄存器(IO0CLR - 0xE002800C, IO1CLR – 0xE002801C)
当管脚配置为GPIO输出模式时,可使用该寄存器从管脚输出低电平。写入1使对应管脚输出低电平并清零IOSET寄存器中相应的位。写入0无效。如果一个管脚被配置为输入或第二功能,写 IOCLR 无效。

 ④、GPIO方向寄存器(IO0DIR - 0xE0028008, IO1DIR – 0xE0028018)
当管脚配置为GPIO模式时,可使用该寄存器控制管脚的方向。任意管脚的方向位的设置必须与管脚功能一致。

 此处仅列举本场景要是用的寄存器,更多芯片详细信息请阅读芯片手册。

2、创建LPC2138工程

2.1、下载安装ARMCC编译器

使用Keil开发LPC2138之前需要安装ARMCC编译器,否则无法进行编译。

ARM官方下载地址:Arm Compiler for Embedded FuSa

其他下载地址:ARMCompiler-506-Windows-x86-b960

由于某些原因,官方下载有时候下载不了,我把它上传到CSDN上了,需要的可以下载。或者通过其他渠道下载的也可以。下载的时候注意其支持的MCU架构,如本场景中我们使用的LPC2138属于ARMv4架构。

 下载完之后解压安装包,双击setup开始安装

 一路next,直到让选择安装目录

在Keil-MDK-ARM的安装目录下新建编译器的安装文件夹,注意必须安装到此处,否则可能有license问题。安装文件夹可以还使用名称ARM_Compiler_5.06u7,这样以后安装其他版本编译器的时候也使用各自的名称,不会弄混淆。

 之后一路next,直到安装完成。

2.2、创建工程

新建Project,芯片选择如下

 配置刚才安装的编译器。按照如下两张图所示1、2、3、4、5、6的顺序配置。

 选择使用刚安装配置的那个编译器

 仿照以前的项目建立分组、文件夹、文件,并设置好头文件目录。

3、GPIO驱动程序

3.1、LED驱动

led.h

#ifndef _LED_H_
#define _LED_H_
	void led_init();
	void led_on(unsigned char site);
	void led_off(unsigned char site);
	char get_led_status(unsigned char site);
	void led_operate(unsigned char site,unsigned char on_off);
#endif

led.c

#include "led.h"

#define PINSEL0 (*(volatile unsigned long *)0xE002C000)
#define IO0PIN (*(volatile unsigned long *)0xE0028000)
#define IO0DIR  (*(volatile unsigned long *)0xE0028008)

void led_init(){
	//PINSEL0最后八位置0 P0.0~P0.3为GPIO模式
	PINSEL0 = PINSEL0 & 0xffffff00;
	//PINSEL0最后四位置1 P0.0~P0.3为输出模式
	IO0DIR = IO0DIR | 0x0f;
}

void led_on(unsigned char site){
	led_init();
	switch(site){
			case 0: 
				IO0PIN &= ~(0x01); 	  //P0.0置0
				break;
			case 1:
				IO0PIN &= ~(0x01<<1); //P0.1置0
				break;
			case 2: 
				IO0PIN &= ~(0x01<<2); //P0.2置0
				break;
			case 3: 
				IO0PIN &= ~(0x01<<3); //P0.3置0
				break;
			default:
				break;
	}
}

void led_off(unsigned char site){
	led_init();
	switch(site){
			case 0: 
				IO0PIN |= (0x01); 	 //P0.0置1
				break;
			case 1:
				IO0PIN |= (0x01<<1); //P0.1置1
				break;
			case 2: 
				IO0PIN |= (0x01<<2); //P0.2置1
				break;
			case 3: 
				IO0PIN |= (0x01<<3); //P0.3置1
				break;
			default:
				break;
	}
}

char get_led_status(unsigned char site){
		switch(site){
			case 0: 
				return (IO0PIN >> 0) & (0x01);
			case 1:
				return (IO0PIN >> 1) & (0x01);
			case 2: 
				return (IO0PIN >> 2) & (0x01);
			case 3: 
				return (IO0PIN >> 3) & (0x01);
			default:
				return -1;
	}
}
//on_off 0:开灯 1:关灯
void led_operate(unsigned char site,unsigned char on_off){
	if(on_off == 0){
		led_on(site);
	}else if(on_off == 1){
		led_off(site);
	}
}

3.2、Key驱动

key.h

#ifndef _KEY_H_
#define _KEY_H_
	void key_init();
	char scan_keyboard();
#endif

key.c

#include "delay.h"
#include "key.h"

#define PINSEL0 (*(volatile unsigned long *)0xE002C000)
#define IO0PIN (*(volatile unsigned long *)0xE0028000)
#define IO0DIR  (*(volatile unsigned long *)0xE0028008)

#define GET_IO0PIN(x) ((IO0PIN >> x) & (0x01))

void key_init(){
	//PINSEL0第九位~第二十位置0 P0.4~P0.9为GPIO模式
	PINSEL0 = PINSEL0 & 0xfff000ff;
	//PINSEL0第五位~第十位置0 P0.4~P0.9为输入模式
	IO0DIR = IO0DIR & 0xfffffc0f;
}

char scan_keyboard(){ //返回当前操作过的按键位置
	key_init();
	char site = -1;
	if(GET_IO0PIN(4) == 0){
		delayms(10);
		if(GET_IO0PIN(4) == 0){
			while(GET_IO0PIN(4)==0);
			site = 0;
		}
	}else if(GET_IO0PIN(5) == 0){
		delayms(10);
		if( GET_IO0PIN(5) == 0){
			while(GET_IO0PIN(5) == 0);
			site = 1;
		}
	}else if(GET_IO0PIN(6) == 0){
		delayms(10);
		if( GET_IO0PIN(6) == 0){
			while(GET_IO0PIN(6) == 0);
			site = 2;
		}
	}else if(GET_IO0PIN(7) == 0){
		delayms(10);
		if( GET_IO0PIN(7) == 0){
			while(GET_IO0PIN(7) == 0);
			site = 3;
		}
	}else if(GET_IO0PIN(8) == 0){
		delayms(10);
		if( GET_IO0PIN(8) == 0){
			while(GET_IO0PIN(8) == 0);
			site = 4;
		}
	}else if(GET_IO0PIN(9) == 0){
		delayms(10);
		if( GET_IO0PIN(9) == 0){
			while(GET_IO0PIN(9) == 0);
			site = 5;
		}
	}
	return site;
}

delay.h

#ifndef _DELAY_H_
#define _DELAY_H_
	void delayms(unsigned int xms);
#endif

delay.c

#include "delay.h"

void delayms(unsigned int xms){	//毫秒级延时函数
	unsigned int i,j;
	for(i=xms;i>0;i--){
		for(j=2500;j>0;j--);
	}
}

3.3、蜂鸣器驱动

buzzer.h

#ifndef _BUZZER_H_
#define _BUZZER_H_
	void buzzer_init();
	void buzzer_open();
	void buzzer_off();
#endif

buzzer.c

#include "buzzer.h"

#define PINSEL0 (*(volatile unsigned long *)0xE002C000)
#define IO0PIN (*(volatile unsigned long *)0xE0028000)
#define IO0DIR  (*(volatile unsigned long *)0xE0028008)

void buzzer_init(){
	//PINSEL0第21位、22位置0 P0.10为GPIO模式
	PINSEL0 = PINSEL0 & 0xffcfffff;
	//PINSEL0第11位置1 P0.10为输出模式
	IO0DIR |= (0x01<<10); 
}

void buzzer_open(){
	buzzer_init();
	IO0PIN |= (0x01<<10); //P0.10置1
}

void buzzer_off(){
	buzzer_init();
	IO0PIN &= ~(0x01<<10); //P0.10置0
}

4、GPIO应用程序

application.c

#include "led.h"
#include "key.h"
#include "delay.h"
#include "buzzer.h"
#define MAX_VOL 20
#define MIN_VOL 1

int main(void){
	
	led_operate(0,1);
	led_operate(1,1);
	led_operate(2,1);
	led_operate(3,1);
	
	unsigned char volume = 10;
	//周期计数,忽略键盘扫描,用于蜂鸣器控制
	//如volume = 10,即20个周期有10个输出高电平
	unsigned char cycle = 0;
	while(1){
		unsigned char key_site = scan_keyboard(); //扫描按键状态
		char led_status = -1;
		//便于观察蜂鸣器指示灯频率
		delayms(10);
		cycle++;
		switch(key_site){
				case 0:												//按键一被按了一次
					led_status = get_led_status(0);
					if(led_status == 0){
							led_operate(0,1);						//D1状态改变
					}else if(led_status == 1){
							led_operate(0,0);						//D1状态改变
					}
					break;
					
				case 1:
					led_status = get_led_status(1);
					if(led_status == 0){
							led_operate(1,1);						//D2状态改变
					}else if(led_status == 1){
							led_operate(1,0);						//D2状态改变
					}
					break;
					
				case 2:
					led_status = get_led_status(2);
					if(led_status == 0){
							led_operate(2,1);						//D3状态改变
					}else if(led_status == 1){
							led_operate(2,0);						//D3状态改变
					}
					break;
					
				case 3:
					led_status = get_led_status(3);
					if(led_status == 0){
							led_operate(3,1);						//D4状态改变
					}else if(led_status == 1){
							led_operate(3,0);						//D4状态改变
					}
					break;
					
				case 4:												//蜂鸣器音量+
					if(volume < MAX_VOL){
						volume++;
					}
					break;
				
				case 5:												//蜂鸣器音量-
					if(volume > MIN_VOL){
						volume--;
					}
					break;
				
				default:
					break;
		}
		if(cycle >= 0){
			buzzer_open();
		}if(cycle >= volume){
			buzzer_off();
		}if(cycle >= (MAX_VOL+1)){
			cycle = 0;
		}
	}
	return 0;
}

5、编译并仿真

5.1、编译出hex文件。其生成位置在Keil5_LPC2138_GPIO_Project\Objects

  5.2、双击LPC2138芯片加载.axf或.hex文件

axf文件、hex文件、bin文件都是可以运行在ARM核MCU上的,它们都存储了编译器根据源代码生成的机器码,根据应用场合的不同,它们又有所区别。axf文件:包含调试信息。hex文件:包含地址信息。bin文件:最直接的代码映像。

三、资料下载 

源码与仿真电路下载地址:https://download.csdn.net/download/qq_54140018/87693458

芯片手册与参考资料下载地址:https://download.csdn.net/download/qq_54140018/87646124

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值