转载来至:https://blog.csdn.net/sinat_36184075/article/details/54917518
《Makefile编写、链接脚本编写、裸板shell框架》
<tips>
' 工具:UtraEdit 代码编辑工具
// 此工具里面 Ctrl + h 查看ASCII码
<tips>
vi中命令模式下:
':e main.c // 打开main.c
':bn // 回到刚才的.c文件
1. 关于 \r \n的问题
windows系统中:
\r 回到行首 0x0d
\n 到下一行 0x0a
linux系统中,只需要一个字符:\n
hexdump -C 1.txt // 1.txt 里面敲回车
一、关于Makefile - // 查着用即可不用学
1) env\GNU make v3.80完整版中文指南.pdf
2) 跟我一起写Makefile(后补--->)
makefile主要分为三部分:
目标文件[target]:依赖文件[dependencies]
[1个Tab]规则
'make与makefile 的作用:
make工具用来管理大型软件的编译:
- 自动判断一个程序的哪些文件需要编译,并且能用命令来执行编译操作;
- 实现类似集成开发环境(IDE)自动化编译;
makefile定义了一系列的规则来指定:
- 哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译更复杂的功能操作。
- makefile中可以执行操作系统的命令
现阶段达标:
可以写最简单的Makefile。
$:'vi Makefile
' $< 代表依赖文件
' $@ 代表目标文件
-
/** 代码演示 - Makefile **/
-
NAME=shell
-
ELF=$(NAME).elf #shell.elf
-
BIN=$(NAME).bin #shell.bin
-
OBJS=main.o uart.o
-
-
CC=arm-cortex_a9-linux-gnueabi-gcc
-
LD=arm-cortex_a9-linux-gnueabi-ld
-
OBJCOPY=arm-cortex_a9-linux-gnueabi-objcopy
-
-
CFLAGS=-nostdlib -Wall
-
LDFLAGS=-nostdlib -nostartfiles
-
-
$(BIN):$(ELF)
-
$(OBJCOPY) -O binary $(ELF) $(BIN)
-
cp $(BIN) /tftpboot/
-
$(ELF):$(OBJS)
-
$(LD) $(LDFLAGS) -Ttext= 0x48000000 -emain $(OBJS) -o $(ELF)
-
%.o:%.c
-
$(CC) $(CFLAGS) -c $< -o $@
-
-
clean:
-
rm -rf $(BIN) $(ELF) $(OBJS) /tftpboot/$(BIN)
-
// rm -vf ... 可以显示删除的文件列表提示信息
二、关于链接脚本
【链接器】
- 将若干输入文件(.o file) 根据一定规则合并为一个输出文件;
- 将标号的地址??; // 半句话
【链接脚本】
- 连接工具的输入文件;
- 链接脚本有自己的语法;
'链接脚本的作用:
1) 主要用于规定如何把输入文件内的SECTION放入输出文件内;
2) 控制输出文件内各部分在程序地址空间内的布局;
链接脚本文件
$:'vi shell.lds
-
/** 代码演示 - shell.lds **/
-
ENTRY (main) // -emain
-
SECTIONS {
-
. = 0x48000000; // 指定 .text的起始位置
-
.text : { // 代码段
-
main.o (.text) // 可执行文件以main.o最先链接
-
* (.text)
-
}
-
.data : { // 数据段
-
* (.data)
-
}
-
.bss : { // BSS堆
-
* (.bss)
-
}
-
}
-
// 写完链接脚本文件后,Makefile 文件中对应可修改此句:
-
$(ELF):$(OBJS)
-
$(LD) $(LDFLAGS) -Tshell.lds $(OBJS) -o $(ELF)
' C语言指针复习【关注内存】
-
char s1;
-
char* s2;
-
char s3[ 10];
-
char* s4[ 10];
-
s1 = 'a';
-
s2 = 'b'; // 不合理,警告
-
s2 = "12345";
-
s2++;
-
(*s2)++; // 【段错误】,12345是常量,在只读代码段,自增段错误!
-
strcpy (s2, "hello"); // 【段错误】,(readonly)非法写入。
-
s3[ 0] = 'c';
-
s3[ 0] += 1;
-
s3++; // 【错误】数组首地址,是地址常量,自增编译不通过(非左值)
-
strcpy (s3, "world");
-
/** 特例演示 - 指针 **/
-
-
-
int main (void) {
-
char* s1 = "hello";
-
char* s2 = s1; // 【内存】s2 在代码段 - 只读常量区
-
strcpy (s2, "world..");
-
printf ( "s2 = %s\n", s2); // 段错误!
-
return 0;
-
}
-
/** 函数指针 **/
-
方法一:
-
int (*pfunc1) ( int, int);
-
pfunc1 = add;
-
pfunc1 ( 1, 2); // 3
-
方法二:
-
typedef int (*PFUNC) (int, int);
-
PFUNC pfunc2 = add;
-
pfunc2 ( 10, 20); // 30
-
方法三: // 最难的用法
-
(*(( int (*) ( int, int)) 0x0000000000400544)) ( 1000, 2000); // 3000
-
/** 代码演示 - 函数指针、typedf、函数常地址 **/
-
-
int add (int x, int y) {
-
printf ( "Enter add func...\n");
-
return x + y;
-
}
-
// 声明一个指针变量pFunc
-
// 该4字节存储函数的地址
-
// 存 <返回值是int,参数为int,int类型的函数地址>
-
typedef int (*pFunc) (int, int);
-
int main (void) {
-
int res = 0;
-
// res = add (1, 2); 正常调用
-
// 定义一个指针变量pfunc1
-
// 这个变量是个指针,bit32-4字节
-
// 该4字节存储1个函数的地址
-
// 存 <返回值是int,参数为int,int类型的函数地址>
-
int (*pfunc1) ( int, int);
-
pfunc1 = add; // 函数名和数组名一样,可以代表其首地址
-
res = pfunc1 ( 10, 20);
-
printf ( "res = %d\n", res); // 30
-
// ---------- typedef -----------
-
pFunc pfunc2;
-
pfunc2 = add;
-
res = pfunc2 ( 100, 200);
-
printf ( "res = %d\n", res); // 300
-
// 函数指针最难的用法
-
res = (*(( int (*) ( int, int)) 0x0000000000400544)) ( 1000, 2000);
-
printf ( "res = %d\n", res); // 3000
-
return 0;
-
}
三、编写一个shell框架
-
/** 代码演示 - 主函数 main.c **/
-
/* 不需要写 include ,关键字 extern 可以实现跨文件使用函数。
-
#include "uart.h"
-
#include "led.h"
-
#include "beep.h"
-
#include "mystrcmp.h"
-
*/
-
-
char cmd_buf[CMD_MAX_LED];
-
int main (void) {
-
// 8N1 115200 non-FIFO polling
-
uart_init (); // uart 串口初始化
-
led_init (); // led 初始化
-
beep_init (); // beep 初始化
-
while ( 1) {
-
// 输出命令提示符
-
uart_puts ( "\nmyArmShell#: ");
-
// 接收用户输入的数据
-
uart_gets (cmd_buf, CMD_MAX_LED);
-
if (! mystrcmp (cmd_buf, "ledon")) {
-
led_on ();
-
uart_puts ( "\nledon success...");
-
}
-
if (! mystrcmp (cmd_buf, "ledoff")) {
-
led_off ();
-
uart_puts ( "\nledoff success...");
-
}
-
if (! mystrcmp (cmd_buf, "beepon")) {
-
beep_on ();
-
uart_puts ( "\nbeepon success...");
-
}
-
if (! mystrcmp (cmd_buf, "beepoff")) {
-
beep_off ();
-
uart_puts ( "\nbeepoff success...");
-
}
-
}
-
return 0;
-
}
-
/** 代码演示 - uart.h **/
-
-
-
extern void uart_init (void);
-
extern void uart_puts (char*);
-
extern void uart_gets (char*, int);
-
-
-
/** 代码演示 - uart.c **/
-
-
-
-
-
-
-
-
-
-
-
-
-
-
void uart_init (void) {
-
/* uart0 clk disable */
-
UART0CLKENB &= ~( 1 << 2);
-
// GPIOD18(Tx 接收管脚) GPIOD14(Rx 发送管脚) 配置功能Function1
-
GPIOD_ALTFN0 &= ~( 3 << 28); // GPIOD14
-
GPIOD_ALTFN0 |= ( 1 << 28);
-
GPIOD_ALTFN1 &= ~( 3 << 4); // GPIOD18
-
GPIOD_ALTFN1 |= ( 1 << 4);
-
// 时钟配置:选择PLL[1] 800MHz
-
UART0CLKGEN0L &= ~( 7 << 2);
-
UART0CLKGEN0L |= ( 1 << 2);
-
// 分频设置 800/(0x0f+1)=50MHz
-
UART0CLKGEN0L &= ~( 0xff << 5); // [12:5] 8个位
-
UART0CLKGEN0L |= ( 0xf << 5); // [12:5] 4个位设置为1111
-
// UART控制器设置
-
ULCON0 = 0x03; // 8N1
-
UCON0 = 0x05; // 0101 == 0x05 polling
-
UFCON0 |= ( 3 << 1); // 清空FIFO,解决开发板命令行下运行有多余字符的bug
-
UFCON0 &= ~( 1 << 0); // non-FIFO disable
-
UBRDIV0 = 26; // 50000000/(115200*16) - 1 == 26.13
-
UFRACVAL0 = 2; // 0.13*16 == 2.08
-
/* uart0 clk enable */
-
UART0CLKENB |= ( 1 << 2);
-
}
-
void uart_putc (char c) {
-
// UTRSTAT0 bit[1] == 1, 缓存寄存器为empty
-
// 轮询是否为空
-
while (! (UTRSTAT0 & 0x02)); // 分析?
-
UTXH0 = c;
-
if (c == '\n')
-
uart_putc ( '\r');
-
}
-
void uart_puts (char* str) {
-
if (! str)
-
return ;
-
-
while (*str) {
-
uart_putc (*str);
-
str++;
-
}
-
}
-
char uart_getc (void) {
-
// 轮询 polling UTRSTAT0 bit[0] = 1 received data
-
while (! (UTRSTAT0 & 0x01));
-
return ( char)(URXH0 & 0xff); // 只取低8位,是有效数据
-
}
-
void uart_gets (char* buf, int len) {
-
int i = 0;
-
char tmp = 0;
-
while (i < len - 1) {
-
tmp = uart_getc ();
-
// 回显,注释掉该句验证效果?
-
uart_putc (tmp);
-
buf[i] = tmp;
-
if (tmp == '\r')
-
break;
-
i++;
-
}
-
// 添加字符串结束标志
-
buf[i] = '\0';
-
}
-
/** 代码演示 - mystrcmp.h **/
-
-
-
int mystrcmp (const char*, const char*);
-
-
-
/** 代码演示 - mystrcmp.c **/
-
-
int mystrcmp (const char* s1, const char* s2) {
-
while (*s1) {
-
if (*s1 > *s2)
-
return 1;
-
else if (*s1 < *s2)
-
return -1;
-
s1++;
-
s2++;
-
}
-
return *s2 == 0 ? 0 : -1;
-
}
-
/** 代码演示 - led.h **/
-
-
-
extern void led_text (void);
-
extern void led_on (void);
-
extern void led_off (void);
-
-
-
/** 代码演示 - led.c **/
-
-
-
-
void led_init (void) {
-
// 配置对应管脚为GPIO功能
-
GPIOC_ALTFN0 &= ~ ( 3 << 24); // clear bit 24,25
-
GPIOC_ALTFN0 |= ( 1 << 24); // set bit 24
-
// 选择为输出功能
-
GPIOC_OUTENB |= ( 1 << 12); // OUTPUT
-
}
-
void led_on (void) {
-
GPIOC_OUT &= ~ ( 1 << 12); // clear bit 12
-
}
-
void led_off (void) {
-
GPIOC_OUT |= ( 1 << 12); // set bit 12
-
}
-
/** 代码演示 - beep.h **/
-
-
-
extern void beep_init (void);
-
extern void beep_on (void);
-
extern void beep_off (void);
-
extern void delay (unsigned int);
-
-
-
/** 代码演示 - beep.c **/
-
-
-
-
void beep_init (void) {
-
// 配置GPIO管脚
-
GPIOC_ALTFN0 &= ~( 3 << 28);
-
GPIOC_ALTFN0 |= ( 1 << 28);
-
// 设置输出功能
-
GPIOC_OUTENB |= ( 1 << 14);
-
}
-
void beep_on (void) {
-
// while (1) { // 暂无法中断,故先注销
-
GPIOC_OUT |= ( 1 << 14); // 鸣叫
-
// delay (10000000);
-
// GPIOC_OUT &= ~(1 << 14); // 不叫
-
// delay (10000000);
-
// }
-
}
-
void beep_off (void) {
-
GPIOC_OUT &= ~( 1 << 14);
-
}
-
void delay (unsigned int n) {
-
while (--n);
-
}
自我验证补充:
-
"extern - 跨文件调用函数 "
-
// add.h
-
-
-
extern int add (int, int);
-
-
// add.c
-
int add (int x, int y) {
-
return x + y;
-
}
-
// main.c
-
-
int main (void) {
-
int i = 10;
-
int j = 20;
-
int sum = add (i, j);
-
printf ( "sum = %d\n", sum);
-
return 0;
-
}
-
" static - 静态局部变量 "
-
// 数据段,作用域:当前函数,生命周期:整个当前程序。
-
-
int add (int x, int y) {
-
static int sum;
-
return x + y;
-
}
-
int* add1 (int x, int y) {
-
static int s = 1; // static int s = x + y; 错误:初始值设定元素不是常量
-
printf ( "&s is : %p\n", &s); // 0x601020
-
printf ( "s is : %d\n", s); // 1
-
return &s;
-
}
-
int main (void) {
-
int* p_num = add1 ( 10, 20); // s is : 1
-
printf ( "p_num is : %p\n", p_num); // 0x601020
-
printf ( "*p_num is : %d\n", *p_num); // 1
-
*p_num = 2;
-
add1 ( 11, 22); // s is : 2
-
// printf ("&sum is : %p", &sum); // sum 未声明
-
return 0;
-
}
-