14.Makefile和mkv210_image.c


14.1.Makefile概述
(1)为什么需要Makefile:Makefile是用来管理工程的,在一个正式的软件项目中,由很多个.c和.h文件构成,此时如果直接在命令行编译,需运行”gcc 星号.c 星号.S -o exe”命令,每次编译都要重新输入该命令,此问题严重影响工作效率,我们通过Makefile来进行一键编译。
(2)Makefile中的一些基本概念:目标(目标顶格写,后面是冒号,冒号后面是依赖);依赖(依赖是用来产生目标的原材料);命令(命令前面一定是Tab,不能是定格,也不能说多个空格,命令即要生成目标需要做的动作)。
(3)Makefile的基本工作原理:当我们执行”make xx”的时候,Makefile会自动执行xx这个目标下面的命令语句;当我们”make xx”的时候,是否执行命令是取决于依赖的,依赖如果成立就会执行命令,否则不执行;我们直接执行make和make第1个目标的效果是相同的(第1个目标即默认目标)。
(4)进阶学习Makefile的资料:我们学习Makefile的思路为先学会基本的概念和应用,先理解Makefile的概念和使用方法及工作原理;先自己会写简单的Makefile来管理工程,一般先学到这里就可以了,更深入的内容可在工作中碰到再学习即可;若需深入学习Makefile建议参考《跟我一起学Makefile》(作者:陈皓)学习(前期学习前6个章节内容即可)。


14.2.mkv210_image.c的使用演示
(1)裸机程序中的Makefile(即真实项目的Makefile)是把程序的编译和链接过程分开的(平时我们用gcc a.c -o exe该种方式来编译时,实际上把编译和链接过程一步完成了;在内部实际上编译和链接永远是分开独立进行的,编译要使用编译器gcc,链接要使用链接器ld)。
(2)链接器得到led.elf其实就是我们的可执行程序,(如果是在操作系统下,这个led.elf就可以执行了)但是在嵌入式裸机中我们需要的是可以烧写的文件(可烧写的文件就叫镜像image),因此我们需要用这个led.elf为原材料来制作镜像,制作工具是交叉编译工具链中的arm-linux-objcopy。
(3)我们使用arm-linux-objdump工具进行反编译(反汇编),反汇编其实就是把编译后的elf格式的可执行程序给反过来得到对应的汇编源代码,我们使用反汇编主要是用来学习。
(4)mkv210_image.c该程序其实最终不是在开发板上执行的,而是在主机linux中执行的,因此编译该程序用gcc而不是用arm-linux-gcc;该.c文件编译后得到可执行程序mkgec210,执行该程序可由xxx_usb.bin得到xxx_sd.bin(xxx_sd.bin是通过SD卡启动时的裸机镜像,该镜像需由xxx_usb.bin来加工得到,加工的具体方法和原理要看mkv210_image.c)。
(5)GNU编译器gcc四步:预处理(gcc hello.c -E -o hello.i 调用预处理器cpp,源程序->文件.i);编译(gcc hello.c -S -o hello.s 调用编译器cc1,源程序->汇编文件.s);汇编(gcc hello.c -c -o hello.o 调用汇编器as,源程序->目标文件.o);链接(gcc hello.c -o hello 调用链接器ld,源程序->可执行文件elf)。


14.3.S5PV210的启动过程回顾
(1)210启动后先执行内部iROM中的BL0,BL0执行完后会根据OMpin的配置选择某个外部设备来启动(有很多,包括了usb启动和SD卡启动);从usb启动时内部BL0读取到BL1后不做校验,直接从BL1的实质内容0xd0020010开始执行,则usb启动的镜像xxx_usb.bin不需要头信息,则我们从usb启动时直接将镜像下载到0xd0020010去执行即可;从SD启动时内部BL0会首先读取sd卡得到完整镜像(完整指的是xxx_usb.bin和16字节的头),然后BL0会根据实际镜像(指xxx_usb.bin)来计算出校验和checksum,然后和完整镜像头部中的checksum来比对,若对应则执行BL1,若不对应则启动失败(会转入执行2st启动,即SD2启动,若这里已经是2st启动了,这里校验通不过就完了)。


14.4.mkv210_image.c和main函数形参的作用
(1)为BL1添加校验头:我们通过交叉编译工具链只得到了xxx_usb.bin,而xxx_sd.bin的得到和交叉编译工具链完全无关,由xxx_usb.bin得到xxx_sd.bin的过程是三星的S5PV210所特有的,则我们写了mkv210_image.c来完成(见图1)。
(2)整个程序工作流分析:整个程序中首先申请一个16KB大小的buffer,然后把所有内容按照各自的位置填充进去,最终把填充好的buffer写入到某个文件(名叫xxx_sd.bin)就形成了我们想要的镜像。
(3)main函数接收2个形参:argc和argv(argc是用户通过命令行来执行该程序时实际传递的参数个数;argv该字符串数组中存储的字符串就是每个具体的参数);譬如我们执行程序时使用”./mkx210 xxx_usb.bin xxx_sd.bin”(argc = 3;argv[0] = “./mkx210”;argv[1] = xxx_usb.bin;argv[2] = xxx_sd.bin)。


14.5.glibc读写文件接口和校验和算法
(1)linux中要读取某个文件,可使用fopen打开文件,fread读取文件,读完之后fclose关闭文件;要写文件用fwrite来写;这些函数是glibc的库函数,可通过man 3查询。
(2)校验和算法:校验和其实就是需要校验的内存区域中,所有内存中的内容按照字节为单位来进行相加,最终相加的和为校验和(实现时注意指针的类型为char *)。


这里写图片描述


14.Makefile/ 
cal.c
/*
 * 公司:XXXX
 * 作者:Rston
 * 博客:http://blog.csdn.net/rston
 * GitHub:https://github.com/rston
 * 项目:函数指针实战和typedef
 * 功能:结构体内嵌函数指针实现分层,此文件为下层,主要实现具体的计算器运算。
 */
#include <stdio.h>
#include "framework.h"
#include "cal.h"

int main(int argc, char **argv)
{
    int ret = 0;
    struct cal_t myCal;         // 定义结构体变量

    myCal.a = 14;               // 给结构体变量赋值
    myCal.b = 2;
    myCal.func = multiply;      

    ret = calculator(&myCal);   // 调用上层接口
    printf("ret = %d.\n", ret);

    return 0;
}

// 加法
int add(int a, int b)
{
    return (a + b);
}

// 减法
int sub(int a, int b)
{
    return (a - b);
}

// 乘法
int multiply(int a, int b)
{
    return (a * b);
}

// 除法
int divide(int a, int b)
{
    return (a / b);
}
*********
cal.h
#ifndef __CAL_H__
#define __CAL_H__

// 具体的运算函数声明
int add(int a, int b);
int sub(int a, int b);
int multiply(int a, int b);
int divide(int a, int b);

#endif
*********
framework.c
/*
 * 公司:XXXX
 * 作者:Rston
 * 博客:http://blog.csdn.net/rston
 * GitHub:https://github.com/rston
 * 项目:函数指针实战和typedef
 * 功能:结构体内嵌函数指针实现分层,此文件为上层,主要实现计算器业务逻辑。
 */
#include "framework.h"

// 实现计算器业务逻辑
int calculator(const struct cal_t *p)
{
    return (p->func(p->a, p->b)); 
}
*********
framework.h
#ifndef __FRAMEWORK_H__
#define __FRAMEWORK_H__

// 定义函数指针类型pFunc
typedef int (*pFunc)(int, int);

// 定义结构体类型struct cal_t
struct cal_t
{
    int a;              // 运算数据a
    int b;              // 运算数据b
    pFunc func;         // 具体函数的封装
};

// 计算器上层函数接口,被下层调用
int calculator(const struct cal_t *p);

#endif
*********
Makefile
# 基本的Makefile示例
# 自动变量$@(规则的目标文件名)$<(规则的依赖文件名)$^(依赖的文件集合)

CC=gcc
TARGET=main
SRCS=$(wildcard *.c) 
OBJS=$(patsubst %.c, %.o, $(SRCS))

$(TARGET):$(OBJS)
    $(CC) -o $@  $^
%.o:%.c
    $(CC) -o $@ -c $<

clean:
    rm *.o $(TARGET)

14.Template/
readme.txt
xxx_usb.bin是usb启动下载的镜像
xxx_sd.bin是SD卡启动下载的镜像

项目名:Template
作  者:Rston
博客:http://blog.csdn.net/rston 
GitHub:https://github.com/rston
描  述:构建基本的裸机程序项目模板。
*********
mkv210_image.c
/*
 * mkv210_image.c作用:将usb启动时使用的xxx_usb.bin制作得到由sd卡启动的镜像xxx_sd.bin
 * 本文件来自于友善之臂的裸机教程,据友善之臂的文档中讲述,本文件是一个热心网友提供,在此表示感谢。
 *
 * 在BL0阶段,Irom内固化的代码读取nandflash或SD卡前16K的内容,
 * 并比对前16字节中的校验和是否正确,正确则继续,错误则停止。
 */
#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            "S5PC110 HEADER  "    // 16字节,随便16个字节即可

int main (int argc, char *argv[])
{
    FILE *fp;
    char *Buf, *a;
    int BufLen;
    int nbytes, fileLen;
    unsigned int checksum, count;
    int i;

    // 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("malloc 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,
    // 注意若裸机程序大于16KB,则裸机程序会出问题,在后续做LCD实验时需特别注意
    count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE))
        ? fileLen : (IMG_SIZE - SPL_HEADER_SIZE);
    // 3.4在buffer[0~15]中存放"S5PC110 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中所有的字节数据加和起来得到的结果
    a = Buf + SPL_HEADER_SIZE;
    for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
        checksum += (0x000000FF) & *a++;
    // 4.2将校验和保存在buffer[8~12]
    a = Buf + 8;    // Buf是xxx_sd.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;
}
*********
Makefile
# 基于210裸机项目的Makefile模板

name:=led
$(name).bin:$(name).o 
    arm-linux-ld -Ttext 0x0 -o $(name).elf $^
    arm-linux-objcopy -O binary $(name).elf $(name)_usb.bin
    arm-linux-objdump -D $(name).elf > $(name)_elf.dis
    gcc mkv210_image.c -o mkgec210
    ./mkgec210 $(name)_usb.bin $(name)_sd.bin

%.o : %.S
    arm-linux-gcc -o $@ $< -c

%.o : %.c
    arm-linux-gcc -o $@ $< -c 

clean:
    rm *.o *.elf *.bin *.dis mkgec210 -f
*********
write2sd.sh
#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=xxx_sd.bin of=/dev/sdb seek=1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值