Schlafenszeit §

AVR Assembler Kochbuch

Ziel

Der Code zeigt, wie der ATmega8 in den Tiefschlaf geschickt werden kann, so dass er kaum noch Strom braucht (typisch 0.5µA) und per Tastendruck erwacht, ein Licht aus- oder einschaltet und anschliessend wieder schlafen geht.

Code

; stable-decisions-trigger.asm
; -------------------------------------------------------------------------
; begin                 : 2012-04-01
; copyright             : Copyright (C) 2012 by Manfred Morgner
; email                 : manfred.morgner@gmx.net
; =========================================================================
;                                                                         |
;   This program is free software; you can redistribute it and/or modify  |
;   it under the terms of the GNU General Public License as published by  |
;   the Free Software Foundation; either version 2 of the License, or     |
;   (at your option) any later version.                                   |
;                                                                         |
;   This program is distributed in the hope that it will be useful,       |
;   but WITHOUT ANY WARRANTY; without even the implied warranty of        |
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
;   GNU General Public License for more details.                          |
;                                                                         |
;   You should have received a copy of the GNU General Public License     |
;   along with this program; if not, write to the                         |
;                                                                         |
;   Free Software Foundation, Inc.,                                       |
;   59 Temple Place Suite 330,                                            |
;   Boston, MA  02111-1307, USA.                                          |
; =========================================================================
;
; This programm will toggle the light at Arduino LED at Digital Pin 13
; or on your ATmega Micro Controller at PORTB/BIT5 (which is the same)
;
; Furhter it will switch the light to the other state if you pull Arduino
; Digital Pin 2 to ground or - respectively - PORTD/BIT2 on your ATmega MC
;
; Between button pressing the micro controller goes to 'Power Down' sleep
; mode to set it's own power consumption to about 2.5 micro Watt
;
; Enabeling DEBUG mode (in Predefinition Section) will set a LED on
; Arduino Digital Pin 10 to 'ON' if the button is pressed and to 'OFF'
; after the button is released and the main program reaches its end.
;
; The reality is much more complex, but this is how it 'feels'!
;
; DEBUG may assigned TRUE or 1 or FALSE or 0

; This program is for ATmega8 micro controller
.DEVICE atmega8


; PREDEFINITION SECTION

.equ FALSE = 0
.equ TRUE  = 1

.equ DEBUG = TRUE


; MACRO SECTION

.macro DEB_INI ; (ddr,  bit)                               => Set 'bit' on 'ddr' to Output Mode
  .if DEBUG
       sbi        @0,   @1
  .endif
.endmacro

.macro DEB_LOF ; (port, bit)                               => Set LED on 'port/bit' to 'OFF' (sleeping)
  .if DEBUG
       cbi        @0,   @1
  .endif
.endmacro

.macro DEB_LON ; (port, bit)                               => Set LED on 'port/bit' to 'ON' (awake)
  .if DEBUG
       sbi        @0,   @1
  .endif
.endmacro


; DEFINITION SECTION

;aaa nnnnnnnnnnnnnnnnnn = vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ; ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc

.equ ddrOutput          = DDRB                             ; Data Direction Register for the Output Port we use
.equ prtOutput          = PORTB                            ; PORT we use for Output
.equ pinOutput          = PINB                             ; PIN register associated to our Output Port

.equ ddrInput           = DDRD                             ; Data Direction Register for the Input Port we use
.equ prtInput           = PORTD                            ; PORT we us for Input
.equ pinInput           = PIND                             ; PIN register associated to our Input Port

                                                           ; Arduino Pin 13 is BIT 5 of PORTB on the ATmega8 MC chip
.equ bitOutput          = 5                                ; Output bit on prtOutput (Digital Pin 13 on Arduino)
.equ bitDebug           = 2                                ; Debug  bit on prtOutput (Digital Pin 10 on Arduino)
.equ bitINT0            = 2                                ; Input  bit on prtInput  (Digital Pin  2 on Arduino)

; Power Save Mode constants 'mska' = Mask to AND with Register, 'msko' = Mask to OR with Register

; ATmega8 Power Consumption                                => ATmega8 active: 4.35 mA on 8MHz
.equ mskaPowerMode      = 0b00001111                       ; Mask Out Sleep Mode Bits
; Important:                                               => BIT7 (Sleep Enable) has to be ON to sleep
.equ mskoPowerIdle      = 0b10000000                       ; Idle...............: 1 mA on 4 MHz
.equ mskoAdcReduct      = 0b10010000                       ; ADN Noise Reduction: 
.equ mskoPowerDown      = 0b10100000                       ; Power Down.........: down to 0.0005 mA
.equ mskoPowerSave      = 0b10110000                       ; Power Save.........: 
.equ mskoStandyMod      = 0b11100000                       ; Standby............: 

; Interrupt Trigger Mode constants

.equ mskaExtInt0        = 0b11111100                       ; to Mask Out 'External INT0' trigger reason bits
.equ mskoExtInt0LvlLow  = 0b00000000                       ; level low - enables wake up from power donw mode
.equ mskoExtInt0LvlChng = 0b00000001                       ; any level change
.equ mskoExtInt0EdgeH2L = 0b00000010                       ; falling edge
.equ mskoExtInt0EdgeL2H = 0b00000011                       ; rising edge

.equ mskaExtInt1        = 0b11110011                       ; to Mask Out 'External INT1' trigger reason bits
.equ mskoExtInt1LvlLow  = 0b00000000                       ; level low - enables wake up from power donw mode
.equ mskoExtInt1LvlChng = 0b00000100                       ; any level change
.equ mskoExtInt1EdgeH2L = 0b00001000                       ; falling edge
.equ mskoExtInt1EdgeL2H = 0b00001100                       ; rising edge

; Names for Registers

.def regTemp            = r16                              ; regTemp has to be a 'high register', r16 is the first one


; INTERRUPT SERVICE ROUTINES - ADDRESS TABLE

.org 0x0000
            rjmp    start                                  ; register 'start' as Programm Start Routine
            rjmp    ext_int0                               ; INT0 input interrupt


; MICRO CONTROLLER INITIALISATION SECTION

;llllllllllllllllllllllllll:
start:

;           ddddddd ooooooooooooo rrrrrrrrrrrrrrrrrrrrrrrr ; ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc

            cli                                            ; Disable interrupts (SREG) while setting up

            ldi     regTemp,      high(RAMEND)             ; Initialise stack for interrupt handling
            out     SPH,          regTemp
            ldi     regTemp,      low(RAMEND)
            out     SPL,          regTemp

; On power save configuration read: "8-bit AVR with 8KBytes In-System Programmable Flash" page 35

; The ADC should be disabled                               => “Analog-to-Digital Converter” on page 189

            in      regTemp,      ADCSRA                   ; ADC Control and Status Register A
            andi    regTemp,      ~(1 << ADEN)             ; Switch off 'AD Enable' bit
            out     ADCSRA,       regTemp                  ; Rewrite Register Bits

; The Analog Comparator should be disabled                 => “Analog Comparator” on page 186

            ldi     regTemp,      1 << ACD                 ; Analog Comarator Disable bit
            out     ACSR,         regTemp                  ; Analog Comparator Control and Status Register

; The Brown-out Detector should be turned off              => “Brown-out Detection” on page 40

            in      regTemp,      MCUCSR                   ; MCU Control and Status Register
            andi    regTemp,      ~(1 << BORF)             ; Switch off Brown Out Reset Flag
            out     MCUCSR,       regTemp                  ; Rewrite Register Bits

; * for details on the start-up time                       => Refer to “Internal Voltage Reference” on page 42
; The Watchdog Timer should be turned off                  => “Watchdog Timer” on page 43

            in      regTemp,      MCUCSR                   ; MCU Control and Status Register
            andi    regTemp,      ~(1 << WDRF)             ; Switch off WatchDog Reset Flag
            out     MCUCSR,       regTemp                  ; Rewrite Register Bits

; All port pins should be configured to use minimum power  => “Digital Input Enable and Sleep Modes” on page 55

            ldi     regTemp,      0xFF                     ; Set all ports to output
            out     DDRB,         regTemp
            out     DDRC,         regTemp
            out     DDRD,         regTemp

            ldi     regTemp,      0x00                     ; Set all pins to LOW 
            out     PORTB,        regTemp
            out     PORTC,        regTemp
            out     PORTD,        regTemp

; Prepair the ports and bits we need for our use

            DEB_INI ddrOutput,    bitDebug                 ; Set PORTB/BIT2 to output mode
            DEB_LON prtOutput,    bitDebug                 ; Set debug LED to 'on'

            sbi     ddrOutput,    bitOutput                ; Set PORTB/BIT5 to output mode
            sbi     prtOutput,    bitOutput                ; Set LED on bit 5 to 'on' (signal for 'program started')

            cbi     ddrInput,     bitINT0                  ; Set PORTD/bit2 to input mode
            sbi     prtInput,     bitINT0                  ; Enable pullup resistor on PORTD/bit2

; Switch on interrupts from INT0 (PORTD/BIT2)

; 1) Switch on INT0 as Interrupt Source                    => Not here - but before each SLEEP command
;           in      regTemp,      GICR                     ; General Interrupt Control Register
;           ori     regTemp,      1 << INT0                ; Switch on INT0 bit
;           out     GICR,         regTemp                  ; Rewrite Register Bits

; 2) Switch on INT0 as Interrupt Source
            in      regTemp,      GIFR                     ; General Interrupt Flag Register 
            ori     regTemp,      1 << INT0                ; Switch on INT0 bit
            out     GIFR,         regTemp                  ; Rewrite Register Bits

; Set Wakeup Mode
            in      regTemp,      MCUCR                    ; MCU Control Register
            andi    regTemp,      mskaExtInt0              ; Masking out INT0
            ori     regTemp,      mskoExtInt0LvlLow        ; ORing in 'Level Low' pattern as Trigger for Interrupt
            out     MCUCR,        regTemp                  ; Rewrite Register Bits

; Set Sleep Mode to "Power Down Mode"

; In this mode, the External Oscillator is stopped, while the external interrupts, the Two-wire Serial Interface address watch,
; and the Watchdog continue operating (if enabled). Only 
;   * an External Reset,
;   * a  Watchdog Reset,
;   * a  Brown-out Reset,
;   * a  Two-wire Serial Interface address match interrupt, or
;   * an external level interrupt on INT0 or INT1,
; can wake up the MCU. This sleep mode basically halts all generated clocks, allowing operation of asynchronous modules only.

            in      regTemp,      MCUCR                    ; MCU Control Register
            andi    regTemp,      mskaPowerMode            ; Masking out Sleep Mode pattern
            ori     regTemp,      mskoPowerDown            ; ORing in 'Power Down' pattern for Sleep Mode
            out     MCUCR,        regTemp                  ; Rewrite Register Bits

            sei                                            ; Enable interrupts (SREG) after setting up the Micro Controller


; PROGRAM SECTION

main:
; Program loop code here

; Program goes to sleep if INT0 bit if left HIGH (button not pressed)

            sbis    pinInput,     bitINT0                  ; Test if INT0 Button is released yet
            rjmp    no_sleep                               ; Not released => dont SLEEP!

            DEB_LOF prtOutput,    bitDebug                 ; Set debug LED to 'OFF' (sleeping)

            in      regTemp,      GICR                     ; General Interrupt Control Register
            ori     regTemp,      1 << INT0                ; Switch on INT0 bit as Interrupt Source
            cli                                            ; from here on, we must not be interrupted!
            out     GICR,         regTemp                  ; Rewrite Register Bits

            sei                                            ; hopefully, no one interrupts with INT0 before 'sleep'
            sleep                                          ; Sleep as hard as possible (Power Down Mode)

no_sleep:
            rjmp    main                                   ; Start 'main' all over


; INTERRUPT SERVICE SECTION

ext_int0:                                                  ; Interrupt Service Routine (ISR) for INT0
            DEB_LON prtOutput,    bitDebug                 ; Set debug LED to 'on'

            sbis    pinOutput,    bitOutput                ; Is Output LED in state ON?
            jmp     led_on                                 ; NO => jump to 'Switch LED ON'
led_off:
            cbi     prtOutput,    bitOutput                ; Switch Output LED OFF
            jmp     ext_int0_end                           ; Leave Interrupt Service Routine (ISR)
led_on:
            sbi     prtOutput,    bitOutput                ; Switch Output LED ON

ext_int0_end:                                              ; 'leave ISR' subroutine

            push    regTemp                                ; next step we modify regTemp, so we have to keep the content back
; switch off Interrupts for INT0 to forget pending signal
            in      regTemp,      GICR                     ; General Interrupt Control Register
            andi    regTemp,      ~(1 << INT0)             ; switch off INT0 bit
            out     GICR,         regTemp
            pop     regTemp                                ; here es the old regTemp content again

            reti

 

- 18.04.2012 bis 18.04.2012 -

Freigesetzt