Assembler translation from UNO to DUE

I am trying to translate some code that was written for the UNO to my DUE. Almost everything is working fine, but I have very little understanding of assembler commands. So I thought maybe someone could help me to translate this little block of code to the Arduino Due?

This is regarding the SPI connection. I am trying to read from and write to the Wolfson 8737 ADC/DAC chip via SPI. The data I am trying to read/write are two 16bit words. The transmission looks like in the image I attached. So by pulling LRC (pin 10) high, I start the transmission from the chip. Then I'd like to read and write via SPI.

This is the working code for the UNO for this operation:

static inline void AudioCodec_data(int* _lin, int* _rin, int _lout, int _rout) {

  int _out_temp = _lout;
  PORTB |= (1<<PORTB2);  // toggle ss pina
  asm volatile ("out %0, %B1" : : "I" (_SFR_IO_ADDR(SPDR)), "r" (_out_temp) );
  PORTB &= ~(1<<PORTB2); // toggle ss pin
  while(!(SPSR & (1<<SPIF))){ // wait for data transfer to complete
  }
  asm volatile ("out %0, %A1" : : "I" (_SFR_IO_ADDR(SPDR)), "r" (_out_temp) );
  asm volatile ("in r3, %0" : : "I" (_SFR_IO_ADDR(SPDR)) : "r3" );
  _out_temp = _rout;
  while(!(SPSR & (1<<SPIF))){ // wait for data transfer to complete
  }
  asm volatile ("out %0, %B1" : : "I" (_SFR_IO_ADDR(SPDR)), "r" (_out_temp) );
  asm volatile ("in r2, %0" : : "I" (_SFR_IO_ADDR(SPDR)) : "r2" );
  asm volatile ("movw %0, r2" : "=r" (*_lin) : : "r2", "r3" );
  while(!(SPSR & (1<<SPIF))){ // wait for data transfer to complete
  }
  asm volatile ("out %0, %A1" : : "I" (_SFR_IO_ADDR(SPDR)), "r" (_out_temp) );
  asm volatile ("in r3, %0" : : "I" (_SFR_IO_ADDR(SPDR)) : "r3" );
  while(!(SPSR & (1<<SPIF))){ // wait for data transfer to complete
  }
  asm volatile ("in r2, %0" : : "I" (_SFR_IO_ADDR(SPDR)) : "r2" );
  asm volatile ("movw %0, r2" : "=r" (*_rin) : : "r2", "r3" );
}

(source: http://www.openmusiclabs.com/)

And this is what I have for the Arduino DUE so far:

void TC3_Handler(){
    TC_GetStatus(TC1, 0);

    if(HIGH) g_APinDescription[10].pPort -> PIO_SODR = g_APinDescription[10].ulPin;  //Setting LRC High
    else    g_APinDescription[10].pPort -> PIO_CODR = g_APinDescription[10].ulPin;

   /// CODE MISSING

    if(LOW) g_APinDescription[10].pPort -> PIO_SODR = g_APinDescription[10].ulPin;  //Setting LRC LOW
    else    g_APinDescription[10].pPort -> PIO_CODR = g_APinDescription[10].ulPin;

    /// CODE MISSING
     
}

Almost nothing. :smiley:

I know the register to read from SPI would be REG_SPI0_RDR (page 697, SAM3X/A datasheet), but how do I do this in assembler?

Most importantly I really need someone to help me to translate this (if this is even possible...). Thanks in advance!

I can't tell you what to do, but I can almost garrantee it won't involve assembler. You can access all the registers using C, you just have to study the data sheet and figure out the register names and the method they are addressed.

Hopefully someone here is up to speed on the SAM registers. Maybe study the existing SPI code, that should give you a pretty good lead.


Rob

Looks like you're trying to fake I2S (audio) protocol with SPI.

Thanks Graynomad! Yeah I think I already found the registers. SPDR on the Uno is REG_SPI0_RDR on the Due and SPSR on the Uno is like REG_SPI0_SR on the Due. I just thought Assembler might work faster?

You're right Paul. I am trying to ready audio but I thought it might be easier by setting the Wolfson8731 to DSP mode. LRC just tells the Wolfson in Slave mode when data is being transmitted.
Setting up the Wolfson chip works fine already. I'm just not able to read or write any audio data. I'd so much appreciate any help on this.

Thanks guys!

This probably won't help at all for Due (which is Cortex-M3 instead of Cortex-M4), but I've been working for the last year on an audio library. It supports the WM8731 with I2S.

This graphical design tool is the best way to get a feeling for my library's capabilities....

http://www.pjrc.com/teensy/beta/gui/

there is i2s on the due, so it might almost be easier to muck around and get that working. otherwise, try using the standard arduino library, and see how far it gets you. you might just be able to do:

digitalwrite cs pin high
spi write data out first 16b
digitalwrite cs pin low
spi write data out second 16b

although i suspect it wont work, as it will wait for the transfer to complete before doing the cs low. here is the code from the maple library, which is another m3 device, so that might help:

// spi setup procedure
void codec_maple_spi_setup(void) {
  HardwareSPI spi(1); // use spi1
  spi.begin(SPI_9MHZ, MSBFIRST, 0);
  SPI1_BASE->CR1 = 0x00000b54; // master mode 0, 9MHz, 16b
  SPI1_BASE->CR2 = 0x00000000; // make sure interrupts and DMA are off
  pinMode(10, OUTPUT); // reset ss as output
}
// codec data transfer function
static inline void AudioCodec_data(int16* _lin, int16* _rin, int16 _lout, int16 _rout) {

  GPIOA_BASE->BSRR = 0x00000010; // set ss high
  SPI1_BASE->DR = _lout; // send out left data
  asm volatile ("nop"); // delay for appropriate timing
  asm volatile ("nop");
  asm volatile ("nop");
  asm volatile ("nop");
  asm volatile ("nop");
  asm volatile ("nop");
  asm volatile ("nop");
  asm volatile ("nop");
  asm volatile ("nop");
  GPIOA_BASE->BSRR = 0x00100000; // set ss low
  while (!(SPI1_BASE->SR & (1 << SPI_SR_TXE_BIT))) { // wait for data to be sent
  }
  SPI1_BASE->DR = _rout; // send out right data
  while (!(SPI1_BASE->SR & (1 << SPI_SR_RXNE_BIT))) { // wait for data to arrive
  }
  *_lin = SPI1_BASE->DR; // recieve left data
  while (!(SPI1_BASE->SR & (1 << SPI_SR_RXNE_BIT))) { // wait for data to arrive
  }
  *_rin = SPI1_BASE->DR; // recieve right data
}

unfortunately, the nops are critical to get the timing right, which im sure will be different on the due. if you have an oscilloscope you can just add a few more, or take some out till its right. but maybe the i2s isnt looking as bad anymore.

actually, might be easier to get the i2s going, it looks like someone has written a library for it already:

Thank you all for the very helpfull answers and links!
I just found out that I used the wrong SPI header. Since the Arduino website says

Note that MISO, MOSI, and SCK are available in a consistent physical location on the ICSP header; this is useful, for example, in designing a shield that works on every board.

(source: http://arduino.cc/en/pmwiki.php?n=Reference/SPI)

I thought I could use the SPI as well as the ICSP header because they are the same. Well turns out they are not. I changed my shield to use the SPI header and now i receive at least random chunks of bits and not only 0xFF...

I will now try to make SPI work and if i can't I will try the I2S connection.

The library at this link (GitHub - delsauce/ArduinoDueHiFi: An I2S audio codec driver library for the Arduino Due board.) seems like a good source for an I2S transmission, but since there is only one open issue and that one is exactly with my codec chip I'll try SPI at first. :sleeping:

BTW, i read about I2C, I2S, SSC... Is that all the same and one just the hardware connection while the other refers to the protocol? Couldn't really figure out the differences. :roll_eyes:

I2C is completely different from I2S & SSC.

I2S is a protocol for continuously streaming stereo audio. Two clocks are used. The data signal is unidirectional, so if data flows in both directions, 2 separate pins are needed to transmit and receive. I2S is designed to stream data continuously, without any checking if the other side is present or receiving the data.

Many chips with I2S require another "master clock", typically at 256 times the sample rate clock. Arduino Due appears to be incapable of generating this extra clock (but I could be wrong about that), so it's probably only possible to use Due with I2S chips that create their own clocks. On Teensy 3.1, the audio library default is generating all 3 clocks, which has a nice benefit that the audio timing remains perfectly synchronous to the processor's clock.

I2C is a general purpose control protocol. One clock is used. The data signal is bidirectional. Typically short messages are used, with lengthy silent times between them. I2C has features to allow each chip to detect if the other is present and has properly received the message, because it's meant for infrequent control messages, rather than continuously streaming data.

Many audio chips have both I2S (for sending & receiving the audio) and I2C (for controlling parameters, like headphone amplifier volume).

SSC is the name of the on-chip peripheral on Arduino Due which is capable of I2S communication. Atmel could have simply named it the "I2S peripheral", but since it's capable of a variety of similar formats, they probably wanted a more generic name to imply its wide range of capabilities.

I saw here recently some timer code that could put out the PLL signals direct
to a pin, beyond the range of my GSPS 'scope to see in fact!

However the issue is needing a 12.288MHz or 6.144MHz crystal/oscillator I think.
Unless you are happy with a 2.4% pitch error (40% of a semitone) the 12MHz achievable
from the Due isn't helpful. 12.288M / 256 = 48kHz

Alas I've not got round to trying to get I2S working on the Due but one day...