Skip to content

9. Embedded programming

Learning outcomes

  • Identify relevant information in a microcontroller data sheet.
  • Implement programming protocols.

Have you?

  • Documented what you learned from reading a microcontroller datasheet.
  • What questions do you have? What would you like to learn more about?
  • Programmed your board
  • Described the programming process/es you used
  • Included your code

Group assignment

For the group documentation, please check group assignment page here: Embedded programming (Group page)

Individual assignment

From the board produced on Electronics design week i decided to implement 2 modes:

  • “blink” mode, program switched on/off (blink) the led
  • “switch” mode, that turns led On, once the button is pressed

The microcontroller used on hello board was a ATtiny44A, so the respective complete data sheet file: Microchip doc8183

Attiny44 attributes Value
Program Memory Type Flash
Program Memory Size 4 KB
Clock 20 Mhz
SRAM 256 B
Data Electrically Erasable Programmable Read-Only Memory (EEPROM) size 256 B
Digital Communication Peripherals 1-SPI, 1-I2C
Capture/Compare/PWM Peripherals 1 Input Capture, 1 CCP, 4PWM
Timers 1 x 8-bit, 1 x 16-bit
Temperature Range -40 to 85 °C
Operating Voltage Range 1.8 to 5.5 V
Pin Count 14
Low Power Yes

Using GNU AVR toolchain

Started by trying the workflow with C programming language, based on Neil’s hello.ftdi.44.echo.c and make file example (from the week 9 lessons page, under “ATtiny44”).

So some of my initial questions before reading the datasheet:

  • In the begining of the hello.ftdi.44.echo.c code, Neil’s has this lfuse to be set to 0x5E for 20Mhz xtal, this is already in the .make file when program-usbtiny task runs. But why this value?
  • Without using Arduino IDE/Platform, how to turn a led on/off? (led pin PA2)
  • Without using Arduino IDE/Platform, how to detect a button pressed? (button pin PA3)

Time to get back to the data sheet.

I’m using a 20Mhz ceramic resonator, connected to PB0 and PB1 (ATtiny44A clock in), so this is an external clock and it seams that it needs to be selected (11.2.1 Registers).

Found ‘6.2.1 External Clock -> 6.2.5 Crystal Oscillator / Ceramic Resonator’, which has tables for CKSEL (Clock select) and SUT (Start-up Time from Power Down) fuses.

Neil’s says 0x5E which is ‘01011110’ in binary. Taking in account the Fuse low byte table below, CKSEL[3:1] = ‘111’ (Frequency from 8.0 Mhz), CKSEL[0] = ‘0’ and SUT[1:0] = ‘01’ (Start-up time from power-down 258 CK with addditional delay 14CK + 64ms). So the recommended usage is “Ceramic resonator, slowly rising power”, which is the case).

For the next question, the led is connected to PA2 and button to PA3. These belong to Port A, a 8-bit bi-directional I/O port with internal pull-up resistors (selected for each bit).

So being I/O ports i needed to set PA2 and as output PA3 for input. From “10. I/O Ports” i read about three I/O memory address for each port.

Now i need to know how to set a bit. which i found how in the “10.1.7 Program Examples”.

Then, for my hello board:

  • the led in PA2 requires the port to be output: DDRA = (1<<PA2)
  • the button in PA3 requires the port to be input, which is alread set to 0. To detect the button press i’m going to use the internal pull-up, so need to enable it: PORTA = (1<<PA3)

To read the port pins i can use PINA, and check the state of bit 3 (PA3) for the button press.

How is this register bit assignment "PORTA = (1<<PA3)" working?

PORTA represents 8-bit memory. The integer 1 is represented in bits by ‘00000001’. The ‘<<’ in C is a bit shifting operator, in this case, a left shift. So ‘00000001’ shifted 3 times to the left is ‘00001000’, which sets bit 3 of memory to 1, enabling the pull-up.

Going into the code, from Neil’s hello.ftdi.44.echo.c and making changes for the blink mode. Removed the serial code and added PORT, DIRECTION AND LED macros. Then used Neil’s macros to set output direction and turn led on/off.

// 
//
// hello.ftdi.44.blink.c
//
// Tiago Fernandes
// 27/05/2020
//
//
// hello.ftdi.44.echo.c
//
// 115200 baud FTDI character echo
//
// set lfuse to 0x5E for 20 MHz xtal
//
// Neil's Gershenfeld
// 12/8/10
//
// (c) Massachusetts Institute of Technology 2010
// This work may be reproduced, modified, distributed,
// performed, and displayed for any purpose. Copyright is
// retained and must be preserved. The work is provided
// as is; no warranty is provided, and users accept all 
// liability.
//

#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#define output(directions,pin) (directions |= pin) // set port direction for output
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set

#define PORT PORTA
#define DIRECTION DDRA
#define LED_PIN PA2
#define LED_PIN_OUT (1 << LED_PIN)

int main(void) {
   //
   // main
   //

   // DDRA -> to set PA2 as output pin (Data Direction Register)
   output(DIRECTION, LED_PIN_OUT);

   //
   // set clock divider to /1
   //
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

   //
   // main loop
   //
   while (1) {

      set(PORT, LED_PIN_OUT);
      _delay_ms(500);
      clear(PORT, LED_PIN_OUT);
      _delay_ms(500);

   }

   return 0;
}

To compile the code and flash the microcontroller just run:

make -f hello.ftdi.44.blink.c.make program-usbtiny

You should see compile C code into .hex binary file and then copy it to ATtiny44.

  hello.ftdi.44.blink git:(master)  make -f hello.ftdi.44.blink.c.make program-usbtiny
avr-gcc -mmcu=attiny44 -Wall -Os -DF_CPU=20000000 -I./ -o hello.ftdi.44.blink.out hello.ftdi.44.blink.c
avr-objcopy -O ihex hello.ftdi.44.blink.out hello.ftdi.44.blink.c.hex;\
    avr-size --mcu=attiny44 --format=avr hello.ftdi.44.blink.out
AVR Memory Usage
----------------
Device: attiny44

Program:     106 bytes (2.6% Full)
(.text + .data + .bootloader)

Data:          0 bytes (0.0% Full)
(.data + .bss + .noinit)


avrdude -p t44 -P usb -c usbtiny -U flash:w:hello.ftdi.44.blink.c.hex

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e9207 (probably t44)
avrdude: 1 retries during SPI command
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "hello.ftdi.44.blink.c.hex"
avrdude: input file hello.ftdi.44.blink.c.hex auto detected as Intel Hex
avrdude: writing flash (106 bytes):

Writing |                                                    | 0% 0.00s
avrdude: error: usbtiny_send: Broken pipe (expected 64, got -32)
Writing | #########################                          | 50% 0.00savrdude: 1 retries during SPI command
avrdude: 1 retries during SPI command
avrdude: 1 retries during SPI command
avrdude: 1 retries during SPI command
avrdude: 1 retries during SPI command
Writing | ############################                       | 55% 0.09savrdude: 1 retries during SPI command
Writing | ###############################                    | 61% 0.11savrdude: 1 retries during SPI command
Writing | #####################################              | 73% 0.13savrdude: 1 retries during SPI command
Writing | ###############################################    | 94% 0.16savrdude: 1 retries during SPI command
Writing | #################################################  | 98% 0.17savrdude: 1 retries during SPI command
Writing | ################################################## | 100% 0.18s

avrdude: 106 bytes of flash written
avrdude: verifying flash memory against hello.ftdi.44.blink.c.hex:
avrdude: load data flash data from input file hello.ftdi.44.blink.c.hex:
avrdude: input file hello.ftdi.44.blink.c.hex auto detected as Intel Hex
avrdude: input file hello.ftdi.44.blink.c.hex contains 106 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.19s

avrdude: verifying ...
avrdude: 106 bytes of flash verified

avrdude: safemode: Fuses OK (E:FF, H:D7, L:5E)

avrdude done.  Thank you.

This workflow uses GNU AVR toolchain avr-gcc compiler to compile the C code into binary to run on AVR RISC microcontroller and avr-objcopy to copy binary into Intel .hex format. The .hex format is recognized by avrdude, which is used to flash the ATTiny microcrontroller EEPROM (in-system programming (ISP) via Serial Peripheral Interface (SPI)) to then run the program. The steps are automated by using the GNU make tool.

You can download the code hello.ftdi.44.blink.c and makefile hello.ftdi.44.blink.c.make.

Video demonstrating the blink mode:


For the 2nd mode, to switch the led on/off, started from previous code and added PINS and BUTTON macros. Changed main loop to detect button.

Took me a while to go through ATtiny44 datasheed and understand the use of registers with the internal pull-up. Once figured this out, the button press to turn the led on was done. Below is the C code changes for switch mode.

#define PINS PINA
#define BUTTON_PIN PA3
#define BUTTON_PIN_IN (1 << BUTTON_PIN)

int main(void) {
   //
   // main
   //

   // DDRA -> to set pins as input/output (Data Direction Register)
   output(DIRECTION, LED_PIN_OUT);
   set(PORT, BUTTON_PIN_IN);

   //
   // set clock divider to /1
   //
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

   //
   // main loop
   //
   while (1) {

      if(bit_test(PINS, BUTTON_PIN)){
         clear(PORT, LED_PIN_OUT);
      }else{
         set(PORT, LED_PIN_OUT);
      }

   }

   return 0;
}

Video demonstrating the switch mode:

Using Ardunio (IDE + toolchain + libraries)

The Arduino IDE was already installed and to work with Attiny44a i have installled also the ATTinyCode libraries.

This allows to use Arduino IDE to compile and flash ATTiny microcontrollers more or less same way as Arduino boards. At least, this was my expectation.

Managed to program the previous blink example, same code with Arduino IDE.

Next step was to test my board by trying to have Arduino bootloader into ATTiny44 and then have Arduino IDE to upload the standard blink example into the board.

Here have it some problems. While trying to flash bootloader, Arduino IDE displays “Done burning bootloader”. This should be good news but after this step the connection via usb->serial doesn’t work while trying to upload the Arduino blink sketch into the board. Compilation is ok, but upload fails. This will require further work to get to work properly (maybe test/use Micronucleus a bootloader for AVR ATtiny microcontrollers with Arduino IDE).

Programming with Rust language?

This is a language that i have some curiosity, so did a quick search on google to find information on how i could write the blink program in Rust and then compile and flash the ATtiny44a microcontroller board.

Note: There are some links below about Rust.

Found the avr-rust project. In my case i used brew on MacOS to “brew install rustup-init”. This allows to install rust nightly build. Run the command

rustup-init

and customize installation with:

Options Value
default host triple x86_64-apple-darwin
default toolchain nightly
profile default
modify PATH variable yes

Then press 1 and enter to install Rust. After the command finishes you should see some output similar to:

nightly installed - rustc 1.48.0-nightly (fc2daaae6 2020-09-28)

After installing have cloned blink repository and trid to compile the example to then test and see if i could program and Arduino Uno (ATMega328P). The compilation process fails and for now i don’t know why. Issues that might happen to anyone when trying to lear a new language and use it as you go and try something for the 1st couple of times :)

Will get back to this later and try to fix and then re-write Neil’s code in Rust.