AVR, C and Arduino ================== [TOC] --- ## Our chip today: ATtiny44 ![](https://i.imgur.com/OGhk8z5.png =350x) * Small: 14 pins * Cheap: [~0.5€](https://octopart.com/attiny44a-ssu-microchip-77762206?r=sp&s=cLMPgPaiTSiSU4DqBeDlyg) * Same family as most **Arduino** chips, **AVR** manufactured by **Microchip** formerly **Atmel**. * The **Arduino UNO** uses the bigger **ATMega** range, mostly the **ATMEGA328P**. --- ![](https://i.imgur.com/l7fHZTJ.png) --- ## The Arduino abstraction ![](https://i.imgur.com/7Y7a5qx.png) ```arduino void setup() { pinMode(6, OUTPUT); } void loop() { digitalWrite(6, HIGH); delay(1000); digitalWrite(6, LOW); delay(1000); } ``` --- :::success :rocket: Follow the [instructions](http://highlowtech.org/?p=1695) to use **Arduino** with the **ATtiny** family. ::: --- :::info **How does Arduino turns this in to real AVR C?** ![](https://i.imgur.com/gm5zokZ.jpg) [Learn more](https://github.com/arduino/Arduino/wiki/Build-Process) ::: --- ## The "real" thing :eyes: **Let's look at the datasheet [ATtinyX4 Datasheet](http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7701_Automotive-Microcontrollers-ATtiny24-44-84_Datasheet.pdf)** ![](https://i.imgur.com/sWj3a7O.png) --- ### Memory ![](https://i.imgur.com/Qwu2ju4.png) --- ### Registers ![](https://i.imgur.com/l0cMGt7.png) ![](https://i.imgur.com/iZOm8HS.png) --- :::info **What a byte is?** | Lenght | Name | Example | | -------- | -------- | -------- | | 1 | Bit | 0 | | 8 | Byte | 10110101 | _Let's think on them as collection of 8 switches:_ ![](https://i.imgur.com/TOabFbl.png =150x) ::: --- ### Addressing registers * **DDRx register:** Configures pins as input or output _Do we want the pin for a button or an led?_ (1=OUTPUT and 0=INPUT) * **PORTx register:** Read data from pins _Is the button pressed or not?_ (1=HIGH and 0=LOW) * **PINx register:** Write data to pins _Turn on or off the led_. (1=HIGH and 0=LOW, on _INPUT_ pins 1=PULLUP) --- ![](https://i.imgur.com/zljpdxU.png) ![](https://i.imgur.com/Hs8tltg.png) --- | Name | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 |Bit 2 | Bit 1 | Bit 0 | | ---- | ----- | ----- | ----- | ----- | ----- |----- | ----- | ----- | | 0b | 0| 0| 0| 0| 0| 0| 0| 0| | **PORTA**|PORTA7 |PORTA6 |PORTA5 |PORTA4 |PORTA3 |PORTA2|PORTA1 |PORTA0 | | **PDDRA**|DDRA7 |DDRA6 |DDRA5 |DDRA4 |DDRA3 |DDRA2 |DDRA1 |DDRA0 | | **PINA** |PINA7 |PINA6 |PINA5 |PINA4 |INTA3 |PINA2 |PINA1 |PINA0 | --- Test this with your Arduino IDE as before: ```arduino void setup() { DDRA = 0b01000000; } void loop() { PORTA = 0b01000000; delay(1000); PORTA = 0b00000000; delay(1000); } ``` --- Now, let's do the examples using plain AVR C as on the FabAcademy examples: :::success :rocket: To test the following examples in **plain AVR C without Arduino** download the template [here](https://gist.github.com/pral2a/274c1925f1606a4b1a1ae1e1d1179537) with the make file included ::: --- ```c++ #include <avr/io.h> #include <util/delay.h> int main(void) { DDRA = 0b01000000; while (1) { PORTA = 0b01000000; _delay_ms(1000); PORTA = 0b00000000; _delay_ms(1000); } } ``` --- ### Bits manipulation **Bit manipulation** is the act of [algorithmically](https://en.wikipedia.org/wiki/Algorithm "Algorithm") manipulating [bits](https://en.wikipedia.org/wiki/Bit "Bit") or other pieces of [data](https://en.wikipedia.org/wiki/Data_(computing) "Data (computing)") shorter than a [word](https://en.wikipedia.org/wiki/Word_(data_type) "Word (data type)"). In [digital computer](https://en.wikipedia.org/wiki/Digital_computer "Digital computer") [programming](https://en.wikipedia.org/wiki/Computer_programming "Computer programming"), a **bitwise operation** operates on one or more [bit patterns](https://en.wikipedia.org/wiki/Bit_pattern "Bit pattern") or [binary numerals](https://en.wikipedia.org/wiki/Binary_numeral_system "Binary numeral system") at the level of their individual [bits](https://en.wikipedia.org/wiki/Bit "Bit"). _Wikipedia_ --- #### Bit Shifting ``` 1<<1 = 0b00000001 1<<2 = 0b00000010 1<<3 = 0b00000100 ... 1<<6 = 0b01000000 ``` ![](http://2016-aalto-c.mooc.fi/en/static/pics/bit-shift.jpg) --- #### Bit Masking `PORTA = 0b00000000;` OR operation: ``` 0b01000000 <= (1<<6) 0b00000000 <= PORTA -------------------- 0b01000000 <= PORTA ``` `PORTA = 0b01000000;` `PORTA = (1 << 6) | PORTA` --- Let's apply bit masking in our example: ```c++ #include <avr/io.h> #include <util/delay.h> int main(void) { DDRA = (1 << 6) | DDRA; while (1) { PORTA = (1 << 6) | PORTA; _delay_ms(1000); PORTA = ~(1 << 6) & PORTA; _delay_ms(1000); } } ``` --- #### Macros In the [C preprocessor](https://en.wikipedia.org/wiki/C_preprocessor) a **macro** is a fragment of code which has been given a name. Whenever the name is used, it is replaced by the contents of the macro. ```arduino #define rocketscience 6 digitalWrite(rocketscience, HIGH); ``` Is the same as this: ```arduino digitalWrite(6, HIGH); ``` --- Let's turn our bit masks in to macros: ```c++ #include <avr/io.h> #include <util/delay.h> #define pinOutput(port, pin) ((1 << pin) | port) #define digitalWriteHigh(port, pin) ((1 << pin) | port) #define digitalWriteLow(port, pin) (~(1 << pin) & port) int main(void) { pinOutput(DDRA, 6) while (1) { digitalWriteHigh(PORTA, 6) _delay_ms(1000); digitalWriteLow(PORTA, 6) _delay_ms(1000); } } ``` --- Let's turn our Macros in to Neil's style macros. First just the functions: ```c++ #include <avr/io.h> #include <util/delay.h> #define output(directions,pin) (directions |= pin) #define set(port,pin) (port |= pin) #define clear(port,pin) (port &= (~pin)) int main(void) { output(DDRA, 1 << PA6) while (1) { set(PORTA, 1 << PA6) _delay_ms(1000); clear(PORTA, 1 << PA6); _delay_ms(1000); } } ``` --- And then the pins names also: ```c++ #include <avr/io.h> #include <util/delay.h> #define output(directions,pin) (directions |= pin) #define set(port,pin) (port |= pin) #define clear(port,pin) (port &= (~pin)) #define output_port PORTA #define output_direction DDRA #define output_pin (1 << PA6) int main(void) { output(output_direction, output_pin) while (1) { set(output_port, output_pin) _delay_ms(1000); clear(output_port, output_pin); _delay_ms(1000); } } ``` --- :::info **Are you telling me the Arduino is doing all this things for me?** Yes, check the [source code](https://github.com/arduino/Arduino/blob/2bfe164b9a5835e8cb6e194b928538a9093be333/hardware/arduino/avr/cores/arduino/wiring_digital.c#L165) ::: --- ## Toolchain ![](https://i.imgur.com/gCS5i6Z.png =500x) --- :::info **How to install?** [For Linux and Mac](http://maxembedded.com/2015/06/setting-up-avr-gcc-toolchain-on-linux-and-mac-os-x/) Linux `$ sudo apt-get install gcc-avr binutils-avr avr-libc avrdude` Mac `$ brew tap osx-cross/avr` `$ brew install avr-gcc avrdude --with-usb` ::: --- ### Compiler GCC, the [GNU Compiler Collection](https://en.wikipedia.org/wiki/GNU_Compiler_Collection), turns your C and C++ code in to machine instructions your microcontroller can use. It's fully Open Source is somehow the father of Linux and the Open Source movement. --- * Compile the code: _avr-gcc_ `$ avr-gcc -mmcu=attiny44 -Wall -Os -DF_CPU=1000000 -I./ -o hello.blink.44.out hello.blink.44.c` * Convert the code: _avr-avr-objcopy_ `$ avr-objcopy -O ihex hello.blink.44.out hello.blink.44.hex` * Check the size of the code: _avr-size_ `$ avr-size --mcu=1000000 --format=avr hello.blink.44.out` --- ### Programmer software and hardware * Upload the code using your fabISP / tinyISP `$ avrdude -p t44 -P usb -c usbtiny -U flash:w:hello.blink.44.hex` ![](https://i.imgur.com/fwdDAI0.jpg =500x) --- #### Fuses Fuses are configuration parameters, or like the chip's BIOS. They control things like which oscillator to use, and what speed to run at (ie. the internal 8MHz oscillator, or an external crystal), brownout detection, and the size of the boot flash. _For example, you added a 20Mhz external crystal to your microcontroller PCB. In this case we will need to set the fuses for exteternal clock at 20 Mhz._ --- There are **3 bytes** of permanent storage in the chip called **fuse low byte, fuse high byte and fuse extended byte**. These bytes are called fuses and can be reprogrammed as many times as you want and determines the behaviour of the chip. To do that, their value is not erased when the chip is powered off or reprogrammed. --- We can set the fuses using **avrdude** as on the exemple below where `-U hfuse:w:0xDF:m` refears to the **high byte** and `-U lfuse:w:0xFF:m` to the **low byte**. `$ avrdude -c usbtiny -p attiny44 -U hfuse:w:0xDF:m -U lfuse:w:0xFF:m` --- :::info **How do I know the values of the fuses?** All the information about the fuses is on the microcontroller [datasheet](http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7701_Automotive-Microcontrollers-ATtiny24-44-84_Datasheet.pdf) _(see: 21.2 Fuse Bytes, pg. 142)_. However we have tools to make our live easier as the [**Fuses generator**](http://fabacademy.org/archives/2015/doc/fuses.html ) On the **Arduino IDE** when we set the **Clock Speed** parameter under the Tools menu, the software will internally update the fuses when we upload the code. ::: --- ### Make files When you need to deal with multiple configurations and commands when compiling a software, you can use the [_make_](http://en.wikipedia.org/wiki/Make_(software)) command on Linux/Max for automatizing this task. --- :::info `$ make` reads automatically a _Makefile_ file in the folder where you launch it (it should be the folder where your project can be found). Otherwise, you can specify it with `$ make -f filename`, especially if you have more _Makefiles_ with different names. ::: Read more on [Fab Academy and Make Files](http://archive.fabacademy.org/archives/2016/doc/makefile.html) --- **Example Makefile** ```cmake PROJECT=hello.blink.44 SOURCES=$(PROJECT).c MMCU=attiny44 MMCU_PROG=t44 F_CPU=1000000 CFLAGS=-mmcu=$(MMCU) -Wall -Os -DF_CPU=$(F_CPU) $(PROJECT).hex: $(PROJECT).out avr-objcopy -O ihex $(PROJECT).out $(PROJECT).c.hex;\ avr-size --mcu=$(MMCU) --format=avr $(PROJECT).out $(PROJECT).out: $(SOURCES) avr-gcc $(CFLAGS) -I./ -o $(PROJECT).out $(SOURCES) program-bsd: $(PROJECT).hex avrdude -p $(MMCU_PROG) -c bsd -U flash:w:$(PROJECT).c.hex program-dasa: $(PROJECT).hex avrdude -p $(MMCU_PROG) -P /dev/ttyUSB0 -c dasa -U flash:w:$(PROJECT).c.hex program-avrisp2: $(PROJECT).hex avrdude -p $(MMCU_PROG) -P usb -c avrisp2 -U flash:w:$(PROJECT).c.hex program-usbtiny: $(PROJECT).hex avrdude -p $(MMCU_PROG) -P usb -c usbtiny -U flash:w:$(PROJECT).c.hex program-dragon: $(PROJECT).hex avrdude -p $(MMCU_PROG) -P usb -c dragon_isp -U flash:w:$(PROJECT).c.hex ``` --- Download the test code [here](https://gist.github.com/pral2a/274c1925f1606a4b1a1ae1e1d1179537) --- ## Support Videos --- #### Bits and bytes {%youtube a5_J9C1XwHs %} {%youtube HRmfXA4EUBs %} #### Arduino and registers {%youtube 6q1yEb_ukw8 %} {%youtube B4bsPDFBJhA %} #### Reading a datasheet for an AVR chip {%youtube D0VKuZuuvW8 %}