【嵌入式实验】《ARM汇编与接口设计》

一.实验目的

熟悉裸板开发环境构建,掌握利用ADS开发工具或arm-linux-gcc开发工具编写裸板系统下程序的基本步骤和方法,掌握裸板程序的基本架构,熟悉汇编设计的基本指令和伪指令的使用方法,掌握S3C6410接口开发基本方法和步骤,并编程设计LED流水灯和看门狗程序设计。深刻体会软件控制硬件工作的基本思路和方法。

二.实验内容

实验1.1 熟悉ADS开发工具或交叉编译器arm-linux-gcc的安装和基本使用
实验1.2 LED流水灯实验
实验1.3 看门狗实验

三.预备知识

C 语言、微机接口等

四.实验设备及工具(包括软件调试工具)

硬件:ARM 嵌入式开发平台、PC 机Pentium100 以上、串口线。
软件: WinXP或UBUNTU开发环境。

五.实验步骤

5.1 ADS开发工具安装和使用

步骤:
第一步,ADS工具安装在(WINDOWS平台)下,按照类似于VC++开发工具的使用方法和步骤来进行使用。


下载地址是这个:ARM ADS开发工具,下不下无所谓,这个后面用不到


第二步,利用ADS打开demo项目模板,查看ADS中配置中几个重要选项。
第三步,参照demo项目代码结构,编写裸板程序完成两整数加和两整数减函数,分别用C代码实现,写出完成汇编启动代码和C代码。

汇编代码:

// init.S
AREA init,CODE,READONLY
ENTRY
MOV R13,  #0X33000000 ; 设置栈
BL Main ; 跳转到C入口
END

C语言代码:

//Main.c
int AddFun(int a,int b) {  return a + b; }
int SubFun(int a,int b) {  return a - b; }
int Main()
{   
	int a = 10;
	int b = 5;
	AddFun(a, b);
	SubFun(a, b);
	return 0;
}

第四步 用ADS自带的ARM模拟器调试上述代码,查看调用AddFun的汇编代码,可以看到变量a和变量b被编译器优化到寄存器(R0)、(R1)中,函数返回汇编语句为(MOV PC, R14),在这条语句中分别用到寄存器(PC)、(R14)。

插话

从这后面开始都是建立在 Linux 系统上的操作,建议使用 UBUNTU,我习惯用 DeepIn 系统,因此我的实验环境是 DeepIn

5.2 arm-linux-gcc编译工具安装和使用

第一步:arm-linux-gcc开发工具安装于(linux平台)下,按照类似于gcc开发工具的使用方法和步骤来进行使用。
第二步:参看相关实验样例,一般基于arm-linux-gcc编译的裸板程序通常包含汇编启动代码文件,C功能代码文件和make工具文件Makefile。

具体安装方法(可跳过)

可以去官网下载,http://www.linaro.org/downloads/
但是速度比较慢,可以直接用我下载好的。
链接: https://pan.baidu.com/s/1jL_G6kbTC9h_bF8HHXBWxw 提取码: 67u4

1.先把下载好的安装包移动到根目录下的tmp目录中(/tmp)

2.使用tar命令解压安装包,即在Terminal中输入以下命令:(前面的sudo表示使用root权限执行该命令)

sudo tar -xjvf /tmp/arm-linux-gcc-4.6.4-arm-x86_64.tar.bz2 -C /

注意是大写的字母C,此命令会把安装包解压到根目录下的opt的TuxamitoSoftToolchains里面(/opt/TuxamitoSoftToolchains)

3.解压完成后,再在(/usr/local)中创建一个新目录arm,即在Terminal中输入以下命令:

sudo mkdir /usr/local/arm

创建arm目录成功后,还需要给它解放全部权限,即在Terminal中输入以下命令:

sudo chmod 777 /usr/local/arm

4.在解压出来的目录中找到并把整个gcc-4.6.4目录复制到刚刚建好的arm目录中,命令如下:
先cd切换到gcc-4.6.4所在目录(切换后先ls看一下有没有gcc-4.6.4目录):

cd /opt/TuxamitoSoftToolchains/arm-arm1176jzfssf-linux-gnueabi/

再执行 cp 复制命令,-r 表示整个目录以及里面的任何东西

sudo cp -r gcc-4.6.4 /usr/local/arm

5.打开(/etc/profile)配置环境变量和库变量,目的是以后可以在任何位置使用该交叉编译器,命令如下:

sudo vi /etc/profile

用vi或者vim打开后,在文件最后添加两行,并输入以下代码:第一行是添加执行程序的环境变量,第二行是库文件的路径

export PATH=$PATH:/usr/local/arm/gcc-4.6.4/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/arm/gcc-4.6.4/lib

然后保存退出即可。
在这里插入图片描述
6.使用source命令重新加载生效该配置文件

source /etc/profile

7.检验是否安装成功,在 Terminal 输入以下命令输出版本信息:

arm-linux-gcc -v

在这里插入图片描述
具体使用参考 【嵌入式】Linux开发工具arm-linux-gcc安装及使用

5.3 LED流水灯设计实验

本实验要求使用arm-linux-gcc编译。备注,控制LED1的GPIO口为GPM0

步骤1:编写代码

参看相关实验样例,编写LED1报警灯代码,实现LED1以1秒左右的时间进行闪烁,要求LED驱动代码编写在leddrv.c中,功能代码编写在main.c文件中,启动代码文件和Makefile文件参照实验样例代码来设计。

启动代码文件

这个是不需要我们写的,直接用已有的即可。

// 启动代码
.global _start
_start:
// 把外设的基地址告诉CPU
//对于6410来说,内存(0x00000000~0x60000000),外设(0x70000000-0x7fffffff)
ldr r0, =0x70000000
orr r0, r0, #0x13	    //外设大小:256M
mcr p15,0,r0,c15,c2,4  //把r0的值(包括了外设基地址+外设大小)写给cpu
// 关看门狗
ldr r0, =0x7E004000
mov r1, #0
str r1, [r0]  
// 设置栈
ldr sp, =0x0c002000
// 调用C函数点灯
bl main
halt:0
b halt	

在这里插入图片描述

驱动层leddrv.c文件

后面的内容需要参考S3C6410的手册中GPM口部分。
在这里插入图片描述
题目要求是:实现LED1以1秒左右的时间进行闪烁,控制LED1的GPIO口为GPM0,因此我们可以翻一下芯片手册,找到对应的内容。
本实验要求完成LED流水灯设计,所以需要设置控制器中端口寄存器:
GPMCON----设置相应位为输出;
GPMDAT-----控制相应位输出高电平-----点亮LED灯,输出低电平-----熄灭LED灯。

#define rGPMCON  *(volatile unsigned long *)0x7F008820
#define rGPMDAT  *(volatile unsigned long *)0x7F008824
#include "leddrv.h"

void ledconfig()
{
    // 要控制的是LED1,控制LED1的GPIO口为GPM0
    // 由芯片手册可知GPM0对应bit0~3
    rGPMCON &= !(0xF<<0); // 把bit0~3清0
    rGPMCON |= (0x1<<0); // 把bit0~3置1

	return;
}

void ledon()
{
	rGPMDAT &= !(0x1<<0); //把bit0清0	
    rGPMDAT |= (0<<0); //把bit0写入0x0值
} 

void ledoff()
{
	rGPMDAT &= !(0x1<<0); //把bit0清0	
    rGPMDAT |= (0x1<<0); //把bit0写入0x1值
}

在这里插入图片描述

功能层main.c文件

#include "leddrv.h"

void delay()
{   
	volatile int i = 0x10000;
	while (i--);
}

int main()
{
    ledconfig();
    
	// 跑马灯
	while (1)
	    {
		    ledon(); // 小灯亮
		    delay(); // 停顿一段时间
		    ledoff();// 小灯灭
		    delay(); // 停顿一段时间
	}

	  return 0;
}

在这里插入图片描述

步骤2:编译

编译步骤为:由于MakeFile已经编写好,我们只需要在终端输入 make 即可。会依次产生 start.o、main.o、leddrv.o、led.elf、led.bin文件。
在这里插入图片描述

步骤3:加载到内存中运行

步骤为:
(无法实现)
将实验板用micro usb数据线于微机相连,安装好驱动后用相应软件加载到内存中运行。

实验箱断电后再重新加电,能否再次观察到LED1不停闪烁现象(不能)。

步骤4:烧写裸板程序到NAND设备

步骤为:
(无法实现)
将实验板上相应按钮拨到 nand flash 档,用步骤3中的软件进行烧写

实验箱断电后再重新打开电源,能否再次观察到LED1不停闪烁现象()。

步骤5:编写代码完成8个LED灯流水效果

实验箱上共有8个LED报警灯,分别有GPM0/GPM1/GPM2/GPM3/ GPM4/GPM5/GPQ1/GPQ2控制,编写代码完成8个LED灯流水效果(即先逐一点亮然后逐一熄灭,依次循环)。

这里需要参考S3C6410手册中GPQ口部分,我把用到的部分放出来。

在这里插入图片描述
在这里插入图片描述

#define rGPMCON  *(volatile unsigned long *)0x7F008820
#define rGPMDAT  *(volatile unsigned long *)0x7F008824
#define rGPQCON  *(volatile unsigned long *)0x7F008180
#define rGPQDAT  *(volatile unsigned long *)0x7F008184
#include "leddrv.h"
/**
 * 实验箱上共有8个LED报警灯
 * 分别由GPM0/GPM1/GPM2/GPM3/GPM4/GPM5/GPQ1/GPQ2控制
 * 实现8个LED灯流水效果(即先逐一点亮然后逐一熄灭,依次循环)
 */

void ledconfig()
{   
    /*
     * GPM0~GPM5 
     */
    rGPMCON &= !(0xFFFFFF<<0);// bit0~23清零
    // bit0,bit4,bit8,bit12,bit16,bit20置1
    rGPMCON |= (0x111111<<0);
    // 由于所有口都用上了,其实可以这么写 // srGPMDAT=0;

    /*
     * GPQ1~GPQ2
     */
    rGPQCON &= !(0xF<<2); // bit2~5清零
    rGPQCON |= (0x5<<2); // bit2,bit4置1,二进制0101==0x5

	return;
}

void iLedOn(unsigned int iLed){
	if(iLed < 6){
		rGPMDAT &= !(0x1<<iLed); // 清0
	   	rGPMDAT |= (0x0<<iLed); // 可省略
	} else if(iLed >=6 && iLed < 8){
		rGPMDAT &= !(0x1<<(iLed%6)); // 清0
		rGPMDAT |= (0x0<<(iLed%6)); // 可省略
	}else{
		return;
	}
}
void iLedOff(unsigned int iLed){
	if(iLed < 6){
		rGPMDAT &= !(0x1<<iLed); // 清0
   		rGPMDAT |= (0x1<<iLed); // 赋值	
	} else if(iLed >= 6 && iLed < 8){
		rGPMDAT &= !(0x1<<(iLed%6)); // 清0
		rGPMDAT |= (0x1<<(iLed%6)); // 赋值
	} else {
		return;
	}
}
void main(){
	for(i = 0; i < 8; i++){
		iLedOn(i++)
		delay();
	}
	for(i = 0; i < 8; i++){
		iLedOff(i++)
		delay();
	}
}

/*
 * 软件延时2秒
 */
void delay()
{
	volatile int i = 0x100000;
	while (i--);
}

子实验1:同时点亮和熄灭所有的LED灯

#define rGPMCON  *(volatile unsigned long *)0x7F008820
#define rGPMDAT  *(volatile unsigned long *)0x7F008824
#define rGPQCON  *(volatile unsigned long *)0x7F008180
#define rGPQDAT  *(volatile unsigned long *)0x7F008184
#include "leddrv.h"
/**
 * 实验箱上共有8个LED报警灯
 * 分别由GPM0/GPM1/GPM2/GPM3/GPM4/GPM5/GPQ1/GPQ2控制
 * 实现8个LED灯流水效果(即先逐一点亮然后逐一熄灭,依次循环)
 */

void ledconfig()
{   
    /*
     * GPM0~GPM5 
     */
    rGPMCON &= !(0xFFFFFF<<0);// bit0~23清零
    // bit0,bit4,bit8,bit12,bit16,bit20置1
    rGPMCON |= (0x111111<<0);
    // 由于所有口都用上了,其实可以这么写 // srGPMDAT=0;

    /*
     * GPQ1~GPQ2
     */
    rGPQCON &= !(0xF<<2); // bit2~5清零
    rGPQCON |= (0x5<<2); // bit2,bit4置1,二进制0101==0x5

	return;
}

void ledon()
{
    /*
     * GPM0~GPM5 
     */
    // bit0,bit1,bit2,bit3,bit4,bit5清0
	rGPMDAT &= !(0x3F<<0); // 二进制111111 == 0x3F
    // bit0,bit1,bit2,bit3,bit4,bit5写入0x0值
    rGPMDAT |= (0x0<<0);
    // 由于是所有口都用上了,其实可以这么写 // rGPMDAT=0x0F;


    /*
     * GPQ1~GPQ2
     */
    // bit1,bit2清0
    rGPQDAT &= !(0x3<<1); // 二进制11==0x3
    // bit1,bit2写入0x0值
    rGPQDAT |= (0x0<<1);
}

void ledoff()
{
	 /*
     * GPM0~GPM5 
     */
    // bit0,bit1,bit2,bit3,bit4,bit5清0
	rGPMDAT &= !(0x3F<<0); // 二进制111111 == 0x3F
    // bit0,bit1,bit2,bit3,bit4,bit5写入0x1值
    rGPMDAT |= (0x3F<<0); // 二进制111111 == 0x3F

    /*
     * GPQ1~GPQ2
     */
    // bit1,bit2清0
    rGPQDAT &= !(0x3<<1); // 二进制11==0x3
    // bit1,bit2写入0x1值
    rGPQDAT |= (0x3<<1);
}

5.4 参照相关参考样例,将5.3实验嵌入式看门狗驱动代码,完成硬件看门功能

(1)汇编启动代码

IMPORT Main
AREA Init, CODE READONLY		
ENTRY	
_start
MOV  sp, #0x33000000
B Main;  /*跳转到C语言程序*/
END

(2)C 主函数设计

#define rWTDAT*(volatile unsigned long *)0x7E004004
#define rWTCNT*(volatile unsigned long *)0x7E004008

void openWTDog()
{
	rWTCNT = 0XFFFF; 
	rWTDAT= 0XFFFF;
	RWTCON = (0XFF<<8)||(0<<5)||(3<<8)||(0<<2)||(1<<0); 
} 
void feedWTDog() 
{ 
	rWTCNT = 0XFFFF; 
} 
int Main() 
{	
	int i,j;
	clock_init(); 	// 时钟初始化
					// 设置PCLK=66.5MHz
	uart_init(); 	// 串口初始化
	uart_sendString( “\r\nWATCHDOG TEST\r\n”); 
	openWTDog(); 
	while(1)
	{
	for( i=0;i<10000;i++)
		for( j=0;j<10000;j++); // 延时
	uart_send("I am alive now! \r\n");
	feedWTDog(); 
	}	
	return 0;
}
发布了178 篇原创文章 · 获赞 59 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43734095/article/details/105363524