iTop-4412 裸机程序(三)- 点亮一颗LED灯源码分析

目录

  • 代码文件分析
  • led 原理图分析
  • start.S
  • link.lds
  • mkbl1
  • Makefile


代码文件分析

root@ubuntu:~/4412NoOS/led/1.0_点亮一颗led# ls
link.lds  Makefile  mkbl1.c  s3c4412_gpio.h  start.S

start.S : 汇编文件,编译生成最后的 led.bin 文件
s3c4412_gpio.h : Exynos4412 上的 GPIO 寄存器地址
link.lds : 链接脚本文件。规定如何把输入文件内的 section 放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局。
mkbl1.c : mkbl1 的源文件,给 led.bin 文件添加头。
Makefile : 编译脚本

led 原理图分析

iTop-4412 裸机程序(三)- 点亮一颗LED灯源码分析_第1张图片如图,KP_COL0 和 VDD50_EN 输入高点平信号时,NPN 三级管导通 LED 灯亮
iTop-4412 裸机程序(三)- 点亮一颗LED灯源码分析_第2张图片iTop-4412 裸机程序(三)- 点亮一颗LED灯源码分析_第3张图片由原理图可知 KP_COL0 和 VDD50_EN 对应的引脚分别是 GPL2_0 和 GPK1_1 。再由 Exynos4412 得知对应的 GPIO 寄存器地址。使用宏定义写到 s3c4412_gpio.h 文件中。

#ifndef S3C4412_GPIO_H
#define S3C4412_GPIO_H

#define GPIOBASE 0x11000000

#define GPK1CON (GPIOBASE + 0x0060)
#define GPK1DAT (GPIOBASE + 0x0064)
#define GPK1PUD (GPIOBASE + 0x0068)
#define GPK1DRV (GPIOBASE + 0x006C)

#define GPL2CON (GPIOBASE + 0x0100)
#define GPL2DAT (GPIOBASE + 0x0104)
#define GPL2PUD (GPIOBASE + 0x0108)
#define GPL2DRV (GPIOBASE + 0x010C)

#define GPX0CON (GPIOBASE + 0x0C00)
#define GPX0DAT (GPIOBASE + 0x0C04)
#define GPX0PUD (GPIOBASE + 0x0C08)
#define GPX0DRV (GPIOBASE + 0x0C0C)

#endif

start.S

#include "s3c4412_gpio.h"

#define POW_MNG_UNIT_BASE 0x10020000 
#define PS_HOLD_CONTROL (POW_MNG_UNIT_BASE + 0x330C)
/* 占位符 4字共16字节,预留用来加 bl1 校验头,填充什么数据无所谓 */
.word 0x0
.word 0x0
.word 0x0
.word 0x0

_start:
	
	/* 将 PS_HOLD_CONTRO 置为输出模式且使能。如果不设置在设备冷启动时程序运行一遍后就停止,重新按复位才会重新运行程序。详细原因后面再介绍 */
	ldr r0, =PS_HOLD_CONTROL
	ldr r1, [r0]
	#orr r1, r1, #0x300
	ldr r1, =0x0300
	str r1, [r0]

	/* 将 GPX0PUD Disable上下拉模式,如果不设置发现程序每隔几秒重新运行。这里原理还没理解,知道后再补充 */
	ldr r0, =GPX0PUD
	ldr r1, =0x0
	str r1, [r0]

	/* 设置 GPL2 为输出模式 */
	ldr r0, =GPL2CON
	mov r1, #(0x01 << 0)
	str r1, [r0]

	/* 设置 GPK1 为输出模式 */
	ldr r0, =GPK1CON
	mov r1, #(0x01 << 4)
	str r1, [r0]

	/* 设置 GPK1_1 为低电平 */
	ldr r0, =GPK1DAT
	mov r1, #0x0
	str r1, [r0]

	/* 设置 GPL2_0 为高电平 */
	ldr r0, =GPL2DAT
	mov r1, #0x1
	str r1, [r0]
b .

link.lds

SECTIONS
{
	/* 指定链接地址为 0x02023400, 需保证位置有关代码需运行在相应的内存地址上 */
	. = 0x02023400;
	/* 代码段(文本段),存储代码编译后的二进制文本 */
	.text : {
			start.o
			* (.text)
	}
	/* 只读数据段(read only data) */
	.rodata ALIGN(4) : {
		*(.rodata*)
	}
  /* 数据段 */
	.data ALIGN(4) : {
		*(.data)
	}
	/* bss段(Block Started by Symbol)未初始化的或者初始化为 0 的全局变量和静态变量,又叫 ZI 段(Zero Inital) */
	bss_start = .;
	.bss ALIGN(4): {
			* (.bss)
			* (COMMON)
	}
	
	bss_end = .;
}

mkbl1

#include 
#include 
#include 

#define BUFFER_SIZE (8 * 1024)
#define HEADER_SIZE (16)

int main(int argc, char *argv[])
{
	FILE *fp;
	unsigned char buffer[BUFFER_SIZE];
	unsigned char header[HEADER_SIZE];
	unsigned int checksum, count;
	int i, len;

	if (argc != 2) 
	{
		printf("Usage: mkbl1 \n");
		return -1;
	}

	fp = fopen(argv[1], "r+b");
	if (fp == NULL) 
	{
		printf("Can not open file '%s'\n", argv[1]);
		return -1;
	}

	fseek(fp, 0L, SEEK_END);
	len = ftell(fp);
	count = (len < BUFFER_SIZE) ? len : BUFFER_SIZE;

	fseek(fp, 0L, SEEK_SET);
	memset(buffer, 0, sizeof(buffer));
	if (fread(buffer, 1, count, fp) != count) 
	{
		printf("Can't read %s\n", argv[1]);
		fclose(fp);
		return -1;
	}

	for (i = 16, checksum = 0; i < count; i++) 
	{
		checksum += buffer[i] & 0xff;
	}

	memset(header, 0, sizeof(header));

	header[3] = (0x1f >> 24) & 0xff;
	header[2] = (0x1f >> 16) & 0xff;
	header[1] = (0x1f >> 8) & 0xff;
	header[0] = (0x1f >> 0) & 0xff;

	header[7] = (checksum >> 24) & 0xff;
	header[6] = (checksum >> 16) & 0xff;
	header[5] = (checksum >> 8) & 0xff;
	header[4] = (checksum >> 0) & 0xff;

	header[0] ^= 0xbc;
	header[1] ^= 0xca;
	header[2] ^= 0xba;
	header[3] ^= 0xcb;
	header[4] ^= 0xcb;
	header[5] ^= 0xce;
	header[6] ^= 0xcd;
	header[7] ^= 0xdf;
	header[8] ^= 0xb7;
	header[9] ^= 0xba;
	header[10] ^= 0xbe;
	header[11] ^= 0xbb;
	header[12] ^= 0xba;
	header[13] ^= 0xad;
	header[14] ^= 0xdf;
	header[15] ^= 0xdf;

	for (i = 1; i < HEADER_SIZE; i++)
	{
		header[i] ^= header[i - 1];
	}

	fseek(fp, 0L, SEEK_SET);
	if (fwrite(header, 1, sizeof(header), fp) != sizeof(header))
	{
		printf("Write header error %s\n", argv[1]);
		fclose(fp);
		return -1;
	}

	fclose(fp);

	printf("The checksum is 0x%08x for %ld bytes [0x%lx ~ 0x%x]\n",
	       checksum, (count - sizeof(header)), sizeof(header), (count - 1));
	return 0;
}

mkbl1 的作用就是给 bin 文件添加 16 字节的签名,由于这份代码是直接修改了 bin 文件的前 16 字节,所以需要保证 start.S 签名预留 16 字节的占位符。

Makefile

CFLAGS := -nostdlib -O0
TARGET := led.bin
LOCATION := /dev/sdb 
CROSS_COMPILE := arm-none-linux-gnueabi-
Q := @
$(TARGET) : start.o
	$(Q)$(CROSS_COMPILE)ld -T link.lds -o led.elf $^
	$(Q)$(CROSS_COMPILE)objcopy -O binary led.elf $@
	$(Q)$(CROSS_COMPILE)objdump -D led.elf > led.dis

%.o : %.S
	$(Q)$(CROSS_COMPILE)gcc -o $@ $< -c

%.o : %.c
	$(Q)$(CROSS_COMPILE)gcc $(CFLAGS) -o $@ $< -c

.PHONY:clean install
clean:
	rm -rf *.o *.elf *.bin *.dis mkbl1

install:
	$(Q)gcc ./mkbl1.c -static -o mkbl1
	$(Q)./mkbl1 $(TARGET) 
	$(Q)if [ -b $(LOCATION) ]; then \
	#sudo mkfs.vfat -F 32 -I $(LOCATION); \
	dd if=/dev/zero of=$(LOCATION) bs=512 seek=1 iflag=dsync oflag=dsync count=16; \
	dd if=./$(TARGET) of=$(LOCATION) bs=512 seek=1 iflag=dsync oflag=dsync; \
	fi

makefile 主要提供如下几个功能

  1. make
    编译生成 led.elf,转换生成 led.bin,反汇编生成 led.dis
  2. make install
    编译生成 mkbl1 并加工 led.bin 文件,如果 LOCATION 有效则刷写到 LOCATION 目录
  3. make clean
    清空所有过程和目的文件
  4. 刷新 led.bin 到指定目录
    make && make install LOCATION=/dev/sdb

2021年8月25日

Kilento

你可能感兴趣的:(Exynos4412,exynos,嵌入式)