[转贴]gcc经验点滴(二)---鱼,熊掌.AVR的两种位操作的比较(wjc3k发于21ic)(位域与C位操作的比较) (www.ourdev.cn)
你尚未登录 |登录 |快速注册 |退出 首页 酷贴 搜索 在线 本网站由阿莫电子邮购独家赞助
AVR (原ourAVR.com) 技术论坛 -> 分类清单 -> [转贴]gcc经验点滴(二)---鱼,熊掌.AVR的两种位操作的比较(wjc3k发于21ic)(位域与C位操作的比较) 跳转至: 未登录
↓↓ [转贴]gcc经验点滴(二)---鱼,熊掌.AVR的两种位操作的比较(wjc3k发于21ic)(位域与C位操作的比较) 回复数:118,点击数:23343
抱歉本贴的回复数太多,为了减轻网络流量,只显示帖子正文的前后各10个回复。本贴的正文内容中间隐藏了98个回复。你需要 点击此处 才能查看全部内容。
对为你带来的访问不便表示歉意。不过我们仍会坚持即使没有登录,仍能共享我们网站的所有资料。你没有 登录后就能消除这个访问上的不便(可增加到显示60个回复)。
如果你是新用户请先 注册。注册是免费的,并且手续简单只需要填写用户名与口令。
↓↓↓↓↓↓↓↓↓↓↓↓
【楼主位】 dfgeoff 嗜血蜗牛

积分:692
派别:
等级:------
来自:江苏南京
转贴前言:一直看到做51转做AVR的朋友,想继续使用bit这样的操作,讨论也不少。其实对于设计人员的未来考虑,还是使用纯C比较好,毕竟适应性强。C51增加的bit、sbit是考虑了51的SRAM小的特点的。移植性就受到影响了。能放弃,尽量放弃。
-------------------------------------------
wjc3k 发表于 3/5/2003 10:39:38 AM AVR 单片机 
大家好,得到大家的大力支持,本小小菜现如今胆儿大了点,脸皮厚了点儿,又来献丑啦。今儿就不说没用的话了,免得鸡蛋横飞过来啦。

AVR的两种位操作的比较(位域方式和移位宏方式)

测试环境如下:
硬件:AT90S2313
软件:    WiinAVR gcc3.3   -Os级优化(最小size)。


说明:
    由于AVR不支持位操作,所以必须通过软件来实现。下面对我所知道的两种方法进行一个简单的比较。
    1、位域方式。先定义一个位域,
            typedef struct _bit_struct
            {
                unsigned char bit0 : 1 ;
                unsigned char bit1 : 1 ;
                unsigned char bit2 : 1 ;
                unsigned char bit3 : 1 ;
                unsigned char bit4 : 1 ;
                unsigned char bit5 : 1 ;
                unsigned char bit7 : 1 ;
                unsigned char bit6 : 1 ;
            }bit_field;
        再用一个宏    ,来指向要操作的位。
             #define LED             GET_BITFIELD(PORTB).bit0
             #define BUTTON      GET_BITFIELD(PINB).bit7
        使用时只需要直接赋值即可:如LED =     0 ,LED = 1,  或者直接判断 LED==0    ,    LED ==1.
        这种方法类似C51中的位操作。直接。
    2、位移宏方式。主要有三个.
                #define Set_Bit(val, bitn)    (val |=(1<<(bitn)))
                #define Clr_Bit(val, bitn)     (val&=~(1<<(bitn)))
                #define Get_Bit(val, bitn)    (val &(1<<(bitn)) )
         三个分别用来设置某一位,清除某一位,取某一位的值.
           使用方法为.Set_Bit(PORTA,3);   Clr_Bit(PORTB,2);   Get_Bit(val,5);
    3、测试程序.
           说明,假设PORTB.7接按纽,PORTB.0 接LED
           测试程序完成如下操作。
                   当BUTTON == 0时 ,LED输出1 否则输出0,
                   这样的目的是即测试了输入,又测试了输出1和输出0,相对全面一点。  C代码如下.

                    // testled.c     测试AVR的位操作.
                // 这是gcc;如是其它编译器,请修改。
                #include <avr/io.h>

                // 定义一个寄存器(Register)或端口(Port)的八个位
                typedef struct _bit_struct
                {
                    unsigned char bit0 : 1 ;
                    unsigned char bit1 : 1 ;
                    unsigned char bit2 : 1 ;
                    unsigned char bit3 : 1 ;
                    unsigned char bit4 : 1 ;
                    unsigned char bit5 : 1 ;
                    unsigned char bit7 : 1 ;
                    unsigned char bit6 : 1 ;
                }bit_field;

                  //定义一个宏,用来得到每一位的值
                #define GET_BITFIELD(addr) (*((volatile  bit_field *) (addr)))

                //定义每一个位
                #define LED             GET_BITFIELD(PORTB).bit0
                #define BUTTON      GET_BITFIELD(PINB).bit7


                #define Set_Bit(val, bitn)    (val |=(1<<(bitn)))
                #define Clr_Bit(val, bitn)     (val&=~(1<<(bitn)))
                #define Get_Bit(val, bitn)    (val &(1<<(bitn)) )

                int main( void )
                {
                    DDRB = 0x41;   //配置PB0为输出,PB7为输入
                    if ( BUTTON==0 )     LED = 1; else LED = 0;
                    //if(!Get_Bit(PINB,7) )  Set_Bit(PORTB,0);    else Clr_Bit(PORTB,0);
                    while(1);
                }
                //     ----------------------        end         -----------------------------
    4、测试过程。
       a.先使用位域方式。
       主程序中使用 if ( BUTTON==0 )     LED = 1; else LED = 0;
       结果如下:
                     int main( void )
                    {
                      4a:    cf ed           ldi    r28, 0xDF    ; 223
                      4c:    d0 e0           ldi    r29, 0x00    ; 0
                      4e:    de bf           out    0x3e, r29    ; 62
                      50:    cd bf           out    0x3d, r28    ; 61
                        DDRB = 0x41;      //配置PB0为输出,PB7为输入
                      52:    81 e4           ldi    r24, 0x41    ; 65
                      54:    87 bb           out    0x17, r24    ; 23
                        if ( BUTTON==0 )     LED = 1; else LED = 0;
                      56:    86 b3           in    r24, 0x16    ; 22
                      58:    e8 2f           mov    r30, r24
                      5a:    ff 27           eor    r31, r31
                      5c:    80 81           ld    r24, Z
                      5e:    86 fd           sbrc    r24, 6
                      60:    07 c0           rjmp    .+14         ; 0x70
                      62:    88 b3           in    r24, 0x18    ; 24
                      64:    e8 2f           mov    r30, r24
                      66:    ff 27           eor    r31, r31
                      68:    80 81           ld    r24, Z
                      6a:    81 60           ori    r24, 0x01    ; 1
                      6c:    80 83           st    Z, r24
                      6e:    06 c0           rjmp    .+12         ; 0x7c
                      70:    88 b3           in    r24, 0x18    ; 24
                      72:    e8 2f           mov    r30, r24
                      74:    ff 27           eor    r31, r31
                      76:    80 81           ld    r24, Z
                      78:    8e 7f           andi    r24, 0xFE    ; 254
                      7a:    80 83           st    Z, r24
                        while(1);
                      7c:    ff cf           rjmp    .-2          ; 0x7c

         main函数共52Bytes.其中,从lst文件看得出:main函数的初始化用了4条指令,8Bytes. 最后一句while(1);用了1条指令2Bytes.( for循环和do-while也是)
         DDRB=0x41用了2条指令4Bytes. 计算一下:52-8-4-2=38Bytes,即if ( BUTTON==0 )     LED = 1; else LED = 0; 这句用了19条指令38Bytes. (居然运用了3个寄存器白r24,r30,r31,和一个Z,代码真是苦涩,,我看不懂,准备以后作代码加密用:).  )
       b.使用移位宏方式。
       将 if ( BUTTON==0 )     LED = 1; else LED = 0;  换为等效的     if(!Get_Bit(PINB,7) )  Set_Bit(PORTB,0);    else Clr_Bit(PORTB,0);

       结果,main函数仅24Bytes.其它代码一样,略去. 所以,上面这句代码仅用了24-14=10Bytes ,5条指令。生成的代码如下:
              56:    b7 99           sbic    0x16, 7    ; 22
              58:    02 c0           rjmp    .+4          ; 0x5e
              5a:    c0 9a           sbi    0x18, 0    ; 24
              5c:    01 c0           rjmp    .+2          ; 0x60
              5e:    c0 98           cbi    0x18, 0    ; 24
    5. 菜论:鱼和熊掌。
      由于AVR可以对I/O脚进行sbic,sbi,cbi,这样的位操作,所以使用I/O脚操作时,移位宏可以产生高效的代码。
      例如,要实现上面的几个简单的指令,为了实现LED=1这样的类似C51的sbit的效果,我必须多付出(38-10=28Bytes)的代价。

    6......
        对于I/O脚,可以产生这样高效的代码,是因为有sbi和cbi这样的指令,那么对于一般的变量,又如何呢?................

 
2005-01-12,17:32:28
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【1楼】 hotpower 菜农

积分:2136
派别:
等级:------
来自:西安大雁塔村
通用MCU8位16位24位32位变量定义头文件MCUBIT.H

PICC菜鸟玩PICC位变量潇洒走一回

本人对移植的看法
-----此内容被hotpower于2005-01-12,19:21:46编辑过
 
2005-01-12,19:16:05
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【2楼】 jeewood

积分:760
派别:
等级:------
来自:
好贴子啊,不顶不行的!
 
2005-01-12,21:02:01
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【3楼】 lanmp

积分:259
派别:
等级:------
来自:
3/5/2003 好早啊
atmel 的application note上面也有,很清楚。
 
2005-01-12,22:22:37
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【4楼】 onorg 自由之神

积分:347
派别:
等级:------
来自:湖南 湘潭
看了不顶,是白痴
 
2005-11-23,12:08:18
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【5楼】 benladn911 AVR猎手

积分:2285
派别:
等级:------
来自:孤独地带
我也有些小方法,呵呵,见笑了:
看下下面的程序吧:
/********************************************************************
 *  主    页 :  HTTP://WWW.QLMCU.COM             
 *                                                              
 *  程序功能 :  流水灯的左移右移实验
 *                                                                         
 *  应用软件 :  WinAVR                                               
 *                                                                      
 *  版    本 :  WinAVR-20050214-install
 *                                                         
 *  硬    件 :  WS9500
 *                                                               
 *  创建时间 :  2005-11-10
 *              
 *  编    写:   benladn911           
 *              
 *  注:为了有更多实用的实验程序供大家学习,部分程序参考网上的资源,
 *      在此谢谢这些无私奉献的朋友!!!       
 *                        
 ********************************************************************/  
#include <avr/io.h>
#include <avr/delay.h> 
//注: 内部函数_delay_ms() 最高延时  262.144mS@1MHz 即 32.768ms@8MHz
//    该函数可以实现较精确的定时for()/while()指令很难计算延时时间
//    为了使 _delay_ms()函数的延时正确,须在makefile中设定F_CPU为实际的系统时钟频
//    本范例为6MHz外部晶振振荡器 即 F_CPU=6000000

//-----------位操作定义------------------------<<<<<<<<<<<<<<<<<<<<<<<<<这里
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))

#define LED_ON  cbi(PORTD,7)//在程序用LED_ON代替cbi(PORTD,7)<<<<<<<<<<<<<<

//---------------------------------------------------------------
//内部函数_delay_ms() 最高延时  262.144mS@1MHz 即 32.768ms@8MHz
void delay_ms(unsigned int ms);//----ms级延时

//内部函数_delay_us() 最高延时  768 us@1MHz 即 96 us@8MHz
void delay_us(unsigned int us);//----us级延时


//--------------------------------------------------------------------------
int main(void)
{
 unsigned char i,j; //定义变量

 DDRA=0xFF; //定义了端口PORTA全部为输出
 PORTA=0xFF; //PA口设为输出高电平,灯灭PORTA
 
   //第一种方法选通PD7(LED的电源控制的端)
   //DDRD=0xFF;          //PORTD全部设置为输出
   //PORTD=PORTD & 0x7F; //0x7F=0b0111 1111
 
//第二种方法选通PD7(LED的电源控制的端)
//DDRD=0xFF;            //PORTD全部设置为输出
//PORTD=0x7F;           //0b0111 1111
 
 //第三种方法选通PD7(LED的电源控制的端)
 sbi(DDRD,7);//对PD7的DDR清零,PD7设置为输出
 LED_ON;//PD7输出低电平

 while(1)
 {
i=0x01;
for (j=0;j<8;j++)  //循环8次,即PA0~~PA7轮流闪亮  
{
PORTA=~i; //反相输出,低电平有效
delay_ms(100);
i=i<<1; //左移一位
// 0b00000001 PB0
// 0b00000010 PB1
// 0b00000100 PB2
// 0b00001000 PB3
// 0b00010000 PB4
// 0b00100000 PB5
// 0b01000000 PB6
// 0b10000000 PB7
}

i=0x80;  
for (j=0;j<8;j++)  //循环8次,即PA0~~PA7轮流闪亮  
{
PORTA=~i; //反相输出,低电平有效,
delay_ms(100);
i=i>>1; //右移一位
}
 }
}

//----------------ms级延时---------------
void delay_ms(unsigned int ms)
{
    unsigned int i;
    for(i=0;i<ms;i++) _delay_ms(1); //延时 i*ms=   毫秒,可自行调节
}

//----------------us级延时---------------
void delay_us(unsigned int us)
{
    unsigned int i;
    for(i=0;i<us;i++) _delay_us(1); //延时 i*us=   毫秒,可自行调节
}



 
2005-11-23,12:38:00
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【6楼】 hotpower 菜农

积分:2136
派别:
等级:------
来自:西安大雁塔村
哈哈,现在被AVR感化了,反而见了bit就晕...
(1 << bit)挺好的,见了它就知道和位有关了,出错几率反而更小了.
 
2005-12-06,19:05:21
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【7楼】 whaul
积分:64
派别:
等级:------
来自:浙江宁波
新手学习中,致谢
 
2005-12-12,10:58:38
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【8楼】 zhi1980
积分:16
派别:
等级:------
来自:
不错,谢了
 
2005-12-13,23:04:42
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【9楼】 zhengkaike 菜鸟先飞
积分:52
派别:
等级:------
来自:
顶~~~~~~~~~~
 
2005-12-15,15:25:19
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【10楼】 rf232
积分:7
派别:
等级:------
来自:
顶一下
 
2005-12-26,10:10:12
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
↑↑↑↑↑↑↑↑↑↑↑↑
抱歉本贴的回复数太多,为了减轻网络流量,只显示帖子正文的前后各10个回复。本贴的正文内容中间隐藏了98个回复。你需要 点击此处 才能查看全部内容。
对为你带来的访问不便表示歉意。不过我们仍会坚持即使没有登录,仍能共享我们网站的所有资料。你没有 登录后就能消除这个访问上的不便(可增加到显示60个回复)。
如果你是新用户请先 注册。注册是免费的,并且手续简单只需要填写用户名与口令。
↓↓↓↓↓↓↓↓↓↓↓↓
【109楼】 haso2007 错别字-妹影
积分:174
派别:
等级:------
来自:
【16楼】 alexant 
注意GET_BITFIELD(&address).bit##b中的address前面的取址符"&",这是唯一与楼主所讲的不一样的地方,大家可以该该再看看 
下面是if(dat&0x80)nRF905_MOSI=1;else nRF905_MOSI=0;的汇编代码: 
_______________________________________________________________
着实代码效率也很高,是不是lz的定义不够好呢?
 
2010-01-06,09:13:32
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【110楼】 yx0315 明晚的星星
积分:1
派别:
等级:------
来自:湘农大机械
cvavr里面直接对位操作是何故?
 
2010-02-04,01:19:02
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【111楼】 wcw19781009
积分:8
派别:
等级:------
来自:
方法挺多啊,挺好,以前我也一直被bit运算所困惑,不过现在我不了,我觉得我现在用的方法也很好,如下,大家可以看看。

在IAR 4.2 下 包含 iom16.h 库后,其实大家就能用位运算了。 规则如下 : sfr名称.BitN 

例如 PORTB2 作为输出: 设置后 DDRB 后 可以这样写 PORTB.Bit2=1;或 PORTB.Bit2=0; 注意大小写 
如果要将端口作为输入: 设置后 DDRB 后 可以这样写 N=PINB.Bit2;或 N=PINB.Bit2; 注意大小写 

所以我们以后再也不怕位运算了,也无需去自己定义那个结构体和共用体了,方便吧?希望大家支持 
 
2010-02-17,18:07:57
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【112楼】 yutianyiren

积分:382
派别:
等级:------
来自:
不错不错,都是好方法
 
2010-03-02,08:25:20
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【113楼】 wukaka
积分:332
派别:
等级:------
来自:
俺喜欢楼主的这个方法: 
2、位移宏方式。主要有三个. 
#define Set_Bit(val, bitn)    (val |=(1<<(bitn))) 
#define Clr_Bit(val, bitn)     (val&=~(1<<(bitn))) 
#define Get_Bit(val, bitn)    (val &(1<<(bitn)) ) 
顶你
 
2010-05-21,14:41:37
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【114楼】 sxcrgm
积分:32
派别:
等级:------
来自:
很有学术性质啊
 
2010-05-22,21:06:47
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【115楼】 cain.lee
积分:113
派别:
等级:------
来自:
这个位移是我开始接触avr的时候兴起的··还要多多注意用法···
 
2010-05-22,21:38:54
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【116楼】 xyang18 狗头军师
积分:417
派别:
等级:------
来自:
好好学习中
__________________________
不要相信我所说过的,希望大家能更多的指出我的不足
2010-05-22,22:59:22
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【117楼】 xingyu
积分:21
派别:
等级:------
来自:
大大大ddddddddd
 
2010-05-22,23:08:47
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
【118楼】 sunpei0337115
积分:5
派别:
等级:------
来自:
好好学习了
 
2010-05-29,21:53:31
资料 邮件 回复 引用回复   ↑↑   ↓↓ 编辑 删除
↑↑↑↑↑↑↑↑↑↑↑↑
抱歉本贴的回复数太多,为了减轻网络流量,只显示帖子正文的前后各10个回复。本贴的正文内容中间隐藏了98个回复。你需要 点击此处 才能查看全部内容。
对为你带来的访问不便表示歉意。不过我们仍会坚持即使没有登录,仍能共享我们网站的所有资料。你没有 登录后就能消除这个访问上的不便(可增加到显示60个回复)。
如果你是新用户请先 注册。注册是免费的,并且手续简单只需要填写用户名与口令。
↑↑  快速回复帖子主题:[转贴]gcc经验点滴(二)---鱼,熊掌.AVR的两种位操作的比较(wjc3k发于21ic)(位域与C位操作的比较)
你尚未登录,不能回复以上帖子。
AVR (原ourAVR.com) 技术论坛 -> 分类清单 -> [转贴]gcc经验点滴(二)---鱼,熊掌.AVR的两种位操作的比较(wjc3k发于21ic)(位域与C位操作的比较) 跳转至: 未登录
任何建议或投诉,欢迎随时与我们联系。 ourDEV.cn 我们的电子开发网,友好交流气氛,乐于开源共享,“这里远比混乱的现实世界美好”。
网站负责人:  阿莫(armok),手机:13433056000,电邮:armok ourdev.cn(请将空格改成@)
地址与电话:  东莞市东城八一路同沙工业园内,0769-22298000,传真请按4
 阿莫邮购门市与雕刻机展示厅:东莞市莞城区莞太路和生文具礼品批发市场A2018,A2035 杨小姐 13711915767
 雕刻机的技术咨询、订单查询、生产进度,可查询:13600266008吴先生(雕刻机设计人员kaif_w)
声明:  本网站由东莞科士通(Infostone)科技有限公司属下的阿莫电子邮购部提供独家赞助。
 有正式的工商注册与网站备案。详情可到东莞工商局查询。
 本网站(ourDEV.cn)原名称为ourAVR.com(我们的AVR),自2007-09-01起从专业的AVR网转成综合电子网站。
 本网站系统由armok Using Oracle + Java/JSP + WebLogic + Linux 2001/12/20 -- 2007/09/13
 本BBS所有代码均为原创,版权归 armok 所有。本网站自:2004-10-03,16:31:17 起开始运行。
法律顾问:  本网站尊重知识产权,自2007年08月15日起,魏龙律师担任ourDEV.cn(原ourAVR.com)常年法律顾问。

本网站已经通过了东莞市公安局安全认证许可,请大家遵守国家法律法规。粤ICP备09047143号