笔记目录点这里:南邮(通达)计算机专业基础
熟悉裸板开发环境构建,掌握利用ADS开发工具或arm-linux-gcc开发工具编写裸板系统下程序的基本步骤和方法,掌握裸板程序的基本架构,熟悉汇编设计的基本指令和伪指令的使用方法,掌握S3C6410接口开发基本方法和步骤,并编程设计LED流水灯和看门狗程序设计。深刻体会软件控制硬件工作的基本思路和方法。
实验1.1 熟悉ADS开发工具或交叉编译器arm-linux-gcc的安装和基本使用
实验1.2 LED流水灯实验
实验1.3 看门狗实验
C 语言、微机接口等
硬件:ARM 嵌入式开发平台、PC 机Pentium100 以上、串口线。
软件: WinXP或UBUNTU开发环境。
步骤:
第一步,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。
第一步: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安装及使用。
本实验要求使用arm-linux-gcc编译。备注,控制LED1的GPIO口为GPM0
参看相关实验样例,编写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
后面的内容需要参考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值
}
#include "leddrv.h"
void delay()
{
volatile int i = 0x10000;
while (i--);
}
int main()
{
ledconfig();
// 跑马灯
while (1)
{
ledon(); // 小灯亮
delay(); // 停顿一段时间
ledoff();// 小灯灭
delay(); // 停顿一段时间
}
return 0;
}
编译步骤为:由于MakeFile已经编写好,我们只需要在终端输入 make
即可。会依次产生 start.o、main.o、leddrv.o、led.elf、led.bin文件。
步骤为:
(无法实现)
将实验板用micro usb数据线于微机相连,安装好驱动后用相应软件加载到内存中运行。
实验箱断电后再重新加电,能否再次观察到LED1不停闪烁现象(不能)。
步骤为:
(无法实现)
将实验板上相应按钮拨到 nand flash 档,用步骤3中的软件进行烧写
实验箱断电后再重新打开电源,能否再次观察到LED1不停闪烁现象(能)。
实验箱上共有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--);
}
#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);
}
(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;
}