文章目录


编译过程

预处理,编译,汇编,链接

最后生成可执行文件

预处理:处理语法错误,把宏展开,把头文件包含进来

.c 预处理 .i .i 编译 .s 汇编代码 .s 汇编 .o object .o 链接 可执行程序

gcc

编译器

x86 windows 应用程序

VC6.0

x86 ubuntu应用程序

gcc

arm裸机

arm-linux-gcc

gcc常用选项

选项

功能

无参数

生成a.out的可执行程序

-v

查看gcc编译器版本,显示gcc执行时的详细过程

-o

指定输出文件名(不能跟源文件同名,会产生覆盖)

-E

只预处理,不会编译,汇编,链接

-S

只编译,不会汇编和链接

-c

编译和汇编,不会连接

动/静态链接

​动态链接​​使用动态链接库进行链接,生成的程序在执行的时候加载所需的动态库才能运行,动态链接生成的程序体积较小,但是必须依赖所需的动态库,否则无法执行

​静态链接​​使用静态链接库进行链接,生成的程序包含程序运行所需的全部库,可以直接运行,不过静态链接生成的程序体积较大

Makefile

组织管理文件。

规则执行的条件:


  1. 目标文件不存在
  2. 某个依赖文件比目标文件新。

执行命令make,后面可以跟目标名 。如果没有,默认第一个目标。

通配符
test: a.o b.o c.o
gcc -o test $^

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


# $@ 表示目标文件
# $^ 表示所有的依赖文件
# $< 表示第一个依赖文件
# $? 表示比目标还要新的依赖文件列表
假想目标
test: a.o b.o c.o
gcc -o test $^

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

clean:
rm *.o test

# $@ 表示目标文件
# $^ 表示所有的依赖文件
# $< 表示第一个依赖文件
# $? 表示比目标还要新的依赖文件列表

#定义为假想目标,实际文件中有`同名`clean文件也可以执行
.PHONY: clean
变量
test: a.o b.o c.o
gcc -o test $^

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

clean:
rm *.o test

# $@ 表示目标文件
# $^ 表示所有的依赖文件
# $< 表示第一个依赖文件
# $? 表示比目标还要新的依赖文件列表

#定义为假想目标,实际文件中有`同名`clean文件也可以执行
.PHONY: clean
函数
A=a b c

# $(foreach var,list,text)

#B=$(foreach f,$(A),c.o)
#输出:B=c.o c.o c.o

B=$(foreach f,$(A),$(f).o)
#输出:B=a.o b.o c.o
C=a v/ b c/
#从列表里取出符合 %/格式的所有值,out取出不符合的值
D= $(filter %/,$(C))
E=$(filter-out %/,$(C))

#符合格式的文件
files = $(wildcard *.c)

#从这些文件里取出真实存在的文件
files2 = a.c b.c c.c d.c e.c
files3 = $(wildcard $(files2))

# $(patsubst pattern,replacement,$(var))
dep_files = $(patsubst %.c,%.d,$(files2))

all:
@echo B=$(B)
@echo D=$(D)
@echo E=$(E)
@echo files=$(files)
@echo files3=$(files3)
@echo dep_files=$(dep_files)

# $(wildcard pattern)

最终完善版Makefile

objs = a.o b.o c.o 

# ↓依赖文件 使用文件把.o文件替换成.d文件
dep_files := $(patsubst %,%.d,$(objs))

# 确定文件存在
dep_files := $(wildcard $(dep_files))

# 要求GCC将所有的警告当成错误进行处理,b.c c.c要包含stdio.h
CFLAGS = -Werror

# I指定头文件包含目录
INC = -I ./include

# test依赖于.o文件,于是向下查找
All:${objs}
gcc -o test $^

#这变量如果不等于空就包含他进去
# ifneq ($(dep_files))
# include $(dep_files)
# endif

# .o文件依赖于.c于是执行命令

#关键命令
# gcc -M c.c 打印出依赖
# gcc -M -MF c.d c.c 把依赖写入文件c.d
# gcc -c -o c.o c.c -MD -MF c.d
# $@ 表示目标文件
# $^ 表示所有的依赖文件
# $< 表示第一个依赖文件
# $? 表示比目标还要新的依赖文件列表

# -MD -MF 编译.o文件,把依赖写入文件c.d
%.o:%.c
gcc $(INC) $(CFLAGS) -c -o $@ $< -MD -MF $@.d

clean:
rm *.o test

distclean:
rm $(dep_files)

.PHONY:clean

ARM时钟体系

Sytem on chip

CPU:FCLK ​​(functional clock)​

AHB总线:HCLK​​高速设备使用高速时钟​

APB总线:PCLK​​低速设备使用高速时钟​

​手册1-4~1-5​


S3C2440没开启时钟前默认使用12M晶振效率低下,超频后最高可达400M.


Linux S3C2440 学习笔记03_Makefile

FCLK = 400M

Linux S3C2440 学习笔记03_linux_02

HCLK=FCLK/4

PCLK=FCLK/4/2=HCLK/2

​P7-25​

Linux S3C2440 学习笔记03_单片机_03

时钟源:12M晶振

PLL:锁相环

示例
//.text 指定了后续编译出来的内容放在代码段
.text
.global _start

//_start是一个函数的起始地址,也是编译、链接后程序的起始地址。由于程序是通过加载器来加载的,必须要找到 _start名字的函数,
//因此_start必须定义成全局的,以便存在于编译后的全局符合表中,供属其它程序【如加载器】寻找到。
_start:
//关闭看门狗
ldr r0,=0x53000000
ldr r1,=0
str r1,[r0]

//设置MPLL,FCLK:HCLK:PCLK =400M:100M:50M
//LOCKTIME(0X4C000000)=0XFFFFFFFF
ldr r0, =0X4C000000
ldr r1, =0xffffffff
str r1, [r0]

//CLKDIVN(0x4c000014)=0x5,tFCL:tHCLK:tPCLK 1:4:8
ldr r0, =0x4c000014
ldr r1, =0x5
str r1, [r0]

//设置CPU工作于异步模式
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc000000
mcr p15,0,r0,c1,c0,0

//设置MPLLCON(0x4C000004)=(92<<2)|(1<<4)|(1<<0)
//m=MDIV+8=92+8=100
//p=PDIV+2=1+2=3
//s=SDIV=1
//FCLK=2*m*Fin/(p*2^s)=2*100*12/(3*2^1)=400M
ldr r0, =0x4C000004
ldr r1, =(92<<2)|(1<<4)|(1<<0)
str r1, [r0]

//一旦设置PLL,就会锁定lock time直到pll 输出稳定
//然后CPU工作于新的频率

//设置内存 sp 栈 nand启动时,nand大小是4K,所以把栈设置在内存的顶部
ldr sp, =4096

//nor 启动
ldr sp, = 0x40000000+4096

//传参
mov r0,#4
//调用Main 跳转过去执行Main 并把返回的地址保存到lr里面
bl main

// 死循环 b常用于不返回的跳转
halt:
b halt
var=start.o led.o 

all:${var}
arm-linux-ld -Ttext 0 ${var} -o led.elf
arm-linux-objcopy -O binary -S led.elf a.bin
%.o:%.c
arm-linux-gcc -c $< -o $@
%.o:%.S
arm-linux-gcc -c $< -o $@
clean:
rm *.bin *.o *.elf *.dis
编写Makefile 出现问题总结

我调试了2个多小时,淦!


  1. ​链接时要注意顺序​
  2. ​.S 和 .c文件要分开写​

var=start.o led.o 
//`连接时要注意顺序`
all:${var}
arm-linux-ld -Ttext 0 ${var} -o led.elf
arm-linux-objcopy -O binary -S led.elf a.bin
//.S 和 .c文件要分开写
%.o:%.c
arm-linux-gcc -c $< -o $@
%.o:%.S
arm-linux-gcc -c $< -o $@
clean:
rm *.bin *.o *.elf *.dis

串口发送接收简单配置

​串口运行在低速总线上,50M频率​

/*
* @Description:
* @Author: ou
* @Date: 2022-02-05 17:12:09
* @LastEditTime: 2022-02-05 20:30:07
* @LastEditors: ou
*/
#include "s3c2440_soc.h"


void uart0_init()
{
/* 设置引脚用于串口 */
/* GPH2,3用于TxD0, RxD0 */
GPHCON &= ~((0xf<<4));
GPHCON |= ((0xa<<4));

GPHUP &= ~((3<<2)); /* 使能内部上拉 */

/* 设置波特率 */
/* UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1
* UART clock = 50M
* UBRDIVn = (int)( 50000000 / ( 115200 x 16) ) –1 = 26
*/
UCON0 = 0x5; /* PCLK,中断/查询模式 :不断读取某一个数据位*/
UBRDIV0 = 26;

/* 设置数据格式 */
ULCON0 = 0x3; /* 8n1: 8个数据位, 无较验位, 1个停止位 */
}

void my_putchar(unsigned char c)
{
/* UTRSTAT0 */
/* UTXH0 */
while (!(UTRSTAT0 & (1<<2)));
UTXH0 = c;
}

int getchar()
{
while (!(UTRSTAT0 & 1));
return URXH0;
}

int puts(const char* s)
{
while (*s)
{
my_putchar(*s++);
}
}
/*
* @Description:
* @Author: ou
* @Date: 2022-02-05 17:11:33
* @LastEditTime: 2022-02-05 20:29:07
* @LastEditors: ou
*/
#include "uart.h"
#include "s3c2440_soc.h"

void delay(int d)
{
while(d--);
}

int main()
{
unsigned char c;
uart0_init();
puts("hello ouou20220205\n");
while(1)
{
c = getchar();
my_putchar(c);
}
return 0;
}

自行实现printf相关前置知识学习示例

/*
* @Description:
* @Author: ou
* @Date: 2022-02-05 21:46:26
* @LastEditTime: 2022-02-05 23:55:10
* @LastEditors: ou
*/
#include <stdio.h>
#include <stdarg.h>

// GCC默认4字节对齐
//__attribute__((packed)),让所作用的结构体取消在编译过程中的优化对齐,
//按照实际占用字节数进行对齐

//__attribute((aligned(n))),让所作用的结构体成员对齐在n字节边界上。
//如果结构体中有成员变量的字节长度大于n,则按照最大成员变量的字节长度来算
struct person
{
/* data */
char *name;
int age;
char score;
};

struct person1
{
/* data */
char *name;
int age;
char score;
} __attribute__((packed));

struct person2
{
/* data */
char *name;
int age;
char score;
} __attribute__((aligned(2)));

//...传参
// gcc -m32 -o push_test push_test.c
int push_test(const char *format, ...)
{
char *p = (char *)&format;
int i;
struct person per;

va_list vp;
va_start(vp, format);
int k = va_arg(vp, int);
printf("\nk:%d\n", k);

//存在字节对齐需要写int
struct person per2 = va_arg(vp, struct person);
printf("\nname:%s\n", per2.name);
printf("\nage:%d\n", per2.age);
printf("\nscore:%c\n", per2.score);
va_end(vp);

// printf("arg1:%s\n",format);
// //p=p+sizeof(char*);
// i=*((int*)(p+sizeof(char*)));
// printf("arg2:%d\n",i);

// p=(p+sizeof(char*))+sizeof(int);
// per=*((struct person*)p);
// printf("%s,%d,%c",per.name,per.age,per.score);
// return 0;
}

int main(int argc, char **argv)
{
struct person per = {"123", 22, 'A'};
push_test("abcd", 123, per);

printf("\n%d\n", sizeof(struct person));
printf("\n%d\n", sizeof(struct person1));
printf("\n%d\n", sizeof(struct person2));
}