0

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?

Nguyen
  • 11
  • 1
  • 4

1 Answers1

3

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


Pablo Yaggi
  • 1,061
  • 5
  • 14
  • Ah yes, my bad. Well I know a bit about bits manipulation, but as you can see, it's a little tricky and looks quite ugly. I'm looking for an alternative, cleaner way to manipulate bits, at least at the level of CVAVR, but haven't found anything yet. In a project with a few thousand lines of code, those bit manipulations assignments could lead to nasty bugs that could be avoided if written in a cleaner way. – Nguyen Oct 25 '20 at 10:01
  • There is nothing ugly about it, this is how you do it. That or create a new programming language or god forbid use bitfields. Has nothing whatsoever to do with the target instruction set. The beauty comes in what the compiler does with it for the specific instruction set. It is extremely portable and the norm of how things are done in baremetal. Ports are usually a chip thing not an ISA thing so in many cases a read-modify-write is required, some have shortcuts in the peripheral design and you would take advantage of that in the macro. – old_timer Oct 25 '20 at 17:20
  • Using access functions instead of volatile pointers is even cleaner and more portable and reliable. – old_timer Oct 25 '20 at 17:21
  • But in embedded environments can cost you a lot, compilers wont warranty inlining the call to the access funcion. – Pablo Yaggi Oct 25 '20 at 17:45