Table of Contents

last revision
07 November 2016, 11:02am
book quality
just begun

Avr Microprocessor Programming ‹↑›

This book is about programming the avr microcontroller probably with an arduino type board. Its purpose is to investigate the avr architecture from an assembly programming perpective.

Generally my interest is focussed on coding rather than hardware, or circuit design or robotics or what-not. So I am largely content to experiment over a serial connection.

Goals ‹↑›

To learn how to call code using indirect addressing (ie using the x, y, z registers) so we can do something like

 call bx  ; x86 architecture

To develop a simple repl loop, "read, evaluate, print, loop" So a simple read-only forth style interface over a serial connection- what used to be known as a Forth "Home Brew"

play chess on an atmega328 microcontroller over a serial connection.

Tools ‹↑›

Many arduino boards use the atmega328 avr chip which has 32K of program memory

turn on the led on arduino pin 13 ----

;hello.asm ; turns on an LED which is connected to PB5 (digital out 13)

.include "/usr/share/avra/m328Pdef.inc" ;or .include "/usr/share/avra/m328def.inc"

ldi r16,0b00100000

tools
avra - an assembler to use with arduino
avrdude - a tool to program the arduino board
out PortB,r16 ; send a 'high' to PB5 only Start: rjmp Start ; hang in an infinite loop ,,,

upload this hex file to the arduino with

 avrdude -p m328p -c stk500v1 -b 57600 -P /dev/ttyUSB0 -U flash:w:hello.hex

This is not working. The problem is the 'stk500' which may be correct for the 'uno' arduino but not for the duemilenovo. see below for a working example. The problem seems to be the -c stk500v1 option

also try for arduino uno s etc

 -b 115200 and the port -P /dev/ttyACM0

Architecture ‹↑›

The atmel atmega chips are 8 bit risc processors. They have a stack and a register file. Data memory and program memory are separate (program memory is stored in flash).

Avr Assembly ‹↑›

a good avr assembly site

 https://sites.google.com/site/avrasmintro/

Adc ‹↑›

http://maxembedded.com/2011/06/the-adc-of-the-avr/ A reasonable explanation of the functioning of the adc for avr chips.

Adc is yet another acronym meaning analogue to digital conversion and is included in almost all microcontrollers. Most sensors work by varying resistance or voltage depending on some environmental factor such as light, sound, temperature, pressure etc. This varying resistance gets translated into a varying voltage input into one of the avr chips input pins. The varying voltage is refered to as an 'analogue signal' or analogue input.

The ADC module of the microcontroller converts this into a digital number within a certain range. So that the MC can then analyse the value of the input.

Pwm ‹↑›

This stands for pulse width modulation. There is a duty cycle. This is a kind of approximated analogue output using digital highs and lows.

pwm code for an atmega8 (not sure if applicable to m328)


#define fillrate OCR2A 

  // main()

  PORTB=0x00;
  DDRB=0x08;  //We use PORTB.3 as output, for OC2A, see the atmega8 reference manual

  // Mode: Phase correct PWM top=0xFF
  // OC2A output: Non-Inverted PWM
  TCCR2A=0x81;
  // Set the speed here, it will depend on your clock rate.
  TCCR2B=0x02;
  // for example, this will alternate between 75% and 42% PWM
  while(1)
  {
      fillrate = 191; // ca. 75% PWM
          delay_ms(2000);

              fillrate = 107; // ca. 42% PWM
                  delay_ms(2000);
   }

Timers ‹↑›

http://electronics.stackexchange.com/questions/2057/polyphonic-sounds-from-a-microcontroller some timer info

timer (or counter) in normal mode, counts 0 to 255 then sets overflow. Prescaler, or divider, divides the system clock by value

set the prescaler

    ldi r16, 0b00000011    ; set timer prescaler = 64
    out TCCR0B, r16        ; timer control register B

Prescaler values are limited to the following. The higher the prescaler value, then the slower the timer with count up to 256

1, TCCR0B = 0b0000001 8, TCCR0B = 0b0000010 64, TCCR0B = 0b0000011 256, TCCR0B = 0b0000100 1024, TCCR0B = 0b0000101

To calculate delay for timer/counter user clock/prescaler * 256 eg 16 Mhz /1024 * 256 = ??

blinking a led using the timer (counter) with prescaler

  ; blinks an LED which is connected to PB5 (digital out 13)

   .include "m328Pdef.inc"

  rjmp start

  sleep:
    in r16, TIFR0          ; get timer flags register 0
    andi r16, 0b00000010   ; tov0 ~ timer overflow flag (bit 2) 
    breq sleep             ; if overflow not set, then loop
    ldi r16, 0b00000010    ; reset timer overflow flag by setting it
    out TIFR0, r16         ; to 1
    ret
  
  start:

    ldi r19,0b00100000  ; or try etc "sbi ddrb, 0"
    out DDRB,r19        ; ddrb ~ data direction register B
    ldi r16, 0b00000101    ; set timer prescaler = 1024
    out TCCR0B, r16        ; timer control register B
    
  again:
    ldi r19,0b00100000  ;  or try sbi instruction for toggling
    out PortB,r19       ; 
    rcall sleep
    ldi r19,0b00000000  ; turn off led
    out PortB,r19       ; send to pin on port b
    rcall sleep
    rjmp again

If we preload the counter with eg 128 then the timer overflows twice as quickly

preload the timer counter

    ldi r16, 128        ; preload timer with 128
    out TCNT0, r16      ; timer count register := 128

blinking a led using the timer with prescaler and preloaded counter

  ; LED connected to PB5 (digital out 13)

   .include "m328Pdef.inc"

  rjmp start

  sleep:
    in r16, TIFR0          ; get timer flags register 0
    andi r16, 0b00000010   ; tov0 ~ timer overflow flag (bit 2) 
    breq sleep             ; if overflow not set, then loop
    ldi r16, 0b00000010    ; reset timer overflow flag by setting it
    out TIFR0, r16         ; to 1
    ret
  
  start:

    ldi r19,0b00100000  ; or try etc "sbi ddrb, 0"
    out DDRB,r19        ; ddrb ~ data direction register B
    ldi r16, 0b00000101    ; set timer prescaler = 1024
    out TCCR0B, r16        ; timer control register B
    
  again: 

    ldi r16, 128        ; preload timer with 128
    out TCNT0, r16
    ldi r19,0b00100000  ;  or try sbi instruction for toggling
    out PortB,r19       ; 
    rcall sleep
    ldi r19,0b00000000  ; turn off led
    out PortB,r19       ; send to pin on port b
    rcall sleep
    rjmp again

By using interrupts the avr is free to do other things

use the timer using interrupts ---------

.include "m328Pdef.inc" .def a = r16 ;general purpose accumulator .org 0000 rjmp on_reset ;reset vector .org 0003 rjmp tim0_ovf ; timer0 overflow handler

on_reset: sbi ddrb, 0 ; set portb0 for output ldi a, 0b00000101 ; set prescaler to /1024 out tccr0b, a ; timer/counter control register "b" ldi a, 0b00000010 ; enable timer-overflow interupt

; No, timsk0 is not accessible with out ; out TIMSK0, a ; timer interrupt mask register

; sts = store direct to data space sts timsk0, a ; timer interrupt mask register sei ; enable interupts globally

; timer overflow interrupt handler tim0_ovf: sbi pinb,0 ; flip the 0 bit reti

; main loop start: nop ;do nothing rjmp start

,,,

Delays ‹↑›

simple blinking code using about a 1 second delay

  ;  turns on an LED which is connected to PB5 (digital out 13)

  .include "/usr/share/avra/m328Pdef.inc"

  ldi r19,0b00100000  ; or try etc "sbi ddrb, 0"
  out DDRB,r19        ; ddrb ~ data direction register B

 again:
  ldi r19,0b00100000  ;  
  out PortB,r19       ; 
  clr r16             ; set reg16 := 0
  clr r17
  ldi r18, 40 
  here:              ; a delay loop 40*256^2 instructions
    dec r16          ; 
    brne here 
    dec r17
    brne here 
    dec r18
    brne here 
  ldi r19,0b00000000
  out PortB,r19
  ldi r18, 40
  far:
    dec r16 
    brne far 
    dec r17
    brne far 
    dec r18
    brne far 

  rjmp again

Arduino ‹↑›

install the arduino java software

 sudo apt-get install arduino

Then select the serial device and board (eg duemilenove) Then select file/preferences and check box for verbose info during upload

run arduino software with privelidges

 sudo arduino

If you dont have the right priviledges the serial port will be greyed out.

It doesnt seem to matter what usb socket the cable is plugged into at all. (at least not on my linux mint machine)

the following worked for me

 sudo avrdude -v -v -v -v -p atmega328p -carduino -P/dev/ttyUSB0 -b57600 -D -Uflash:w:hello.hex:i

the same without verbose output

 sudo avrdude -p atmega328p -carduino -P/dev/ttyUSB0 -b57600 -D -Uflash:w:hello.hex:i

below is a typical avrdude command from the ide

 /usr/share/arduino/hardware/tools/avrdude -C/usr/share/arduino/hardware/tools/avrdude.conf -v -v -v -v -patmega328p -carduino -P/dev/ttyUSB0 -b57600 -D -Uflash:w:/tmp/build5695632546898703228.tmp/sketch_mar13a.cpp.hex:i

out DDRB,r16 ; DDRB == data direction register B

Nop ‹↑›

code to make an avr microcontroller do nothing

   here: rjmp here

Sound ‹↑›

http://avrprog.pbworks.com/w/page/9345379/AvrSound a good simple program to play midi file squeakily on a piezo buzzer

Piezo Buzzers ‹↑›

This is a small device for playing sound. Apparently it can also operate as a sensor

The piezo buzzer can make tunes by simply toggling the output on a pin at a certain frequency. But this job is perhaps better done with pulse width modulation using a %50 duty-cycle square wave, at different frequencies.

All this can be done much better with timers and interrupts. we can also simplify by using the pinb trick to toggle and output pin

circuit: pin13->100R(??)-->piezo-->Ground

buzz a piezo connected to arduino pin 13 then to ground

.include "/usr/share/avra/m328Pdef.inc"

  ldi r19,0b00100000
  out DDRB,r19
 again:
  ldi r19,0b00100000
  out PortB,r19
  clr r16
  clr r17
  ldi r18, 4 
  here:
    dec r16 
    brne here 
    dec r17
    brne here 
    dec r18
    brne here 
  ldi r19,0b00000000
  out PortB,r19
  ldi r18, 4
  far:
    dec r16 
    brne far 
    dec r17
    brne far 
    dec r18
    brne far 

  rjmp again

The code is not working properly below

another pause routine adapted from 'retrodan' code


  .include "/usr/share/avra/m328Pdef.inc"

  .DEF A = R16      ;GENERAL PURPOSE ACCUMULATOR
  .DEF I = R20      ;INDEX
  .DEF N = R22      ;COUNTER

;.ORG 0000
ON_RESET:
  ldi r19,0b00100000
  out DDRB,r19
   ; SBI DDRB,5           ;SET PORTB0 FOR OUTPUT     
   main:
      SBI   PINB,5       ;TOGGLE THE BIT
      ;RCALL PAUSE        ;WAIT/PAUSE
      rjmp main       ;go back and do it again
  ; pause routines

PAUSE: LDI N,0           ;DO NOTHING LOOP
PLUPE:
  ; RCALL np   ; CALLS ANOTHER DO NOTHING LOOP
       DEC N          ; CHECK IF WE COME BACK TO ZERO  
       BRNE PLUPE    ; IF NOT LOOP AGAIN
       RET             ;RETURN FROM CALL

np: LDI I,0           ;START AT ZERO
MPLUP: DEC I             ;SUBTRACT ONE
        BRNE MPLUP       ;KEEP LOOPING UNTIL WE HIT ZERO
         RET             ;RETURN FROM CALL

Below is working but there is a mysterious error with high(ramend) etc

another attempt

  .include "/usr/share/avra/m328Pdef.inc"
  ;  .INCLUDE "M169DEF.INC"
    .DEF A = r16
    .DEF I = r21
    .org $0000
    reset:
    ;ldi a, high(ramend)
    ;OUT SPH,A
    ;LDI A,LOW(RAMEND)
    ;OUT SPL,A
    SBI DDRB,5  
    again: rcall bpause
     sbi pinb,5 ; toggle arduino output 13 pin
     rjmp again 

    bpause:
    BLOOP: LDI I,5
    BPLUPE: DEC I
    BRNE BPLUPE
    DEC A
    BRNE BLOOP
    RET

    ;SETUP THE STACK POINTER
    ;AT TOP OF MEMORY AND
    ;GROW DOWNWARDS
    ;CONFIG SPEAKER PORT
    ;WAIT
    ;CLICK THE SPEAKER
    ;DO IT AGAIN
    ;PAUSE ROUTINE
    ;TIME DEPENDS ON "A"

Power ‹↑›

Batteries are rated by their mAh which means "milliamp hours" so an AA battery rated at 1000mAh could in theory produce 1 milli-amp for 1000 hours. If a project is powered by battery or solar panel, then its really a good idea to reduce power consumption.

Arduino Uno draws 42mA (per hour)

https://www.openhomeautomation.net/arduino-battery/ an excelent tutorial for powering an atmega328 on a breadboard with only 2 AA batteries and no voltage regulator. The atmega is powered at 3V with an external crystal. Also discusses ways to make the atmega328 use less power

http://arduino.cc/en/main/standalone How to put the arduino on a bread-board powered with a ~9volt battery and using a 7805 voltage regulator to reduce voltage to 5V. Apparently this type of regulator wastes energy

http://arduino.cc/en/Tutorial/ArduinoToBreadboard similar to above but without incircuit programming of the atmega chip.

https://alanbmitchell.wordpress.com/2011/10/02/operate-arduino-for-year-from-batteries/ another low power article

Low Power ‹↑›

The atmega328 has 5 modes to reduce power consumption which can be activated through code.

Ideas to reduce power consumption, use the on-chip oscillator and clock at 8Megahz. Use a 'switching' voltage regulator. run chip at 3V or 3.3V turn off unused stuff, like ADC etc

Clock ‹↑›

you can throttle the internal clock of the ATmega328 on the fly. see CLKPR. You can educe the internal 8MHz clock to 31.250kHz with two lines of code.

Sleep Modes ‹↑›

http://www.engblaze.com/hush-little-microprocessor-avr-and-arduino-sleep-mode-basics/ a tutorial about avr sleep modes.

Battery Power ‹↑›

CR2032 20mm coin cell battery runs at 3V and doesnt require a voltage regulator. but only has a rating of 200mAh so low power consumption is necessary.

LR123 type lithium cell. high amp hours rating

Solar Power ‹↑›

Powering an arduino or atmega with solar seems feasible put panels in series to increase voltage, and in parallel to increase amperage.

http://www.instructables.com/id/Solar-powered-arduino-on-the-back-of-a-playing-car/ This is a very interesting blog on how to create a playing card sized solar panel to power an arduino

Avra ‹↑›

avra is an assembler for use with avr chips. This is good if you dont like c or java.

to use register names you need an 'inc' file for the chip

 eg: /usr/share/avra/tn13def.inc

avra doesnt seem to come with an inc file for the atmega328 but one should be findable

compile a file to a hex file

 avra hello.asm

Vim And Avr Programming ‹↑›

Vim can be used to compile, and upload to the arduino board code in a text file, or even code contained within another type of text document.

map the key sequence ';av' to compile the whole file with avra

 map ;as :!avra %<cr>
or >> map ;as :!avra % -o %:r.hex<cr>

The second example is not necessary since by default avra creates a file called name.hex where the source file is 'name.asm'

In the examples below, the complete assembly program is supposed to be within 2 'markers' within a document. The markers are '---' on a line by itself and ',,,' on a line by itself. These 2 markers mark the beginning and end of the assembly program within the document. Also the cursor needs to be between these 2 markers.

compile an avr assembly program within a document to 'test.hex'

 map ;ac :?^ *---?+1,/,,,/-1w ! ( cat - ) > test.asm; avra test.asm;

compile source and upload to the arduino 'duemilanove' board

 map ;av :?^ *---?+1,/,,,/-1w ! ( cat - ) > test.asm; avra test.asm; sudo avrdude -p atmega328p -carduino -P/dev/ttyUSB0 -b57600 -D -Uflash:w:test.hex:i

The following is useful for determining how much space is left within a boot file (which is limited to 512 bytes)

see how big a compiled file is without running it

 map ;ab :?^ *---?+1,/,,,/-1w ! ( cat - ) > test.asm; avra test.asm; ls -la

DOCUMENT-NOTES:

avrdude options
-c the programmer
-P the serial port