SAMD51 32bit SPI transactions

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

Hey guy.

     I'm working on a custom board using an ATSAMD51G19A-MU processor and I'm running into trouble on the SPI function. I have successfully configured the peripheral and I see a good stable and clear signal being transferred and received on my oscilloscope. The problem I am facing is that the slave I am interacting with is a MAX31855 thermocouple amplifier and it transmits in 32bit transactions. However it seems I cannot configure the hardware SPI on the MCU to do any more than 4 8bit "characters" with the equivalent of approximately 4 clock pulses in between. During that time the SS line is going high and then being pulled low again (ref 35.6.3.5). Something I noticed is that on the TX from the master (not actually connected to the slave as there is no MOSI) the 32nd bit is actually the 25th bit to appear on the signal as illustrated below. This happens in data order MSB. In data order LSB the bits are reversed but the character segments are not. I'm really not sure how to move forward on this. The big question here is if the hardware SPI is even capable of utilizing the signal structure of the slave. More and more I'm fearing I will have to use software SPI to make this work. The datasheet makes me believe this is possible due to figure 35-2 showing a continuous low SS will 3 characters are read/transmitted. 

 

I'd appreciate any help I can get on this. Thanks for reading.

 

 

MSB:

7........0    15........8    23........16    31........24

 

LSB:

0........7    8........15    16........23    24........31

 

Signal received from slave;

 

31....................................................0

 

A link to the datasheet for your viewing pleasure.

http://www.microchip.com/mymicro...

 

This topic has a solution.
Last Edited: Mon. Jul 27, 2020 - 01:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's not clear how you configured the SERCOM. There is no need to use the 32-bit extension mode if that is what you have configured (that would be more appropriate for a high bandwidth stream, e.g. from the MCU to a SPI display). Possibly the 32-bit extension mode should work for your case (including with MSSEN set) but I would start with the more standard 8 bit mode. If you do use the 8 bit mode then you will have to control SS manually (MSSEN cleared) because:

If back-to-back frames are transmitted, the SS pin will always be driven high for a minimum of one baud cycle between frames.

lowrider2962 wrote:
The big question here is if the hardware SPI is even capable of utilizing the signal structure of the slave.

It would be very strange if HW SPI was out of the question for this device, I doubt it's that exotic and a quick google finds this arduino lib using hardware SPI:

https://github.com/enjoyneering/MAX31855/blob/master/src/MAX31855.cpp

/Lars

 

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

It's not clear how you configured the SERCOM.

*crickets* yeaaaah, forgot to post my code didn't I....... sorry bout that...

 

I tried disabling the 32bit extension mode and it still doesn't read back to back frames without the 3-4 cycle gap and the SS going high. I've tried a very simple software control SS function but it doesn't seem that I got that right. I agree that it seems odd to think this wouldn't work. 

 

I hadn't previously found the library you've linked.

While it does have helpful info that I will be using I'm unable to use it as written because I cant seem to get Atmel Studio 7 to play nicely with Arduino libraries.

 

Thanks for the reply,

what are you thoughts?

 

/*
 * SPI_0.h
 *
 * Created: 7/25/2020 2:16:13 PM
 *  Author: lowri
 */ 

#ifndef SPI_0_H_
#define SPI_0_H_
 
 void SPI_init()
 {
    
     MCLK->APBAMASK.bit.SERCOM0_ = 1;
     
     
     GCLK->GENCTRL[10].reg = GCLK_GENCTRL_SRC_DFLL | GCLK_GENCTRL_DIV(6) | GCLK_GENCTRL_GENEN; 
     //GCLK->GENCTRL[10].reg = 6 | 1<<8 | 3<<17; //Configure src = DFLL, enable generator, and divide by = 6
     
     GCLK->PCHCTRL[7].reg = 74; 
     
     while (GCLK->SYNCBUSY.reg > 0);
     
     SERCOM0->SPI.CTRLA.reg = 0; //set everything to 0
     SERCOM0->SPI.CTRLA.bit.DIPO = 0x3; //PAD0 = MOSI, PAD1 = SCK, PAD2 = SS, PAD3 = MISO
     SERCOM0->SPI.CTRLA.bit.MODE = 0x3; //Master
     
     
     SERCOM0->SPI.CTRLB.bit.RXEN = 1; //RX enable 
     SERCOM0->SPI.CTRLB.bit.MSSEN = 1; //Hardware SS enable
     
     SERCOM0->SPI.CTRLC.bit.DATA32B = 1; //Enable 32bit extention mode
     
     SERCOM0->SPI.BAUD.reg = 79; // set baudrate at 20us per period
     
     SERCOM0->SPI.LENGTH.bit.LEN = 32;
     SERCOM0->SPI.LENGTH.bit.LENEN = 1;
     
     NVIC_EnableIRQ(SERCOM0_0_IRQn);
     
     SERCOM0->SPI.INTENSET.bit.RXC = 1;
     
     PORT->Group[0].PINCFG[4].bit.PMUXEN = 1; // pin 9 = PA04 = pad 0 = MOSI
     PORT->Group[0].PMUX[2].bit.PMUXE = 3;
     
     PORT->Group[0].PINCFG[5].bit.PMUXEN = 1; // pin 10 = PA05 = pad 1 = SCK
     PORT->Group[0].PMUX[2].bit.PMUXO = 3;
     
     PORT->Group[0].PINCFG[6].bit.PMUXEN = 1; // pin 11 = PA06 = pad 2 = SS
     PORT->Group[0].PMUX[3].bit.PMUXE = 3;
     
     PORT->Group[0].PINCFG[7].bit.PMUXEN = 1; // pin 12 = PA07 = pad 3 = MISO
     PORT->Group[0].PINCFG[7].bit.PULLEN = 1; // enable pull down control
     PORT->Group[0].PMUX[3].bit.PMUXO = 3;
     
     
     //PORT->Group[0].DIRSET.reg = 1<<9; //configure pin 14 (PA09) for software SS
     //PORT->Group[0].OUTSET.reg = 1<<9; // drive SS high
     
     
     SERCOM0->SPI.CTRLA.bit.ENABLE =1;
     
 }

void SERCOM0_0_Handler(void)
{
    if (SERCOM0->SPI.INTFLAG.bit.RXC != 0)
    {
        //PORT->Group[0].OUTSET.reg = 1<<9; //drive sofware SS high
    }
     
}

uint32_t SPI_READ(void)
{
    
    //PORT->Group[0].OUTCLR.reg = 1<<9; //drive software SS low
    
    SERCOM0->SPI.DATA.reg = 1<<0;// trigger a transaction
    
        uint32_t v = SERCOM0->SPI.DATA.reg; //read data register
        
        // ignore bottom 4 bits - they're just thermocouple data
        v >>= 4;
        
        // pull the bottom 11 bits off
        float internal = v & 0x7FF;
        // check sign bit!
        if (v & 0x800) {
            // Convert to negative value by extending sign and casting to signed type.
            int16_t tmp = 0xF800 | (v & 0x7FF);
            internal = tmp;
        }
        internal *= 0.0625; // LSB = 0.0625 degrees
        // Serial.print("\tInternal Temp: "); Serial.println(internal);
        return internal;    

}

#endif /* SPI_0_H_ */

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

SUCCESS!!!!!!

 

I followed your suggestion and after disabling the 32bit extension function and it's companion functions.I was down to figuring out the software SS commands and conditioning the value. Found a really simple code to reverse the value on google and I'm in business!

 

Thanks for you help! Below is the functioning code.

 

P.S. is it normal to have to reverse the value? I had to switch to the LSB data order so that the frames were in the right order but then the value was backwards.

 

/*
 * SPI_0.h
 *
 * Created: 7/25/2020 2:16:13 PM
 *  Author: lowri
 */ 

#ifndef SPI_0_H_
#define SPI_0_H_
 
 uint32_t raw = 0;
 uint32_t rev = 0;
 
 void SPI_init()
 {
    
     MCLK->APBAMASK.bit.SERCOM0_ = 1;
     
     
     GCLK->GENCTRL[10].reg = GCLK_GENCTRL_SRC_DFLL | GCLK_GENCTRL_DIV(6) | GCLK_GENCTRL_GENEN; 
     //GCLK->GENCTRL[10].reg = 6 | 1<<8 | 3<<17; //Configure src = DFLL, enable generator, and divide by = 6
     
     GCLK->PCHCTRL[7].reg = 74; 
     
     while (GCLK->SYNCBUSY.reg > 0);
     
     SERCOM0->SPI.CTRLA.reg = 0; //set everything to 0
     SERCOM0->SPI.CTRLA.bit.DIPO = 0x3; //PAD0 = MOSI, PAD1 = SCK, PAD2 = SS, PAD3 = MISO
     SERCOM0->SPI.CTRLA.bit.MODE = 0x3; //Master
     SERCOM0->SPI.CTRLA.bit.DORD = 1; // LSB
     
     
     //SERCOM0->SPI.CTRLB.bit.RXEN = 1; //RX enable 
     //SERCOM0->SPI.CTRLB.bit.MSSEN = 1; //Hardware SS enable
     
     SERCOM0->SPI.CTRLC.bit.DATA32B = 1; //Enable 32bit extention mode
     
     SERCOM0->SPI.BAUD.reg = 79; // set baudrate at 20us per period
     
     //SERCOM0->SPI.LENGTH.bit.LEN = 32;
     //SERCOM0->SPI.LENGTH.bit.LENEN = 1;
     
     NVIC_EnableIRQ(SERCOM0_0_IRQn);
     
     SERCOM0->SPI.INTENSET.bit.RXC = 1;
     
     PORT->Group[0].PINCFG[4].bit.PMUXEN = 1; // pin 9 = PA04 = pad 0 = MOSI
     PORT->Group[0].PMUX[2].bit.PMUXE = 3;
     
     PORT->Group[0].PINCFG[5].bit.PMUXEN = 1; // pin 10 = PA05 = pad 1 = SCK
     PORT->Group[0].PMUX[2].bit.PMUXO = 3;
     
     //PORT->Group[0].PINCFG[6].bit.PMUXEN = 1; // pin 11 = PA06 = pad 2 = SS
     //PORT->Group[0].PMUX[3].bit.PMUXE = 3;
     
     PORT->Group[0].PINCFG[7].bit.PMUXEN = 1; // pin 12 = PA07 = pad 3 = MISO
     PORT->Group[0].PINCFG[7].bit.PULLEN = 1; // enable pull down control
     PORT->Group[0].PMUX[3].bit.PMUXO = 3;
     
     
     PORT->Group[1].DIRSET.reg = 1<<10; //configure pin 19 (PB10) for software SS
     PORT->Group[1].OUTSET.reg = 1<<10; // drive SS high
     
     
     SERCOM0->SPI.CTRLA.bit.ENABLE =1;
     
 }

void SERCOM0_0_Handler(void)
{
    
    if (SERCOM0->SPI.INTFLAG.bit.RXC == 1 )
    {  
        PORT->Group[1].OUTSET.reg = 1<<10; //drive sofware SS high
        SERCOM0->SPI.CTRLB.bit.RXEN = 0; //RX disable
    }
     
}

uint32_t SPI_read(void)
{
    SERCOM0->SPI.CTRLB.bit.RXEN = 1; //RX enable
    while(SERCOM0->SPI.SYNCBUSY.bit.CTRLB != 0){}
        
    PORT->Group[1].OUTCLR.reg = 1<<10; //drive software SS low
    
    SERCOM0->SPI.DATA.reg = 1<<0;// trigger a transaction
    
    while(SERCOM0->SPI.INTFLAG.bit.RXC == 0){}
        
    PORT->Group[1].OUTSET.reg = 1<<10;
    
        raw >>=32;
    
        raw = SERCOM0->SPI.DATA.reg; //read data register
        
        while (raw > 0) 
    { 
        // bitwise left shift  
        // 'rev' by 1 
        rev <<= 1; 
          
        // if current bit is '1' 
        if (raw & 1 == 1) 
            rev ^= 1; 
          
        // bitwise right shift  
        // 'n' by 1 
        raw >>= 1; 
              
    }
        
        
        return rev;
}
    
    

#endif /* SPI_0_H_ */

the MAX31855 library I'm still writing:

 

 

/*
 * max31855_library.h
 *
 * Created: 7/26/2020 4:53:53 PM
 *  Author: lowri
 */ 

#ifndef MAX31855 LIBRARY_H_
#define MAX31855 LIBRARY_H_

#include "SPI_0.h"

uint32_t v;

uint32_t Temp_internal(void)
{
    
    v = SPI_read();
    
    // ignore bottom 4 bits - they're just thermocouple data
    v >>= 4;
    
    // pull the bottom 11 bits off
    float internal = v & 0x7FF;
    // check sign bit!
    if (v & 0x800) {
        // Convert to negative value by extending sign and casting to signed type.
        int16_t tmp = 0xF800 | (v & 0x7FF);
        internal = tmp;
    }
    internal *= 0.0625; // LSB = 0.0625 degrees
    // Serial.print("\tInternal Temp: "); Serial.println(internal);
    return internal;

}

#endif /* MAX31855 LIBRARY_H_ */