As a fun way to improve my C, I started programming the Arduino using Atmel Studio instead of the friendlier Arduino IDE.
Below is my very first program. It blinks a red LED and a white LED according to a preset pattern.
#define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> struct Led { int pin; int blinks; int on_duration; int off_duration; }; void delay_ms(int ms) { while (ms-- > 0) { _delay_ms(1); } } void blink(const struct Led* const led) { for (int i = 0; i < led->blinks; i++) { PORTB |= led->pin; delay_ms(led->on_duration); PORTB &= ~led->pin; delay_ms(led->off_duration); } } int main(void) { const struct Led white_led = { 1<<PB6, 10, 100, 500 }; const struct Led red_led = { 1<<PB7, 10, 500, 1000 }; DDRB |= white_led.pin; DDRB |= red_led.pin; while (1) { blink(&white_led); blink(&red_led); } }
With C in Atmel Studio and the AVR-LIBC library, IO ports are manipulated by changing bit patterns in the registers associated with the relevant ports. This requires a good understanding of bitwise operations in C despite the availability of macros to simplify the task.
For example, to set pin 12 of the Arduino to output mode, bit 6 of register DDRB must be set to 1. To do so requires an |
(OR) operation with a bit pattern operand where bit 6 is set to 1 and the rest set to 0, so that the states of the other bits in the register are not disturbed.
Using macros DDRB
and PB6
defined in AVR-LIBC, this is done like this: DDRB |= 1 << PB6 .
If you are new to C and are unfamiliar with macros, you might wonder about that statement. Besides, DDRB
and PB6
are not referenced anywhere else in my program, so how does this line of code work?
DDRB
is a macro that expands into C code to dereference the address of the register associated with setting the mode for pin 12, and PB6
is just a symbolic constant for the value 6. In the statement above, by shifting the value 1 left by 6 positions, we create a new value which is then applied to the bit pattern stored at the dereferenced address with an |
operation to turn bit 6 of the register to 1. In this case, this sets pin 12 to output mode.
In a nutshell, the sequence of operations is as follows.
Step 1:
1 << 6 = 01000000
Step 2:
Assuming the register DDRB is initially 00000001
:
00000001 | 01000000 = 01000001
In my C program, the result of step 1 is assigned to struct field Led.pin
and is used as the second operand for the operation in step 2.
It took about an hour to refresh my knowledge of bitwise operations, but the real challenge was interpreting the Arduino schema and the information in datasheets, especially to find the right registers to manipulate.