Reputation: 121
I am using the PIC18f46k42 microcontroller, XC8 2.3, with MPLABX/ MCC. My goal is to use the single SPI hardware peripheral to interface with multiple devices using various GPIO pins as chip selects. I have an LCD and an SD card that both need to talk to the MCU (obv not at the same time). My issue is when I try to change the SPI hardware configuration registers to switch between devices. So I tried reducing the problem and have come to this:
If I only use one spi configuration to drive the screen, it works. However if I try to close that spi connection and then reopen the same spi connection, the screen doesnt work. I believe the issue then lies in the SPI1_Open() generated by mcc.
spi1.h:
#ifndef SPI1_MASTER_H
#define SPI1_MASTER_H
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
/* SPI interfaces */
typedef enum {
SPI1_DEFAULT
} spi1_modes_t;
void SPI1_Initialize(void);
bool SPI1_Open(spi1_modes_t spi1UniqueConfiguration);
void SPI1_Close(void);
uint8_t SPI1_ExchangeByte(uint8_t data);
void SPI1_ExchangeBlock(void *block, size_t blockSize);
void SPI1_WriteBlock(void *block, size_t blockSize);
void SPI1_ReadBlock(void *block, size_t blockSize);
void SPI1_WriteByte(uint8_t byte);
uint8_t SPI1_ReadByte(void);
#endif //SPI1_H
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[] = {
{ 0x3, 0x60, 0x2, 0x3, 0 }
};
void SPI1_Initialize(void)
{
//EN disabled; LSBF MSb first; MST bus master; BMODE every byte;
SPI1CON0 = 0x03;
//SMP Middle; CKE Active to idle; CKP Idle:High, Active:Low; FST disabled; SSP active high; SDIP active high; SDOP active high;
SPI1CON1 = 0x60;
//SSET disabled; TXR required for a transfer; RXR data is not stored in the FIFO;
SPI1CON2 = 0x02;
//CLKSEL FOSC;
SPI1CLK = 0x00;
//BAUD 3;
SPI1BAUD = 0x03;
TRISCbits.TRISC1 = 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.TRISC1 = 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(!PIR2bits.SPI1RXIF);
return SPI1RXB;
}
void SPI1_ExchangeBlock(void *block, size_t blockSize)
{
uint8_t *data = block;
while(blockSize--)
{
SPI1TCNTL = 1;
SPI1TXB = *data;
while(!PIR2bits.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;
}
main.c:
#include "mcc_generated_files/mcc.h"
#include "ST7735.h"
#define led0 PORTAbits.RA7
#define led1 PORTAbits.RA6
#define led2 PORTAbits.RA5
#define led3 PORTAbits.RA4
void main(void)
{
// Initialize the device
SYSTEM_Initialize();
led0 = SPI1_Open(SPI1_DEFAULT);
led0 = SPI1CON0bits.EN;
LCD_RESET = 1;
delay_us(500);
LCD_RESET = 0;
delay_us(500);
LCD_RESET = 1;
delay_us(500);
__delay_ms(1000);
ST7735_initR();
ST7735_fillScreen(ST7735_BLACK);
while (1)
{
// Add your application code
}
}
I can include as well what's in ST7735.h/ST7735.c but I'm certain they are fine because they work just fine when spi1 is configured normally. The poor behavior only starts when I try to toggle the spi configuration which I must do in order to inevitably toggle between multiple devices. In the above code, SPI is defaulted to EN=0. I can include the version where SPI is defaulted EN=1 and uses SPI1_Close() to toggle it off. However both behave the same and this version is slightly simpler. I greatly appreciate any input you might have.
Upvotes: 0
Views: 1331
Reputation: 121
I solved the issue myself.
this is the offending function:
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.TRISC1 = spi1_configuration[spi1UniqueConfiguration].operation;
SPI1CON0bits.EN = 1;
return true;
}
return false;
}
the line :
SPI1CON2 = spi1_configuration[spi1UniqueConfiguration].con2 | (_SPI1CON2_SPI1RXR_MASK | _SPI1CON2_SPI1TXR_MASK);
should be just:
SPI1CON2 = spi1_configuration[spi1UniqueConfiguration].con2;
this became apparent when looking at the initialize function, which works when the default state is enable (i.e. SPI1CON0 = 0x83):
void SPI1_Initialize(void)
{
//EN disabled; LSBF MSb first; MST bus master; BMODE every byte;
SPI1CON0 = 0x03;
//SMP Middle; CKE Active to idle; CKP Idle:High, Active:Low; FST disabled; SSP active high; SDIP active high; SDOP active high;
SPI1CON1 = 0x60;
//SSET disabled; TXR required for a transfer; RXR data is not stored in the FIFO;
SPI1CON2 = 0x02;
//CLKSEL FOSC;
SPI1CLK = 0x00;
//BAUD 3;
SPI1BAUD = 0x03;
TRISCbits.TRISC1 = 0;
}
this function which works correctly does not "or" SPI1CON2 with (_SPI1CON2_SPI1RXR_MASK | _SPI1CON2_SPI1TXR_MASK).
when tested, this behaves correctly. credit to Oleg Mazurov, who's comment made me recheck the mcc generated code. while the mcc generated code has never failed me before, if you are doing anything sufficiently advanced with mcc, I recommend checking it carefully before assuming it is working correctly.
Upvotes: 0