dfgeoff 发表于 2005-1-12 17:32:28

[转贴]gcc经验点滴(二)---鱼,熊掌.AVR的两种位操作的比较(wjc3k发于21ic)(位域与C位操

转贴前言:一直看到做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) (*((volatilebit_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这样的指令,那么对于一般的变量,又如何呢?................

hotpower 发表于 2005-1-12 19:16:05

通用MCU8位16位24位32位变量定义头文件MCUBIT.H



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



本人对移植的看法
-----此内容被hotpower于2005-01-12,19:21:46编辑过

jeewood 发表于 2005-1-12 21:02:01

好贴子啊,不顶不行的!

lanmp 发表于 2005-1-12 22:22:37

3/5/2003 好早啊

atmel 的application note上面也有,很清楚。

onorg 发表于 2005-11-23 12:08:18

看了不顶,是BC

benladn911 发表于 2005-11-23 12:38:00

我也有些小方法,呵呵,见笑了:

看下下面的程序吧:

/********************************************************************

*主    页 :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_ONcbi(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=   毫秒,可自行调节

}

hotpower 发表于 2005-12-6 19:05:21

哈哈,现在被AVR感化了,反而见了bit就晕...

(1 << bit)挺好的,见了它就知道和位有关了,出错几率反而更小了.

whaul 发表于 2005-12-12 10:58:38

新手学习中,致谢

zhi1980 发表于 2005-12-13 23:04:42

不错,谢了

zhengkaike 发表于 2005-12-15 15:25:19

顶~~~~~~~~~~

rf232 发表于 2005-12-26 10:10:12

顶一下

fqs0920 发表于 2005-12-28 16:06:42

用字节进行逻辑运算吗!很简单!就可以对任何位置位,清0

Char_Gato 发表于 2006-3-18 12:43:30

顶~~~~~~~~

c551 发表于 2006-4-12 16:33:46

又学到了知识,非常感谢.

lyping 发表于 2006-4-12 18:12:16

为什么我按楼主写的测试位操作不行呢?哪位老兄给解释一下!单片机是MAGE8的。

xwyjianghu 发表于 2006-7-6 13:27:27

看了就长见识了。呵呵。顶了

alexant 发表于 2006-7-6 23:16:15

本来已经忘了这个话题,我一直这样用的:

#define GET_BITFIELD(addr)         (*((volatilebit_field *)(addr)))



#define GETBIT(address,b)        GET_BITFIELD(&address).bit##b





#define PD7__               GETBIT(PORTD,7)

#define PD6__               GETBIT(PORTD,6)

#define PD5__                GETBIT(PORTD,5)

#define PD4__               GETBIT(PORTD,4)

#define PD3__               GETBIT(PORTD,3)

#define PD2__               GETBIT(PORTD,2)

#define PD1__               GETBIT(PORTD,1)

#define PD0__               GETBIT(PORTD,0)





#define                nRF905_MOSI        PD7__

#define                nRF905_SCK                PD6__



注意GET_BITFIELD(&address).bit##b中的address前面的取址符"&",这是唯一与楼主所讲的不一样的地方,大家可以该该再看看

下面是if(dat&0x80)nRF905_MOSI=1;else nRF905_MOSI=0;的汇编代码:



           if(dat&0x80)

3da:        87 ff               sbrs        r24, 7

3dc:        02 c0               rjmp        .+4              ; 0x3e2 <SpiWriteByte+0xa>

                        nRF905_MOSI=1;

3de:        5f 9a               sbi        0x0b, 7        ; 11

3e0:        01 c0               rjmp        .+2              ; 0x3e4 <SpiWriteByte+0xc>

           else

                        nRF905_MOSI=0;

3e2:        5f 98               cbi        0x0b, 7        ; 11

                nRF905_SCK=1;

3e4:        5e 9a               sbi        0x0b, 6        ; 11







究其原因,AVR标准头文件中的是这样定义一个寄存器的:

#define PIND   _SFR_IO8(0x10)

实际上是这样的

#define PIND    (*(volatile uint8_t *)(0x10))



可以看出PIND已经成为指向地址0x10的内容,,而我们是要对地址进行位操作,所以需要用"&PIND"将其还原为地址再使用.

-----此内容被alexant于2006-07-06,23:32:47编辑过


-----此内容被alexant于2006-07-06,23:45:22编辑过

kaiwenavr 发表于 2006-7-7 17:46:20

^_^

真正感觉到C的灵活

jstwjw 发表于 2006-7-8 07:03:10

有启发,好文

kltiantang 发表于 2006-7-8 14:35:40

好,以后就这样用呵。谢谢了,顶起来!

ml07077 发表于 2006-7-8 23:44:09

WinAvr 2006.4.21版有 <avr/portpins.h>

slamkk 发表于 2006-9-18 23:39:34

请问这一块,最后一部分是什么意思?

#define GET_BITFIELD(addr)    (*((volatilebit_field *)(addr)))

ama_killer 发表于 2006-9-30 12:13:04

*

alexant 发表于 2006-9-30 15:57:11

bit_field就是楼主帖子里的那个位域啊:

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 bit6 : 1 ;

unsigned char bit7 : 1 ;

}bit_field;

#define GET_BITFIELD(addr)    (*((volatilebit_field *)(addr)))是将addr强制为位域指针,方便其他的宏来访问addr内容的某个位.

不知这样解释是否达意,还望各位多多赐教!

yanhanyu 发表于 2006-10-4 23:37:37

强烈支持hotpower 菜农

sundmon 发表于 2006-10-5 02:51:25

学习中

martin7wind 发表于 2006-10-6 08:42:26

学习中

zsulrq 发表于 2006-11-17 11:15:38

请问可以这样定义吗?

typedef struct _bit_struct

{

unsigned char bit0 : 8 ;

unsigned long bit1 : 17 ;

unsigned char bit2 : 7 ;

unsigned char bit3 : 8 ;   

}bit_field;



如果这样定义的话,占用的字节数会是多少?5个?

谢谢!

MYMCU 发表于 2006-11-17 11:49:40

不错.如果先定义好led的状态,用起来更方便一点.如:

#define led_on PORTB&=0xfe//置0

#define led_off PORTB|=0x01//置1

#define led_cpl PORTB^=0x01//取反

要开led,直接led_on,程序看起来更直观.

zsulrq 发表于 2006-11-17 12:07:30

请问可以这样定义吗?

typedef struct _bit_struct   

{   

unsigned char bit0 : 8 ;   

unsigned long bit1 : 17 ;   

unsigned char bit2 : 7 ;   

unsigned char bit3 : 8 ;   

}bit_field;



如果这样定义的话,占用的字节数会是多少?5个?

谢谢!



在网上找了一下,只有一个网友说像上面这样定义是不行的,但没有验证。



这里有没有大虾讲解多一下??

zsulrq 发表于 2006-11-17 15:45:21

敝人验证了一下,证明是可以这样定义的(在winavr中)

而且在网上找了一个网友发表的文章,他说编译器不同可能压缩的方法就不同。

所以敝人所说可以这么定义,而且内存定位是压缩排列的前提是使用GCC的winavr,其他情况没有验证。

还有提个现象:ouravr上回答他人问题的人真的越来越少了,有时候急的时候真等也等不到呀……

wentao 发表于 2006-11-17 16:32:47

顶一下

jiushiainile 发表于 2006-12-31 22:20:06

我也来顶一下,受益非浅,谢谢各位了

swordKING 发表于 2007-2-2 17:02:21

谢谢,什么时候我才能上传我的东西跟大家分享呢,哈哈,初学者的心愿!

joson 发表于 2007-2-2 17:30:08

好贴!不过我还是喜欢用"与","或" 进行字节操作.

microcon 发表于 2007-2-2 18:42:48

顶!



对于bit变量采用下面的方法操作也很方便:



typedef struct {

unsigned BIT0: 1;

unsigned BIT1: 1;

unsigned BIT2: 1;

unsigned BIT3: 1;

unsigned BIT4: 1;

unsigned BIT5: 1;

unsigned BIT6: 1;

unsigned BIT7: 1;

}AVRBIT;//定义一个只能按位域寻址的新变量类型



typedef union {

AVRBIT BIT;//可以按位域寻址

unsigned char BYTE;//可以按字节寻址

}AVRBITBYTE;//定义一个既能按位域寻址也可按字节寻址的新变量类型



   AVRBITBYTErWorkFlag;



#define    fPowerOn      rWorkFlag.BIT.BIT0

#define    fModelWork      rWorkFlag.BIT.BIT1

#define    fOnWork         rWorkFlag.BIT.BIT2

#define    fSelfDisp       rWorkFlag.BIT.BIT6   

#define    fSetFlash5s   rWorkFlag.BIT.BIT7



            int main( void )

            {

            //      DDRB = 0x41;   //配置PB0为输出,PB7为输入

                  while(1)

                  {

                  if ( fPowerOn==1 ) fOnWork = 1; else fOnWork = 0;

                  if ( fPowerOn==0 )fPowerOn =1;

                     }

                }

John_Lee 发表于 2007-2-3 14:35:09

各位的方法都有一个不足的地方:不能定义多于1位的bitfield。

例如:atmega64外接了一个29SF040,地址A0-A18,按32Kbytes分页,29SF040的A0-A14接到MCU地址总线,A15-A18做分页地址,连接到一般I/O上,例如PORTF0-PORTF3。

在程序中如何方便地定义和访问这种多于1位的位域?即某个位域变量可以从一个字节的bit0到bit7中的任何一位开始,宽度可以从1位到7位。

bluemidi 发表于 2007-2-3 16:14:48

楼上的,给你地址你看看,这儿有介绍.

http://www.programfan.com/article/showarticle.asp?id=2695

John_Lee 发表于 2007-2-4 00:38:14

可能我没说明白意思,算了,我直接给出我的答案:终极位域定义。

下面是头文件:

---------------------------------------------

#ifndef __BITWISE_H

#define __BITWISE_H



#ifdef __cplusplus

extern "C" {

#endif



#define _BITFIELD_(_W)                                \

        typedef union {                                        \

                struct {                                        \

                        uint8_t used        :_W;        \

                };                                                        \

                uint8_t dummy;                                \

        } _bit_0_ ## _W ## _t



#define _BITFIELD1_(_S, _W)                        \

        typedef union {                                        \

                struct {                                        \

                        uint8_t res                :_S;        \

                        uint8_t used        :_W;        \

                };                                                        \

                uint8_t dummy;                                \

        } _bit_ ## _S ## _ ## _W ## _t



_BITFIELD_ (1);

_BITFIELD_ (2);

_BITFIELD_ (3);

_BITFIELD_ (4);

_BITFIELD_ (5);

_BITFIELD_ (6);

_BITFIELD_ (7);

_BITFIELD_ (8);



_BITFIELD1_ (1, 1);

_BITFIELD1_ (1, 2);

_BITFIELD1_ (1, 3);

_BITFIELD1_ (1, 4);

_BITFIELD1_ (1, 5);

_BITFIELD1_ (1, 6);

_BITFIELD1_ (1, 7);



_BITFIELD1_ (2, 1);

_BITFIELD1_ (2, 2);

_BITFIELD1_ (2, 3);

_BITFIELD1_ (2, 4);

_BITFIELD1_ (2, 5);

_BITFIELD1_ (2, 6);



_BITFIELD1_ (3, 1);

_BITFIELD1_ (3, 2);

_BITFIELD1_ (3, 3);

_BITFIELD1_ (3, 4);

_BITFIELD1_ (3, 5);



_BITFIELD1_ (4, 1);

_BITFIELD1_ (4, 2);

_BITFIELD1_ (4, 3);

_BITFIELD1_ (4, 4);



_BITFIELD1_ (5, 1);

_BITFIELD1_ (5, 2);

_BITFIELD1_ (5, 3);



_BITFIELD1_ (6, 1);

_BITFIELD1_ (6, 2);



_BITFIELD1_ (7, 1);



#define SFR(_P, _S, _W)        (* (_bit_ ## _S ## _ ## _W ## _t volatile *) (_SFR_ADDR (_P))).used



#ifdef __cplusplus

}

#endif



#endif

/* EOF */

-------------------end--end--end---------------------------



使用方法:

要使用的位域变量或寄存器,只需按下面的规则定义一下就可以了:

#define <BITFIELD_VAL> SFR(<BYTE_ADDR>,<START_BIT>,<BIT_WIDTH>)

BITFIELD_VAL: 要使用的位域变量名。

BYTE_ADDR: 所需的位域变量所在的字节地址。

START_BIT: 所需的位域变量在该字节的起始位置。

BIT_WIDTH: 位域变量的宽度。



比如:我要定义PORTF3-PORTF6作为一个整体变量使用,变量名为PAGE_REG,我只需定义:

#define PAGE_REG    SFR (PORTF, 3, 4)



在使用时就直接读写 PAGE_REG 就可以了,决不会影响 PORTF0, PORTF1, PORTF2, PORTF7的值。



再举例:定时器预分频设置:

#define T0_CK       SFR (TCCR0, 0, 3)



使用时:

T0_CK = 0;   // stop

T0_CK = 1;   // OSC

T0_CK = 2;   // OSC/8

T0_CK = 3;   // OSC/64

T0_CK = 4;   // OSC/256

等等。



是不是很方便?

caixiong 发表于 2007-2-6 16:42:02

学习中!

eagle2006 发表于 2007-4-9 00:46:17

指针没学好,看到指针就晕了

zl345 发表于 2007-5-15 22:48:57

为什么我按楼主和5楼写的测试位操作不行呢?哪位老兄给解释一下!用studio4.12仿真的。

./emotion/em045.gif

zhuyu 发表于 2007-6-18 16:59:32

GET_BITFIELD(&address).bit##b

在这个式子中最后的##b,其中##是代表什么意思?

有谁可以解释一下的吗 ?

zhuyu 发表于 2007-6-18 17:17:11

ps : 是连接的意思,刚查到 !

snow_xsj 发表于 2007-6-28 23:40:02

个人有个人的习惯,不必说谁好谁坏;就像学英语是美式好还是英式好,一言难尽。

对从C51转过来搞AVR的人来说,对位操作还是情有独钟的。

感谢楼主的分享。

Tomson 发表于 2007-8-17 13:56:18

我非常赞成snow_xsj的观点

Tomson 发表于 2007-8-18 12:48:35

我用了楼主的方法,但在编译后出现了很多警告:

2549: warning: cast to pointer from integer of different size,



我的引用如下:

unsigned char flag4;



#define image_off GET_BITFIELD(flag4).bit0

#define image_art GET_BITFIELD(flag4).bit1

#define image_wb GET_BITFIELD(flag4).bit2

#define picture_lr GET_BITFIELD(flag4).bit3

#define picture_ud GET_BITFIELD(flag4).bit4

#define mask_on GET_BITFIELD(flag4).bit5

#define ext_on GET_BITFIELD(flag4).bit6

#define alarm_on GET_BITFIELD(flag4).bit7



#define GET_BITFIELD(addr) (*((volatilebit_field *) (addr)))

/*########################################################*/

    typedef struct _bit_struct

            {

                unsignedbit0 : 1 ;

                unsignedbit1 : 1 ;

                unsignedbit2 : 1 ;

                unsignedbit3 : 1 ;

                unsignedbit4 : 1 ;

                unsignedbit5 : 1 ;

                unsignedbit6 : 1 ;

                unsignedbit7 : 1 ;

            }bit_field;

/*########################################################*/

请各位大虾帮我看看是哪里出了问题,如何更改?

brotherwyz 发表于 2007-8-18 13:25:20

6......

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



期待有人答复这一点,因为我编了108K的程序,RAM也用了3.4K,再往后编时,为了节省RAM,怕不够用,就用了一些位域来定义一些标志变量,但不是I/0,这样是不是代码加长了呢?

frozenstar 发表于 2007-11-23 22:03:19

好啊,顶

Chenxg 发表于 2007-12-4 13:02:36

好,学习了.谢谢各位!!小弟祝大家天天开心.

tuy0326 发表于 2007-12-6 02:04:27

学习!

ATmega32 发表于 2007-12-6 08:55:21

在IAR下,用位域读写IO口非常高效的。

0400110222wmm 发表于 2008-4-21 16:19:40

帮助很大

chen1986sl 发表于 2008-4-21 21:19:51

对于AVR的为操作 我一般是喜欢宏定义
例如    :#define ledon (PORTB|=(1<<PB0);

AVR猎手的这个更不错 ./emotion/em021.gif./emotion/em021.gif
//-----------位操作定义------------------------<<<<<<<<<<<<<<<<<<<<<<<<<这里
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))

bjj9217 发表于 2008-4-21 22:13:30

学习!

ndust 发表于 2008-5-29 00:13:50

记号

273504522 发表于 2008-5-29 06:26:16

好帖,不过有些地方还不是很明白

wisebaby 发表于 2008-5-29 11:30:17

我和16楼的用法一样

xad74 发表于 2008-5-29 11:44:18

先顶一下,待会再看

lofeng 发表于 2008-5-29 12:24:41

ndust 发表于 2008-6-3 23:55:07

应该好好看看!

cds828 发表于 2008-6-4 22:53:32

这个不错,收藏了

525133174 发表于 2008-6-11 14:38:31

LZ,你错了.16楼的才是正确的:

*************************************************************************
【16楼】 alexant

注意GET_BITFIELD(&address).bit##b中的address前面的取址符"&",这是唯一与楼主所讲的不一样的地方,大家可以该该再看看
下面是if(dat&0x80)nRF905_MOSI=1;else nRF905_MOSI=0;的汇编代码:

         if(dat&0x80)
3da:      87 ff               sbrs      r24, 7
3dc:      02 c0               rjmp      .+4            ; 0x3e2 <SpiWriteByte+0xa>
                        nRF905_MOSI=1;
3de:      5f 9a               sbi      0x0b, 7      ; 11
3e0:      01 c0               rjmp      .+2            ; 0x3e4 <SpiWriteByte+0xc>
             else
                        nRF905_MOSI=0;
3e2:      5f 98               cbi      0x0b, 7      ; 11
                nRF905_SCK=1;
3e4:      5e 9a               sbi      0x0b, 6      ; 11



究其原因,AVR标准头文件中的是这样定义一个寄存器的:
#define PIND   _SFR_IO8(0x10)
实际上是这样的
#define PIND    (*(volatile uint8_t *)(0x10))

可以看出PIND已经成为指向地址0x10的内容,,而我们是要对地址进行位操作,所以需要用"&PIND"将其还原为地址再使用.

525133174 发表于 2008-6-11 15:02:57

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

typedef union {
unsigned char BYTE;//可以按字节寻址

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 BIT6      :1;                                    
    unsigned char BIT7      :1;                                    
} BIT;          //定义一个只能按位域寻址的新变量类型                                       
} BITFIELD;   //定义一个既能按位域寻址也可按字节寻址的新变量类型


#define GET_FIELD(addr) (*((volatileBITFIELD *) (&addr))) //使addr所指寄存器既能按位域寻址也可按字节寻址

#define GET_BIT(addr,b) GET_FIELD(addr).BIT.BIT##b //定义一个宏,用来得到每一位的值

#define nRF905_MOSI    GET_BIT(PORTD,6)

#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 = 0xff;   //配置PB0为输出,PB7为输入
while(1)
{   
    nRF905_MOSI=1;
    nRF905_MOSI=0;
    Set_Bit(PORTB,0);
    Clr_Bit(PORTB,0);
}
}
//   ----------------------      end         -----------------------------

******************测试结果***********************************
---- main.c ---------------------------------------------------------------------------------------
32:       {
+00000049:   EF8F      SER   R24            Set Register
+0000004A:   BB87      OUT   0x17,R24         Out to I/O location
36:         nRF905_MOSI=1;
+0000004B:   9A96      SBI   0x12,6         Set bit in I/O register   
37:         nRF905_MOSI=0;
+0000004C:   9896      CBI   0x12,6         Clear bit in I/O register
38:         Set_Bit(PORTB,0);
+0000004D:   9AC0      SBI   0x18,0         Set bit in I/O register
39:         Clr_Bit(PORTB,0);
+0000004E:   98C0      CBI   0x18,0         Clear bit in I/O register
+0000004F:   CFFB      RJMP    PC-0x0004      Relative jump
***************
结果一样,根本不存在代码效率问题.所以bit field法,大家可以放心用,真的方便多了

tekjian 发表于 2008-6-11 19:15:33

各有高招,乱七八糟            <--- 瞎说的
可否综合一下

tekjian 发表于 2008-6-11 22:33:16

仿真测试了一下,38楼JohnLee的可用:

#ifndef __MYBIT_H__
#define __MYBIT_H__

#ifdef __cplusplus
extern "C" {
#endif


// AVR 位操作宏
// Compiler: WinAVR

#include <avr/io.h>

/***********************************************************************************/
// Set, Clear and Reverse a bit in a byte
#define SBI__(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define CBI__(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define RBI__(sfr, bit) (_SFR_BYTE(sfr) ^= _BV(bit))

/***********************************************************************************
使用方法:

要使用的位域变量或寄存器,只需按下面的规则定义一下就可以了:

#define <BITFIELD_VAL> SFR__(<BYTE_ADDR>,<START_BIT>,<BIT_WIDTH>)

BITFIELD_VAL: 要使用的位域变量名。
BYTE_ADDR: 所需的位域变量所在的字节地址。
START_BIT: 所需的位域变量在该字节的起始位置。
BIT_WIDTH: 位域变量的宽度。

比如:我要定义PORTF3-PORTF6作为一个整体变量使用,变量名为PAGE_REG,我只需定义:

#define PAGE_REG    SFR__ (PORTF, 3, 4)

在使用时就直接读写 PAGE_REG 就可以了,决不会影响 PORTF0, PORTF1, PORTF2, PORTF7的值。

再举例:定时器预分频设置:

#define T0_CK       SFR__ (TCCR0, 0, 3)

使用时:
T0_CK = 0;   // stop
T0_CK = 1;   // OSC
T0_CK = 2;   // OSC/8
T0_CK = 3;   // OSC/64
T0_CK = 4;   // OSC/256
等等。

***********************************************************************************/

#define _BITFIELD0_(_S, _W)         \
typedef union {                     \
    struct {                        \
      uint8_t used:_W;            \
    };                              \
    uint8_t dummy;                  \
    } _bit_##_S##_##_W##_t;

#define _BITFIELD1_(_S, _W)         \
typedef union {                     \
    struct {                        \
      uint8_t res   :_S;            \
      uint8_t used:_W;            \
    };                              \
    uint8_t dummy;                  \
    } _bit_##_S##_##_W##_t;

_BITFIELD0_ (0, 1);
_BITFIELD0_ (0, 2);
_BITFIELD0_ (0, 3);
_BITFIELD0_ (0, 4);
_BITFIELD0_ (0, 5);
_BITFIELD0_ (0, 6);
_BITFIELD0_ (0, 7);
_BITFIELD0_ (0, 8);

_BITFIELD1_ (1, 1);
_BITFIELD1_ (1, 2);
_BITFIELD1_ (1, 3);
_BITFIELD1_ (1, 4);
_BITFIELD1_ (1, 5);
_BITFIELD1_ (1, 6);
_BITFIELD1_ (1, 7);

_BITFIELD1_ (2, 1);
_BITFIELD1_ (2, 2);
_BITFIELD1_ (2, 3);
_BITFIELD1_ (2, 4);
_BITFIELD1_ (2, 5);
_BITFIELD1_ (2, 6);

_BITFIELD1_ (3, 1);
_BITFIELD1_ (3, 2);
_BITFIELD1_ (3, 3);
_BITFIELD1_ (3, 4);
_BITFIELD1_ (3, 5);

_BITFIELD1_ (4, 1);
_BITFIELD1_ (4, 2);
_BITFIELD1_ (4, 3);
_BITFIELD1_ (4, 4);

_BITFIELD1_ (5, 1);
_BITFIELD1_ (5, 2);
_BITFIELD1_ (5, 3);

_BITFIELD1_ (6, 1);
_BITFIELD1_ (6, 2);

_BITFIELD1_ (7, 1);

// P: the port; S: start bit; W: bits width
#define SFR__(_P, _S, _W)(* (_bit_##_S##_##_W##_t volatile *) (_SFR_ADDR (_P))).used

/***********************************************************************************/

#ifdef __TEST_MYBIT_H__

#include <avr/io.h>
#include "mybit.h"

#define LED6_ON   PORTD |=_BV(6) //位置高
#define LED6_OFFPORTD &= ~_BV(6) //位置低
#define LED6_ALTPORTD ^=_BV(6) //位取反
#define LED6_READ PIND   &_BV(6) //位读

#define LED6SFR__ (PORTD, 6, 1)
#define LED67 SFR__ (PORTD, 6, 2)

int main(void)
{
DDRD = 0xFF;

LED6_ON;
LED6_OFF;
LED6_ALT;

SBI__(PORTD, 6);
CBI__(PORTD, 6);
RBI__(PORTD, 6);

LED6 = 0b0;
LED67 = 0b10;
LED6 = LED6_READ;

return 0;
}

/***********************************************************************************
@00000049: main
---- mybit.h --------------------------------------------------------------------------------------
13:       {
+00000049:   EF8F      SER   R24            Set Register
+0000004A:   BB81      OUT   0x11,R24         Out to I/O location
16:         LED6_ON;
+0000004B:   9A96      SBI   0x12,6         Set bit in I/O register
17:         LED6_OFF;
+0000004C:   9896      CBI   0x12,6         Clear bit in I/O register
18:         LED6_ALT;
+0000004D:   B382      IN      R24,0x12         In from I/O location
+0000004E:   E490      LDI   R25,0x40         Load immediate
+0000004F:   2789      EOR   R24,R25          Exclusive OR
+00000050:   BB82      OUT   0x12,R24         Out to I/O location
20:         SBI__(PORTD, 6);
+00000051:   9A96      SBI   0x12,6         Set bit in I/O register
21:         CBI__(PORTD, 6);
+00000052:   9896      CBI   0x12,6         Clear bit in I/O register
22:         RBI__(PORTD, 6);
+00000053:   B382      IN      R24,0x12         In from I/O location
+00000054:   2789      EOR   R24,R25          Exclusive OR
+00000055:   BB82      OUT   0x12,R24         Out to I/O location
24:         LED6 = 0b0;
+00000056:   9896      CBI   0x12,6         Clear bit in I/O register
25:         LED67 = 0b10;
+00000057:   B382      IN      R24,0x12         In from I/O location
+00000058:   738F      ANDI    R24,0x3F         Logical AND with immediate
+00000059:   6880      ORI   R24,0x80         Logical OR with immediate
+0000005A:   BB82      OUT   0x12,R24         Out to I/O location
26:         LED6 = LED6_READ;
+0000005B:   B380      IN      R24,0x10         In from I/O location
+0000005C:   9896      CBI   0x12,6         Clear bit in I/O register
29:       }
***********************************************************************************/

#endif


/***********************************************************************************/

#ifdef __cplusplus
}
#endif

#endif //define __MYBIT_H__

/* ---------------------------------- end of file ------------------------------------- */

616058952 发表于 2009-2-25 21:05:00

顶起来了!!!!

1860 发表于 2009-2-26 14:52:42

好多高手,赶快学习

eduhf_123 发表于 2009-2-26 16:55:31

MARK

nicksean 发表于 2009-2-26 22:54:20

我是这么用的:

//标识符合并宏
#define __ID_COMBINE(left, right)    (left##right)

//标识符合并中间宏
#define _ID_COMBINE(l, r)    __ID_COMBINE(l, r)

#define _PORT(a)    _ID_COMBINE(PORT, a)
#define _DDR(a)   _ID_COMBINE(DDR, a)
#define _PIN(a)   _ID_COMBINE(PIN, a)


#define _SET_BIT(target, bit) ((target) |= (1 << (bit)))
#define _CLR_BIT(target, bit) ((target) &= ~(1 << (bit)))
#define _GET_BIT(target, bit) ((target) & (1 << (bit)))
#define _FLP_BIT(target, bit) ((target) ^= (1 << (bit)))


#define _PIN_HI(flag, bit)      (_SET_BIT(_PORT(flag), bit))
#define _PIN_LO(flag, bit)      (_CLR_BIT(_PORT(flag), bit))
#define _PIN_GT(flag, bit)      (_GET_BIT(_PIN(flag), bit))

#define _PIN_OUT(flag, bit)      _DDR(flag) |= BIT(bit)
#define _PIN_IN(flag, bit)   _DDR(flag) &= ~BIT(bit)
#define _PIN_IN_PH(flag, bit)   _PIN_IN(flag, bit); _PIN_HI(flag, bit)



//LCD
#define _LCD_nRST_OUT         _PIN_OUT(B, 1)
#define _LCD_nRST_HI            _PIN_HI(B, 1)
#define _LCD_nRST_LO            _PIN_LO(B, 1)

#define _LCD_nCS1_OUT         _PIN_OUT(A, 6)
#define _LCD_nCS1_HI            _PIN_HI(A, 6)
#define _LCD_nCS1_LO            _PIN_LO(A, 6)

#define _LCD_nCS2_OUT         _PIN_OUT(A, 7)
#define _LCD_nCS2_HI            _PIN_HI(A, 7)
#define _LCD_nCS2_LO            _PIN_LO(A, 7)

#define _LCD_E_OUT            _PIN_OUT(C, 7)
#define _LCD_E_HI               _PIN_HI(C, 7)
#define _LCD_E_LO               _PIN_LO(C, 7)

#define _LCD_RnW_OUT            _PIN_OUT(A, 5)
#define _LCD_RnW_HI             _PIN_HI(A, 5)
#define _LCD_RnW_LO             _PIN_LO(A, 5)

#define _LCD_DnI_OUT            _PIN_OUT(B, 0)
#define _LCD_DnI_HI             _PIN_HI(B, 0)
#define _LCD_DnI_LO             _PIN_LO(B, 0)

#define _LCD_DATA_IN            _DDR(D) = 0x00
#define _LCD_DATA_OUT         _DDR(D) = 0xFF
#define _LCD_OUT(x)             _PORT(D) = x
#define _LCD_IN(x)            x = _PIN(D)               



void HD61202_MoveCursorToChar(BYTE x, BYTE yCh)
{
    if(x < _SCREEN_BLOCK_WIDTH)
    {
      _LCD_nCS1_HI;
      _LCD_nCS2_LO;
    }
    else
    {
      _LCD_nCS1_LO;
      _LCD_nCS2_HI;
    }

    HD61202_CMD(_DISP_PAGE(yCh));
    HD61202_CMD(_DISP_Y(x));
}

hzxiaobao 发表于 2009-4-24 21:14:40

学习中,顶................

avrwoo 发表于 2009-4-26 13:39:14

学习啦!

hzxiaobao 发表于 2009-6-7 22:55:08

标记

zlutian 发表于 2009-6-8 09:11:01

谢谢,mark

sdjackal 发表于 2009-6-8 17:28:18

mark

bellz 发表于 2009-6-25 09:38:21

果然好贴!!!

chinamanzhong 发表于 2009-6-30 18:33:02

zxmok 发表于 2009-6-30 22:19:40

好贴再顶!

yongshi01 发表于 2009-7-8 17:26:16

mark

deepin 发表于 2009-7-10 09:04:55

顶了!
不错!

yu_studio 发表于 2009-7-10 09:23:37

好贴,收藏!

zhangxun0712 发表于 2009-7-11 15:50:31

好贴,转载了

bbi3014 发表于 2009-7-12 12:54:41

以后学

yanghuagui 发表于 2009-8-8 02:52:02

好贴!顶起!

hpdell 发表于 2009-9-4 16:49:49

高手如云,值得学习学习!!!!!!!!!!

qingzhou 发表于 2009-9-16 14:30:00

学习

guyuqiang 发表于 2009-9-16 16:01:54

记好!

FREEXP 发表于 2009-9-16 16:57:28

好资料,值得学习!!!

qinzhifeng 发表于 2009-9-16 20:22:18

好贴

jydq 发表于 2009-9-16 22:53:57

学习了,谢谢分享。

51hubao 发表于 2009-9-20 13:36:29

mark1

Stillness 发表于 2009-9-21 11:26:29

mark

xiongtoto 发表于 2009-10-10 13:26:37

CoolBird007 发表于 2009-10-13 18:44:31

好贴,看了这么多,我还是用 PORTD |=_BV(PD1);这样的东西,大家一看就知道。效率是编译器的事。

feng741 发表于 2009-10-19 22:13:29

mark

archldx 发表于 2009-10-25 01:46:26

我就是因为移位操作,我的ad 转换结果就错了,上面的写的很好;谢谢分享

leiyun121 发表于 2009-10-25 08:53:49

好贴!

super373 发表于 2009-10-26 23:07:49

好贴~~~顶一个!

QQ373466062 发表于 2009-10-28 22:25:20

Mark

sxndwg 发表于 2009-10-31 08:16:39

mark
页: [1] 2
查看完整版本: [转贴]gcc经验点滴(二)---鱼,熊掌.AVR的两种位操作的比较(wjc3k发于21ic)(位域与C位操