16-bit SPI data transfers using assembler code

I found an application brief from ATMEL giving information on how to perform 8-bit or 16-bit SPI data transfers. I have been using ATMega hardware transfers for 8-bit transfers of data but have been unsuccessful get 16-bit transfers working.

The only catch is that the code is in Assembler. The code is quite simple. The notes in the code indicate that the entire set of routines compile down into 35 words of memory.

My question is:

  1. is there some way I can compile the code into a callable Arduino routine?
  2. is it a reasonable task to try to recode the code into C?

Jim

;* SAMPLE APPLICATION, READY TO RUN ON AN AT90S1200

.cseg
.org      0
Rvect:      rjmp      Reset




;****
;* FUNCTION
;*      init_spi
;*
;* DESCRIPTION
;*      Initialize our port pins for use as SPI master.
;*
;* CODE SIZE:
;*      8 words
;*
;*****

init_spi:
      ss_inactive            ;set latch bit hi (inactive)
      sbi      ddrb,nss      ;make it an output
      ;
      sck_lo                  ;set clk line lo
      sbi      ddrb,sck      ;make it an output
      ;
      mosi_lo                  ;set data-out lo
      sbi      ddrb,mosi      ;make it an output
      ;
      cbi      ddrb,miso      ;not really required, it powers up clr'd!
      ret



;*
;* FUNCTION
;*      ena_spi
;*
;* DESCRIPTION
;*      Init data & clock lines, then assert /SS.  Note that if more than
;*      one slave is used, copies of this could be made that would each
;*      reference a different /SS port pin (use SS_ACTIVE0, SS_ACTIVE1, ...)
;*
;* CODE SIZE:
;*      4 words
;*
;*

ena_spi:
      sck_lo                  ;(should already be there...)
      mosi_lo
      ss_active
      ret


;***
;* FUNCTION
;*      disa_spi
;*
;* DESCRIPTION
;*      De-assert /SS.  Since this routine is so short, it might be better
;*      to use the SS_INACTIVE statement directly in higher level code.
;*      Again, if multiple slaves exist, additional copies of this could
;*      be created; or ONE routine that disabled ALL /ss signals could be
;*      used instead to make the code less error-prone due to calling the
;*      wrong Disable routine.
;*
;* CODE SIZE:
;*      2 words
;*
;***

disa_spi:
      ss_inactive
      ret

;***
;* FUNCTION
;*      rw_spi
;*
;* DESCRIPTION
;*      Write a word out on SPI while simultaneously reading in a word.
;*      Data is sent MSB-first, and info read from SPI goes into
;*      the same buffer that the write data is going out from.
;*      Make sure data, clock and /SS are init'd before coming here.
;*      SCK high time is ((delay * 3) + 1) AVR clock cycles.
;*
;*      If 8-bit use is needed, change  LDI TEMP,16  to ,8  and also
;*      eliminate the ROL SPI_HI statement.
;*
;* CODE SIZE:
;*      21 words
;* NUMBER OF CYCLES:
;*      Overhead = 8, loop = 16 * (16 + (2* (delay_value*3)))
;      (With call + return + delay=4, it is about 648 cycles.)
;*
;****

rw_spi:      
      ldi      temp,16            ;init loop counter to 16 bits
       ;ldi      temp,8            ;use THIS line instead if 8-bit desired
      ;
spi_loop:
      lsl      spi_lo            ;move 0 into D0, all other bits UP one slot,
      rol      spi_hi            ; and C ends up being first bit to be sent.
 ;If 8-bit desired, also comment out the preceding ROL SPI_HI statement
      ;
      brcc      lo_mosi
      mosi_hi
      rjmp      mosi_done      ;this branch creates setup time on MOSI
lo_mosi:
      mosi_lo
      nop                  ;also create setup time on MOSI
mosi_done:
      ;
      sck_hi
      ;
 ;must now time the hi pulse - not much else we can do here but waste time
      ;
      set_delay temp,4      ;(4 * 3) cycle delay; range is from 1 to 7!
time_hi:
      inc_delay temp            ;inc upper nibble until it rolls over; then,
      brcs      time_hi            ; C gets CLEARED, & temp has original value
      ;
      sck_lo                  ;drop clock line low
      ;
 ;must now delay before reading in SPI data on MISO
      ;
      set_delay temp,4
time_lo:
      inc_delay temp
      brcs      time_lo
      ;
      sbic      pinb,miso      ;after delay, read in SPI bit & put into D0
      inc      spi_lo            ;we FORCED D0=0, so use INC to set D0.
      ;
      dec      temp
      brne      spi_loop
      ret

;** End of SPI routines **

;**** Application example ****

Reset:      rcall      init_spi
      ser      temp            ;load w/ FF
      out      DDRD,temp
      rjmp      Main


Main:      ldi      R22,0xA3      ;misc data
      mov      spi_lo,R22      ;set up information to be sent
      mov      spi_hi,R22      ;COMMENT THIS OUT IF 8-BIT MODE
      rcall      ena_spi            ;activate /SS
      rcall      rw_spi            ;send/receive 16 bits (or 8 bits)
      rcall      disa_spi      ;deactivate /SS
      rcall      use_spi_rcv      ;go use whatever we received
      rjmp      Main


Use_spi_rcv:                  ;just copy rcv'd data to Port D pins
      out      PortD,R22
      ret

;**** End of File ****

Hey jkraeme, did you ever get an answer to you question? i'm having the same problem. I'm going to look into this site called avrfreaks.com and see if they can help us.

sorry, that's avrfreaks.NET

Hummm...

Arduino supports ASM?, because if this is possible you can build a C function and inside paste your code in ASM.

I don't know if Arduino support it, i hope yes, test the #ASM directive?.

I'm looking for some quite similar, but i want to connecto some Atmegas like slaves to a master, using SPI, and both must read/write on this bus.

I want to write code to build a SPI UART for my serials application, i have wroted this in I2C, and works GREAT!, have a protocol to ask to the slaves UART if they have data to send.

Pleare response this post if some one wants to play a little with this..and have a couples of MEga!

Best Regards
Frank

Arduino i think is based con GCC, so this must support works whit ASM functions, take a look at:

looks this:
int foo ()
{
int x = 42;
int *y = &x;
int result;
asm ("magic stuff accessing an 'int' pointed to by '%1'"
"=&d" (r) : "a" (y), "m" (*y));
return result;
}

unfortunately, the asm directive does not work :frowning:

For inline, try this thread:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1212174920;start=all

Details of error :
Code:

  int foo ()
     {
      int x = 42;
      int *y = &x;
      int result;
      asm ("magic stuff accessing an 'int' pointed to by '%1'"
            "=&d" (r) : "a" (y), "m" (*y)); // Error Line  ;D edit
      return result;
     }

Error:

 In function 'int foo()':
error: expected `)' before '(' token

Keep in mind "strange" errors are sometimes the result of the Arduino parser making a mistake. Take a look for the temporary files for the post-processed .cpp file to see if it looks weird.

--Phil.

would those files be in the sketch folder?

would those files be in the sketch folder?

From memory the file (named <something>.cpp) will be in the applet directory of your sketch folder or your system temporary directory--the exact location I think depends on whether the sketch has been successfully uploaded to the board or not.

--Phil.