1. 程式人生 > >ARM:Makefile編寫、連結指令碼編寫、裸板shell框架

ARM:Makefile編寫、連結指令碼編寫、裸板shell框架

《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

' $< 代表依賴檔案
' [email protected] 代表目標檔案
/** 程式碼演示 - 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 
[email protected]
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");

/** 特例演示 - 指標 **/
#include <stdio.h>
#include <string.h>
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、函式常地址 **/
#include <stdio.h>
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"
*/
#define CMD_MAX_LED 32
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 **/
#ifndef _UART_H_
#define _UART_H_
extern void uart_init (void);
extern void uart_puts (char*);
extern void uart_gets (char*, int);
#endif //_UART_H_

/** 程式碼演示 - uart.c **/
#define UART0CLKENB     *((volatile unsigned int*)0xc00a9000)
#define UART0CLKGEN0L   *((volatile unsigned int*)0xc00a9004)
#define GPIOD_ALTFN0    *((volatile unsigned int*)0xc001d020)
#define GPIOD_ALTFN1    *((volatile unsigned int*)0xc001d024)
#define GPIOD_PULLENB   *((volatile unsigned int*)0xc001d060)
#define ULCON0          *((volatile unsigned int*)0xc00a1000)
#define UCON0           *((volatile unsigned int*)0xc00a1004)
#define UFCON0          *((volatile unsigned int*)0xc00a1008)
#define UTRSTAT0        *((volatile unsigned int*)0xc00a1010)
#define UTXH0           *((volatile unsigned int*)0xc00a1020)
#define URXH0           *((volatile unsigned int*)0xc00a1024)
#define UBRDIV0         *((volatile unsigned int*)0xc00a1028)
#define UFRACVAL0       *((volatile unsigned int*)0xc00a102c)
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 **/
#ifndef _MYSTRCMP_H
#define _MYSTRCMP_H
int mystrcmp (const char*, const char*);
#endif //_MYSTRCMP_H

/** 程式碼演示 - mystrcmp.c **/
#include "mystrcmp.h"
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 **/
#ifndef _LED_H_
#define _LED_H
extern void led_text (void);
extern void led_on (void);
extern void led_off (void);
#endif // _LED_H

/** 程式碼演示 - led.c **/
#define  GPIOC_OUT     *((volatile unsigned int*)0xc001c000)
#define  GPIOC_OUTENB  *((volatile unsigned int*)0xc001c004)
#define  GPIOC_ALTFN0  *((volatile unsigned int*)0xc001c020)
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 **/
#ifndef _BEEP_H_
#define _BEEP_H_
extern void beep_init (void);
extern void beep_on (void);
extern void beep_off (void);
extern void delay (unsigned int);
#endif //_BEEP_H_

/** 程式碼演示 - beep.c **/
#define GPIOC_ALTFN0  *((volatile unsigned int*)0xc001c020)
#define GPIOC_OUTENB  *((volatile unsigned int*)0xc001c004)
#define GPIOC_OUT     *((volatile unsigned int*)0xc001c000)
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
#ifndef _ADD_H
#define _ADD_H
extern int add (int, int);
#endif //_ADD_H
// add.c
int add (int x, int y) {
    return x + y;
}
// main.c
#include <stdio.h>
int main (void) {
    int i = 10; 
    int j = 20; 
    int sum = add (i, j); 
    printf ("sum = %d\n", sum);
    return 0;
}
" static - 靜態區域性變數 "
// 資料段,作用域:當前函式,生命週期:整個當前程式。
#include <stdio.h>
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;
}


相關推薦

ARMMakefile編寫連結指令碼編寫shell框架

《Makefile編寫、連結指令碼編寫、裸板shell框架》<tips>' 工具:UtraEdit 程式碼編輯工具// 此工具裡面 Ctrl + h 檢視ASCII碼<tips>vi中命令模式下:':e main.c   // 開啟main.c':b

單向非迴圈連結串列連結串列建立節點插入連結串列列印節點長度計算連結串列清空連結串列銷燬

/* 單向非迴圈連結串列:    初始化    前插入     後插入    列印    連結串列長度    清空 &

arm-linux-ld命令 ld連結指令碼(不錯,推薦可以學習一些lds連結指令碼檔案)

OUTPUT_FORMAT("elf32&shy;littlearm", "elf32&shy;littlearm", "elf32&shy;littlearm") ;指定輸出可執行檔案是elf格式,32位ARM指令,小端 OUTPUT_ARCH(arm) ;指定輸出可執行檔案的平臺為

動態庫靜態庫編譯測試含靜態庫連結動態庫靜態庫,動態庫連結靜態庫動態庫

本文的目的是測試各種型別庫的編譯後的使用效果,包括庫又連結其他庫的編譯方法,使用方法,依賴性等。 太長不看版:請跳至文章最後的總結對比表。 一。內容包含: ①靜態庫libbb.a依賴靜態庫libaa.a的測試; ②靜態庫libbb.a依賴動態庫libaa.so的測試;

C++類中單鏈表的實現(頭插尾插頭刪尾刪指定位置插入指定位置刪除連結串列長度清空連結串列連結串列排序)

#include<iostream> using namespace std; class Node { public:Node():next(NULL){}Node(int n,Node *p = NULL):value(n),next(p){}int val

《MySQL必知必會》學習筆記(一)MySQL指令碼下載執行及USESELECT的使用

本文主要介紹《MySQL必知必會》書中,所述的MySQL指令碼下載以及執行,並簡單介紹USE、SELECT關鍵字的基本使用。 1、MySQL指令碼下載及執行 開啟MySQL command Line client(本文利用的是MySQL 8.0.

Qt之豐富的容器類---陣列QVector連結串列QLinkedList對映表QMap雜湊表QHash

本文轉載:http://www.cnblogs.com/newstart/archive/2013/05/09/3068625.html 在C++裡做大型程式時,少不了要與陣列、連結串列等資料結構打交道。就是最簡單的字串也常常讓頭痛萬分,Qt中有QString解決了字串的頭痛,那麼其他陣列等

01 Shell概述 編寫及執行指令碼 Shell變數 總結和答疑

Top NSD SHELL DAY01 案例1:Shell基礎應用 案例2:簡單Shell指令碼的設計 案例3:使用Shell變數 案例4:變數的擴充套件應用 1 案例1:Shell基礎應用 1.1 問題 本案例要求熟悉Linux Shell環境

Linux下的C/C++開發基礎(編寫makefile編譯C/C++連結可執行程式)

本文重點介紹C/C++原始碼工程的編譯連結,編譯器gcc/g++的安裝配置略過... 1. 安裝配置gcc g++ 2. 建立檔案 test.h /test.c / file.h  / file.cpp  3. 編譯.o庫: gcc -c / g++ -c     連結生成靜

[轉]Xilinx Vivado的使用詳細介紹(1)創建工程編寫代碼行為仿真Testbench

always 選擇器 資料 多個 sign bench 通過 output tar 新建工程 打開Vivado軟件,直接在歡迎界面點擊Create New Project,或在開始菜單中選擇File - New Project即可新建工程。 點擊Next 輸入工程名稱和

5編寫自動化測試指令碼

上面已經對頁面元素進行了封裝,接下來就是動手寫指令碼了。 這裡寫了2個指令碼,直接上程式碼: 1、test_baidu_news.py # -*- coding:utf-8 -*- import unittest import sys,os from framework.browser

Linux 學習之路(六)bash指令碼編寫

bash指令碼程式設計:整數測試及特殊變數 exit:退出指令碼 exit # 如果指令碼沒有明確定義退出狀態碼,那麼,最後執行的一條命令的退出碼即為指令碼的退出狀態碼。 bash中常用的條件測試有三種: 測試方法: 命令測試法 [ expression ] 關

Linux Shell指令碼編寫規範例子

一、規範 Linux的Shell種類眾多,常見的有:Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shell(/usr/bin/ksh)、She

Scala學習筆記(二)--陣列列表元祖和scala指令碼編寫及讀取檔案

使用型別引數化陣列(Array) 在scala中使用new例項化物件(或者叫類例項)。例項化過程中,可以用值和型別使物件引數化。引數化的意思是指在建立例項的同時完成對它的“設定”。 例如, val greeting:Array[String] = new Array[Str

Linux下c++呼叫自己編寫的matlab函式通過mcc動態連結庫.so實現

之前在這裡和這裡呼叫了matlab自帶的一些函式,是通過matlab引擎來實現的。那裡呼叫的是matlab自帶的函式,那麼如果想呼叫自己寫的.m函式該怎麼辦呢?其實很簡單,原理類似,方法也不止一種。這篇筆記我先嚐試通過mcc將.m函式編譯成動態連結庫供c++呼叫的方式。在另

【0day shellcode編寫藝術】—— jmp esp動態獲取api。後續編碼壓縮

此次主要徒手體會了一下編寫shellcode 的不容易。當真不容易,看著作者的程式碼,都感覺自己無處可以下手了。 需要的底層原理知識也還挺多需要補充上去的。 打算後期再逐漸補充。目前階段將jmp esp弄懂了。後面動態獲取api在主機上出錯了。問題和搜尋jmp esp程式碼

jenkins-1建立pipeline及相關指令碼編寫

新建立一個pipeline專案。 然後寫第一個指令碼,例如: pipeline { agent any stages { stage('Build') { steps {

XMLSchema三種編寫Schema的方式

針對DTD檔案的不足之處:(不能出現相同名稱的元素,DTD的語法不是xml的語法)出現的Schema,需要使用什麼名稱空間的東西就先引入,使用xmlns,後面加字尾,不加就不用字尾 定義Schema檔案,字尾名為xsd 引入XMLSchema那個名稱空間

C語言學習篇-1Hello, World!(編寫編譯連結執行)

說明:初識第一個程式。 開發工具的選擇 寫程式碼的工具:記事本、ULtraEdit、Vim、Xcode等。 選擇Xcode的原因:蘋果公司官方提供的開發利器、簡化開發的工程、有高亮顯示功能。

EmmetHTML/CSS代碼快速編寫神器教程

face 匹配 代碼 htm embedded XML ets 屬性 strong Emmet的前身是大名鼎鼎的Zen coding,如果你從事Web前端開發的話,對該插件一定不會陌生。它使用仿CSS選擇器的語法來生成代碼,大大提高了HTML/CSS代碼編寫的速度,比如下面