首页 AVR035 高效AVR C编程

AVR035 高效AVR C编程

举报
开通vip

AVR035 高效AVR C编程AVR035: 高效AVR C编程 翻译:邵子扬 AVR035: 高效 AVR C 编程 翻译:邵子扬 2006年12月30日 http://shaoziyang.bloger.com.cn shaoziyang@gmail.com 1 特点 · 访问 I/O 内存 · 访问内存映射的 I/O 端口 · 访问 Flash 中数据 · 访问 EEPROM 中数据 · 创建 EEPROM 数据文件 · 高效...

AVR035 高效AVR C编程
AVR035: 高效AVR C编程 翻译:邵子扬 AVR035: 高效 AVR C 编程 翻译:邵子扬 2006年12月30日 http://shaoziyang.bloger.com.cn shaoziyang@gmail.com 1 特点 · 访问 I/O 内存 · 访问内存映射的 I/O 端口 · 访问 Flash 中数据 · 访问 EEPROM 中数据 · 创建 EEPROM 数据文件 · 高效使用变量和数据类型 · 使用位和位掩码 · 使用宏和函数 · 18个减少代码大小的 方法 快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载 · 5个减少 RAM 需求的方法 · 程序调试步骤 · 更新到支持 IAR V2 编译器 2 介绍 C 高级语言在单片机编程中变得越来越受欢迎。使用C语言相对汇编语言有很多优点:减少开发时间、容易维护和移植(译者:其实写得不好的C程序和汇编一样难以维护和移植),以及方便重复利用代码。其代价可能是更大的代码大小和降低了运行速度。为了减少这些影响, AVR架构调整了典型的C编译器产生的解码和执行指令。 IAR C编译器在AVR架构和指令集规范发布之前就完成了。编译器开发团队和AVR开发团队合作的结果就是产生出高效、高性能的单片机代码。 这篇应用笔记描述了怎样利用AVR架构的优点和开发工具产生比其他单片机更加高效率的C代码。 3 为C代码调整的硬件架构 32 个工作寄存器是高效C编程的关键。这些寄存器和传统的累加器有相同的功能,除了数量是32个以外。在一个时钟周期内,AVR单片机可以将任意两个寄存器的内容送到ALU中进行运算,并将结果写回到寄存器中。 当数据存放在32个工作寄存器时,每个运算指令无需从内存中移动数据或移动数据到内存。一些寄存器还可以联合起来组成16位指针,用来提高访问程序和内存中数据的效率。对于很大的内存,指针还可以联合第三个8位寄存器组成24位指针,用来直接访问 16M 字节空间的数据,而不用分页。 3.1 地址模式 AVR架构有4个内存指针,用来访问数据和程序空间。堆栈指针(SP)专门用来存放函数返回地址;C 编译器分配一个指针作为参数堆栈;剩下的两个指针可以被C编译器用于载入和存放数据。下面的例子显示了C语言中典型指针操作的效率。 char *pointer1 = &table[0]; char *pointer2 = &table[49]; *pointer1++ = *--pointer2; 产生下面的汇编代码: LD R16,-Z ; 预先递减Z指针并载入数据 ST X+,R16 ; 存放数据和递增 下面显示了4种指针寻址的模式和例子。所有指针操作都是单字指令,执行时需要两个时钟周期。 1) 间接寻址:寻址数组和指针变量。如 *pointer = 0x00; 2) 带有偏移量的间接寻址:允许指针指向第一个元素,通过增加偏移量而不是修改指针值的方式访问一个结构体的所有单元。也用于访问软件堆栈和数组。 3) 带有递增的间接寻址:在访问后,数组和指针变量将递增的高效寻址方式。如 *pointer++ = 0xFF; 4) 带有预先递减的间接寻址:在访问前,数组和指针变量将递减的高效寻址。如 *--pointer = 0xFF; 指针也用于访问 Flash 程序空间。此外,指针的间接寻址也能用在直接寻址数据空间。这使得可以用双字指令访问整个数据空间。 3.2 16/32位变量的支持 AVR 指令集包括了处理16位数的特殊指令,包括加/减一个数到字(ADIW, SBIW)。算术计算和比较16位数在两个指令以及两个时钟周期中完成。32位算术计算和比较需要4个指令和4个时钟周期。这比大多数16位处理器有更高的效率。 4 AVR单片机的C编程 4.1 初始化堆栈 在上电后或复位后,需要在调用任何函数之前设置堆栈指针。由连接命令文件判定堆栈指针的位置和大小。内存大小的配置和堆栈指针设置在应用笔记“AVR032: 修改连接命令文件“中说明了。 4.2 访问I/O端口 AVR的I/O在C语言中很容易访问。所有I/O寄存器都在名为“ioxxxx.h“头文件中声明,这里 xxxx 是AVR 单片机的 型号 pcr仪的中文说明书矿用离心泵型号大全阀门型号表示含义汽车蓄电池车型适配表汉川数控铣床 。下面的代码显示了访问I/O 端口的例子,每一行C代码下是对应的汇编代码。 #include /* Include header file with symbolic names */ __C_task void main(void) { char temp; /* Declare a temporary variable*/ /*To read and write to an I/O register*/ temp = PIND; /* Read PIND into a variable*/ // IN R16,LOW(16) ; Read I/O memory TCCR0 = 0x4F; /* Write a value to an I/O location*/ // LDI R17,79 ; Load value // OUT LOW(51),R17 ; Write I/O memory /*Set and clear a single bit */ PORTB |= (1< #define reg (* (char *) 0x8004)/* Declare a memory mapped I/O address*/ __C_task void main(void) { char temp; /* Local temp variable */ reg = 0x05; /* Write a value to the memory mapped address*/ temp = reg; /* Read the memory mapped I/O address */ } 如果访问连续地址映射内存,最有效的方法是声明一个常数指针并使用偏移量来访问。下面的例子显示了怎样使用这个方法。 /* Define the memory mapped addresses */ #define data 0x0003 #define address_high 0x0002 #define address_low 0x0001 __C_task void main(void) { /* Start address for memory map */ unsigned char *pointer = (unsigned char *) 0x0800; // LDI R30,LOW(0) ; Init Z-pointer // LDI R31,8 *(pointer+address_low) |= 0x40; /* Read and modify one address*/ // LDD R18,Z+1 ; Load variable // ORI R18,LOW(64) ; Modify // STD Z+1,R18 ; Store Back *(pointer+address_high) = 0x00; /* Write an address*/ // STD Z+2,R30 ; Store zero PORTC = *(pointer+data); /* Read an address*/ // LDD R16,Z+3 ; Load variable // OUT LOW(21),R16 ; Output to port } 注意 Z 指针在访问内存前初始化,LDD 和STD (载入和保存偏移量) 指令用于访问数据。LDD和STD是单字指令,执行时需要两个时钟周期,指针只载入一次。内存映射端口声明为 volatile 类型,表明它可能会被硬件修改并且此代码在优化时不能被删除。 5.1 访问EEPROM的数据 AVR单片机内部的 EEPROM 可以在正常操作时读写。对于IAR编译器,在ina90.h中包括了读写EEPROM的宏。下面的宏可以用于正常情况下读写EEPROM: #define _EEGET(VAR,ADR) /* Read data in EEPROM address ADR into variable VAR */ \ { \ while(EECR & 0x02); /* Check if EEPROM is ready*/ \ EEAR = (ADR); /* Write EEPROM address register*/ \ EECR |= 0x01; /* Set the read strobe*/ \ (VAR) = EEDR; /* Read the data into variable in the next cycle */ \ } #define _EEPUT(ADR,VAL) /* Write data in VAL into EEPROM address ADR*/\ {\ while(EECR&0x02); /* Check if EEPROM is ready*/ \ EEAR = (ADR); /* Write EEPROM address register*/ \ EEDR = (VAL); /* Write EEPROM data register*/ \ EECR |= 0x04; /* Set master write enable signal*/ \ EECR |= 0x02; /* Set write strobe*/ \ } 使用上面的宏读写EEPROM的例子: #include #include #define EE_ADDRESS 0x010 /* Define address constant for EEPROM data */ __C_task void main(void) { char temp; /* Local variable for temporary storage */ _EEGET(temp,EE_ADDRESS); /* Read data from EEPROM*/ temp += UDR; /* Add UART data to temp variable */ _EEPUT(EE_ADDRESS,temp); /* Write data to EEPROM*/ } 注意到如果允许了中断,它们需要在EEPROM写的过程中被禁止,用来保证在EEPROM写过程中EEMWE位不会超时。如果在中断程序里包括了访问EEPROM的代码,在读取EEPROM前也要禁止中断,避免EEPROM地址寄存器被破坏。 6 创建EEPROM数据文件 (IAR v2) 访问EEPROM中的变量在IAR编译器2.0的版本中得到了很大的提升。这一章专门介绍如何使用EEPROM变量。为了在老版本的IAR编译器中使用EEPROM变量,请参考下一章。 为了使用下面的方法,必须允许 “使用内部EEPROM“ 选项,并且需要在编译选项中指定正确的大小。段EEPROM_I必须在连接文件中预先定义,EEPROM变量必须声明为全局变量。 例子: 声明一个EEPROM变量: __eeprom volatile unsigned char ee_var = 0xff; 为了在程序中访问这个变量,只需要象其他变量一样正常的读写。 __C_task void main(void) { unsigned char temp; temp = ee_var; // LDI R20,LOW(ee_var) // LDI R21,(ee_var) >> 8 // CALL __eeget8_16 PORTB = temp; // OUT 0x18,R16 ee_var = TCNT0; // IN R16,0x32 // LDI R20,LOW(ee_var) // LDI R21,(ee_var) >> 8 // CALL __eeput8_16 } 如果EEPROM变量声明时带有初始化参数,这些参数可以生成一个单独的hex 文件,这样可以用任何AVR编程器将其写入单片机的EEPROM中。 IAR编译器提供了一个名叫“postlink.exe“的程序来创建这个hex文件,它在IAR编译器安装文件夹下的“\bin“目录中。不带参数运行这个程序,将提示程序的用法。 为了使用postlink.exe程序,XLINK输出 格式 pdf格式笔记格式下载页码格式下载公文格式下载简报格式下载 必须设置为产生simple 格式。 7 创建EEPROM数据文件 (IAR v1) 使用IAR v2 编译器时参考上一章,这里说明了使用IAR 编译器v1访问EEPROM方法。 在一些情况下可以转换放在EEPROM的初始数据,并通过C代码访问。IAR 工具可以用于产生EEPROM的初始数据,使用头文件定义数据结构,可以保证数据结构可以通过C代码进行寻址。 EEPROM数据和程序代码是两个独立的工程,需要分别编译和连接。一个说明EEPROM数据结构的头文件被两个工程同时包含,保证EEPROM数据可以通过符号名被它们引用。 7.1 例子 下面将通过一个例子来进行说明。在这个例子中,假定EEPROM的结构是: 1. 一个字符数组(100 字节). 2. 一个整数(两个字节). 3. 两个无符号字符(两个字节). 7.2 EEPROM 头文件 EEPROM 头文件被定义EEPROM数据内容的程序和访问EEPROM的C程序所同时包含。EEPROM 头文件定义如下: #define EEPROMADR(x) (unsigned int) (&((TypeEEPROMStruct *)0x0000)->x) typedef struct { char cArray[100]; /* The character array */ int iNumber; /* The integer */ unsigned char uMinorVersion; /* The first unsigned character */ unsigned char uMajorVersion; /* The second unsigned character */ } TypeEEPROMStruct; /* Just a type name */ #define中的宏定义,用于C程序里获取结构变量的地址。它包含了一个参量指针(0x0000)。为了修改EEPROM数据的位置,需要修改这个指针(需要同时修改EEPROM 连接文件,参考下面)。 7.3 EEPROM 程序文件 EEPROM程序文件(eeprom.c) 包含了头文件中定义的数据结构的初始化。 #include "eeprom.h" /* Include the structure type definition */ #pragma memory=constseg(EEPROM) /* Make it a named segment */ const TypeEEPROMStruct __EEPROMInitializationData = {"Testing ", /* Initialize cArray */ 0x100 , /* Initialize iNumber */ 0x10 , /* Initialize uMinorVersion */ 0xFF }; /* Initialize uMajorVersion */ 7.4 EEPROM 连接文件 EEPROM程序文件需要一个很简单的连接文件(eeprom.xcl): -ca90 -! Define CPU (AVR) -! -Z(CODE)EEPROM=0-1FF -! EEPROM address space(internal EEPROM memory-! 这里的地址范围设置为0-1FF (AT90S8515),需要修改为适合你使用的单片机的参数。段的名称是EEPROM,对应于文件“eeprom.c“中的语句 #pragma memory=constseg(EEPROM)。 想使用EEPROM的其它位置,需要修改 EEPROM段的起始地址(参考7.3EEPROM头文件小节)。 7.5 建立Intel-HEX格式的EEPROM数据文件 为了产生Intel-Hex 格式的文件,使用下面命令: icca90 eeprom.c (注意 -v1 -ms 等参数不重要) xlink -f eeprom.xcl -B -Fintel-standard eeprom.r90 -o eeprom.hex 在连接过程中,会产生下面的错误信息: Error[46]: Undefined external ?CL0T_1_41_L08 referred in eeprom ( eeprom.r90 ) C 程序产生外部符号参考,保证编译后的程序连接到正确版本的库中。因为我们不连接任何库,我们可以忽略这个错误。-B 选项保证了即使有错误也能创建“eeprom.hex“文件。文件可以按下面选项连接: xlink -f eeprom.xcl -D?CL0T_1_41_L08=0 -Fintel-standard eeprom.r90 -o eeprom.hex 定义的符号依赖于处理器版本(-v0, -v1 等), 内存模式(-mt, -ms, 等)以及编译器版本。所以符号so the symbol can vary from installation to installation (尝试连接它,使用 –D=0检查未定义的符号)。产生的intel-hex 格式“eeprom“文件看起来如下(eeprom.h): :1000000054657374696E67202000000000000000D2 :1000100000000000000000000000000000000000E0 :1000200000000000000000000000000000000000D0 :1000300000000000000000000000000000000000C0 :1000400000000000000000000000000000000000B0 :1000500000000000000000000000000000000000A0 :0800600000000000000110FF88 :00000001FF 7.6 在C程序中访问EEPROM数据结构 下面的程序访问了EEPROM中数据结构 (main.c): #include "eeprom.h" /* We use the structure and macro */ #include /* Defines the EEPROM locations */ void error(void) /* An error routine to catch errors */ { for(;;) /* Do nothing */ ; } void C_task main(void) { int i; /* Used for readback of integer */ EEAR = EEPROMADR(cArray); /* Set up address to 1st element */ EECR |=1; /* Start EEPROM Read */ if(EEDR != 'T') /* Check towards initialization */ error(); /* If not as expected -> error */ EEAR = EEPROMADR(iNumber); /* Set up address to 2nd element */ EECR |=1; /* Start EEPROM Read */ i = EEDR ; /* Set low byte of integer */ EEAR = EEPROMADR(iNumber)+1; /* Set up address to second byte */ EECR |=1; /* Start EEPROM Read */ i |= EEDR<<8; /* Set high byte of integer */ if(i!=0x100) /* Check towards initialization */ error(); /* If not as expected -> error */ EEAR = EEPROMADR(uMinorVersion); /* Set up address to 4th element */ EECR |=1; /* Start EEPROM Read */ if(EEDR != 0x10) /* Check towards initialization */ error(); /* If not as expected -> error */ EEAR = EEPROMADR(uMajorVersion); /* Set up address to 3rd element */ EECR |=1; /* Start EEPROM Read */ if(EEDR != 0xFF) /* Check towards initialization */ error(); /* If not as expected -> error */ for (;;) ; /* Do nothing (success) */ } 程序可以用AVR Studio 编译和仿真。文件“eeprom.hex“ 在仿真运行前必须载入到EEPROM 存储器中,否则就会跳转到error()函数中。EEPROM 的载入是在程序仿真载入后使用菜单中的File -> Up/Download memories 功能(注:在AVR Studio 4.12中是在菜单Debug下)。 8 变量和数据类型 8.1 数据类型 因为AVR是8位单片机,所以只有在真正需要时才去使用16位或32位变量。下面的例子显示了使用8位和16位局部变量作为循环计数器时的代码大小: 8.1.1 8 位计数器 unsigned char count8 = 5; /* Declare a varible, assign a value */ // LDI R16,5 ;Init variable do /* Start a loop */ { }while(--count8); /* Decrement loop counter and check for zero */ // ?0004:DEC R16 ; Decrement // BRNE ?0004 ; Branch if not equal 8.1.2 16 位计数器 unsigned int count16 = 6; /* Declare a variable, assign a value */ // LDI R24,LOW(6) ;Init variable, low byte // LDI R25,0 ;Init variable, high byte do /* Start a loop */ { }while(--count16); /* Decrement loop counter and check for zero */ // ?0004:SBIW R24,LWRD(1) ; Subtract 16-bit value // BRNE ?0004 ; Branch if not equal 表 1. 变量和代码大小 (1) 变量 代码大小(字节) 8位 6 16位 8 注:总是使用最小的需要的变量类型。这对于全局变量特别重要。 8.2 高效使用变量 一个 C 程序划分为多个函数,而函数通过参数传递数据和返回数据。在函数内声明的变量称为局部变量,函数外声明的变量是全局变量。每次函数调用时需要保留的局部变量必须声明为静态局部变量。 全局变量被分配到一个SRAM 存储器空间,这个SRAM空间将只保留给这个全局变量,不能在用于其他目的。所以要慎重考虑避免浪宝贵的费SRAM 空间。过多的全局变量使得程序不易阅读和难以修改、维护。 局部变量在声明后被分配到一个寄存器。局部变量将保持同一个寄存器直到函数结束,或者不在使用它。全局变量在访问前需要从SRAM中复制到工作寄存器中。 下面的例子比较了局部变量和全局变量的代码大小和执行速度的差异。 char global; /* This is a global variable */ __C_task void main(void) { char local; /* This is a local variable*/ global -= 45; /* Subtraction with global variable*/ // LDS R16,LWRD(global) ; Load variable from SRAM to register R16 // SUBI R16,LOW(45) ; Perform subtraction // STS LWRD(global),R16 ; Store data back in SRAM local -= 34; /* Subtraction with local variable*/ // SUBI R16,LOW(34) ; Perform subtraction directly on local : variable in register R16 } 注意LDS和STS (直接载入和保存到SRAM)用于访问SRAM中的变量。它们是双字指令,需要两个时钟周期。 表 2. 变量的代码大小和执行时间 变量 代码大小(字节) 执行时间(周期) 全局 10 5 局部 2 1 一个静态的局部变量在函数运行时先载入工作寄存器,在函数结束时保存回SRAM。如果在函数内访问变量的次数多于一次,使用静态变量的效率将远大于全局变量。 为了限制使用全局变量,在C语言中可以传递参数到函数,也可以返回参数。多达两个简单数据类型 (char, int, long, float, double) 可以通过寄存器R16 - R23传递给函数,大于两个或复杂数据类型 (arrays, structs) 则通过软件堆栈或指向 SRAM 的指针传递。 当使用全局变量时,可以适当的将它们放入一个结构体中,这样C编译器可以使用间接寻址的方式访问它们。下面的例子显示了全局变量和全局结构体之间的比较。 typedef struct { char sec; }t; t global /* Declare a global structure*/ char min; __C_task void main(void) { t *time = &global; // LDI R30,LOW(global) ; Init Z pointer // LDI R31,(global >> 8) ; Init Z high byte if (++time->sec == 60) { // LDD R16,Z+2 ; Load with displacement // INC R16 ; Increment // STD Z+2,R16 ; Store with displacement // CPI R16,LOW(60) ; Compare // BRNE ?0005 ; Branch if not equal } if ( ++min == 60) { // LDS R16,LWRD(min) ; Load direct from SRAM // INC R16 ; Increment // STS LWRD(min),R16 ; Store direct to SRAM // CPI R16,LOW(60) ; Compare // BRNE ?0005 ; Branch if not equal } } 当通过结构体访问全局变量时,编译器使用Z指针和LDD/STD (偏移量载入/保存) 指令。而直接使用全局变量时,编译器使用了LDS和STS (直接载入/保存到SRAM)指令。代码大小的区别是: 表3. 全局变量的代码大小 变量 代码大小(字节) 结构体 10 无结构体 14 这里没有包括初始化Z指针(4字节)。访问1个字节时代码大小是一样的,但是如果结构体包含多个字节时,这样的效率更高一些。 9 优化全局 标志 禁止坐卧标志下载饮用水保护区标志下载桥隧标志图下载上坡路安全标志下载地理标志专用标志下载 位 大多数程序使用到一些全局标志位来控制程序的 流程 快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计 。在AVR中使用全局变量作为标志位效率比较低,因为变量需要先载入到标志寄存器。为了优化检测标志位,标志位可以放到专门的寄存器中,也可以放入没有使用到的I/O中。 注: 将全局变量放入专门寄存器需要在编译选项中分配寄存器,IAR 编译器v1不支持这个功能。 如果I/O存储器的寄存器对应的外设没有使用时,寄存器可以用来存储全局变量。例如,如果不使用UART,那么波特率寄存器UBRR可以当作通用寄存器;如果EEPROM没有使用,那么EEPROM数据寄存器EEDR和EEPROM地址寄存器EEAR也可以用于存放全局变量。 访问I/O存储器的效率非常高,特别是位置低于0x1F的单元,它们可以按位访问。地址高于0x1F的效率稍低一些,但还是高于位于SRAM中的全局变量。 下面的例子显示了使用SRAM变量、I/O存储器和专门寄存器作为全局标志位之间的代码差异: typedef struct bitfield{ // Bitfield structure unsigned char bit0:1; unsigned char bit1:1; unsigned char bit2:1; }bitfield; 9.1 SRAM全局变量: bitfield global_flag; // Bitfield in a global variable ... global_flag.bit1 = 1; // LDI R30,LOW(global_flag) // LDI R31,(global_flag) >> 8 // LD R16,Z // ORI R16,0x02 // ST Z,R16 代码大小: 10 字节 9.2 全局变量在寄存器R15 注意位结构不允许作为寄存器变量。 __no_init __regvar unsigned char reg_flag@ 15; //Global register R15 ... reg_flag |= 0x01; // SET // BLD R15,0 代码大小: 4 字节 9.3 全局标志位在高于0x1F的未用I/O寄存器 __no_init volatile bitfield high_io_flag@0x55; // Bitfield in I/O above 0x1F, note that 0x30 offset is added to address ... high_io_flag.bit2 = 1; // IN R16,0x35 // ORI R16,0x04 // OUT 0x35,R16 代码大小: 6 字节 9.4 全局标志位在低于0x1F的未用I/O寄存器 __no_init volatile bitfield low_io_flag@0x35; Register Below 0x1F // Bitfield in I/O location below 0x1F ... low_io_flag.bit1 = 1; // SBI 0x15,0x01 代码大小: 2 字节 9.5 GPIO 寄存器(通用寄存器) 新型号的AVR单片机保持了使用I/O寄存器的优点,从ATmega169,ATmega48/88/168到ATtiny2313, 所有的AVR单片机都有一定数量的通用I/O寄存器,包括高于和低于0x1F地址的。这些寄存器可以提高程序的性能,并减少代码的大小。 表 4. 常用操作占用的代码大小(字节) 操作 SRAM 高于0x1F 寄存器文件 低于0x1F 设置/清除1位 10 6 4 2 测试1位 6 4 2 2 设置/清除多位 10 6 4 6 和立即数比较 6 4 4 4 例子显示出使用I/O寄存器对于1位的操作效率非常高,而专门的寄存器对于经常访问变量时有很高的效率。注意使用寄存器作为全局变量会限制编译器对代码的优化,对于复杂的程序它可能会增加代码的大小。 9.6 位结构对比位掩码 保存变量时经常将多个位保存到一个字节中。最常见的用法就是将标志位压缩到一个状态字节中。这可以通过定义位掩码或位结构实现,下面就是一个使用位掩码和位结构的例子: /* Use of bit-mask for status bits*/ /* Define bit macros, note that they are similar to the I/O macros*/ #define SETBIT(x,y) (x |= (y)) /* Set bit y in byte x*/ #define CLEARBIT(x,y) (x &= (~y)) /* Clear bit y in byte x*/ #define CHECKBIT(x,y) (x & (y)) /* Check bit y in byte x*/ /* Define Status bit mask constants */ #define RETRANS 0x01 /* bit 0 : Retransmit Flag*/ #define WRITEFLAG 0x02 /* bit 1 : Flag set when write is due*/ #define EMPTY 0x04 /* bit 2 : Empty buffer flag*/ #define FULL 0x08 /* bit 3 : Full buffer flag*/ __C_task void main(void) { char status; /* Declare a status byte*/ CLEARBIT(status,RETRANS); /* Clear RETRANS and WRITEFLAG*/ CLEARBIT(status,WRITEFLAG); /*Check if RETRANS flag is cleared */ if (!(CHECKBIT(status, RETRANS))) { SETBIT(status,WRITEFLAG); } } 如果状态变量声明为函数内的局部变量,那么在C语言中使用位掩码的效率是非常高的。否则就使用未用的I/O作为局部变量。 位结构可以定义在SRAM和I/O寄存器中,全局寄存器变量只允许简单变量,为了使用位指令,需要用位掩码。 ANSI标准没有定义位结构怎样压缩到字节中。如一个位在一个编译器中可能放在MSB (Most Significant Bit)而在另外一个编译器中却在LSB(Least Significant Bit)。使用位掩码就可以完全控制位在变量中的位置。 9.7 初始化全局变量 在复位后,执行初始化代码。没有初始化的全局变量总是0。 例子: unsigned char global_counter = 100; unsigned int global_flags ;// All global variables are initialized to 0 一般情况下在开始的时候不需要初始化全局变量。 为了获得最好的代码效果,所有的全局变量应当在声明时初始化。作为替换方式,所有的变量可以在单独的程序中进行初始化,这样起始代码中的初始化部分就可以删除了。 9.8 访问Flash存储器 一般定义一个常数的用法时: const char max = 127; 这个常数在开始时从flash中复制到SRAM,在程序运行中一直保留在SRAM里,这样会浪费SRAM空间。为了节约SRAM存储器,常数可以直接保存在flash,在需要时才从flash中读取出来: flash char max = 127; flash char string[] = "This string is stored in
本文档为【AVR035 高效AVR C编程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_644545
暂无简介~
格式:doc
大小:180KB
软件:Word
页数:20
分类:互联网
上传时间:2013-11-16
浏览量:36