Alec Fisher
Alec Fisher

Reputation: 71

Trouble getting SPI functioning on PIC24

I am trying to configure a PIC24FJ512GA610 to use SPI to interface with an ADC module. As such, I am configuring it in two-wire mode (only SCK and SDI, CS controlled by ports). Below is the relevant code I am using to configure the SPI. A header file contains most definitions:

// SPI3 is used to read from 22-bit ADC tied to RTDs

// SPI3CON1L
#define SPI3EN      0<<15   // SPI Enable Pin
// unimplemented 
#define SPI3SIDL    0<<13   // Stop when CPU is in idle mode
#define SPI3DSSDO   1<<12   // Disable SDO (no data sent to ADC module)
#define SPI3MODE1   0<<11   // Data mode: (0,0) transmits 8-bits 
#define SPI3MODE0   0<<10   //  
#define SPI3SMP     1<<9    // Sampling phase: samples at the end of output data time
#define SPI3CKE     0<<8    // Transmits on transition from idle to active
#define SPI3SSEN    0<<7    // only used in slave mode
#define SPI3CKP     0<<6    // clock idle state is low, active state is high
#define SPI3MSTEN   1<<5    // Master mode enabled
#define SPI3DISSDI  0<<4    // Disable SDI (no data received the board))
#define SPI3DISSCK  0<<3 
#define SPI3MCLKEN  1<<2 
#define SPI3SPIFE   0<<1 
#define SPI3ENHBUF  0<<0 

#define SPI3CON1L_MASK (SPI3EN | SPI3SIDL | SPI3DSSDO | SPI3MODE1 | SPI3MODE0 | SPI3SMP |\
                        SPI3CKE | SPI3SSEN | SPI3CKP | SPI3MSTEN | SPI3DISSDI | SPI3DISSCK | SPI3MCLKEN |\
                        SPI3SPIFE | SPI3ENHBUF )

// SPI3CON1H
#define AUDEN3      0<<15   // Audio codec enable
#define SPI3SGNEXT  0<<14   // Sign Extend Enable
#define IGNROV3     0<<13   // Ignore Receive Overflow
#define IGNTUR3     0<<12   // Ignore Transmit Underrun
#define AUDMONO3    0<<11   // Audio data format
#define URDTEN3     0<<10   // Transmit underrun data enable
#define AUDMOD31    0<<9    // Audio protocol mode select
#define AUDMOD30    0<<8    // 
#define FRMEN3      1<<7    // Framed SPI support
#define FRMSYNC3    0<<6    // Frame sync pulse direction control
#define FRMPOL3     0<<5    // Frame sync/slave select polarity bit
#define MSSEN3      0<<4    // Master mode slave select enable
#define FRMSYPW3    0<<3    // Frame sync pulse width bit
#define FRMCNT32    0<<2    // Frame sync pulse counter bits
#define FRMCNT31    0<<1    // 
#define FRMCNT30    0<<0    // 

#define SPI3CON1H_MASK (AUDEN3 | SPI3SGNEXT | IGNROV3 | IGNTUR3 | AUDMONO3 | URDTEN3 | AUDMOD31 |\
                        AUDMOD30 | FRMEN3 | FRMSYNC3 | FRMPOL3 | MSSEN3 | FRMSYPW3 | FRMCNT32 |\
                        FRMCNT31 | FRMCNT30 )

The function that configures the SPI is found elsewhere:

void SPI3Init(void)
// SPI 3 is the thermocouple interface chip or
// the external adc interface for the cryostat
// **************************************
{
#if PROCESSOR == __PIC24FJ512GA610__

    //  Used for SPI communication to read RTDs through ADC
    IEC3bits.SPI3RXIE = 0;          // disable all SPI3 interrupts
    IEC5bits.SPI3TXIE = 0;
    IEC5bits.SPI3IE = 0;
    SPI3BUFL = 0;                   // clear buffer
    SPI3BUFH = 0;

    OSCCONbits.IOLOCK = 0;
    RPINR28bits.SDI3R = 30;         // assign SPI3 SDI to pin 52 (RP30)
//    RPINR28bits.SCK3R = 15;         // assign SPI3 SCK to pin 53 (RP15) 
    RPOR7bits.RP15R = 24;           // Set SCK pin to SPI3 SCK OUT (function 24)
    RPOR8bits.RP16R = 23;           // Map SDO to N/C pin 51 (function 23)
    OSCCONbits.IOLOCK = 1;

    SPI3CON1Lbits.SPIEN = 0;        // disable the port

    SPI3IMSKH = 0;       // disable all interrupts
    SPI3CON1L = 0;       // disable SPI 
    SPI3CON1H = 0;       // turn off AUDEN & FRMEN
    SPI3BRGL = 832;     // 3=16mhz / (2* (3+1)) = 2mhz,  7=16/2(7+1) = 1mhz, 832 -> 9600  )

    SPI3CON1L = SPI3CON1L_MASK;  // Write config masks
    SPI3CON1H = SPI3CON1H_MASK;
    SPI3CON2L = 7;               // 8 bit data

    SPI3STATLbits.SPIROV = 0;   // clear any overflow status
    SPI3CON1Lbits.SPIEN = 1;        // enable the port

    SPI3BUFL = 0;
    ADC_CS_HIGH;
    ADC_CS_LOW;
    ADC_CS_HIGH;
#endif

return;
}

Actually reading the ADC calls this function three times (one for each 8 bits of resolution)

unsigned char Read_SPI3()
{
    unsigned char ioByte = 0;
    if(SPI3STATLbits.SPIROV)
        SPI3STATLbits.SPIROV = 0;  // clear overflow

    SPI3BUFL = ioByte;      // CLK out data on falling edge  
    while(!SPI3STATLbits.SPIRBF)
    ;     

    ioByte = SPI3BUFL;      // CLK in data on rising edge
    return ioByte;
}

I have been going through my code for the last day or two and, after combing the internet, cannot figure out what I am doing wrong. Putting a scope on the SCK line indicates that the serial clock is not running. I have confirmed that the config bits are being successfully written. Can anybody tell me what might be wrong?

Upvotes: 0

Views: 1242

Answers (3)

Mike
Mike

Reputation: 4288

With this:

RPOR7bits.RP15R = 24; 

you are mapping your SCLK pin to RF8. But you had to switch this pin to an output with.

TRISFbits.TRISF8 = 0;  

And you had to switch this pin to digital with

ANSELFbits.ANSELF8 = 0;

Same thing for the SDO pin.

And as @Dan already mentiones, you need a unlock/lock sequence for the PPS. Have aloo at page 163 in the datasheet

Upvotes: 1

Dan1138
Dan1138

Reputation: 1225

The statement you use to unlock the pin selector:

    OSCCONbits.IOLOCK = 0;

Is not what Microchip requires.

Because all of the writable bits in the OSCCON register are "protected" a built in compiler extension command must be used to change them.

The command to unlock the PPS looks like this:

    __builtin_write_OSCCONL(OSCCON & _OSCCON_IOLOCK_MASK);

The command to lock the PPS looks like this:

    __builtin_write_OSCCONL(OSCCON | _OSCCON_IOLOCK_MASK);

Depending on how you have set the configuration words the PPS may be configured so it cannot be unlocked, and once locked it takes a power-on-reset for the PPS to become unlocked.

Upvotes: 0

EBlake
EBlake

Reputation: 755

A few thoughts:

  • Your setting for DISSDI is enabling SDI (comment says disable)
  • TRIS register settings are not given, but the pin for SDI must be 1, and the pin for SCLK must be set to 0
  • by setting MCKLEN, your clock source is REFO - assume it is configured correctly? (e.g. ROEN=1)

Upvotes: 1

Related Questions