Author Topic: SPI implementation on ATTiny85 in assembly not working  (Read 815 times)

0 Members and 1 Guest are viewing this topic.

Offline alexRositoTopic starter

  • Newbie
  • Posts: 8
  • Country: us
  • The greatest teacher, failure is.
SPI implementation on ATTiny85 in assembly not working
« on: May 17, 2024, 02:54:16 am »
Hi! This is my first post in this group and also I'm a beginner in AVR-Assembly.
I already coded a library in C++ for driving a LED Matrix Display based on the MAX7219CNG 7 segment display driver chip and an ATtiny85 as a master.

Because the MAX7219CNG uses the SPI protocol in my library I make use of the tinySPI (by J Christensen)  and everything works as expected.

Since I want to learn how to code in AVR-Assembly I decided to give it a try porting such library to assembly for the ATtiny85.

In the same directory I have my source code in C++ I've created a file with the same name as the library I wrote but with  .S extension. Everything assembles and compiles with no errors but the ATtiny85 just doesn't work. One thing I have to mention is that I need to use PB0 as SS because I just want to output data from the MCU (with the tinySPI library that's not a problem at all).

My code in assembly is as follows:

Code: [Select]
# include <avr/io.h>
# define __SFR_OFFSET 0x00

.global SPI_begin
.global SPI_setDataMode
.global SPI_transfer
.global SPI_end

.equ SPI_MODE1, 0x04

SPI_begin:
   

    ; Configure USI for three-wire mode, software clock strobe (USITC toggling SCK)
   
   ; To ensure the clock is slower, we'll configure it with a prescaler
    ldi        r16, (1 << USIWM0) | (1 << USICS1) | (1 << USICLK)       ;3-wire mode, internal clock source (16 MHz)
   
   ; Applying a prescaler to slow down the clock to meet the MAX7219 requirements
    ldi       r17, (1 << USICS1) | (1 << USICLK) | (1 << USICS0)         ; Prescaler setting
    out      USICR, r17

    ; Clear the USI counter overflow flag
    ldi       r16, (1 << USIOIF)
    out      USISR, r16

    ret

SPI_transfer:
    cli                             ; Disable interrupts
    ; Load data into the USI data register
    out       USIDR, r24

    ; Clear the counter overflow flag
    ldi        r16, (1 << USIOIF)
    out      USISR, r16

SPITransfer_loop:
   
    ; Clock the USI by toggling the USITC bit
    ldi        r17, (1 << USITC)
    out       USICR, r17

    ; Wait for USI counter overflow flag
    in        r16, USISR
    sbrs     r16, USIOIF              ; Skip next instruction if USIOIF is set
    rjmp    SPITransfer_loop       ; Repeat until the transfer is complete
    sei                                     ; Enable interrupts
    ret

SPI_setDataMode:
    push     r16
    in         r16, USICR              ;READ THE CONTENT OF USICR INTO r16
    andi      r16, ~(1<<USICS0) ;CLEAR THE BIT USICS0 IN r16
    out       USICR, r16       ;MOVE CONTENT OF r16 BACK TO USICR SO ASSUME SPI_MODE0
    cpi r24,SPI_MODE1       ;CHECK IF THE USER WANTS SPI_MODE1
    breq     SetMode1       ;IF INDEED THE USER WANTS SPI_MODE1 GO AND SET SPI_MODE1
    rjmp     End       ;ELSE JUMP TO DIRECTLY TO END TO RETAIN THE SPI_MODE0
SetMode1:
    in         r16, USICR              ;READ AGAIN THE CONTENT OF USICR INTO r16
    ori        r16, 1<<USICS0     ;SET THE BIT USICS0 IN r16
    out       USICR, r16             ;MOVE CONTENT OF r16 BACK TO USICR SO ASSUME SPI_MODE1
End:
    pop       r16
    ret

SPI_end:

  ;CLEARING BITS USIWM0 AND USIWM1 IN REGISTER USICR
    push      r16
    in          r16, USICR
    andi       r16, ~(1<<USIWM1)
    out        USICR, r16
    andi       r16, ~(1<<USIWM0)
    out        USICR, r16
    pop        r16
    ret

Something I have noticed is that when I use the "# define __SFR_OFFSET 0x00"  directive I can see activity in PB2 (SCLK) but PB1 (DO) and PBO (SS) are inactive.
When I remove the previous directive the <avr/io.h> libray sets "# define __SFR_OFFSET 0x20" and I can see activity in PB0 (SS) only.

Any help would be appreciated.
« Last Edit: May 17, 2024, 01:53:13 pm by alexRosito »
“Do or do not, there is no try” - Master Yoda
 

Online ledtester

  • Super Contributor
  • ***
  • Posts: 3076
  • Country: us
Re: SPI implementation on ATTiny85 in assembly not working
« Reply #1 on: May 17, 2024, 04:58:14 am »
It looks like you only posted the library.

Do you have a complete program which uses the library?

Having the smallest example which doesn't work is very helpful in debugging problems like these.

Note that the example program doesn't have to interact with a real device -- if you have a scope (or logic analyzer) you can verify that the correct CLK and DATA signals were generated. You can get a logic analyzer which will work in this case for < $20 from Amazon or < $10 from Aliexpress.

For instance, I would first write a short assembly program that uses the library to send out a byte of data through SPI.

Some links for cheap logic analyzers:

https://www.amazon.com/HiLetgo-Analyzer-Ferrite-Channel-Arduino/dp/B077LSG5P2/
https://www.aliexpress.us/item/3256804585408280.html
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 407
  • Country: be
Re: SPI implementation on ATTiny85 in assembly not working
« Reply #2 on: May 17, 2024, 10:32:06 am »
In the same directory I have my source code in C++ I've created a file with the same name as the library I wrote but with  .S extension. Everything assembles and compiles with no errors

Which perhaps compiles to one object file.
 

Offline alexRositoTopic starter

  • Newbie
  • Posts: 8
  • Country: us
  • The greatest teacher, failure is.
Re: SPI implementation on ATTiny85 in assembly not working
« Reply #3 on: May 17, 2024, 02:14:39 pm »
Hi. Thank you for your assistance:
I've written a complete library that drives an LED Matrix that uses the tinyISP (by J Christensen) and works as intended.
This library is a direct translation from J's code (in C++) to assembly.
I have tried this translation in my library and doesn't work, I have tried it also in another test program (smallest as possible) and same result.
Because I'm new in AVR_Assembly I think that, even everything assembles and compiles correctly, I made some kind of dumb mistake that I'm not aware of, but I can't find it.
I have a toy oscilloscope (one of those really cheap ones) but I think I'll buy a logic analyzer as you suggested.

I will also write a short assembly program that uses the library as you suggested and I will let you know my findings.

Thank you so much.
« Last Edit: May 17, 2024, 02:26:23 pm by alexRosito »
“Do or do not, there is no try” - Master Yoda
 

Offline alexRositoTopic starter

  • Newbie
  • Posts: 8
  • Country: us
  • The greatest teacher, failure is.
Re: SPI implementation on ATTiny85 in assembly not working
« Reply #4 on: May 18, 2024, 02:52:48 am »
I, partially, made it to work. Now I see activity in the pins but still the MAX7219 seems not to be receiving the data transmitted.
Below is the code that I have so far.

Code: [Select]
#define __SFR_OFFSET 0
#include <avr/io.h>

.global SPI_begin
.global SPI_setDataMode
.global SPI_transfer
.global SPI_end

.equ SPI_MODE1, 0x04

SPI_begin:
    ; Clear bits USISIE, USIOIE, and USIWM1 in USICR
    in r16, _SFR_IO_ADDR(USICR)
    cbr r16, (1 << USISIE) | (1 << USIOIE) | (1 << USIWM1)
    out _SFR_IO_ADDR(USICR), r16

    ; Set bits USIWM0, USICS1, and USICLK in USICR
    in r16, _SFR_IO_ADDR(USICR)
    sbr r16, (1 << USIWM0) | (1 << USICS1) | (1 << USICLK)
    out _SFR_IO_ADDR(USICR), r16

    ; Set PB2 (USCK) as output
    sbi _SFR_IO_ADDR(DDRB), DDB2

    ; Set PB1 (DO/MOSI) as output
    sbi _SFR_IO_ADDR(DDRB), DDB1

    ; Set PB0 (DI/MISO) as input
    cbi _SFR_IO_ADDR(DDRB), DDB0

    ret

SPI_setDataMode:
    ; Clear USICS0 bit to set SPI_MODE0 initially
    in r16, _SFR_IO_ADDR(USICR)
    cbr r16, (1 << USICS0)
    cpi r24, SPI_MODE1   ; Check if the mode is SPI_MODE1
    brne EndDataMode     ; If not, jump to EndDataMode

    ; Set USICS0 bit for SPI_MODE1
    sbr r16, (1 << USICS0)
EndDataMode:
    out _SFR_IO_ADDR(USICR), r16
    ret

SPI_transfer:
    ; Disable interrupts
    cli

    ; Load data into USIDR
    out _SFR_IO_ADDR(USIDR), r24

    ; Clear counter overflow interrupt flag
    ldi r16, (1 << USIOIF)
    out _SFR_IO_ADDR(USISR), r16

SPITransfer_loop:
    ; Toggle clock
    sbi _SFR_IO_ADDR(USICR), USITC

    ; Check if USIOIF is set
    in r16, _SFR_IO_ADDR(USISR)
    sbrc r16, USIOIF
    rjmp SPITransfer_loop

    ; Re-enable interrupts
    sei

    ; Load received data
    in r24, _SFR_IO_ADDR(USIDR)
    ret

SPI_end:
    ; Clear USIWM1 and USIWM0 bits to disable USI
    in r16, _SFR_IO_ADDR(USICR)
    cbr r16, (1 << USIWM1) | (1 << USIWM0)
    out _SFR_IO_ADDR(USICR), r16
    ret


Still, if someone has any idea why my LED matrix is not working would be really appreciated.
« Last Edit: May 18, 2024, 03:15:51 am by alexRosito »
“Do or do not, there is no try” - Master Yoda
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf