Week 09 - Mar 14th 2012 - Embedded Programming

Weekly Assignment - Program the hello board in a variety of ways

The first thing to set up was the toolchain to upload .hex files to the hello board. On my mac I used gavrasm to compile assembly files to hex files, and avrdude (via crosspack) to upload the hex files. Alternatively, I also used the Arduino 1.0 IDE to directly program the board according to the instruction here.

Next I was keen to assess the efficiency of assembly language over a higher level language such as Arduino, which is especially important when working with a microcontroller such as the attiny44 with only 4096 bytes of program storage memory. Therefore I wrote very simple programs in both languages and compared the sizes of the resulting hex files.

  • Program 1 - switch on the LED, and switch it off when the button is pressed:
  • Assembly:
    .device attiny44 .def pinsetup = R16 ; where to store initial pin setup (0=input, 1=input) .def testbyte = R17 ; byte to test whether button is pushed ldi pinsetup, 0b11110111 ; prepare PORTA pins (all output except PA3) out DDRA, pinsetup ; set PORTA pin directions loop: in testbyte, PINA ; read PINA register subi testbyte, 0b00001000 ; if PA3 is high (switch not pressed) then this equals zero so Z=1 brne switchoff ; if PA3 is low Z=0 so jump to switchoff sbi PORTA, 7 ; switch on led rjmp loop ; back to beginning of loop switchoff: cbi PORTA, 7 ; switch off led rjmp loop ; back to beginning of loop
  • Arduino:
    /* Button and LED Turns on an LED attached to pin 7 when pushbutton attached to pin 3 is pressed. Circuit setup: * LED attached from pin 7 to GND * pushbutton attached to pin 3 from GND * 10K resistor attached to pin 3 from +5V modified from http://www.arduino.cc/en/Tutorial/Button */ const int buttonPin = 3; // the number of the pushbutton pin const int ledPin = 7; // the number of the LED pin int buttonState = 0; // variable for reading the pushbutton status void setup() { // pinMode(ledPin, OUTPUT); // define pin 7 as output pinMode(buttonPin, INPUT); // define pin 3 as input } void loop(){ buttonState = digitalRead(buttonPin); // read state of pushbutton if (buttonState == HIGH) { // if button is not pressed pin 7 reads +5V digitalWrite(ledPin, HIGH); // so switch on LED } else { // else button is pressed digitalWrite(ledPin, LOW); // so switch off LED } }
  • Hex file sizes Assembly 18 bytes, Arduino 752 bytes
  • Program 2 - switch the LED on brightly, and when the button is pressed dim the LED using PMW:
  • Assembly:
    .device attiny44 .def pinsetup = R16 ; where to store initial pin setup (0=input, 1=input) .def testbyte = R17 ; byte to test whether button is pushed .def temp = R18 ; temp register for setup .def highpmw = R19; .def lowpmw = R20; ldi pinsetup, 0b11110111 ; prepare PORTA pins (all output except PA3) out DDRA, pinsetup ; set PORTA pin directions ldi temp, 0b00100011 out TCCR0A, temp ; set PMW mode 3, OC0B non-inverting ldi temp, 0b00000001 out TCCR0B, temp ; set prescaler /1 ldi temp, 0 out TCNT0, temp ldi temp, 250 out OCR0B, temp ldi highpmw, 250 ldi lowpmw, 5 loop: in testbyte, PINA ; read PINA register subi testbyte, 0b00001000 ; if PA3 is high (switch not pressed) then this equals zero so Z=1 brne switchoff ; if PA3 is low Z=0 so jump to switchoff out OCR0B, highpmw ; bright led rjmp loop ; back to beginning of loop switchoff: out OCR0B, lowpmw ; dim led rjmp loop ; back to beginning of loop
  • Arduino:
    /* Button and LED Dims an LED when a button is pressed Circuit setup: * LED attached from pin 7 to GND * pushbutton attached to pin 3 from GND * 10K resistor attached to pin 3 from +5V modified from http://www.arduino.cc/en/Tutorial/Button */ const int buttonPin = 3; // the number of the pushbutton pin const int ledPin = 7; // the number of the LED pin int buttonState = 0; // variable for reading the pushbutton status void setup() { // pinMode(ledPin, OUTPUT); // define pin 7 as output pinMode(buttonPin, INPUT); // define pin 3 as input } void loop(){ buttonState = digitalRead(buttonPin); // read state of pushbutton if (buttonState == HIGH) { // if button is not pressed so pin 7 reads +5V analogWrite(ledPin, 250); // make LED almost full brightness } else { // else button is pressed analogWrite(ledPin, 5); // dim LED } }
  • Hex file sizes Assembly 38 bytes, Arduino 884 bytes

So while in both cases the programs are doing the same things in the same ways, the hex file produced by the Arduino IDE is massively inflated.

The next thing I wanted to do was to modify Neil's original hello.ftdi.44.echo.asm program to parse the key that had been pressed and, if it was a number key, set the LED brightness according (0 low, 9 high) using PMW. The modified code is shown below:

; modified_echo.asm ; Joel R 21/3/2012 ; based on ; hello.ftdi.echo.44.asm ; ; 115200 baud FTDI character echo ; ; Neil Gershenfeld ; 10/17/10 ; ; (c) Massachusetts Institute of Technology 2010 ; Permission granted for experimental and personal use; ; license for commercial sale available from MIT. ; .include "tn44def.inc" .equ rxpin = PA0; serial input pin .equ txpin = PA1; serial output pin .equ comm_port = PORTA; comm port .equ comm_dir = DDRA; comm direction .equ comm_pins = PINA; comm pins .equ ledpin = PA7; .def bitcnt = R16; bit counter .def temp = R17; temporary storage .def temp1 = R18; temporary storage .def txbyte = R19; transmit byte .def rxbyte = R20; receive byte .def comp = R21; .def loopcount = R22; .def increase = R23; ; ; print ; .macro print ldi zl,low(@0*2) ldi zh,high(@0*2) rcall print_db .endmacro .cseg .org 0 rjmp reset ; ; half_bit_delay ; serial half bit delay ; half_bit_delay: ldi temp, 25; 115200 baud (20 MHz clock /1) half_bit_delay_loop: dec temp brne half_bit_delay_loop ret ; ; putchar ; assumes line driver (inverts bits) ; putchar: ldi bitcnt, 10; 1 start + 8 data + 1 stop bit com txbyte; invert everything sec; set start bit putchar0: brcc putchar1; if carry set cbi comm_port, txpin; send a '0' rjmp putchar2; else putchar1: sbi comm_port, txpin ; send a '1' nop; even out timing putchar2: rcall half_bit_delay; bit delay rcall half_bit_delay; " lsr txbyte; get next bit dec bitcnt; if not all bits sent brne putchar0; send next bit ret; ; ; getchar ; assumes line driver (inverts bits) ; getchar: ldi bitcnt, 9; 8 data + 1 stop bit getchar1: sbic comm_pins, rxpin; wait for start bit rjmp getchar1 rcall half_bit_delay; delay to middle of bit getchar2: rcall half_bit_delay; bit delay rcall half_bit_delay; " clc; clear carry sbic comm_pins, rxpin; if RX pin low skip sec; otherwise set carry dec bitcnt breq getchar3; return if all bits read ror rxbyte; otherwise shift bit into receive byte rjmp getchar2; go get next bit getchar3: ret ; ; print_db ; prints a null-terminated .db string ; print_db: print_loop: lpm mov txbyte,R0 cpi txbyte,0 breq return rcall putchar adiw zl, 1 rjmp print_loop return: ret ; ; main program ; reset: ; ; set fuse low byte to 0x7E for 20 MHz resonator ; ; set clock divider to /1 ; ldi temp, (1 << CLKPCE) ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0) out CLKPR, temp out CLKPR, temp1 ; ; set stack pointer to top of RAM ; ldi temp, high(RAMEND) out SPH, temp ldi temp, low(RAMEND) out SPL, temp ; ; init comm pin ; sbi comm_port, txpin sbi comm_dir, txpin ; ; setup led pmw ; sbi comm_dir, ledpin ldi temp, 0b00100011 out TCCR0A, temp ; set PMW mode 3, OC0B non-inverting ldi temp, 0b00000001 out TCCR0B, temp ; set prescaler /1 ldi temp, 0 out TCNT0, temp ldi temp, 255 out OCR0B, temp ; ; start main loop ; loop: rcall getchar mov temp1, rxbyte ldi comp, 57 sub comp, temp1 brlt notanumber ldi comp, 47 sub comp, temp1 brge notanumber mov txbyte, rxbyte rcall putchar print message1 message1: .db " is a number!",0 ldi txbyte, 10 rcall putchar ldi increase, 28 mov loopcount, temp1 subi loopcount, 47 ldi temp1, -25 loopback: add temp1, increase dec loopcount brne loopback out OCR0B, temp1 rjmp loop notanumber: mov txbyte, rxbyte rcall putchar print message2 message2: .db " is not a number!",0 ldi txbyte, 10 rcall putchar rjmp loop
The resulting hex file is 216 bytes; still reasonable! When the board receives a number, the LED is set to the appropriate brightness (worth noting that the PWM values are set across a linear scale, which does not equate to a linear brightness scale - 0,1,2 are all very dim, 7,8,9 are all very bright). The interaction with the serial port is seen using the serial monitor built into the Arduino IDE:

<<< Week 08

Week 10 >>>