neo 发表于 2014-5-3 08:57:27

winavr位操作的探讨

本帖最后由 neo 于 2014-5-3 09:07 编辑

CVAVR下的程序
#include <mega16.h>
#define LED PORTA.4

void main( void )
{
DDRA=0x10;
LED=0;
}
对应的反汇编

--- 无源文件 -----------------------------------------------------------------------

--- C:\Users\AX\Desktop\avr_test2\main.c ---------------------------------------
DDRA=0x10;
00000048LDI R30,0x10                Load immediate
00000049OUT 0x1A,R30                Out to I/O location
LED=0;
0000004ACBI 0x1B,4                Clear bit in I/O register
--- 无源文件 -----------------------------------------------------------------------
0000004BRJMP PC-0x0000                Relative jump
0000004CNOP               Undefined
0000004DNOP               Undefined 在winAVR里没有和CVAVR里同样的位操作指令,在网上看到一篇帖子WINAVR中avr单片机的位操作 其中提到了位域的方法,从C编写的角度看该方法和CVAVR里的比较类似,对应的C代码如下:
#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 bit6: 1 ;
    unsigned char bit7: 1 ;
}bit_field;

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

//定义每一个位
#define LED GET_BITFIELD(PORTA).bit4


int main( void )
{
DDRA=0x10;
LED=0;
return 0;
}
其反汇编如下:

--- C:\Users\AX\Desktop\avr_test/main.c ----------------------------------------
{
DDRA=0x10;
00000036LDI R24,0x10                Load immediate
00000037OUT 0x1A,R24                Out to I/O location
LED=0;
00000038IN R30,0x1B                In from I/O location
00000039LDI R31,0x00                Load immediate
0000003ALDD R24,Z+0                Load indirect with displacement
0000003BANDI R24,0xEF                Logical AND with immediate
0000003CSTD Z+0,R24                Store indirect with displacement
}
0000003DLDI R24,0x00                Load immediate
0000003ELDI R25,0x00                Load immediate
0000003FRET               Subroutine return
--- 无源文件 -----------------------------------------------------------------------
00000040CLI               Global Interrupt Disable
00000041RJMP PC-0x0000                Relative jump


从中可见CVAVR使用的是CBI指令对PORTA.4进行操作。WINAVR位域的方法是使用掩码的间接实现方法对PORTA.4进行操作,共使用了5条指令,代码效率不高。
CVAVR这种PORTA.x这种赋值方法有一大好处就是可以实现如下的定义
当LED点灯电路使用的是OC接法时
#define OFF 1
#define ON 0
#define LED PORTA.4
如果LED电路是直接高电平驱动则定义
#define OFF 0
#define ON 1
#define LED PORTA.4

对应的点亮部分只需执行LED=ON就可以了,不必要关心硬件电路究竟如何,这样就非常方便硬件的调试。不知WinAVR里有没有类似的方法。
1、C接口友好。
2、直接使用SBI CBI对端口位操作。

szxszx 发表于 2014-5-3 09:30:30

#define LED_FLASH                 PORTB ^= (1 << PB1)
#define LED_ON                 PORTB |= (1 << PB1)
#define LED_OFF                 PORTB &= ~(1 << PB1)

neo 发表于 2014-5-3 09:32:04

PORTA&=~(1<<PA5);
0000003DCBI 0x1B,5                Clear bit in I/O register

PORTA&=~(1<<PA5);会直接被编译为CBI,如果能实现PORTA(addr,value)封装就方便了。

neo 发表于 2014-5-3 09:42:17

szxszx 发表于 2014-5-3 09:30
#define LED_FLASH                 PORTB ^= (1

如果定义了N多个端口 当外部硬件发生了变化就要一个个修改定义 这样不是很方便

hygbeyond 发表于 2014-5-3 15:32:03

#define DEF_2( a, b )      a ## b

#define PORT( a )            DEF_2( PORT, a )
#define SBI(a, b )            a != (1<<b )

#define SBI_P( a, b )      SBI( PORT(a), b )

SBI_P( A, 2 );    //PORTA |= (1<<2 );

hygbeyond 发表于 2014-5-3 15:32:34

这样移植比较方便
页: [1]
查看完整版本: winavr位操作的探讨