• AVR Freaks

Helpful ReplyHot!Can't get SPI1 to work with MCC on PIC18F27Q43

Author
Balth
New Member
  • Total Posts : 8
  • Reward points : 0
  • Joined: 2020/11/19 14:48:48
  • Location: 0
  • Status: offline
2020/11/20 01:06:49 (permalink)
0

Can't get SPI1 to work with MCC on PIC18F27Q43

Hi,

I'm requesting your help for a strange problem I have : I'm working on a PIC18F27Q43 and I need to use SPI1 as Master 0, 250kHz clock (Fosc/128) on RC2 = SS, RC3 = SCK, RC4 = MISO1 and RC5 = MOSI.

I used MCC to generate the proper configuration and libraries, but strangely when I try to use SPI1 and to visualize SCK or MOSI on my oscilloscope, I have nothing.

MCC generated files are perfectly working for ADCC and UART1, strangely.

I activated SPI1 in PMD6 bits. Did I miss some bits to start SPI1 module or SPI1 clock ?

Other strange thing, I tried a Microchip example project for a PI18F57Q43 (same PIC with more pins) using SPI (https://github.com/microc...audio-record-playback) but SPI doesn't work neither.


I'm really stucked, I already double checked my code, but can't find the error. I will really appreciate your advices and help

Below my codes :


main.c :

void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();

    // If using interrupts in PIC18 High/Low Priority Mode you need to enable the Global High and Low Interrupts
    // If using interrupts in PIC Mid-Range Compatibility Mode you need to enable the Global Interrupts
    // Use the following macros to:

    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();
    
    // Blink on start
    for (int i = 0; i < 2; i++) {
        LED_LED_SetHigh();
        __delay_ms(200);
        LED_LED_SetLow();
        __delay_ms(200);
    }

    while (1)
    {
        SPI1_Open(SPI1_DEFAULT);
        UART1_Write(0x55);
        SPI1_WriteByte(0xFF);
        __delay_ms(1000);
        SPI1_Close();
    }
}



pin_manager.c :

void PIN_MANAGER_Initialize(void)
{
    /**
    LATx registers
    */
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;

    /**
    TRISx registers
    */
    TRISA = 0x3F;
    TRISB = 0xFD;
    TRISC = 0x94;

    /**
    ANSELx registers
    */
    ANSELC = 0x04;
    ANSELB = 0x10;
    ANSELA = 0x20;

    /**
    WPUx registers
    */
    WPUE = 0x00;
    WPUB = 0x00;
    WPUA = 0x00;
    WPUC = 0x00;

    /**
    RxyI2C registers
    */
    RB1I2C = 0x00;
    RB2I2C = 0x00;
    RC3I2C = 0x00;
    RC4I2C = 0x00;

    /**
    ODx registers
    */
    ODCONE = 0x00;
    ODCONA = 0x00;
    ODCONB = 0x00;
    ODCONC = 0x00;

    /**
    SLRCONx registers
    */
    SLRCONA = 0xFF;
    SLRCONB = 0xFF;
    SLRCONC = 0xFF;

    /**
    INLVLx registers
    */
    INLVLA = 0xFF;
    INLVLB = 0xFF;
    INLVLC = 0xFF;
    INLVLE = 0x0F;





   
    
 
    SPI1SCKPPS = 0x13; //RC3->SPI1:SCK1;
    RC3PPS = 0x31; //RC3->SPI1:SCK1;
    RC5PPS = 0x32; //RC5->SPI1:SDO1;
    RC6PPS = 0x20; //RC6->UART1:TX1;
    U1RXPPS = 0x17; //RC7->UART1:RX1;
    SPI1SDIPPS = 0x14; //RC4->SPI1:SDI1;
}



device_config.c :

// Configuration bits: selected in the GUI

// CONFIG1
#pragma config FEXTOSC = OFF // External Oscillator Selection->Oscillator not enabled
#pragma config RSTOSC = HFINTOSC_64MHZ // Reset Oscillator Selection->HFINTOSC with HFFRQ = 64 MHz and CDIV = 1:1

// CONFIG2
#pragma config CLKOUTEN = OFF // Clock out Enable bit->CLKOUT function is disabled
#pragma config PR1WAY = ON // PRLOCKED One-Way Set Enable bit->PRLOCKED bit can be cleared and set only once
#pragma config CSWEN = ON // Clock Switch Enable bit->Writing to NOSC and NDIV is allowed
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable bit->Fail-Safe Clock Monitor enabled

// CONFIG3
#pragma config MCLRE = EXTMCLR // MCLR Enable bit->If LVP = 0, MCLR pin is MCLR; If LVP = 1, RE3 pin function is MCLR
#pragma config PWRTS = PWRT_OFF // Power-up timer selection bits->PWRT is disabled
#pragma config MVECEN = OFF // Multi-vector enable bit->Interrupt contoller does not use vector table to prioritze interrupts
#pragma config IVT1WAY = ON // IVTLOCK bit One-way set enable bit->IVTLOCKED bit can be cleared and set only once
#pragma config LPBOREN = OFF // Low Power BOR Enable bit->Low-Power BOR disabled
#pragma config BOREN = SBORDIS // Brown-out Reset Enable bits->Brown-out Reset enabled , SBOREN bit is ignored

// CONFIG4
#pragma config BORV = VBOR_2P85 // Brown-out Reset Voltage Selection bits->Brown-out Reset Voltage (VBOR) set to 2.8V
#pragma config ZCD = OFF // ZCD Disable bit->ZCD module is disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON
#pragma config PPS1WAY = ON // PPSLOCK bit One-Way Set Enable bit->PPSLOCKED bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle
#pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit->Stack full/underflow will cause Reset
#pragma config LVP = ON // Low Voltage Programming Enable bit->Low voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignored
#pragma config XINST = OFF // Extended Instruction Set Enable bit->Extended Instruction Set and Indexed Addressing Mode disabled

// CONFIG5
#pragma config WDTCPS = WDTCPS_10 // WDT Period selection bits->Divider ratio 1:32768
#pragma config WDTE = SWDTEN // WDT operating mode->WDT enabled/disabled by SWDTEN bit

// CONFIG6
#pragma config WDTCWS = WDTCWS_7 // WDT Window Select bits->window always open (100%); software control; keyed access not required
#pragma config WDTCCS = LFINTOSC // WDT input clock selector->WDT reference clock is the 31.0 kHz LFINTOSC

// CONFIG7
#pragma config BBSIZE = BBSIZE_512 // Boot Block Size selection bits->Boot Block size is 512 words
#pragma config BBEN = OFF // Boot Block enable bit->Boot block disabled
#pragma config SAFEN = OFF // Storage Area Flash enable bit->SAF disabled
#pragma config DEBUG = OFF // Background Debugger->Background Debugger disabled

// CONFIG8
#pragma config WRTB = OFF // Boot Block Write Protection bit->Boot Block not Write protected
#pragma config WRTC = OFF // Configuration Register Write Protection bit->Configuration registers not Write protected
#pragma config WRTD = OFF // Data EEPROM Write Protection bit->Data EEPROM not Write protected
#pragma config WRTSAF = OFF // SAF Write protection bit->SAF not Write Protected
#pragma config WRTAPP = OFF // Application Block write protection bit->Application Block not write protected

// CONFIG10
#pragma config CP = OFF // PFM and Data EEPROM Code Protection bit->PFM and Data EEPROM code protection disabled



spi1.c :

#include "spi1.h"
#include <xc.h>

typedef struct {
    uint8_t con0;
    uint8_t con1;
    uint8_t con2;
    uint8_t baud;
    uint8_t operation;
} spi1_configuration_t;


//con0 == SPIxCON0, con1 == SPIxCON1, con2 == SPIxCON2, baud == SPIxBAUD, operation == Master/Slave
static const spi1_configuration_t spi1_configuration[] = {
    { 0x2, 0x40, 0x0, 0x7f, 0 }
};

void SPI1_Initialize(void)
{
    //EN disabled; LSBF MSb first; MST bus slave; BMODE last byte;
    SPI1CON0 = 0x02;
    //SMP Middle; CKE Active to idle; CKP Idle:Low, Active:High; FST disabled; SSP active high; SDIP active high; SDOP active high;
    SPI1CON1 = 0x40;
    //SSET disabled; TXR not required for a transfer; RXR data is not stored in the FIFO;
    SPI1CON2 = 0x00;
    //CLKSEL FOSC;
    SPI1CLK = 0x00;
    //BAUD 127;
    SPI1BAUD = 0x7F;
    TRISCbits.TRISC3 = 0;
}

bool SPI1_Open(spi1_modes_t spi1UniqueConfiguration)
{
    if(!SPI1CON0bits.EN)
    {
        SPI1CON0 = spi1_configuration[spi1UniqueConfiguration].con0;
        SPI1CON1 = spi1_configuration[spi1UniqueConfiguration].con1;
        SPI1CON2 = spi1_configuration[spi1UniqueConfiguration].con2 | (_SPI1CON2_SPI1RXR_MASK | _SPI1CON2_SPI1TXR_MASK);
        SPI1CLK = 0x00;
        SPI1BAUD = spi1_configuration[spi1UniqueConfiguration].baud;
        TRISCbits.TRISC3 = spi1_configuration[spi1UniqueConfiguration].operation;
        SPI1CON0bits.EN = 1;
        return true;
    }
    return false;
}

void SPI1_Close(void)
{
    SPI1CON0bits.EN = 0;
}

uint8_t SPI1_ExchangeByte(uint8_t data)
{
    SPI1TCNTL = 1;
    SPI1TXB = data;
    while(!PIR3bits.SPI1RXIF);
    return SPI1RXB;
}

void SPI1_ExchangeBlock(void *block, size_t blockSize)
{
    uint8_t *data = block;
    while(blockSize--)
    {
        SPI1TCNTL = 1;
        SPI1TXB = *data;
        while(!PIR3bits.SPI1RXIF);
        *data++ = SPI1RXB;
    }
}

// Half Duplex SPI Functions
void SPI1_WriteBlock(void *block, size_t blockSize)
{
    uint8_t *data = block;
    while(blockSize--)
    {
        SPI1_ExchangeByte(*data++);
    }
}

void SPI1_ReadBlock(void *block, size_t blockSize)
{
    uint8_t *data = block;
    while(blockSize--)
    {
        *data++ = SPI1_ExchangeByte(0);
    }
}

void SPI1_WriteByte(uint8_t byte)
{
    SPI1TXB = byte;
}

uint8_t SPI1_ReadByte(void)
{
    return SPI1RXB;
}

#1
mbrowning
USNA79
  • Total Posts : 1846
  • Reward points : 0
  • Joined: 2005/03/16 14:32:56
  • Location: Melbourne, FL
  • Status: online
Re: Can't get SPI1 to work with MCC on PIC18F27Q43 2020/11/20 08:34:34 (permalink) ☄ Helpfulby Balth 2020/11/27 00:53:38
+1 (1)
I think your main problem is setting BMODE=0. You need TXR=RXR=BMODE=1 if you aren't going to load the transfer counter. See Table 35-1.
 
Every time I look at MCC code I like it less. What convoluted SPI code, and all magic numbers for configuration.
SPI1_Initialize() presumably called by SYSTEM_Initialize(), and then everything rewritten by SPI1_Open() which is silly - just initialize and enable SPI once and don't open and close on every byte.
 
RC2 is chip select - but you have it set as input and analog. That makes no sense. Of course you don't use it which is another issue.
I use K42 parts and not Q43 yet, but the SPI ports are nearly identical. I've never set both PPS input and output registers for SPI clock. Input for slave and output for master, but never both. Maybe Q43 is different, but I doubt it.
MVECEN = OFF - mistake. Use the vectored interrupts - much easier than the old way.
#2
ric
Super Member
  • Total Posts : 29435
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: online
Re: Can't get SPI1 to work with MCC on PIC18F27Q43 2020/11/20 13:32:32 (permalink)
+2 (2)
mbrowning
Every time I look at MCC code I like it less. What convoluted SPI code, and all magic numbers for configuration.
SPI1_Initialize() presumably called by SYSTEM_Initialize(), and then everything rewritten by SPI1_Open() which is silly - just initialize and enable SPI once and don't open and close on every byte.

That's what happens when you try to allow for every possible variation.
That will be a side effect of allowing access to multiple SPI devices on the same bus, possibly using different SPI modes each.
I'm the same, I think MCC is "a horse designed by a committee", severely compromising its design...
 

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#3
Balth
New Member
  • Total Posts : 8
  • Reward points : 0
  • Joined: 2020/11/19 14:48:48
  • Location: 0
  • Status: offline
Re: Can't get SPI1 to work with MCC on PIC18F27Q43 2020/11/25 09:07:17 (permalink)
0
Thank you for your help, I have a clock now ;)
I still don't really understand how does full-duplex mode operates...
Here is my SPI "init" code :
 
    // Configures pins inouts
    FLASH_SS_TRIS = 0;
    FLASH_SCK_TRIS = 0;
    FLASH_MOSI_TRIS = 0;
    FLASH_MISO_TRIS = 1;
    
    FLASH_SS_LAT = 0;
    FLASH_SCK_LAT = 0;
    FLASH_MOSI_LAT = 0;
    FLASH_MISO_LAT = 0;
    
    // Reset SPI1 configuration
    SPI1CON0 = 0;
    SPI1CON1 = 0;
    SPI1BAUD = 0;
    
    // MSB First
    SPI1CON0bits.LSBF = 0;
    // Master mode
    SPI1CON0bits.MST = 1;
    // Bit length mode select
    SPI1CON0bits.BMODE = 1;
    
    // Sample phase control at middle of output time
    SPI1CON1bits.SMP = 0;
    // CLK edge active to idle
    SPI1CON1bits.CKE = 1;
    // Clock polarity low level
    SPI1CON1bits.CKP = 0;
    // Fast start disabled
    SPI1CON1bits.FST = 0;
    // Slave select active low
    SPI1CON1bits.SSP = 1;
    // MISO active high
    SPI1CON1bits.SDIP = 0;
    // MOSI active high
    SPI1CON1bits.SDOP = 0;
    
    // SS_out driver active when transfer counter is not zero
    SPI1CON2bits.SSET = 0;
    // TxFIFO required for transfer
    SPI1CON2bits.TXR = 1;
    // Transfers suspended when RxFIFO is full
    SPI1CON2bits.RXR = 1;
    
    // Clock is Fosc
    SPI1CLK = 0b0000;
    
    // Clock divider = 127 -> SCK = 500kHz
    SPI1BAUD = 0x7F;
    
    // ENABLE SPI1
    SPI1CON0bits.EN = 1;

 
How do I transfer n bytes to my slave, and receive it when transfer is finished ?
For now I tried with ExchangeBlock and ExchangeByte from MCC, but on my scope, data buses are random or transfers are not starting.
 
Thank you again !
#4
Aussie Susan
Super Member
  • Total Posts : 3807
  • Reward points : 0
  • Joined: 2008/08/18 22:20:40
  • Location: Melbourne, Australia
  • Status: offline
Re: Can't get SPI1 to work with MCC on PIC18F27Q43 2020/11/25 18:33:21 (permalink)
+1 (3)
SPI is an exchange protocol - for every value the master sends to the slave, the slave sends a value to the master at the same time.
Therefore the slave has to be programmed so that it will always have the appropriate value to send back on the next exchange and then read the value it has been sent after the exchange completes.
Similarly the master must send the next value to the slave and read the value that has been sent back from the slave. Further, because the master controls when the exchanges complete, it may need to make sure that the slave has enough time to process a value sent to it before starting the next exchange if it expects a return value.
As for what those values are that you send/receive, you need to understand what the slave is expecting (either in the data sheet or by your design).
Susan
#5
Balth
New Member
  • Total Posts : 8
  • Reward points : 0
  • Joined: 2020/11/19 14:48:48
  • Location: 0
  • Status: offline
Re: Can't get SPI1 to work with MCC on PIC18F27Q43 2020/11/26 09:37:32 (permalink)
0
So now I think it's working :
I have a clock, a MOSI a Slave Select and sometimes a MISO. I think the SPI is working, the next subject is getting the SPI slave to work... new challenge !
My slave is a FLASH memory, I succeed to read it's par ID so it's a good point.
The most difficult will be to write/read on it, but the original issue is resolved.
 
I attach my WriteByte and WriteBlock codes, if it can help somebody in the future
 
 
uint8_t SPIExchangeByte(uint8_t data) {
    SPI1TXB = data;
    while(!PIR3bits.SPI1RXIF);
    
    Nop();
    
    uint8_t ret = SPI1RXB;
    return ret;
}

void SPIExchangeBlock(void *block, size_t blockSize) {
    
    uint8_t *data = block;
    uint8_t dummy = SPI1RXB;
    
    SPI1CON2bits.SSET = 1;
    while(blockSize--)
    {
        SPI1TXB = *data;
        while(!PIR3bits.SPI1TXIF);
        while(!PIR3bits.SPI1RXIF);
        *data++ = SPI1RXB;
    }
    SPI1CON2bits.SSET = 0;
    
    Nop();
}

 
Thanks to everyone who helped me !
#6
ric
Super Member
  • Total Posts : 29435
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: online
Re: Can't get SPI1 to work with MCC on PIC18F27Q43 2020/11/26 12:20:12 (permalink) ☄ Helpfulby Balth 2020/11/27 00:53:26
+2 (2)
Rather than

    uint8_t ret = SPI1RXB;
    return ret;

You can just do
    return SPI1RXB;

 
There's no need to check SPI1TXIF in the WriteBlock routine. Just checking SPI1RXIF will keep you synchronised.

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#7
Jump to:
© 2021 APG vNext Commercial Version 4.5