SPI on m328P using assembler

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Timer overflow triggers an interrupt which should commence an SPI transmission of two bytes but this does not happen. Seems to get stuck in the waiting loop for the SPI. Very new to avr asm so please tell me all the things I'm doing wrong.

 

.cseg
.org $0000
	jmp RESET ; Reset Handler
.org $001A
	jmp TIM1_OVF ; Timer2 Compare A Handler

TIM1_OVF: ; Service Routine
	cli
	in R15, SREG ; save status
	sbi PIND, 7				; LED
	inc R17
	cbi PORTB, DDB2
	; Start transmission of data (r17)
	out SPDR, R17
	Wait_Transmit_byte_0:
	; Wait for transmission complete
	in R17, SPSR
	sbrs R17, SPIF
	rjmp Wait_Transmit_byte_0
	out SPDR, R17
	Wait_Transmit_byte_1:
	; Wait for transmission complete
	in R17, SPSR
	sbrs R17, SPIF
	rjmp Wait_Transmit_byte_1
	sbi PORTB, DDB2
	out SREG, R15 ; restore status
	reti ; return back and enable interrupts

RESET:
	ldi R16, high(RAMEND)		; Main program start
	out SPH,R16					; Set Stack Pointer to top of RAM
	ldi R16, low(RAMEND)
	out SPL,R16
	ldi R16, (1 << SE)			; enable sleep
	sts SMCR, R16
	ldi R16, (1 << PRTWI) | (1 << PRTIM2) | (1 << PRTIM0) | (1 << PRUSART0) | (1 << PRADC) ; save power
	sts PRR, R16

	ldi R16, (1 << DDD7)		; LED
	out DDRD, R16				; LED

	ldi R16, (1 << DDB5) | (1 << DDB3) | (1 << DDB2);
	out DDRB, R16
	sbi PORTB, DDB2  ; set SS high

	ldi R16, (1 << SPE) | (1 << MSTR) | (1 << SPR0)
	sts SPCR, R16

	ldi R16,1<<TOIE1			; Overflow Interrupt Enable Timer 1
	sts TIMSK1,R16				; set interrupt-mask of the timer

	ldi R16, (1 << CS11) | (1 << CS10)
	sts TCCR1B, R16

	sei ; set interrupt flag
	LOOP: ; Main program loop
	sleep ; processor to sleep
	rjmp LOOP ; go back to sleep

 

sol i sinne - brun inne

Last Edited: Mon. Jun 26, 2017 - 11:39 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your code "looks" as if it should "work".
.
I would follow the ASM conventional format. e.g. labels in column 1, put comments in lined up comment field.
.
I could make all sorts of semantic points. e.g. using DDR BIT_FIELD names in PORT statements.
.
But the main thing is. You can run this in the Simulator. Watch exactly what happens.
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
TIM1_OVF: ; Service Routine
	cli

	reti ; return back and enable interrupts

Don't mess with I in an ISR. When the AVR jumps to the ISR it already clears the I bit so the CLI is pointless. At the end RETI is "RETurn and re-nable I bit" .

	in R17, SPSR
	sbrs R17, SPIF

Why not simply SBIS ?

 

Also do you really mean to transmit R17 twice ? The first time you do an INC R17 then send it (even though I don't see it ever being initialised in RESET: ?) then second time R17 still has the contents of SPSR - so you really mean to transmit that?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

TIM1_OVF: ; Service Routine
	cli

	reti ; return back and enable interrupts

Don't mess with I in an ISR. When the AVR jumps to the ISR it already clears the I bit so the CLI is pointless. At the end RETI is "RETurn and re-nable I bit" .

	in R17, SPSR
	sbrs R17, SPIF

Why not simply SBIS ?

 

Also do you really mean to transmit R17 twice ? The first time you do an INC R17 then send it (even though I don't see it ever being initialised in RESET: ?) then second time R17 still has the contents of SPSR - so you really mean to transmit that?

R17 is indeed neither initialized nor used in a useful manner, I just want something to be sent over the data lines at all. Regarding SBIS, I just copied the SPI-example code from the M328P-datasheet. Is "sbis SPSR, SPIF" the equivalent operation?
david.prentice wrote:
Your code "looks" as if it should "work". . I would follow the ASM conventional format. e.g. labels in column 1, put comments in lined up comment field. . I could make all sorts of semantic points. e.g. using DDR BIT_FIELD names in PORT statements. . But the main thing is. You can run this in the Simulator. Watch exactly what happens. . David.
I'm not very proficient with the simulator.. I can't get any useful information from it 

sol i sinne - brun inne

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tickstart wrote:
SPI transmission of two bytes but this does not happen.
Because SPI is not enabled.

 

sts SPCR, R16

Nope, "out".

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

The idea of the simulator is that you can step through your code and ensure it does what you expect. Then you can add breakpoints to stop the code at critical places and too look at registers and memory.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tickstart wrote:
I'm not very proficient with the simulator

How about the debugger?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sternst correction seems to have worked.

sol i sinne - brun inne

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would guess that most punters do not have a hardware debugger.   (even though a XMINI-328P is only about $10)

 

They do have access to the Simulator in AS7 which is FREE.

 

And yes,  you can see exactly what happens when you use STS instead of OUT instruction.

 

But I always maintain that "well formatted code" costs nothing but pays hands down for readability.

 

David.

 

p.s.   I would also suggest that your valuable time is better spent with learning C, C++ or even Arduino-ese.

 

Last Edited: Mon. Jun 26, 2017 - 12:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
p.s.   I would also suggest that your valuable time is better spent with learning C, C++ or even Arduino-ese.
Yeah I think so too, this is really tedious. My C-application already works so there's no real reason for me to do this other than for learning.

sol i sinne - brun inne

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BTW I would use the AVR001 macros LOAD and STORE then you never need worry whether a destination register is in OUT or STS space.

david.prentice wrote:
your valuable time is better spent with learning C
You can, of course do both:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

#define SPI_SS (1 << PB2)

static inline uint8_t spi_xchg(uint8_t data) {
        SPDR = data;
        while (SPSR & (1 << SPIF));
        return SPDR;
}

ISR(TIMER1_OVF_vect) {
        PORTB &= ~SPI_SS;
        spi_xchg(0xAA);
        spi_xchg(0x55);
        PORTB |= SPI_SS;
}

int main(void) {
        SMCR = (1 << SE);
        PRR = (1 << PRTWI) | (1 << PRTIM2) | (1 << PRTIM0) | (1 << PRUSART0) | (1 << PRADC);
        DDRD = (1 << PD7);
        DDRB = (1 << DDB5) | (1 << DDB3) | (1 << DDB2);
        SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
        TIMSK1 = (1 << TOIE1);
        TCCR1B = (1 << CS11) | (1 << CS10);
        while(1) {
                sleep_cpu();
        }
}


C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega328p -Os avr.c -g -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   0c 94 34 00     jmp     0x68    ; 0x68 <__ctors_end>
   4:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
   8:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
   c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  10:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  14:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  18:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  1c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  20:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  24:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  28:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  2c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  30:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  34:   0c 94 40 00     jmp     0x80    ; 0x80 <__vector_13>
  38:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  3c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  40:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  44:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  48:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  4c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  50:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  54:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  58:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  5c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  60:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  64:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>

00000068 <__ctors_end>:
  68:   11 24           eor     r1, r1
  6a:   1f be           out     0x3f, r1        ; 63
  6c:   cf ef           ldi     r28, 0xFF       ; 255
  6e:   d8 e0           ldi     r29, 0x08       ; 8
  70:   de bf           out     0x3e, r29       ; 62
  72:   cd bf           out     0x3d, r28       ; 61
  74:   0e 94 5a 00     call    0xb4    ; 0xb4 <main>
  78:   0c 94 6c 00     jmp     0xd8    ; 0xd8 <_exit>

0000007c <__bad_interrupt>:
  7c:   0c 94 00 00     jmp     0       ; 0x0 <__vectors>

00000080 <__vector_13>:
        SPDR = data;
        while (SPSR & (1 << SPIF));
        return SPDR;
}

ISR(TIMER1_OVF_vect) {
  80:   1f 92           push    r1
  82:   0f 92           push    r0
  84:   0f b6           in      r0, 0x3f        ; 63
  86:   0f 92           push    r0
  88:   11 24           eor     r1, r1
  8a:   8f 93           push    r24
        PORTB &= ~SPI_SS;
  8c:   2a 98           cbi     0x05, 2 ; 5
#include <avr/interrupt.h>

#define SPI_SS (1 << PB2)

static inline uint8_t spi_xchg(uint8_t data) {
        SPDR = data;
  8e:   8a ea           ldi     r24, 0xAA       ; 170
  90:   8e bd           out     0x2e, r24       ; 46
        while (SPSR & (1 << SPIF));
  92:   0d b4           in      r0, 0x2d        ; 45
  94:   07 fc           sbrc    r0, 7
  96:   fd cf           rjmp    .-6             ; 0x92 <__vector_13+0x12>
        return SPDR;
  98:   8e b5           in      r24, 0x2e       ; 46
#include <avr/interrupt.h>

#define SPI_SS (1 << PB2)

static inline uint8_t spi_xchg(uint8_t data) {
        SPDR = data;
  9a:   85 e5           ldi     r24, 0x55       ; 85
  9c:   8e bd           out     0x2e, r24       ; 46
        while (SPSR & (1 << SPIF));
  9e:   0d b4           in      r0, 0x2d        ; 45
  a0:   07 fc           sbrc    r0, 7
  a2:   fd cf           rjmp    .-6             ; 0x9e <__vector_13+0x1e>
        return SPDR;
  a4:   8e b5           in      r24, 0x2e       ; 46

ISR(TIMER1_OVF_vect) {
        PORTB &= ~SPI_SS;
        spi_xchg(0xAA);
        spi_xchg(0x55);
        PORTB |= SPI_SS;
  a6:   2a 9a           sbi     0x05, 2 ; 5
}
  a8:   8f 91           pop     r24
  aa:   0f 90           pop     r0
  ac:   0f be           out     0x3f, r0        ; 63
  ae:   0f 90           pop     r0
  b0:   1f 90           pop     r1
  b2:   18 95           reti

000000b4 <main>:

int main(void) {
        SMCR = (1 << SE);
  b4:   81 e0           ldi     r24, 0x01       ; 1
  b6:   83 bf           out     0x33, r24       ; 51
        PRR = (1 << PRTWI) | (1 << PRTIM2) | (1 << PRTIM0) | (1 << PRUSART0) | (1 << PRADC);
  b8:   93 ee           ldi     r25, 0xE3       ; 227
  ba:   90 93 64 00     sts     0x0064, r25
        DDRD = (1 << PD7);
  be:   90 e8           ldi     r25, 0x80       ; 128
  c0:   9a b9           out     0x0a, r25       ; 10
        DDRB = (1 << DDB5) | (1 << DDB3) | (1 << DDB2);
  c2:   9c e2           ldi     r25, 0x2C       ; 44
  c4:   94 b9           out     0x04, r25       ; 4
        SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
  c6:   91 e5           ldi     r25, 0x51       ; 81
  c8:   9c bd           out     0x2c, r25       ; 44
        TIMSK1 = (1 << TOIE1);
  ca:   80 93 6f 00     sts     0x006F, r24
        TCCR1B = (1 << CS11) | (1 << CS10);
  ce:   83 e0           ldi     r24, 0x03       ; 3
  d0:   80 93 81 00     sts     0x0081, r24
        while(1) {
                sleep_cpu();
  d4:   88 95           sleep
  d6:   fe cf           rjmp    .-4             ; 0xd4 <main+0x20>

000000d8 <_exit>:
  d8:   f8 94           cli

000000da <__stop_program>:
  da:   ff cf           rjmp    .-2             ; 0xda <__stop_program>

That is - write it in C then look at the Asm that generates. Quick way to learn AVR assembler while also learning C!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tickstart wrote:
this is really tedious.

Yes - that is the very nature of assembler!

 

The upside is that it gives you complete control - but that means you have to take complete responsibility for each & every minute detail.

 

There is no such thing as a free lunch.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Why not simply SBIS ?

Because it is not in range of that opcode?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good point ;-)