SPI implementation on ATTiny85 in assembly not working - Page 1
Forum section: electronics / general

author: alexRosito
author_rank: Newbie
timestamp: May 17, 2024, 02:54:16 am
content: 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:# include <avr/io.h># define __SFR_OFFSET 0x00.global SPI_begin.global SPI_setDataMode.global SPI_transfer.global SPI_end.equSPI_MODE1, 0x04SPI_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 prescalerldi 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 requirementsldi r17, (1 << USICS1) | (1 << USICLK) | (1 << USICS0) ; Prescaler settingout USICR, r17; Clear the USI counter overflow flagldi r16, (1 << USIOIF)out USISR, r16retSPI_transfer:cli ; Disable interrupts; Load data into the USI data registerout USIDR, r24; Clear the counter overflow flagldi r16, (1 << USIOIF)out USISR, r16SPITransfer_loop:; Clock the USI by toggling the USITC bitldi r17, (1 << USITC)out USICR, r17; Wait for USI counter overflow flagin r16, USISRsbrs r16, USIOIF ; Skip next instruction if USIOIF is setrjmp SPITransfer_loop ; Repeat until the transfer is completesei ; Enable interruptsretSPI_setDataMode:push r16in r16, USICR ;READ THE CONTENT OF USICR INTO r16andi r16, ~(1<<USICS0) ;CLEAR THE BIT USICS0 IN r16out USICR, r16;MOVE CONTENT OF r16 BACK TO USICR SO ASSUME SPI_MODE0cpir24,SPI_MODE1;CHECK IF THE USER WANTS SPI_MODE1breq SetMode1;IF INDEED THE USER WANTS SPI_MODE1 GO AND SET SPI_MODE1rjmp End;ELSE JUMP TO DIRECTLY TO END TO RETAIN THE SPI_MODE0SetMode1:in r16, USICR ;READ AGAIN THE CONTENT OF USICR INTO r16ori r16, 1<<USICS0 ;SET THE BIT USICS0 IN r16out USICR, r16 ;MOVE CONTENT OF r16 BACK TO USICR SO ASSUME SPI_MODE1End:pop r16retSPI_end:;CLEARING BITS USIWM0 AND USIWM1 IN REGISTER USICRpush r16in r16, USICRandi r16, ~(1<<USIWM1)out USICR, r16andi r16, ~(1<<USIWM0)out USICR, r16pop r16retSomething 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.

author: ledtester
author_rank: Super Contributor
timestamp: May 17, 2024, 04:58:14 am
content: 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

author: eutectique
author_rank: Frequent Contributor
timestamp: May 17, 2024, 10:32:06 am
content: Quote from: alexRosito on May 17, 2024, 02:54:16 amWhich perhaps compiles to one object file.

author: alexRosito
author_rank: Newbie
timestamp: May 17, 2024, 02:14:39 pm
content: 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.

author: alexRosito
author_rank: Newbie
timestamp: May 18, 2024, 02:52:48 am
content: 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:#define __SFR_OFFSET 0#include <avr/io.h>.global SPI_begin.global SPI_setDataMode.global SPI_transfer.global SPI_end.equ SPI_MODE1, 0x04SPI_begin:; Clear bits USISIE, USIOIE, and USIWM1 in USICRin 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 USICRin r16, _SFR_IO_ADDR(USICR)sbr r16, (1 << USIWM0) | (1 << USICS1) | (1 << USICLK)out _SFR_IO_ADDR(USICR), r16; Set PB2 (USCK) as outputsbi _SFR_IO_ADDR(DDRB), DDB2; Set PB1 (DO/MOSI) as outputsbi _SFR_IO_ADDR(DDRB), DDB1; Set PB0 (DI/MISO) as inputcbi _SFR_IO_ADDR(DDRB), DDB0retSPI_setDataMode:; Clear USICS0 bit to set SPI_MODE0 initiallyin r16, _SFR_IO_ADDR(USICR)cbr r16, (1 << USICS0)cpi r24, SPI_MODE1 ; Check if the mode is SPI_MODE1brne EndDataMode ; If not, jump to EndDataMode; Set USICS0 bit for SPI_MODE1sbr r16, (1 << USICS0)EndDataMode:out _SFR_IO_ADDR(USICR), r16retSPI_transfer:; Disable interruptscli; Load data into USIDRout _SFR_IO_ADDR(USIDR), r24; Clear counter overflow interrupt flagldi r16, (1 << USIOIF)out _SFR_IO_ADDR(USISR), r16SPITransfer_loop:; Toggle clocksbi _SFR_IO_ADDR(USICR), USITC; Check if USIOIF is setin r16, _SFR_IO_ADDR(USISR)sbrc r16, USIOIFrjmp SPITransfer_loop; Re-enable interruptssei; Load received datain r24, _SFR_IO_ADDR(USIDR)retSPI_end:; Clear USIWM1 and USIWM0 bits to disable USIin r16, _SFR_IO_ADDR(USICR)cbr r16, (1 << USIWM1) | (1 << USIWM0)out _SFR_IO_ADDR(USICR), r16retStill, if someone has any idea why my LED matrix is not working would be really appreciated.