/** 代码演示 - Makefile **/NAME=shellELF=$(NAME).elf #shell.elfBIN=$(NAME).bin #shell.binOBJS=main.o uart.oCC=arm-cortex_a9-linux-gnueabi-gccLD=arm-cortex_a9-linux-gnueabi-ldOBJCOPY=arm-cortex_a9-linux-gnueabi-objcopyCFLAGS=-nostdlib -WallLDFLAGS=-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) // -emainSECTIONS { . = 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 32char 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_Hint 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_Hextern 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_Hextern int add (int, int);#endif //_ADD_H// add.cint 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;}
新闻热点
疑难解答