很简单的代码,昨晚却折腾了很久,没有仔细看datasheet手册。
先说下原理:GPIOB5 6 7 8分别控制着led1 2 3 4,GPBCON的[11,10],[13,12],[15,14],[17,16]bit分别控制GPIOB的输入输出状态,01为输出状态(见datasheet),GPBDAT0 1 2 3位分别控制各led的数据,输出状态下当为0低电平的时候,对应的led会亮。
GPIOF1 4 2 0引脚控制着按键k1 k2 k3 k4,我们需要读取GPIOF的值来判断按键是否有按下,所以GPIOF要置为输入状态(GPFCON对应的各引脚为00),当有按键按下的时候,会产生一个低电平0,此时我们来轮询GPFDAT的值可以判断到是哪个键被按下。
代码如下:
汇编代码start.S
@led start @2010-01-12 @jay .globl _start _start: b reset @预留着以后扩展中断向量表 reset: @disable watchdog ldr r0,=0x53000000 mov r1,#0 str r1,[r0] @setup stack ldr r0,=1024*4 mov sp,r0 @call main bl main b . @loop
因为是裸机跑到程序,主程序又是c,所以要建立基本的c环境,建立堆栈,关闭看门狗以免系统不停的重启,我的程序是准备copy进cpu的片内ram中运行,其只有4k大小,所以栈顶不能超出这个范围。
c代码main.c
#include"main.h" void key_ctl(); void delay(int); int main() { GPBCON =GPB05OUT | GPB06OUT | GPB07OUT | GPB08OUT ; //置GPB为输出状态 key_ctl(); return 0; } //延时函数 void delay(int times) { int i,j; for(i=0;i<times;i++) { for(j=0;j<(1<<18);j++); } } //用按键控制led灯,其GPFCON设置默认的就是输入状态00 所以不需要另外设置 void key_ctl() { unsigned char key; while(1){ key= GPFDAT&0x1f; //保存低五位 if(key==0x1d) //0x1d=11101 也就是GPFDAT的01bit为0,对应K1键 GPBDAT =~(1<<5); else if(key==0x0f) //01111如上分析 GPBDAT =~(1<<6); else if(key==0x1b) GPBDAT =~(1<<7); else if(key==0x1e) GPBDAT =~(1<<8); else GPBDAT =0xffffffff; } }
c代码中按键和led的对应可能有点饶,我昨晚就是饶的晕头了 ,其实很简单,动手写出对应关系就好
K1(GPF1) ------Led1(GPB5)
K2(GPF4) ------Led2(GPB6)
K3(GPF2) ------Led3(GPB7)
K4(GPF0) ------Led4(GPB8)
根据读k的GPFDAT来写Led的GPFDAT
下面的是头文件main.h
//led fanction #define GPBCON (*(volatile unsigned long *)0x56000010) //先将地址值其转化为指针,GPBCON其实就变成了这个地址储存的值鸟 哈哈 #define GPBDAT (*(volatile unsigned long *)0x56000014) #define GPBUP (*(volatile unsigned long *)0x56000018) //key addr #define GPFCON (*(volatile unsigned long *)0x56000050) #define GPFDAT (*(volatile unsigned long *)0x56000054) #define GPFUP (*(volatile unsigned long *)0x56000058) #define GPB05OUT (1<<(5*2)) // datasheet info led1 out:01 #define GPB06OUT (1<<(6*2)) #define GPB07OUT (1<<(7*2)) #define GPB08OUT (1<<(8*2)) #define GPF00OUT (1<<(0*2)) #define GPF01OUT (1<<(1*2)) #define GPF02OUT (1<<(2*2)) #define GPF03OUT (1<<(3*2))
用来定义一些地址
最后的是Makefile
CC=arm-linux-gcc LD=arm-linux-ld OBJ=arm-linux-objcopy #led.bin:start.S main.c # $(CC) -c -g -Wall -o start.o start.S # $(CC) -c -g -Wall -o main.o main.c # $(LD) -Ttext 0x00000000 -g -Bdynamic -lgcc_s start.o main.o -o led_elf # $(OBJ) -O binary -S led_elf led.bin all: $(CC) -mabi=aapcs-linux -mno-thumb-interwork -Os -g -Wall -c start.S main.c $(LD) -Ttext 0x00000000 -Bstatic -g start.o main.o $(OBJ) -O binary -S a.out ledboot.bin # -R .comment -R .stab -R .stabstr clean: rm *.o *.bin
被注释掉的部分其实也是可以的,但是生成的bin文件竟然有32K,远大于4k,所以我改用了下面的
其实使用的部分一些参数为也还没有理解,以后学会了就补上
关于注释掉的部分ld的参数-Bdynamic -lgcc_s是google来的,为了避免一个错误
======================================================================
下面说下怎么运行这段程序
我是用jlink下载到cpu片内ram中运行的,先将TQ2440置为nand flash启动,nand flash启动的原理是,nand 会自动将自己的前4k代码复制到cpu的片内ram中,此ram只有4k。此时的片内ram的地址(本来是0x40000000)被映射到0x0地址,所以直接会从此ram运行,这些都是硬件完成的,我们无需干涉,我们能做的就是nand的前4k代码。嗯,扯远了点,但知道了这一原理我们就可以进行下面的操作了
链接好jlink和开发板,打开jlink commader,它会自动识别开发板的信息,
在命令行输入:
loadbin d:/led.bin 0
setpc 0
g
下载命令 目标文件位置 下载到的位置
设置pc值
跳转到0
如果你想从nor flash启动,也是可以的,此时片内ram未映射,地址为0x40000000,所以稍加改动即可
loadbin d:/led.bin 0x40000000
setpc 0x40000000
g
ok,到这里一切大功告成了 ,可以试验下,是否有效了,如果有出入,请仔细对照datasheet和开发板原理图修改