This is my first real attempt at writing code in assembly language for the AVR8 micro-controller. With the AVR, it has always been C.
I wanted to create some functions that I use frequently. In the past, in the MC68HC11 days, I did all coding in assembly language and, I have had a longing to return to it with my use of the AVR – though, many people claim it to be obsolete and primitive. So be it!
So, I began studying the listing of some of the C programs Ive written and digging into the instruction set. It seems that the MC68HC11 was quite a bit easier to program with assembly, than is the AVR. But learning is fun.
I figured I’d begin with a “BLINKY” program. But… I would accomplish that task using an interrupt handler for timing.
In addition, I wanted two forms of delay: (1. I wanted a PAUSE function, where normal CPU program execution is held in check, until the programmable PAUSE time terminated. (2. I wanted several delays that would run concurrently to each other and in the background to the main program loop.
I am posting the listing of my first successful attempt at assembly programming with the AVR in the form of a “BLINKY” program for your critique. I also have some question regarding assembly structure and format. I just need to figure out how to present them in a logical and orderly fashion.
I am using the AVRA flavor of the AVR assembler and the Atmel AVR000 “.inc” controller definitions with the PRAGMA’s commented our so that the program would assemble without warnings or errors.
The target controller is the AVRMega328P.
;*************************** Controller specifit definitions ****************************** .NOLIST .include "/usr/share/avra/m328Pdef.inc" .LIST ;------------------------------------------------------------------------------------------ .equ LED = PB5 ;************************************** EEPROM Stuff ************************************** .ESEG .ORG 0x0000 ;------------------------------------------------------------------------------------------ ;************************************* Data Variables ************************************* .DSEG .ORG 0x0100 ;SRAM_START MCU_Status: .byte 1 ;------------------------------------------------------------------------------------------ ;*************************************** FLASH Stuff ************************************** .CSEG .ORG 0x0000 ;------------------------------------------------------------------------------------------ ;*************************************** IRQ_Vectors ************************************** ; IRQ vectors jmp RESET ; RESET jmp RESET ; INT0 jmp RESET ; INT1 jmp RESET ; PCINT0 jmp RESET ; PCINT1 jmp RESET ; PCINT2 jmp RESET ; WDT jmp RESET ; TIMER2 COMPA jmp RESET ; TIMER2 COMPB jmp RESET ; TIMER2 OVF jmp RESET ; TIMER0 CAPT jmp TIMER1_COMPA ; Vecttor 11 jmp RESET ; TIMER1 COMPB jmp RESET ; TIMER1 OVF jmp RESET ; TIMER0 COMPA jmp RESET ; TIMER0 COMPB jmp RESET ; TIMER0 OVF jmp RESET ; SPI, STC jmp RESET ; USART, RX jmp RESET ; USART, UDRE jmp RESET ; USART, TX jmp RESET ; ADC jmp RESET ; EE READY jmp RESET ; ANALOG COMP jmp RESET ; TWI jmp RESET ; SPM READY ;------------------------------------------------------------------------------------------ ;********************************** Start-Up sequence ************************************* RESET: ; Main program start in r16, MCUSR ; Get the MCU status register sts MCU_Status, r16 ; Save the MCU status after power-up clr r16 ; Clear r16 out MCUSR, r16 ; Clear the MCUSR register for next RESET ldi r16, high(RAMEND) ; Set Stack Pointer to top of RAM out SPH, r16 ldi r16, low(RAMEND) out SPL, r16 sei rcall INIT_IO rcall INIT_Timer1 ;------------------------------------------------------------------------------------------ ;************************************ Main Program loop *********************************** ; Pause resolution = 0.001 Second ; Pause Time = 5000 mS = 0x1388 ; Blinky cycle time = 500 mS ON, 500,S OFF ; 500 mS = 0x01F4 MAIN: ; Setup PauseCounter ldi r16, 0x13 ; Initialize DelayCounter sts high(PauseCounter),r16 ; 5 seconds upon bootup ldi r16, 0x88 sts low(PauseCounter), r16 ; Turn on the LED clr r24 ori r24, (1<<LED) out PORTB, r24 ; Turn on LED ; Wait for a while rcall Pause ; Wait here until Pause done ; Turn off the LED clr r24 ; Pause comlete! andi r24, ~(1<<LED) out PORTB, r24 ; Turn off LED BLINKY: ldi r24, 0x01 sts high(TimerCounter_1), r24 ldi r24, 0xF4 sts low(TimerCounter_1), r24 rcall Timer1 BLINKY_LOOP: lds r24, TimerStatus ; Check for active TimerFlag_1 andi r24, (1<<TimerFlag_1) breq BLINKY_LOOP ; If TimerFlag_1 NOT set, BLINKY in r24, PORTB sbrs r24, LED sbi PORTB, LED sbrc r24, LED cbi PORTB, LED rjmp BLINKY ;------------------------------------- Main loop end -------------------------------------- ;************************************ Initialize PORTB ************************************ Init_IO: ldi r16, (1<<LED) out DDRB, r16 ; Make PB5 an output ldi r16, 0x00 out PORTB, r16 ; Turn LED at PB5 --- OFF --- ret ;------------------------------------------------------------------------------------------ ;************************************ Project includes ************************************ .include "/home/carl/Desktop/Projects/Assembly_Projects/Timer_IRQ.asm" ;------------------------------------------------------------------------------------------ ;****************************************************************************************** ;************************************ Timer1 Interrupt ************************************ ;****************************************************************************************** ;***************************************** Equates **************************************** .equ PauseFlag = 0 .equ TimerFlag_1 = 1 .equ TimerFlag_2 = 2 .equ TimerFlag_3 = 3 .equ TimerFlag_4 = 4 .equ TimerFlag_5 = 5 .equ TimerFlag_6 = 6 .equ TimerFlag_7 = 7 ;------------------------------------------------------------------------------------------ ;*************************************** Data segment ************************************* .DSEG TimerStatus: .byte 1 PauseCounter: .byte 2 TimerCounter_1: .byte 2 ;------------------------------------------------------------------------------------------ ;*************************************** Code segment ************************************* .CSEG ;****************************************** Pause ***************************************** Pause: ; Pause is a holding delay ---> CPU control is not released, until PauseFlag comes TRUE ; The variable --> PuseCounter <-- is loaded pryor to entering this routine ; Maximum delay @ 0.001 second resolution = 65.535 seconds push r24 ; Preserve r24 ; Clear the PauseFlag to start thePauseTimer lds r24, TimerStatus andi r24, ~(1<<PauseFlag) ; TimerStatus &= ~(1<<PauseFlag) sts TimerStatus, r24 ; Hold program control here until PaueFlag becomes true _Pause: lds r24, TimerStatus ; Loop while PauseFlag is FALSE andi r24, (1<<PauseFlag) breq _Pause ; PauseFlag = FALSE? pop r24 ;recover r24 ret ;------------------------------------------------------------------------------------------ ;***************************************** Delay_1 **************************************** Timer1: ; Timer1 is an asychronus delay. CPU control is imediately released back to the caller; ; That is, timing is performed concurrent to norman program operation - in the background. ; The variable --> TimerCounter_1 <-- is loaded pryor to entering this routine ; Maximum delay @ 0.001 second resolution = 65.535 seconds push r24 ; Preserve r24 ; Clear the TimerFlag_1 to start Timer1 lds r24, TimerStatus andi r24, ~(1<<TimerFlag_1) ; TimerStatus &= ~(1<<TimerFlag_1) sts TimerStatus, r24 pop r24 ;recover r24 ret ;------------------------------------------------------------------------------------------ ;*************************************** Timer_Init *************************************** INIT_Timer1: ; Desired value: 1.000mS ; Actual value: 1.000mS (0.0% error) push r24 ; Preserve r24 ; TCCR1B = 0x00 ldi r24, 0x00 sts TCCR1B, r24 ; Stop Timer Counter 1 ; TCNT1 = 0x0000 sts TCNT1H, r24 ; OCR1AH -MUST- be written first sts TCNT1L, r24 ; Clear Timer Counter 1 ; TCCR1A = 0x00 sts TCCR1A, r24 ; Disable OC1A --> NO output at OC1A I/O pin ; Set Output Compare Register 1A for 1mS time interval ; Target resolution = 1.00 millisecond ; 0.001 sec / (1 / (18432000 / 8)) = 2304 ; 2304 decimal = 0x0900 HEX ; OCR1A = 0x0900 ldi r24, 0x09 sts OCR1AH, r24 ; OCR1AH -MUST- be written first ldi r24, 0x00 sts OCR1AL, r24 ; TCCR1B = (1<<WGM12) | (1<<CS11) ldi r24, (1<<WGM12) | (1<<CS11) ; Start Timer1 OCR1A in CTC mode, CLK / 8 sts TCCR1B, r24 ; TIMSK1 |= (1<<OCIE1A) lds r24, TIMSK1 ; Enable OCR1A interrupt ori r24, (1<<OCIE1A) sts TIMSK1, r24 pop r24 ; Recover r24 ret ;------------------------------------------------------------------------------------------ ;********************************** TIMER1_COMPA_vect_11 ********************************** ; TIMER1_COMPA: IRQ_vector_11: ; WGM: 4) CTC mode, TOP = OCR1A, no timer/counter output generated ; Resolution: ; Desired value: 1.0 mS ; Actual value: 1.000 mS (0.0% error) TIMER1_COMPA: ; IRQ vector 11 push r24 ; Preserve r24 in r24, SREG ; Get SREG push r24 ; Preserve SREG content push r25 ; Preserve r25 Pause_IRQ_Service: ; if( (DelayStatus & (1<<PauseFlag)) ) lds r24, TimerStatus ; Check for active PauseFlag andi r24, (1<<PauseFlag) brne Delay1_IRQ_Service ; If PauseFlag set, Delay1_IRQ_Service ; PauseCounter = --PauseCounter lds r24, high(PauseCounter) lds r25, low(PauseCounter) subi r25, 1 ; Decrement PauseCounter by 1 sbci r24, 0 sts high(PauseCounter), r24 sts low(PauseCounter), r25 ; if( PauseCounter-- != 0 ) ; PauseCounter zero check or r24, r25 ; Logically OR PauseCounter high/low bytes brne Delay_IRQ_Exit ; If PauseCounter not zero, Delay_IRQ_Exit ; DelayStatus |= (1<<PauseFlag) lds r24, TimerStatus ; Pause done ori r24, (1<<PauseFlag) ; Set PauseFlag sts TimerStatus, r24 rjmp Delay_IRQ_Exit ; PauseFlag is set, Delay_IRQ_Exit Delay1_IRQ_Service: ; if( (DelayStatus & (1<<TimerFlag_1)) ) lds r24, TimerStatus ; Check for active TimerFlag_1 andi r24, (1<<TimerFlag_1) brne Delay2_IRQ_Service ; If TimerFlag_1 set, Delay2_IRQ_Service ; TimerCounter_1 = --TimerCounter_1 lds r24, high(TimerCounter_1) lds r25, low(TimerCounter_1) subi r25, 1 ; Decrement TimerCounter_1 by 1 sbci r24, 0 sts high(TimerCounter_1), r24 sts low(TimerCounter_1), r25 ; if( TimerCounter_1-- != 0 ) ; TimerCounter_1 Zero check or r24, r25 ; Logically OR TimerCounter_1 high/low bytes brne Delay_IRQ_Exit ; If TimerCounter_1 not zero, Delay_IRQ_Exit ; DelayStatus |= (1<<TimerFlag_1) lds r24, TimerStatus ; Timer_1 done ori r24, (1<<TimerFlag_1) ; Set TimerFlag_1 sts TimerStatus, r24 rjmp Delay_IRQ_Exit ; TimerFlag_1 is set, Delay_IRQ_Exit ; For future use Delay2_IRQ_Service: ; . ; . ; . Delay7_IRQ_Service: Delay_IRQ_Exit: pop r25 pop r24 out SREG, r24 ; Recover SREG content pop r24 reti ;------------------------------------------------------------------------------------------