The product page of the ATTiny44A collects all the essential information about this microcontroller (formely made by Atmel, now part of Microchip Inc.): datasheet, application notes, quickstart guides, pricing and sample info etc.
The datasheet is available in two formats:
The Summary is ok for getting an idea of the features of this microcontroller, but the complete one is full of interesting data.
The hardware info is extremely detailed and includes information on Pins and Packaging, integrated Analog to Digital converter, Interrupts, Clocks, Power etc.
From a programming point of view, it is interesting to note that the ATtiny44A is an 8 bit microcontroller based on a RISC Architecture with 120 instructions, and it uses an Harvard architecture, with separate memories and buses for program and data. Moreover, the micro has 32 GP registers, 4K of program memory, 256 bytes of in-system EEPROM and 256 bytes of RAM.
In Week 6 I showed how to:
This week I tried to blink my on-board LED using 4 different languages: C, Assembly, Basic and Wiring/Arduino.
I started from the blink code written in C and used in week 6 to test the hello board.
#include <avr/io.h> #include <inttypes.h> #include <util/delay.h> #define F_CPU 20000000UL /* 20MHz crystal oscillator */ #define LED_PORT PORTB /* PORTx - register for LED output */ #define LED_BIT PB2 /* bit for button input/output */ #define LED_DDR DDRB /* LED data direction register */ #define BLINK_TIME 100 /* time to wait after a button press */ int main(void) { LED_DDR |= (1 << LED_BIT); // set LED pin as output while(1) { LED_PORT |= (1 << LED_BIT); // turn LED on _delay_ms(BLINK_TIME); // wait BLINK_TIME milliseconds LED_PORT &= ~(1 << LED_BIT); // turn LED off _delay_ms(BLINK_TIME); // wait BLINK_TIME milliseconds } }
The code is simple - the most obscure part is the (1 << LED_BIT); bitwise operation. The (1 << LED_BIT) operation set the PORTB data register to PB2 (=2) - with 1 << Xthe bit number X is converted into a byte value that can be assigned directly to the register. In C we can set bits using bitmasking:
PORT |= 0x02 set the bit 1 only PORT &= ~0x02 clear bit 1 only PORT ^= 0x01 toggle bit 1 only PORT &= 0x02 set also bit 1
So with LED_PORT |= (1 << LED_BIT) we set the PB2 bit, and with LED_PORT |= (1 << LED_BIT) we clear the PB2 bit.
A more readable way of setting
In order to access a particular bit number as a byte value, you use the _BV() macro ("BV" stands for "bit value"). _BV(argument) converts a bit number into a byte value - since it's the compiler which perform the bitshift and then inserts the result into the code, there is no run-time overhead when using _BV().
However, using the _BV macro often makes the program more readable, as you can see below.
int main(void) { LED_DDR = _BV(LED_BIT); LED_PORT |= _BV(LED_BIT); while (1) { _delay_ms(BLINK_TIME); LED_PORT ^= _BV(LED_BIT); } }
In both cases, I compiled the code using the avr-gcc toolchain.
I found a basic Blink assembly code on a post on the Arduino.cc forum. I used the version of the code with the GNU Assembler syntax, so I was able to use again the GNU toolchain.
The code is quite straightforward, but I had to fix because it was targeted to the ATmega168/328p.
The ATtiny44A does not include a JMP opcode in its instruction set, probably because the smaller RJMP is able to reach the whole 4K address space (i.e. 2048 locations - from 0x0000 to 0x07FF). The same applies to the CALL instruction. If you want to know more about the program memory of the ATtiny refers to section 5.1 of the datasheet; registers are described in section 22, while a summary of the instruction set is presented in section 23.
It is worth noting how the code implements the delay function: most RISC instructions in the micro are single clock cycle execution, so they can be used to design a delay subroutine.
; Assembly Code for blinking led ;;; Allow us to use names like "PORTB" #define __SFR_OFFSET 0 #include "avr/io.h" start: SBI DDRB,2 blink: LDI r20,250 RCALL delay_n_ms SBI PORTB,2 LDI r20,250 RCALL delay_n_ms CBI PORTB,2 RJMP blink ;;; Delay about r20 * 1ms. ;;; One millisecond is about 20000 cycles at 20MHz. ;;; The basic loop takes about 5 cycles, so we need about 4000 loops. ;;; Uses registries r20, r30, and r31. delay_n_ms: LDI 31, 5000>>8 ; high(5000) LDI 30, 5000&255 ; low(5000) delaylp: SBIW r30, 1 BRNE delaylp SUBI r20, 1 BRNE delay_n_ms RET
The code can be compiled with the GNU avr-gcc front end compiler.
I have found Great Cow BASIC, a very nice project for creating is a BASIC compiler for PIC and AVR microcontrollers released under the GNU GPL2.
The syntax of Great Cow BASIC is based on that of QBASIC/FreeBASIC, but with some alterations to suit the vastly different system that it compiles for. Great Cow BASIC will allow you to program most 8 bit PIC microcontrollers (10F, 12C, 12F, 16C, 16F, 18C and 18F chips), and also includes near-complete support for most AVR microcontrollers (Classic AVR, Tiny AVR and Mega AVR).
Given the huge amount of hours spent on my old Commodore 64, I decided I needed to give it a try.
I added the getdeb_games repo to my sources list, then I installed freebasic and some other packages withapt install gcc-multilib libc-dev freebasic freebasic-dev lib32ncurses5-dev libncurses5-dev
Then I built GreatCowBasic from sources and installed it with
cd GreatCowBasic/Sources chmod +x install.sh ./install.sh build ./install.sh install
and finally I added GCBasic binaries and lib (PATH=$PATH:/opt/GCBASIC) to system path.
The code is quite readable, and can be compiled with gcbasic and the hex is generated with the makehex.sh script.
#chip tiny44a, 20 #define LED PORTB.2 'Led on PIN 14 via 0.5K resistor DIR LED OUT wait 1 sec ;======== MAIN PROGRAM LOOP ================ Do Flash_LED ( 3,250 ) '3 Flashes 250 ms equal on/off time Wait 2 Sec Flash_LED ( 5,250,500 ) '5 flashes On 250 ms / off 500 ms Wait 2 Sec Flash_LED ( 10,100 ) '10 rapid flashes Wait 2 Sec Loop ;========================================== Sub Flash_LED (in numtimes, in OnTime as WORD, optional OffTime as word = OnTime) repeat numtimes set LED on wait OnTime ms set LED OFF wait OffTime ms end repeat End Sub
The language of the Arduino IDE is derived from Wiring, a (somewhat dumbed-down) standard C/C++ language which provides similar functionality for a more tightly restricted board design, plus a number of simple functions and classes for embedded systems.
Arduino/Wiring programs only need two functions to make a runnable program: setup() and loop(). So, you can think to an Arduino program as a regular C program having the following main
int main() { init(); setup(); for(;;) loop(); return 0; }
The classic Blink.ino file looks like that
void setup() { pinMode(PIN_B2, OUTPUT); } void loop() { digitalWrite(PIN_B2, HIGH); // turn the LED on (HIGH is the voltage level) delay(1000); // wait for a second digitalWrite(PIN_B2, LOW); // turn the LED off by making the voltage LOW delay(1000); // wait for a second }
The Arduino IDE can be extended to support third-party boards and additional microcontrollers. The ATTinyCore project has created an extension for essentially every ATtiny processor that makes sense to use with Arduino (including the ATtiny44).
I also wanted to use Arduino headlessly, that is compile my code without passing through the UI. Following the instructions posted here, I was able to setup a fully working workflow.
First, I installed Arduino IDE ver. 1.6.13 (the scripts for using Arduino from command line have been tested on 1.6 only until now - March 2017).
Then, I open and I installed the package SpenceKonde ATTinyCore simply cloning the git repo within the arduino-1.6.13/hardware/ATTinyCore/avr directory (first create the ATTinyCore/avr directory tree, then clone or copy the files within it).
You can also install the package from the IDE following the instructions posted on their repo, but remember that real programmers use butterflies (or, at least, command line)
Finally, I installed Sudar Arduino-Makefile for Arduino sketches - this suite defines the workflow for compiling code, flashing it to Arduino and even communicating through Serial. I had some troubles using the packaged version for Ubuntu, so I pulled the latest version from github.
The Sudar makefile suite consists of two main parts – a file named “Arduino.mk” that resides in a folder named Arduino-Makefile and a Makefile that you create for your project and locate in your project's folder.
The most complex part of this process is creating your project Makefile. I created the following Makefile, which
PROJECT_DIR = /home/emanuele/Desktop/ardu AVR_GCC_VERSION := $(shell expr `avr-gcc -dumpversion | cut -f1` \>= 4.9) ARDMK_DIR = /home/emanuele/Arduino-Makefile ARDUINO_DIR = /home/emanuele/arduino-1.6.13 ARDUINO_SKETCHBOOK = /home/emanuele/arduino-1.6.13 USER_LIB_PATH := $(realpath $(PROJECT_DIR)/$(ARDUINO_DIR)/hardware/tools/avr/avr/include) BOARD_TAG = attinyx4 BOARD_SUB = 44 VARIANT = tinyX4 ALTERNATE_CORE = ATTinyCore F_CPU = 20000000L AVRDUDE = /usr/bin/avrdude AVR_TOOLS_DIR = $(ARDUINO_DIR)/hardware/tools/avr CFLAGS_STD = -std=gnu11 CXXFLAGS_STD = -std=gnu++11 CXXFLAGS += -pedantic -Wall -Wextra CURRENT_DIR = $(shell basename $(CURDIR)) OBJDIR = $(PROJECT_DIR)/$(CURRENT_DIR)/$(BOARD_TAG)/bin include $(ARDMK_DIR)/Arduino.mk
I run the Makefile with make and the script compiled the code and saved all the output file (.elf, .eep, .o and .hex) in the set OBJDIR (attinyx4/bin/). Finally, I uploaded the code on the board as usual with:
avrdude -p t44 -P usb -c usbtiny -U flash:w:Blink.hex
It's worth comparing the size of the binaries produced by different compilers - Assembly wins, while Wiring seems the most greedy language.
Program (.text + .data + .bootloader) | Data (.data + .bss + .noinit) | |
---|---|---|
C | 90 bytes (2.2%) | 0 bytes (0.0 %) |
Assembly | 30 bytes (0.7%) | 0 bytes (0.0 %) |
Basic | 137 bytes (6.69%) | 6 bytes (2.35 %) |
Wiring | 892 bytes (21.8%) | 9 bytes (3.5%) |