本文使用的开发板是九鼎创展的X210 iNand版本。
从以上LED的原理图上,我们发现开发板上一共有5颗LED,其中一颗D26的接法是:正极接5V,负极接地。因此D26这颗LED只要上电就会常亮,所以这颗LED是电源指示灯。
剩下4颗LED的接法是:正极接3.3V,负极接了SoC上的一个引脚(GPIO)。
具体的详细接法如下:
D22:GPJ0_3
D23:GPJ0_4
D24:GPJ0_5
D25:PWMTOUT1(GPD0_1)
二、查阅数据手册中GPJ0的相关部分
由上图可知,GPJ0相关的寄存器包括六个:GPJ0CON、GPJ0DAT、GPJ0PUD、GPJ0DRV、GPJ0CONPDN、GPJ0PUDPDN。实际上真正控制LED的主要是GPJ0CON、GPJ0DAT这两个。
点亮LED,主要的步骤是:
1、GPJ0CON寄存器中,选中Output模式
由上图可以得出以下几点信息:
(1)GPJ0一共有8个引脚,分别记作GPJ0_0 ~ GPJ0_7。
(2)GPJ0CON寄存器每个引脚占4位,例如GPJ0_0对应的bit位为bit0 ~ bit3,GPJ0_3对应的bit位为bit12 ~ bit15。给相应的寄存器位写入相应的值,该引脚硬件就会按照相应的模式去工作,例如给bit12 ~ bit15写入0001,GPJ0_3引脚就成为输出模式了。
(3)GPJ0CON寄存器的地址是0xE0200240,GPJ0DAT寄存器的地址是0xE0200244。
(4)低电平亮、高电平灭。
三、代码结构
(1)Makefile:编译时使用make得到led.bin(USB启动下载的镜像)和210.bin(SD卡启动下载的镜像)。
led.bin: start.o arm-linux-ld -Ttext 0x0 -o led.elf $^ arm-linux-objcopy -O binary led.elf led.bin arm-linux-objdump -D led.elf > led_elf.dis gcc mkv210_image.c -o mkx210 ./mkx210 led.bin 210.bin %.o : %.S arm-linux-gcc -o $@ $< -c %.o : %.c arm-linux-gcc -o $@ $< -c clean: rm *.o *.elf *.bin *.dis mkx210 -f
(2)mkv210_image.c:为BL1添加校验头,由led.bin加工得到210.bin。
#include <stdio.h> #include <string.h> #include <stdlib.h> #define BUFSIZE (16*1024) #define IMG_SIZE (16*1024) #define SPL_HEADER_SIZE 16 #define SPL_HEADER " S5PV210 HEADER " int main (int argc, char *argv[]) { FILE *fp; char *Buf, *a; int BufLen; int i; int nbytes, fileLen; unsigned int checksum, count; // 1. 3个参数 if (argc != 3) { printf("Usage: %s <source file> <destination file>\n", argv[0]); return -1; } // 2. 分配16K的buffer BufLen = BUFSIZE; Buf = (char *)malloc(BufLen); if (!Buf) { printf("Alloc buffer failed!\n"); return -1; } memset(Buf, 0x00, BufLen); // 3. 读源bin到buffer // 3.1 打开源bin fp = fopen(argv[1], "rb"); if(fp == NULL) { printf("source file open error\n"); free(Buf); return -1; } // 3.2 获取源bin长度 fseek(fp, 0L, SEEK_END); // 定位到文件尾 fileLen = ftell(fp); // 得到文件长度 fseek(fp, 0L, SEEK_SET); // 再次定位到文件头 // 3.3 源bin长度不得超过16K-16byte count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE)) ? fileLen : (IMG_SIZE - SPL_HEADER_SIZE); // 3.4 buffer[0~15]存放" S5PV210 HEADER " memcpy(&Buf[0], SPL_HEADER, SPL_HEADER_SIZE); // 3.5 读源bin到buffer[16] nbytes = fread(Buf + SPL_HEADER_SIZE, 1, count, fp); if (nbytes != count) { printf("source file read error\n"); free(Buf); fclose(fp); return -1; } fclose(fp); // 4. 计算校验和 // 4.1 从第16byte开始统计buffer中共有几个1,把buffer中所有的字节数据加和起来得到的结果 a = Buf + SPL_HEADER_SIZE; for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++) checksum += (0x000000FF) & *a++; // 4.2 将校验和保存在buffer[8~15] a = Buf + 8; // Buf是210.bin的起始地址,+8表示向后位移2个字,也就是说写入到第3个字 *( (unsigned int *)a ) = checksum; // 5. 拷贝buffer中的内容到目的bin // 5.1 打开目的bin fp = fopen(argv[2], "wb"); if (fp == NULL) { printf("destination file open error\n"); free(Buf); return -1; } // 5.2 将16k的buffer拷贝到目的bin中 a = Buf; nbytes = fwrite( a, 1, BufLen, fp); if (nbytes != BufLen) { printf("destination file write error\n"); free(Buf); fclose(fp); return -1; } free(Buf); fclose(fp); return 0; }
(3)start.S:控制LED的汇编源代码。
四、编码控制LED
1、点亮中间一颗LED(GPJ0_4)
#define GPJ0CON 0xE0200240 #define GPJ0DAT 0xE0200244 .global _start _start: // 把所有引脚都设置为输出模式 ldr r0, =0x11111111 ldr r1, =GPJ0CON str r0, [r1] // 把中间一颗LED(GPJ0_4)的输出设置为0,其余两颗设置为1,剩下的其它位不管 ldr r0, =((1<<3) | (1<<5)) ldr r1, =GPJ0DAT str r0, [r1] b .
2、LED闪烁效果
#define GPJ0CON 0xE0200240 #define GPJ0DAT 0xE0200244 .global _start _start: // 把所有引脚都设置为输出模式 ldr r0, =0x11111111 ldr r1, =GPJ0CON str r0, [r1] flash: // 全部点亮 ldr r0, =((0<<3) | (0<<4) | (0<<5)) ldr r1, =GPJ0DAT str r0, [r1] // 延时 bl delay // 使用bl进行函数调用 // 全部熄灭 ldr r0, =((1<<3) | (1<<4) | (1<<5)) ldr r1, =GPJ0DAT str r0, [r1] // 延时 bl delay b flash // 延时函数 delay: ldr r2, =9000000 ldr r3, =0x0 delay_loop: sub r2, r2, #1 // r2 = r2 -1 cmp r2, r3 // cmp会影响Z标志位,如果r2等于r3则Z=1,下一句中eq就会成立 bne delay_loop mov pc, lr // 函数调用返回
3、流水灯效果
#define GPJ0CON 0xE0200240 #define GPJ0DAT 0xE0200244 .global _start _start: // 把所有引脚都设置为输出模式 ldr r0, =0x11111111 ldr r1, =GPJ0CON str r0, [r1] flash: // 点亮LED1(GPJ0_3),其它熄灭 ldr r0, =~(1<<3) ldr r1, =GPJ0DAT str r0, [r1] bl delay // 点亮LED2(GPJ0_4),其它熄灭 ldr r0, =~(1<<4) ldr r1, =GPJ0DAT str r0, [r1] bl delay // 点亮LED3(GPJ0_5),其它熄灭 ldr r0, =~(1<<5) ldr r1, =GPJ0DAT str r0, [r1] bl delay b flash // 延时函数 delay: ldr r2, =9000000 ldr r3, =0x0 delay_loop: sub r2, r2, #1 cmp r2, r3 bne delay_loop mov pc, lr