Tendero
Tendero

Reputation: 1166

Programming AVR in C

I'm using an Atmega328. I have output pins that are "randomly" distributed along the pinout, i.e. they don't belong to the same ports. For example, my output pins could be PB0, PB4, PC1, PC3 and PD1.

I have always used Assembly to program microcontrollers, so this would be my first time using C. What I want to know is if there is a way to avoid using DDRx and Px for each pin to set them or clear them.

For example, I would like to use something like this to set the first bit of Port B:

#define NAME_1 DDRB,0

sbi NAME_1;

Is this possible?

EDIT:

Maybe I didn't express myself clearly. What I want is to be able to refer to certain I/O port pins with some meaningful names. For example, name PD3 "blue_LED", so that the code is more readable and, if the blue LED's position is changed later, the code can be easily modified. Stated another way, I want to be able to turn on and off certain pins with their names not being hard-coded. Is there a way?

Upvotes: 1

Views: 1811

Answers (3)

TomServo
TomServo

Reputation: 7409

I use avg-gcc on to program Atmel AVRs and the support packages most definitely know about cbi, sbi, and all instructions for that matter. So there's no need to resort to assembly unless you want to. Here's some disassembled C to prove it.

        PORTD |= (1 << PD0);
d8: 58 9a           sbi 0x0b, 0 ; 11

Now I'll show you how. You can set each bit of each port to do either input or output very easily.

DDRA |= (1<<PA0);

has the effect of making pin 0 on port A an output as shown:

bit76543210
   00000001  // or-ing the 1 adds 1 leaving the other bits alone.

To also make pin A3 an output, do this:

DDRA |= (1<<PA3);
bit76543210
   00001001 //  now the DDRA register looks like this, pins 0 and 3 set as outputs

Tedious, right? Well, these right-side expressions evaluate to constants so you can combine these into one statement:

DDRA |= (1<<PA0) | (1<<PA3);

and the compiler will fold the right hand side into one constant and |= it into the register. So, you really need only one statement like this per port, and the compiler makes it very efficient.

That takes care of direction -- input or output. The next is to set and clear outputs.

To set an output:

PORTD |= (1<<PD); // turns pin 0 on port D on (high) as an output.
PORTD &= ~(1<<PD); // turns pin 0 on port D off (low) as an output.
PORTD ^= (1<<PD); // toggles it (high-> low) or (low->high as an output.

EDIT:

To the extra requirements you named about using meaningful names, of course you can do that. I routinely do this to avoid having to remember what's connected to what. For example, from that same project:

#define LED_INDICATOR_PIN   PA0
#define LED_INDICATOR_PORT  PORTA
#define LED_INDICATOR_DDR   DDRA

I refer to the 'friendly' names in my code:

void initialize(void)
{
    // Set up an output pin to blink an LED to show sign of life.
    LED_INDICATOR_DDR |= (1 << LED_INDICATOR_PIN);

    // Set up TTY port.
    init_uart();
}

void toggleLED(void)
{
    LED_INDICATOR_PORT ^= (1 << LED_INDICATOR_PIN);
}

Much easier to read. Is that what you're after? I know it's what I do every time.

Upvotes: 4

user2371524
user2371524

Reputation:

The sbi instruction is special in that it directly manipulates a bit in an I/O Port on the AVR platform. The normal course with I/O ports is that you have to use other instructions (like out) to copy whole words between the I/O port and a register.

That said, there's no sbi in C. C just doesn't know about these special features of one particular platform. For the assembly you give as an example, you would write in C:

DDRB |= 1<<0;

I personally think this looks quite concise, but of course you COULD define a macro

#define sbi(x,b) (x) |= 1<<(b)

sbi(DDRB, 0);

(where b is the "bit number") and maybe the other way around

#define cbi(x,b) (x) &= ~(1<<(b))

cbi(DDRB, 0)

This would work, but I recommend not to use it. While the first notation DDRB |= 1<<0; is obvious to any C programmer, using macros like this probably isn't.

As a final note, if you are concerned about performance: I haven't verified this, but I'm pretty sure avr-gcc is smart enough to emit sbi and cbi instructions when a bit mask operation on an I/O port effectively just changes a single bit. edit: see JLH's answer for an experimental result that gcc-avr indeed is smart enough to emit these sbi/cbi instructions.

Upvotes: 4

user5329483
user5329483

Reputation: 1272

With C++ I wrote something like this:

//IO = I/O Subsystem
inline void Io_LEDred_Configure() {DDRB  |=   1<<0; } //Port B0
inline void Io_LEDred_On()        {PORTB |=   1<<0; }
inline void Io_LEDred_Off()       {PORTB &= ~(1<<0);}

inline void Io_LEDgreen_Configure() {DDRD  |=   1<<3; } //Port D3
inline void Io_LEDgreen_On()        {PORTD |=   1<<3; }
inline void Io_LEDgreen_Off()       {PORTD &= ~(1<<3);}

If you have to switch an IO to a different port you have to change only those three lines and you're done.

In C++ the compiler emits exactly that code what you as an assembler coder would write. In C you have to use a regular function. But the calls coming with some overhead. If you want to avoid this you have to use macros:

#define IO_LEDRED_CONFIGURE DDRB  |= 1<<0
#define IO_LEDRED_ON        PORTB |= 1<<0
...

Upvotes: 0

Related Questions