搜索
bottom↓
回复: 139

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

[复制链接]

出0入0汤圆

发表于 2005-1-12 17:32:28 | 显示全部楼层 |阅读模式
转贴前言:一直看到做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这样的指令,那么对于一般的变量,又如何呢?................

出0入0汤圆

发表于 2005-1-12 19:16:05 | 显示全部楼层
通用MCU8位16位24位32位变量定义头文件MCUBIT.H



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



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

出0入0汤圆

发表于 2005-1-12 21:02:01 | 显示全部楼层
好贴子啊,不顶不行的!

出0入0汤圆

发表于 2005-1-12 22:22:37 | 显示全部楼层
3/5/2003 好早啊

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

出0入0汤圆

发表于 2005-11-23 12:08:18 | 显示全部楼层
看了不顶,是BC

出0入0汤圆

发表于 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_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=   毫秒,可自行调节

}

出0入0汤圆

发表于 2005-12-6 19:05:21 | 显示全部楼层
哈哈,现在被AVR感化了,反而见了bit就晕...

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

出0入0汤圆

发表于 2005-12-12 10:58:38 | 显示全部楼层
新手学习中,致谢

出0入0汤圆

发表于 2005-12-13 23:04:42 | 显示全部楼层
不错,谢了

出0入0汤圆

发表于 2005-12-15 15:25:19 | 显示全部楼层
顶~~~~~~~~~~

出0入0汤圆

发表于 2005-12-26 10:10:12 | 显示全部楼层
顶一下

出0入0汤圆

发表于 2005-12-28 16:06:42 | 显示全部楼层
用字节进行逻辑运算吗!很简单!就可以对任何位置位,清0

出0入0汤圆

发表于 2006-3-18 12:43:30 | 显示全部楼层
顶~~~~~~~~

出0入0汤圆

发表于 2006-4-12 16:33:46 | 显示全部楼层
又学到了知识,非常感谢.

出0入0汤圆

发表于 2006-4-12 18:12:16 | 显示全部楼层
为什么我按楼主写的测试位操作不行呢?哪位老兄给解释一下!单片机是MAGE8的。

出0入0汤圆

发表于 2006-7-6 13:27:27 | 显示全部楼层
看了就长见识了。呵呵。顶了

出0入0汤圆

发表于 2006-7-6 23:16:15 | 显示全部楼层
本来已经忘了这个话题,我一直这样用的:

#define GET_BITFIELD(addr)         (*((volatile  bit_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编辑过

出0入0汤圆

发表于 2006-7-7 17:46:20 | 显示全部楼层
^_^

真正感觉到C的灵活

出0入0汤圆

发表于 2006-7-8 07:03:10 | 显示全部楼层
有启发,好文

出0入0汤圆

发表于 2006-7-8 14:35:40 | 显示全部楼层
好,以后就这样用呵。谢谢了,顶起来!

出0入0汤圆

发表于 2006-7-8 23:44:09 | 显示全部楼层
WinAvr 2006.4.21版有 <avr/portpins.h>

出0入0汤圆

发表于 2006-9-18 23:39:34 | 显示全部楼层
请问这一块,最后一部分是什么意思?

#define GET_BITFIELD(addr)    (*((volatile  bit_field *)(addr)))

出0入0汤圆

发表于 2006-9-30 12:13:04 | 显示全部楼层
*

出0入0汤圆

发表于 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)    (*((volatile  bit_field *)(addr)))是将addr强制为位域指针,方便其他的宏来访问addr内容的某个位.

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

出0入0汤圆

发表于 2006-10-4 23:37:37 | 显示全部楼层
强烈支持hotpower 菜农

出0入0汤圆

发表于 2006-10-5 02:51:25 | 显示全部楼层
学习中

出0入0汤圆

发表于 2006-10-6 08:42:26 | 显示全部楼层
学习中

出0入0汤圆

发表于 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个?

谢谢!

出0入0汤圆

发表于 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,程序看起来更直观.

出0入0汤圆

发表于 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个?

谢谢!



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



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

出0入0汤圆

发表于 2006-11-17 15:45:21 | 显示全部楼层
敝人验证了一下,证明是可以这样定义的(在winavr中)

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

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

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

出0入0汤圆

发表于 2006-11-17 16:32:47 | 显示全部楼层
顶一下

出0入0汤圆

发表于 2006-12-31 22:20:06 | 显示全部楼层
我也来顶一下,受益非浅,谢谢各位了

出0入0汤圆

发表于 2007-2-2 17:02:21 | 显示全部楼层
谢谢,什么时候我才能上传我的东西跟大家分享呢,哈哈,初学者的心愿!

出0入0汤圆

发表于 2007-2-2 17:30:08 | 显示全部楼层
好贴!不过我还是喜欢用"与","或" 进行字节操作.

出0入0汤圆

发表于 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;//定义一个既能按位域寻址也可按字节寻址的新变量类型



   AVRBITBYTE  rWorkFlag;



#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;

                       }

                }

出0入0汤圆

发表于 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位。

出0入0汤圆

发表于 2007-2-3 16:14:48 | 显示全部楼层
楼上的,给你地址你看看,这儿有介绍.

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

出0入0汤圆

发表于 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

等等。



是不是很方便?

出0入0汤圆

发表于 2007-2-6 16:42:02 | 显示全部楼层
学习中!

出0入0汤圆

发表于 2007-4-9 00:46:17 | 显示全部楼层
指针没学好,看到指针就晕了

出0入0汤圆

发表于 2007-5-15 22:48:57 | 显示全部楼层
为什么我按楼主和5楼写的测试位操作不行呢?哪位老兄给解释一下!用studio4.12仿真的。

出0入0汤圆

发表于 2007-6-18 16:59:32 | 显示全部楼层
GET_BITFIELD(&address).bit##b

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

有谁可以解释一下的吗 ?

出0入0汤圆

发表于 2007-6-18 17:17:11 | 显示全部楼层
ps : 是连接的意思,刚查到 !

出0入0汤圆

发表于 2007-6-28 23:40:02 | 显示全部楼层
个人有个人的习惯,不必说谁好谁坏;就像学英语是美式好还是英式好,一言难尽。

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

感谢楼主的分享。

出0入0汤圆

发表于 2007-8-17 13:56:18 | 显示全部楼层
我非常赞成snow_xsj的观点

出0入0汤圆

发表于 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) (*((volatile  bit_field *) (addr)))

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

    typedef struct _bit_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 ;

            }bit_field;

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

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

出0入0汤圆

发表于 2007-8-18 13:25:20 | 显示全部楼层
6......

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



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

出0入0汤圆

发表于 2007-11-23 22:03:19 | 显示全部楼层
好啊,顶

出0入0汤圆

发表于 2007-12-4 13:02:36 | 显示全部楼层
好,学习了.谢谢各位!!小弟祝大家天天开心.

出0入0汤圆

发表于 2007-12-6 02:04:27 | 显示全部楼层
学习!

出0入0汤圆

发表于 2007-12-6 08:55:21 | 显示全部楼层
在IAR下,用位域读写IO口非常高效的。

出0入0汤圆

发表于 2008-4-21 16:19:40 | 显示全部楼层
帮助很大

出0入0汤圆

发表于 2008-4-21 21:19:51 | 显示全部楼层
对于AVR的为操作 我一般是喜欢宏定义
例如    :#define ledon (PORTB|=(1<<PB0);

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

出0入46汤圆

发表于 2008-4-21 22:13:30 | 显示全部楼层
学习!

出0入0汤圆

发表于 2008-5-29 00:13:50 | 显示全部楼层
记号

出0入0汤圆

发表于 2008-5-29 06:26:16 | 显示全部楼层
好帖,不过有些地方还不是很明白

出0入0汤圆

发表于 2008-5-29 11:30:17 | 显示全部楼层
我和16楼的用法一样

出0入0汤圆

发表于 2008-5-29 11:44:18 | 显示全部楼层
先顶一下,待会再看

出0入0汤圆

发表于 2008-5-29 12:24:41 | 显示全部楼层

出0入0汤圆

发表于 2008-6-3 23:55:07 | 显示全部楼层
应该好好看看!

出0入0汤圆

发表于 2008-6-4 22:53:32 | 显示全部楼层
这个不错,收藏了

出0入0汤圆

发表于 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"将其还原为地址再使用.

出0入0汤圆

发表于 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) (*((volatile  BITFIELD *) (&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法,大家可以放心用,真的方便多了

出0入0汤圆

发表于 2008-6-11 19:15:33 | 显示全部楼层
各有高招,乱七八糟            <--- 瞎说的
可否综合一下

出0入0汤圆

发表于 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_OFF  PORTD &= ~_BV(6) //位置低
#define LED6_ALT  PORTD ^=  _BV(6) //位取反
#define LED6_READ PIND   &  _BV(6) //位读

#define LED6  SFR__ (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 ------------------------------------- */

出0入0汤圆

发表于 2009-2-25 21:05:00 | 显示全部楼层
顶起来了!!!!

出0入0汤圆

发表于 2009-2-26 14:52:42 | 显示全部楼层
好多高手,赶快学习

出0入0汤圆

发表于 2009-2-26 16:55:31 | 显示全部楼层
MARK

出0入0汤圆

发表于 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));
}

出0入0汤圆

发表于 2009-4-24 21:14:40 | 显示全部楼层
学习中,顶................

出0入0汤圆

发表于 2009-4-26 13:39:14 | 显示全部楼层
学习啦!

出0入0汤圆

发表于 2009-6-7 22:55:08 | 显示全部楼层
标记

出0入0汤圆

发表于 2009-6-8 09:11:01 | 显示全部楼层
谢谢,mark

出0入0汤圆

发表于 2009-6-8 17:28:18 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-6-25 09:38:21 | 显示全部楼层
果然好贴!!!

出0入0汤圆

发表于 2009-6-30 18:33:02 | 显示全部楼层

出0入0汤圆

发表于 2009-6-30 22:19:40 | 显示全部楼层
好贴再顶!

出0入0汤圆

发表于 2009-7-8 17:26:16 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-7-10 09:04:55 | 显示全部楼层
顶了!
不错!

出0入0汤圆

发表于 2009-7-10 09:23:37 | 显示全部楼层
好贴,收藏!

出0入0汤圆

发表于 2009-7-11 15:50:31 | 显示全部楼层
好贴,转载了

出0入0汤圆

发表于 2009-7-12 12:54:41 | 显示全部楼层
以后学

出0入0汤圆

发表于 2009-8-8 02:52:02 | 显示全部楼层
好贴!顶起!

出0入0汤圆

发表于 2009-9-4 16:49:49 | 显示全部楼层
高手如云,值得学习学习!!!!!!!!!!

出0入0汤圆

发表于 2009-9-16 14:30:00 | 显示全部楼层
学习

出0入0汤圆

发表于 2009-9-16 16:01:54 | 显示全部楼层
记好!

出0入0汤圆

发表于 2009-9-16 16:57:28 | 显示全部楼层
好资料,值得学习!!!

出0入0汤圆

发表于 2009-9-16 20:22:18 | 显示全部楼层
好贴

出0入0汤圆

发表于 2009-9-16 22:53:57 | 显示全部楼层
学习了,谢谢分享。

出0入0汤圆

发表于 2009-9-20 13:36:29 | 显示全部楼层
mark1

出0入0汤圆

发表于 2009-9-21 11:26:29 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-10-10 13:26:37 | 显示全部楼层

出0入0汤圆

发表于 2009-10-13 18:44:31 | 显示全部楼层
好贴,看了这么多,我还是用 PORTD |=_BV(PD1);这样的东西,大家一看就知道。效率是编译器的事。

出0入0汤圆

发表于 2009-10-19 22:13:29 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-10-25 01:46:26 | 显示全部楼层
我就是因为移位操作,我的ad 转换结果就错了,上面的写的很好;谢谢分享

出0入0汤圆

发表于 2009-10-25 08:53:49 | 显示全部楼层
好贴!

出0入0汤圆

发表于 2009-10-26 23:07:49 | 显示全部楼层
好贴~~~顶一个!

出0入0汤圆

发表于 2009-10-28 22:25:20 | 显示全部楼层
Mark

出0入0汤圆

发表于 2009-10-31 08:16:39 | 显示全部楼层
mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-9 09:30

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表