Hello ,
I started developing SPI slave driver taking BCM2835(Mike McCauley) drivers as base(from userland) since, I didn't find any working examples for SPI slave in RPi 2 Model B(J8-40Pin) so far.
I have implemented with few assumptions from the documentation. It would be great that someone clarifies them.
Currently, the SW polls for the "RX FIFO Empty flag" in FR register to see if there is any incoming data. When there is data from master, I "assume" that the flag goes to '0'.
Now, the DR register will be masked with 0xFF(for lower byte) to read the incoming data.
My initial experiments shows no data reception. It always prints the message "Receive buffer is Empty".
Following questions are popped up while development.
1. How much is the size of the FIFO buffers? Its not explicitly mentioned in the docu.
The Data Register(DR) simply mentions that reading lower 8 bytes will read the data from the RX FIFO buffer and writing to this 8 bytes writes to Tx FIFO.
Why is not mentioned separately as like in the master?
2. The clock phase and polarity configuration simply says "SPI related". In which mode should the master communicate with slave Pi??
I have made the HW connections btwn Master Pi and Slave Pi as follows. I can give the complete files when someone wants to have a deeper look.
##############################
##Pin numbers on J8 connector#
##############################
[b]Signal Master Slave[/b]
Clock Pin 23 --> Pin 35
MOSI Pin 19 <---> Pin 12
MISO Pin 21 <---> Pin 38
CE Pin 24 ---> Pin 40
##############################
Following are the code snippets,
[code]
1.#define BCM2835_BSC_SPI_SLV_BASE 0x214000
bcm2835_spislv = bcm2835_peripherals + BCM2835_BSC_SPI_SLV_BASE/4; /*SPI Slave base address*/
/* Defines for SPI Slave
GPIO register offsets from BCM2835_SPI_SLAVE_BASE.
Offsets into the SPI Slave Peripheral block in bytes as per Chapter 11 SPI/BSC Slave Register Map
*/
/* BSC SLAVE register offsets */
#define BCM2835_SPI_SLV_DR 0x00
#define BCM2835_SPI_SLV_RSR 0x04
#define BCM2835_SPI_SLV_SLV 0x08
#define BCM2835_SPI_SLV_CR 0x0c
#define BCM2835_SPI_SLV_FR 0x10
#define BCM2835_SPI_SLV_IFLS 0x14
#define BCM2835_SPI_SLV_IMSC 0x18
#define BCM2835_SPI_SLV_RIS 0x1c
#define BCM2835_SPI_SLV_MIS 0x20
#define BCM2835_SPI_SLV_ICR 0c24
#define BCM2835_SPI_SLV_DMACR 0x28
#define BCM2835_SPI_SLV_TDR 0x2c
#define BCM2835_SPI_SLV_GPUSTAT 0x30
#define BCM2835_SPI_SLV_HCTRL 0x34
#define BCM2835_SPI_SLV_DEBUG1 0x38
#define BCM2835_SPI_SLV_DEBUG2 0x3c
/* Bitfields in DR */
#define BCM2835_SPI_SLV_DR_RXFLEVEL_MASK 0xF8000000
#define BCM2835_SPI_SLV_DR_TXFLEVEL_MASK 0x07c00000
#define BCM2835_SPI_SLV_DR_RXBUSY 0x00200000
#define BCM2835_SPI_SLV_DR_TXFE 0x00100000 // Tx FIFO Empty
#define BCM2835_SPI_SLV_DR_RXFF 0x00080000 // Rx FIFO Full
#define BCM2835_SPI_SLV_DR_TXFF 0x00040000 // Tx FIFO Full
#define BCM2835_SPI_SLV_DR_RXFE 0x00020000 // Rx FIFO Empty
#define BCM2835_SPI_SLV_DR_TXBUSY 0x00010000 // Transmit Busy
#define BCM2835_SPI_SLV_DR_DATA_MASK 0x000000FF // Data Mask-1B
/* Registers RSR and Slave are NOT Used/Required!*/
#define BCM2835_SPI_SLV_RSR_UE (1 << 1)
#define BCM2835_SPI_SLV_RSR_OE (1 << 0)
/*Bitfields in CR*/
#define BCM2835_SPI_SLV_CR_INVTXF 0x00004000
#define BCM2835_SPI_SLV_CR_HOSTCRTEN 0x00001000
#define BCM2835_SPI_SLV_CR_TESTFIFO 0x00000800
#define BCM2835_SPI_SLV_CR_INVRXF 0x00000800
#define BCM2835_SPI_SLV_CR_RXE 0x00000200
#define BCM2835_SPI_SLV_CR_TXE 0x00000100
#define BCM2835_SPI_SLV_CR_BRK 0x00000080
#define BCM2835_SPI_SLV_CR_CPOL 0x00000010
#define BCM2835_SPI_SLV_CR_CPHA 0x00000008
#define BCM2835_SPI_SLV_CR_I2C 0x00000004
#define BCM2835_SPI_SLV_CR_SPI 0x00000002
#define BCM2835_SPI_SLV_CR_EN 0x00000001
/*Bitfields in FR*/
#define BCM2835_SPI_SLV_FR_RXFLEVEL(reg) (((reg) >> 11) & 0x1F)
#define BCM2835_SPI_SLV_FR_TXFLEVEL(reg) (((reg) >> 6) & 0x1F)
#define BCM2835_SPI_SLV_FR_RXBUSY (1 << 5)
#define BCM2835_SPI_SLV_FR_TXFE (1 << 4)
#define BCM2835_SPI_SLV_FR_RXFF (1 << 3)
#define BCM2835_SPI_SLV_FR_TXFF (1 << 2)
#define BCM2835_SPI_SLV_FR_RXFE (1 << 1)
#define BCM2835_SPI_SLV_FR_TXBUSY (1 << 0)
/* Bitfields in IMSC */
#define BCM2835_SPI_SLV_IMSC_OEIM 0x00000008
#define BCM2835_SPI_SLV_IMSC_BEIM 0x00000004
#define BCM2835_SPI_SLV_IMSC_TXIM 0x00000002
#define BCM2835_SPI_SLV_IMSC_RXIM 0x00000001
/* Bitfields in RIS */
#define BCM2835_SPI_SLV_RIS_OERIS 0x00000008
#define BCM2835_SPI_SLV_RIS_BERIS 0x00000004
#define BCM2835_SPI_SLV_RIS_TXRIS 0x00000002
#define BCM2835_SPI_SLV_RIS_RXRIS 0x00000001
/* Bitfields in MIS */
#define BCM2835_SPI_SLV_MIS_OEMIS 0x00000008
#define BCM2835_SPI_SLV_MIS_BEMIS 0x00000004
#define BCM2835_SPI_SLV_MIS_TXMIS 0x00000002
#define BCM2835_SPI_SLV_MIS_RXMIS 0x00000001
/* Bitfields in ICR - Interrupt clear Register*/
#define BCM2835_SPI_SLV_ICR_OEIC 0x00000008
#define BCM2835_SPI_SLV_ICR_BEIC 0x00000004
#define BCM2835_SPI_SLV_ICR_TXIC 0x00000002
#define BCM2835_SPI_SLV_ICR_RXIC 0x00000001
[/code]
2.
[code]###################################################################################################
###########################Slave Init/ Begin function definition###################################
###################################################################################################
void bcm2835_spi_slv_begin(void)
{
volatile uint32_t* paddr;
/* Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them */
bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_12, BCM2835_GPIO_FSEL_ALT3); /* MOSI */
bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_35, BCM2835_GPIO_FSEL_ALT3); /* CLK */
bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_38, BCM2835_GPIO_FSEL_ALT3); /* MISO */
bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_40, BCM2835_GPIO_FSEL_ALT3); /* CE */
/*Get the base address to Control Register(CR) and Reset the CR to 0*/
paddr = bcm2835_spislv + BCM2835_SPI_SLV_CR/4;
bcm2835_peri_write(paddr, 0); /* All 0s */
/*Enable Device*/
bcm2835_peri_set_bits(paddr, BCM2835_SPI_SLV_CR_EN, BCM2835_SPI_SLV_CR_EN);
/*Set Clock Polarity and phase*/
bcm2835_peri_set_bits(paddr, BCM2835_SPI_SLV_CR_CPOL, BCM2835_SPI_SLV_CR_CPOL);
bcm2835_peri_set_bits(paddr, BCM2835_SPI_SLV_CR_CPHA, BCM2835_SPI_SLV_CR_CPHA);
/*Disable I2C behaviour*/
bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI_SLV_CR_I2C);
/*Enable SPI Mode*/
bcm2835_peri_set_bits(paddr, BCM2835_SPI_SLV_CR_SPI, BCM2835_SPI_SLV_CR_SPI);
#if (DEBUG_SPI_SLV==1)
if(bcm2835_peri_read(paddr) == 27)
printf("Slave Init done successfully now.\n");
#endif
}
[/code]
[code]
3.###################################################################################################
##################################Slave Read function definition###################################
###################################################################################################
/* Read a stream of bytes from SPI Master */
uint8_t bcm2835_spislv_read(uint8_t* buffer_rx, uint8_t nCount)
{
volatile uint32_t* ErrReg = bcm2835_spislv + BCM2835_SPI_SLV_RSR/4;
volatile uint32_t* CtrlReg = bcm2835_spislv + BCM2835_SPI_SLV_CR/4;
volatile uint32_t* DataReg = bcm2835_spislv + BCM2835_SPI_SLV_DR/4;
volatile uint32_t* FlagReg = bcm2835_spislv + BCM2835_SPI_SLV_FR/4;
uint8_t nresult;
bcm2835_peri_write(ErrReg, 0); // Clear the OE and UE errors if any
if (nCount == 0)
{
return -1; // Read 0 bytes
}
uint8_t* pData= ( uint8_t*)buffer_rx;
if(pData == 0)
{
printf("Buffer Not allocated!\n");
return -1;
}
//Enable reception
bcm2835_peri_set_bits(CtrlReg, BCM2835_SPI_SLV_CR_RXE, BCM2835_SPI_SLV_CR_RXE);
//check if the reception is enabled or not
if((bcm2835_peri_read(CtrlReg) & BCM2835_SPI_SLV_CR_RXE) == BCM2835_SPI_SLV_CR_RXE)
printf("Reception enabled");
while (nCount-- > 0)
{
while (bcm2835_peri_read(FlagReg) & BCM2835_SPI_SLV_FR_RXFE)
{
// do nothing // When the data comes from Master will this flag be set to ZERO by HW?
printf("Recieve buffer is Empty\n");
}
if(bcm2835_peri_read(ErrReg) & BCM2835_SPI_SLV_RSR_OE)
{
printf("Over Run Error 1\n");
nresult= -1;
break;
}
// Read the DR(Data Register) and mask for the the data
*pData++ = bcm2835_peri_read(DataReg) & BCM2835_SPI_SLV_DR_DATA_MASK;
nresult++;
}
if(nresult > 0)
{
// Wait for transfer to Stop
while(bcm2835_peri_read(DataReg) & BCM2835_SPI_SLV_DR_RXBUSY)
{
if(bcm2835_peri_read(ErrReg) & BCM2835_SPI_SLV_RSR_OE)
{
printf("Over Run Error 2\n");
nresult = -1;
break;
}
}
}
// Disable reception
bcm2835_peri_set_bits(CtrlReg, 0, BCM2835_SPI_SLV_CR_RXE);
return nresult;
}
[/code]
Your findings are highly helpful and appreciated
Thank you in advance