SPI not working well

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

Hi,

I worked with following code on ATXMEGA128a4U to create SPI communication:

#define SPI_PORT PORTC
#define SPI_MOSI 5
#define SPI_MISO 6
#define SPI_SCK 7
#define SPI_SS 4
#define SPI_STATUS 0x80 //SPI waiting for operation

 

#include <avr/io.h>
#include <util/delay.h>
//#include <avr/interrupt.h>

volatile int16_t i=0,j=0; 

 

int SPI_st(void)  //SPI init
{
    PORTC_DIRSET = (1<<SPI_MOSI);
    
    PORTC_DIRSET = (0<<SPI_MISO);
        
    PORTC_DIRSET = (1<<SPI_SCK);
    PORTC_DIRSET = (1<<SPI_SS);

    //SS pin high
    PORTC_OUTSET = (1<<SPI_SS);

    //SPI_master, SPI Mode=00, CLK2x=0, DORD=0, prescaler=11
    SPIC.CTRL = 0b0010011;
    SPIC.INTCTRL = SPI_INTLVL_OFF_gc ; // no interrupt
    
    //SPI enable
    SPIC.CTRL|=64;
    
    return 0;
}

 

int16_t SPI_re(void) //SPI read
{
    int16_t spi_hodnota=0;
        
    //SS pin low
    PORTC_OUTCLR = (1<<SPI_SS);
     
    while(SPIC.STATUS & 0x80) spi_hodnota=SPIC.DATA;

    //SS high
    PORTC_OUTSET = (1<<SPI_SS);

    return spi_hodnota;
}

uint8_t SPI_wr(uint8_t vstup) //SPI write
{
    
    //SS pin low
    PORTC_OUTCLR=(1<<SPI_SS);
    
    SPIC.DATA=vstup;
        
    while(SPIC.STATUS & 0x80);
    
    //SS high
    PORTC_OUTSET = (1<<SPI_SS);
            
    return 0;
}

void SPI_close(void) //SPI CLOSE
{
    while(SPIC.STATUS & 0x80);
    SPIC_CTRL =0;
}

int main(void)
{
 
    SPI_st();
   
     while(1)
    {
        
    for(j=0;j<=255;j++)
    {
        
    i=SPI_wr(19);
    i=SPI_wr(j);
    _delay_ms(120);    
    }
    }
    SPI_close();
    
   }

 

I'm trying to communicate with SPI potentiometer MCP42010. I first connected it with Arduino Due (3.3V logic), everything was working well. After that I connected it to Atmel ATXMEGA128A4U, but I it wasn't working,

nothing on osciloscope. So I try Xplained Pro with ATXMEGA128A1, the same story. When I did debugging in SPI_uvod, I found all pins are properly setup CTRL register has  value 01010011, which is expected.  Hopefully someone can figure out, what I'm doing wrong.

 

Thx for looking into issue.

 

 

 

 

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

It seems that you misunderstand the SPI status register.
Please fix it like this.

	while(!(SPIC.STATUS & 0x80));

 

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

Thanks kanabas for help.

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

******************Code from Xplained Pro***********************************************

 

#define F_CPU 4000000UL

//Definicia SPI parametrov

#define SPI_PORT PORTC
#define SPI_MOSI 5
#define SPI_MISO 6
#define SPI_SCK 7
#define SPI_SS 4
#define SPI_STATUS 0x80 //SPI waiting for operation

#define SPI_TIMEOUT 800 //[uS]

#include <avr/io.h>
#include <util/delay.h>
//#include <avr/interrupt.h>

volatile int16_t i=0,j=0;  
volatile unsigned long k;

int SPI_uvod(void) //SPI init
{
    PORTC_DIRSET = (1<<SPI_MOSI);
    while(!(PORTC_DIR & (1<<SPI_MOSI)));
    
    PORTC_DIRSET = (0<<SPI_MISO);
       
    PORTC_DIRSET = (1<<SPI_SCK);
    while(!(PORTC_DIR & (1<<SPI_SCK)));
    
    PORTC_DIRSET = (1<<SPI_SS);
    while(!(PORTC_DIR & (1<<SPI_SS)));
    
    
    //SS pin high
    PORTC_OUTSET = (1<<SPI_SS);
    while(!(PORTC_IN & (1<<SPI_SS)));
    
    //SPI_master, SPI Mode=00, CLK2x=1, DORD=0, prescaler=01
    SPIC.CTRL = 0b10010001;
    SPIC.INTCTRL = SPI_INTLVL_OFF_gc ; // no interrupt
    
    //SPI enable
    SPIC.CTRL|=64;
    
    return 0;
}

 

int16_t SPI_citaj(void) //SPI read
{
    int16_t spi_hodnota=0;
        
    //SS pin low
    PORTC_OUTCLR = (1<<SPI_SS);
    while(PORTC_IN | (0<<SPI_SS));
    //_delay_ms(4);
    while(!(SPIC.STATUS & 0x80)) spi_hodnota=SPIC.DATA;

    //SS high
    PORTC_OUTSET = (1<<SPI_SS);
    while(!(PORTC_IN & (1<<SPI_SS)));
    //_delay_ms(4);
    return spi_hodnota;
}

uint8_t SPI_zapis(uint8_t vstup) //SPI write
{
    
    //SS pin low
    PORTC_OUTCLR=(1<<SPI_SS);
       
    SPIC.DATA=vstup;
    while(!(SPIC.STATUS & 0x80));    
    
    
    //SS high
    PORTC_OUTSET = (1<<SPI_SS);
    while(!(PORTC_IN & (1<<SPI_SS)));
    return 0;
}

void SPI_zavriet(void) //Closing SPI
{
    while(!(SPIC.STATUS & 0x80));
    SPIC_CTRL =0;
}

int main(void)
{
 
    SPI_uvod();
    
 
            
    while(1)
    {
        
    for(j=0;j<=254;j++)
    {
    
    SPI_zapis(199);
    //SPI_zapis(19);  //Command for SPI potentiometer to write value
    //SPI_zapis(j);    //Value itself
    //_delay_ms(500); //Delay, to detect changes in resistance on multimeter
    }
    }
    
    SPI_zavriet();
    
      
}

*****End of Explained Pro code

 

**************Reference code from Arduino Due***

#include <SPI.h>
#define SS_SPI_PIN 10 //Pin 10

 

void set_pot(byte pot_setting)
{
digitalWrite(SS_SPI_PIN, LOW);
SPI.transfer(byte(19)); // send command byte
SPI.transfer(byte(pot_setting)); // send value (0~255)
digitalWrite(SS_SPI_PIN, HIGH);
}

 

void setup (void)

  {
  pinMode(SS_SPI_PIN, OUTPUT);
  digitalWrite(SS_SPI_PIN, HIGH);  // ensure SS stays high
  SPI.begin ();
 
  Serial.begin(115200);  
  digitalWrite(SS_SPI_PIN, LOW);    // SS is LOW
 
  } // end of setup

 

void loop (void)
  {
  int i;
  byte c;

 

while(1)

{
 for(i=0;i<=255;i++)
{
  set_pot(i);
  delay(500);
}
}
 
  }

 

 

 

 

 

 

 

 

I used test value 199 to see it on osciloscope. Attached screenshots are for this value. To me they looks OK. When I was testing potentiometer I used combination of 19 and new setting value with delay. No changes on multimeter was observed.

I labeled cables to be sure and connect SPI potentiometer to Arduino Due (3.3V) using same sets of values. Everything working normal. When I connect it to Xplained Pro, wasn't working. I'm adding screenshoot from osciloscope (just cheap one, so it can be some distortion from signal...). MOSI  is pullup 20k resistor, MISO 10k pullup, SS 20k pull-down, SCK 20k pull-down. Since I have only 2 channel osciloscope, attaching combination SCK/MOSI(Blue/Yellow) and SS/MOSI (Blue/Yellow). 1 Clock is 2uS. I try different clocks, but results was the same. Hopefully someone with good HW background will be able to solve it.. Thx

 

Attachment(s): 

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

I'm not sure why you have two functions for the same thing, the SPI sends and receives data at the same time, you only need a single SPI_transfer() function.

uint8_t SPI_Transfer(uint8_t vstup) //SPI
{
    //SS pin low
    PORTC_OUTCLR=(1<<SPI_SS);

    SPIC.DATA=vstup;
    while(!(SPIC.STATUS & 0x80));    

    //SS high
    PORTC_OUTSET = (1<<SPI_SS);
    while(!(PORTC_IN & (1<<SPI_SS)));
    return SPIC.DATA;
}

Will do both tx and rx...

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
stack gold/silver https://www.onegold.com/join/713...

 

 

 

 

Last Edited: Tue. Jun 18, 2019 - 01:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Jim,

I created extra function for read because read, that you should test SPIC.STATUS to be sure, that value is actual "while(!(SPIC.STATUS & 0x80)) spi_hodnota=SPIC.DATA;", but if "return SPIC.DATA" will job , even better. I tested with new function "SPI_Transfer" with 199 value, oscilloscope looks good, but when I switch to actual functionality with "SPI_Transfer(19) and SPI_Transfer(j) to change value of resistance for potentiometer, again is not working.... Not sure what is wrong...

 

Thx

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

Your SPI functions wiggle the SS pin with each byte sent/received, that may not be what the POT wants, check it's data sheet.

It is normal for the SS pin to be handled out side the SPI transfer function, usually in a wrapper function that knows what the slave device is expecting.

Looking at the DS, the device wants a 16 bit value sent, a command byte plus a value byte with CS/ held low for both bytes, your function above does not do that.

Move your SS control outside your function or send both bytes to the function and handle as one command/data transfer.

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
stack gold/silver https://www.onegold.com/join/713...

 

 

 

 

Last Edited: Tue. Jun 18, 2019 - 03:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you Jim again. Now it is working.

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

Your very welcome, if you could post your final solution for others that may follow later, and also mark the solution.

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
stack gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

With hint from Jim, I changed code as follow:

 

#define F_CPU 4000000UL

 

#define SPI_PORT PORTC
#define SPI_MOSI 5
#define SPI_MISO 6
#define SPI_SCK 7
#define SPI_SS 4
#define SPI_STATUS 0x80 //SPI waiting for operation

#define SPI_TIMEOUT 800 //[uS]

#define SS_PIN_HIGH      PORTC_OUTSET = (1<<SPI_SS); while(!(PORTC_IN & (1<<SPI_SS))) //SS pin HIGH
#define SS_PIN_LOW       PORTC_OUTCLR=(1<<SPI_SS); while(PORTC_IN & (1<<SPI_SS)) //SS pin LOW
#define SPI_STATUS_CHECK while(!(SPIC.STATUS & 0x80)) //Check status of SPI

 

#include <avr/io.h>
#include <util/delay.h>
//#include <avr/interrupt.h>

 

volatile int16_t i=0,j=0;
volatile unsigned long k;

int SPI_uvod(void) //SPI init
{
    PORTC_DIRSET = (1<<SPI_MOSI);
    while(!(PORTC_DIR & (1<<SPI_MOSI)));
    
    PORTC_DIRSET = (0<<SPI_MISO);
    
    PORTC_DIRSET = (1<<SPI_SCK);
    while(!(PORTC_DIR & (1<<SPI_SCK)));
    
    PORTC_DIRSET = (1<<SPI_SS);
    while(!(PORTC_DIR & (1<<SPI_SS)));
    
    
    //SS pin high

    SS_PIN_HIGH;

    
    //SPI_master, SPI Mode=00, CLK2x=1, DORD=1, prescaler=01
    SPIC.CTRL = 0b10010001;
    SPIC.INTCTRL = SPI_INTLVL_OFF_gc ; // no interrupt
    
    //SPI enable
    SPIC.CTRL|=64;
    
    return 0;
}

uint8_t SPI_Transfer(uint8_t vstup) //SPI transfer
{
    
    SPIC.DATA=vstup;
    SPI_STATUS_CHECK;
    
    return SPIC.DATA;
}

void SPI_zavriet(void) //Closing SPI

{
    SPI_STATUS_CHECK;
    SPIC_CTRL =0;
}

int main(void)
{
    
    SPI_uvod();
    
    
    
    while(1)
    {
        
        for(j=0;j<=254;j++)
        {
            
            SS_PIN_LOW;
            SPI_Transfer(19);  //Command for SPI potentiometer to write value
            SPI_Transfer(j);    //Value itself
            SS_PIN_HIGH;
            _delay_ms(500); //Delay, to detect changes in resistance on multimeter
        }
    }
    
    SPI_zavriet();
    
    
}

 

It was sugested to pay attention that SS is holding LOW during 2 byte transfer (as outlined in attachment).

 

Thx Jim again.

 

 

 

Attachment(s): 

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



int SPI_uvod(void) //SPI init

{

    PORTC_DIRSET = (1<<SPI_MOSI);

    while(!(PORTC_DIR & (1<<SPI_MOSI)));

    

    PORTC_DIRSET = (0<<SPI_MISO);

    

    PORTC_DIRSET = (1<<SPI_SCK);

    while(!(PORTC_DIR & (1<<SPI_SCK)));

    

    PORTC_DIRSET = (1<<SPI_SS);

    while(!(PORTC_DIR & (1<<SPI_SS)));

    

    

    //SS pin high

    SS_PIN_HIGH;

    

    //SPI_master, SPI Mode=00, CLK2x=1, DORD=1, prescaler=01

    SPIC.CTRL = 0b10010001;

    SPIC.INTCTRL = SPI_INTLVL_OFF_gc ; // no interrupt

    

    //SPI enable

    SPIC.CTRL|=64;

    

    return 0;

}

 I'm a bit confused... isn't it suppose to be something more like 

//using set "|=" and instead of only "=" 

//to avoid arise of all you just set for this port/byte 

 PORTC_DIRSET |= (1<<SPI_MOSI);

 PORTC_DIRSET |= (1<<SPI_SCK);

 PORTC_DIRSET |= (1<<SPI_SS);

 

 

 

 

for example:

 

Thanks!
Dan

Last Edited: Tue. Jul 9, 2019 - 09:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

He's using PORT_DIRSET not PORT_DIR !

 

You do not use |= (or & ~=) with xxxSET, xxxCLR, xxxTGL registers.

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

No.   You have several ways of accessing the PORT registers e.g.

 PORTC_DIR |= (1<<SPI_MOSI);    //Read-Modify-Write (RMW)
 PORTC_DIR &= ~(1<<SPI_MISO);   //RMR
 PORTC_DIRSET = (1<<SPI_MOSI);  //Write Only
 PORTC_DIRCLR = (1<<SPI_MISO);  //Write Only

// I don't know what happens when you try using RMW on a write-only register.
// and there is no point in writing 0 to a write-only "steering" register

The datasheet explains how the Port registers work.    Note that most ARM devices have "steering" registers for Port access.    It is faster and more reliable than RMW.

 

David.

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

Good point.

Was not using Xmega for a year now. Need some refresh.

Thanks!
Dan