1 / 31

ARM C 语言

ARM C 语言. 一、文件包含伪指令. #include < 头文件名 .h> #include “ 头文件名 .h” #include 宏标识符. 1. #include < 头文件名 .h>. #include <stdio.h> #include <string.h>. 2 . #include “ 头文件名 .h ”. #include “2410lib.h” #include “2410b.h” #include “../LCD/lcd_test.h” #include “.. \ LCD \ lcd_tmp.h”.

naiara
Télécharger la présentation

ARM C 语言

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. ARM C语言

  2. 一、文件包含伪指令 • #include <头文件名.h> • #include “头文件名.h” • #include 宏标识符

  3. 1.#include <头文件名.h> #include <stdio.h> #include <string.h>

  4. 2.#include “头文件名.h” #include “2410lib.h” #include “2410b.h” #include “../LCD/lcd_test.h” #include “..\\LCD\\lcd_tmp.h”

  5. 3.#include 宏标识符 #define LCDINCLUDE “c:/2410s/examble/lcd_def.h” #include LCDINCLUDE 展开后: #include “c:/2410s/examble/lcd_def.h”

  6. 二.#define 宏标识符 宏体 #define LED8ADDR (*(volatile unsigned char *) (0x2140000)) void Digit_Led_Symbol(int value) { if((value>=0)&&(value<16)) //符号显示 LED8ADDR=~Symbol[value]; /* 预处理器将LED8ADDR 替换为地址0x2140000的内存单元*/ }

  7. #include <stdio.h> // ARM 2410S的LED程序 #include <stdlib.h> #include <fcntl.h> //ZLG7290 addr, take the addr as the cmd in the ioctl too. #define ZLG7290_ADDR_FlashOnOff 0x0c #define ZLG7290_ADDR_ScanNum 0x0d #define ZLG7290_ADDR_DpRam0 0x10 #define ZLG7290_ADDR_DpRamBase ZLG7290_ADDR_DpRam0 #define ZLG7290_ADDR_DpRam1 (ZLG7290_ADDR_DpRamBase+0x01) #define ZLG7290_ADDR_DpRam2 (ZLG7290_ADDR_DpRamBase+0x02) #define ZLG7290_ADDR_DpRam3 (ZLG7290_ADDR_DpRamBase+0x03) #define ZLG7290_ADDR_DpRam4 (ZLG7290_ADDR_DpRamBase+0x04) #define ZLG7290_ADDR_DpRam5 (ZLG7290_ADDR_DpRamBase+0x05) #define ZLG7290_ADDR_DpRam6 (ZLG7290_ADDR_DpRamBase+0x06) #define ZLG7290_ADDR_DpRam7 (ZLG7290_ADDR_DpRamBase+0x07) #define ZLG7290LED_DEV "/dev/i2c/zlg7290/0"

  8. static int fd = -1; #define DEBUG #ifdef DEBUG #define DPRINTF(x...) printf("Debug:"##x) #else #define DPRINTF(x...) #endif struct LED_REG { unsigned char addr; //寄存器接口方式 unsigned char ctrl; }__attribute__ ((packed)) ; struct LED_MAP { char map; //显示内容 unsigned char raw; //映射段码 };

  9. struct LED_MAP ledmap[] ={ {'0', 0xfc}, {'1', 0x60}, {'2', 0xda}, {'3', 0xf2}, {'4', 0x66}, {'5', 0xb6}, {'6', 0xbe}, {'7', 0xe0}, {'8', 0xff}, {'9', 0xfb}, }; unsigned char led_map_table[128]; unsigned char led_map(char map); int led_clear(int fd, unsigned char addr); #define CYCLE_LEFT (1) #define CYCLE_RIGHT (2)

  10. 三.函数及函数库 • 函数的定义格式 • 函数的原型说明格式

  11. 1.函数的定义格式 • [存储类说明符]类型说明符[修饰符]标识符(参数表){函数体} • 存储类说明符:static和extern • 类型说明符:void、char…… • 修饰符:interrupt、near、far、huge • 标识符:函数名、*函数名、(*函数名)、*(*函数名)……

  12. 1).存储类说明符 • static 表示在本文件定义前和非本函数定义文件中,该函数将不能使用。提高了函数的安全性。 • extern 在C中,函数是全局的,全程序可见。但在该函数定义之前调用该函数时,需要加原型说明;而且在多源文件的程序中,非定义该函数的文件中需要调用该函数时,必须在原型说明中使用extern。

  13. 2).类型说明符 • 简单类型 int、char、float long double等。 • 复合类型 struct、union • 指针类型 • 无类型 void

  14. 3).参数表 • 类型说明符 变量名 • void • 空

  15. 4).修饰符-对函数起修饰作用 • interrupt 将函数修饰为中断函数。中断函数的特点是返回值类型和参数均必须为void。经interrupt修饰的函数,编程时只需要编写中断服务程序的主体部分,中断服务的保护现场前缀段和恢复现场的后缀段,均由编译程序完成。另外,编译程序还把ret指令改为reti指令。 • near、far、huge 规定函数的地址类型。覆盖存储模式规定的函数缺省地址类型,指明函数与被调用函数的距离。near为近调用(16位段内地址);far为远调用(32位段间地址);huge为规范化远调用(32位段间规范地址)。

  16. 2.函数的原型说明格式 [存储类说明符]类型说明符[修饰符]标识符(参数表){函数体}; int led_clear(int fd, unsigned char addr); int led_clear(int , unsigned char); int led_cycle(int fd, int direction); int led_cycle(int , int);

  17. int led_cycle(int fd, int direction); struct LED_REG reg[8]; int main(int argc, char** argv) { int i,j,k; if((fd=open(ZLG7290LED_DEV, O_RDWR))<0){ printf("Error opening %s ZLG7290 device\n", ZLG7290LED_DEV); return 1; } led_map_init(); //led 段码映射 for(i=0; i<=7; i++) { //clear all leds led_clear(fd, ZLG7290_ADDR_DpRam0+i); } for(i=0; i<=7; i++) { reg[i].addr=ZLG7290_ADDR_DpRam0+i; reg[i].ctrl=led_map(‘0’+i); //设定LED显存内容 write(fd, &reg[i], sizeof(struct LED_REG)); } k=0; while(k<12) {led_cycle(fd, CYCLE_RIGHT); k++; } printf("Display over@\n");close(fd);return 0; }

  18. int led_map_init() { int i; for(i=0;i<sizeof(ledmap)/sizeof(ledmap[0]);i++){ led_map_table[ledmap[i].map]=ledmap[i].raw; } return 0; } unsigned char led_map(char map) { return led_map_table[map]; } int led_clear(int fd, unsigned char addr) { struct LED_REG reg; reg.addr=addr; reg.ctrl=0; ioctl(fd, reg.addr, reg.ctrl); }

  19. int led_cycle(int fd, int direction) //led 左右移位函数 { struct LED_REG temp, * preg; int i; preg=&reg[0]; if(direction == CYCLE_LEFT ) { preg+=7; temp = *(preg); do{ preg--; (preg+1)->ctrl = preg->ctrl ; }while(preg!=&reg[0]) ; preg->ctrl=temp.ctrl; } else {temp = *(preg); do{ preg++; (preg-1)->ctrl = preg->ctrl ; }while(preg!=&reg[7]) ; preg->ctrl=temp.ctrl; } for(i=0; i<8; i++) { write(fd, &reg[i], sizeof(struct LED_REG)); } }

  20. 四.变量修饰符 • const • volatile • near、far

  21. 1. const 常量修饰符,指示被修饰的变量或指针变量是常量。在C语言中,单独开辟一个常量区域用于存放const变量。被修饰的变量必须被赋予初值,变量一旦被const修饰后值就不能再改变了。如: const int x=20; x=50;//错误被赋值 指针的修饰有下列两种方式,含义是不同的。 const int *ptr=&m;//常量指针,说明指针指向的对象是常量 int * const ptr=&n;//指针常量,说明指针本身是常量

  22. 2.volatile-易变的、反复无常的(性格) 易失性修饰符。所定义的变量或指针变量是可以被多种原因修改的(如可以在中断服务程序中被修改,会被I/O口修改),这种修改是随机的。因此使用volatile修饰符禁止对该变量的任何形式的优化,禁止该变量作为寄存器变量。防止它被随机改变。 编译器有一种技术是数据流分析,分析程序中的变量在哪里赋值,在哪里使用,在哪里失效。分析结果可以用于常量合并、常量传播等优化。当它判定你的代码没有修改变量的值时,它就可能提供上次访问变量时提供的缓存值,以提高程序的效率。但有时这种优化会带来问题,特别是对于硬件寄存器操作的程序,此时可以使用volatail关键词禁止做这些优化。

  23. 左边的代码会被优化为: int nn,mm,which_int which_int=rEXTENTPND; nn=which_int; mm=which_int; int nn,mm,which_int which_int=rEXTENTPND; nn=which_int; which_int=rEXTENTPND; mm=which_int; 但这样的优化结果可能导致错误。rEXTENTPND寄存器的内容在执行第1次读操作后可能会被外部中断写入新值,那么第2次读操作读出的内容与第1次的不同,变量mm和nn的值就会不同。但优化后的代码中mm和nn是肯定相同的。因此可以使用volatile来防止优化。正确的写法为: volatile int which_int;

  24. volatile的作用 • 不会在两个操作之间把volatile变量缓存在寄存器中。在多任务、中断处理程序中,变量可能被其他程序改变,编译器自己无法知道。volatile就是告诉编译器不要优化,不要使用缓存值。 • 不做常量合并、常量传播等优化,所以下面的代码: volatile int i=1; if(i>0)printf(“%d”,i); if的条件不会当做无条件真。因为变量i被说明为volatile类型就是说该变量可能会被意想不到随时改变。 • 对volatile变量的读写不会被优化掉。如果一个变量赋值后但后面没有用到,编译器常常可以忽略那个赋值操作,然而对有些硬件寄存器的处理是不能这样优化的。

  25. 使用volatile变量的场合 • 硬件寄存器通常要加volatile,因为每次对它的读写都可能有不同的意义。如下列为中断屏蔽寄存器的定义: #define rINTMSK (*(volatile unsigned *)0x1e0000c) • 在中断服务程序中修改的其他程序检测的变量需要加volatile。如: volatile int which_int; • 多任务环境下各任务间共享的标志应该加volatile。在多线程访问某变量时,希望这些访问能读到变量的最新值,同时写到变量的操作能够立即实现。声明字段的时候如果加上了volatile,那么对该字段的任何请求,包括读和写操作,都会立刻得到执行。

  26. 五._ _irq • 为了方便使用高级语言编写异常处理函数,ARM编译器对异常处理函数作了特定扩展,只要使用关键词__irq,编译出来的函数就满足异常响应对现场保护和恢复的需要。

  27. 六.访问绝对地址的内存位置 对于定义 #define pISR_EINT4567 (*(unsigned*)(_ISR_STARTADDRESS+0x74)) 把无符号数“_ISR_STARTADDRESS+0x74”强制转换为指针,指向RAM中的地址空间,可以用下面的方法访问它。 pISR_EINT4567=(int)Eint4567ISR; 为了访问一个绝对地址,把一个整型数强制转换(typecast)为一个指针就可以了。

  28. 编写中断服务程序的基本原则 • 避免在中断服务中做浮点运算。编写中断服务程序应该遵循短而有效(SS,Simple and Short)的原则。若在中断服务程序中做浮点运算严重违背这一原则,有些处理器/编译器不允许在中断服务程序中做浮点运算。 • 中断服务程序不能有返回值,所以中断服务程序的返回值类型都是void。 • 中断服务程序不能传递参数,所以中断服务程序的参数列表void。 void _irqEint4567Isr(void)

  29. 七.定义变量时的技巧 char a; char c; short b; int d; char a; short b; char c; int d; 说明:pad为无效数据。

  30. 八.使用循环时的技巧 int fact2(int n) { float ti=1; for(i=n;i!=0;i--) ti=fact*i } int fact1(int n) { float ti=1; for(i=1;i<n;i++) ti=fact*i } Fact1: …… 0x000010: MUL R2,R1,R2 0x000014: ADD R1,R1,#1 0x000018: CMP R2,R0 0x00001C: BLE 0x10 …… Fact2: …… 0x000010: MUL R0,R1,R0 0x000014: SUBS R1,R1,#1 0x000018: BNE 0x10 ……

  31. 解释 • 累加法比递减法在循环中多用了1条指令,当循环次数比较大时,程序性能会产生明显的差异; • 当进行一个非0常数比较时,必须用专门的CMP指令来执行;ARM指令则可以直接利用条件执行的特性(NE)来进行判别; • 在ARM的体系结构中编程时,尽量使用递减至0的方法来设置循环条件。

More Related