Search code examples
carduinomicrocontrolleravratmelstudio

Controlling one single bit on a certain port in AVR chips using macros in C


I have experiences in AVR programming with CodeVisionAVR. Recently I switched to using Atmel Studio and found a minor but annoying problem: I can't control each PIN/PORT as easy as I did in CodeVisionAVR.

Firstly, let's initialize one certain port:

// Initialzing one port, for example PORT D:
// Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=Out 
DDRD=0x01;
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=1 
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (1<<PORTD0);
// After initializing, Port D bit 0 should be the only pin set to out.

In CodeVisionAVR, I can do this:

PORTD.0 = 0; // Set Port D bit 0 to low
PORTD.0 = 1; // Or to high

But in Atmel Studio, addressing Port D bit 0 as PORTD.0 gives me an error. I have to do this instead:

PORTD = (1<<PORTD0); // Set Port D bit 0 to high
PORTD = (0<<PORTD0); // and low

As you can see, addressing by shifting bits is much less clean and harder to read / write. I thought CVAVR used something like a struct to imitate the dot (.) addressing method (in C we don't have classes), or some overloaded operators, or some macros, but after digging the included header files in CVAVR, I could only find this:

sfrb PORTD=0x12;
// ... other ports ...
sfrb PIND=0x10;

My question is, can I imitate the bit addressing method of CVAVR in Atmel Studio? If yes, how? Or was that some IDE-exclusive feature?


Solution

  • What you are saying about CodeVisionAVR

    PORTD.0 = 0; // Set Port D bit 0 to low
    PORTD.0 = 1; // Or to high
    
    

    is not a valid C semantic, so if you didn't make a mistake on your predicate it must be a codevision compiler extension.

    That kind of assignment in C represent an struct access, but you can not declare a struct member (or any other identifier) starting with a number, so PORTD.0 will produce an error.

    Also when you do this:

    PORTD = (1<<PORTD0); // Set Port D bit 0 to low
    PORTD = (0<<PORTD0); // and high
    

    you are not doing what you comment, you are assigning (assuming PORTD0==1) 0x1 to PORTD in the first expression and 0x0 in the second. If your intention is to manipulate only one bit, this is what you should do:

    PORTD |= (1<<PORTD0); //sets bit PORTD0 high in PORTD 
    PORTD &= ~(1<<PORTD0); //sets bit PORTD0 low in PORTD 
    PORTD ^= (1<<PORTD0); //toggles PORTD0 in PORTD 
    
    

    You should read about bit manipulation in C, here is a post with more examples

    Sometimes those actions are encapsulated in a macro, this an example of how you could do it:

    #define BitSet(Port,Bit) (Port|=(1<<Bit))
    #define BitClear(Port,Bit) (Port&=~(1<<Bit))
    #define BitToggle(Port,Bit) (Port^=(1<<Bit))
    
    #define SetBits(Port,BitMask) (Port|=BitMask)
    #define ClearBits(Port,BitMask) (Port&=~BitMask)
    #define ToggleBits(Port,BitMask) (Port^=BitMask)
    
    
    
    //then you can use it
    BitSet(PORTD,0) ; //Sets the bit0
    BitSet(PORTD,1) ; //Sets the bit1
    BitSet(PORTD,2) ; //Sets the bit2
    // or
    BitSet(PORTD,PORTD0) ; //Sets the bit0
    BitSet(PORTD,PORTD1) ; //Sets the bit1
    BitSet(PORTD,PORTD2) ; //Sets the bit2
    ...
    
    SetBits(PORTD,0x55) ; //Sets the bits 0,2,4, and 6. Leaves the other unchanged
    ClearBits(PORTD,0x55) ; //Clear the bits 0,2,4, and 6. Leaves the other unchanged