rt-thread/bsp/nxp/lpc/lpc176x/drivers/spi.c

238 lines
6.5 KiB
C

/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
*/
#include "LPC17xx.h" /* LPC17xx definitions */
#include "spi.h"
/* bit definitions for register SSPCR0. */
#define SSPCR0_DSS 0
#define SSPCR0_CPOL 6
#define SSPCR0_CPHA 7
#define SSPCR0_SCR 8
/* bit definitions for register SSPCR1. */
#define SSPCR1_SSE 1
/* bit definitions for register SSPSR. */
#define SSPSR_TFE 0
#define SSPSR_TNF 1
#define SSPSR_RNE 2
#define SSPSR_RFF 3
#define SSPSR_BSY 4
/* Local functions */
static uint8_t LPC17xx_SPI_SendRecvByte (uint8_t byte_s);
/* Initialize the SSP0, SSP0_PCLK=CCLK=72MHz */
void LPC17xx_SPI_Init (void)
{
uint32_t dummy;
dummy = dummy; // avoid warning
#if 0
/* Initialize and enable the SSP0 Interface module. */
LPC_SC->PCONP |= (1 << 21); /* Enable power to SSPI0 block */
/* SSEL is GPIO, output set to high. */
LPC_GPIO0->FIODIR |= (1<<16); /* P0.16 is output */
LPC_PINCON->PINSEL1 &= ~(3<<0); /* P0.16 SSEL (used as GPIO) */
LPC17xx_SPI_DeSelect (); /* set P0.16 high (SSEL inactiv) */
/* SCK, MISO, MOSI are SSP pins. */
LPC_PINCON->PINSEL0 &= ~(3UL<<30); /* P0.15 cleared */
LPC_PINCON->PINSEL0 |= (2UL<<30); /* P0.15 SCK0 */
LPC_PINCON->PINSEL1 &= ~((3<<2) | (3<<4)); /* P0.17, P0.18 cleared */
LPC_PINCON->PINSEL1 |= ((2<<2) | (2<<4)); /* P0.17 MISO0, P0.18 MOSI0 */
#else
LPC_SC->PCONP |= (1 << 21); /* Enable power to SSPI0 block */
/* SSEL is GPIO, output set to high. */
LPC_GPIO1->FIODIR |= (1<<21); /* P1.21 is output */
LPC_GPIO1->FIOPIN |= (1<<21); /* set P1.21 high (SSEL inact.)*/
LPC_PINCON->PINSEL3 &= ~(0<<10); /* P1.21 SSEL (used as GPIO) */
/* P3.26 is SD Card Power Supply Enable Pin */
LPC_GPIO3->FIODIR |= (1<<26); /* P3.26 is output */
LPC_GPIO3->FIOPIN &= ~(1<<26); /* set P3.26 low(enable power) */
/* SCK, MISO, MOSI are SSP pins. */
LPC_PINCON->PINSEL3 &= ~(3UL<<8); /* P1.20 cleared */
LPC_PINCON->PINSEL3 |= (3UL<<8); /* P1.20 SCK0 */
LPC_PINCON->PINSEL3 &= ~((3<<14) | (3<<16)); /* P1.23, P1.24 cleared */
LPC_PINCON->PINSEL3 |= ((3<<14) | (3<<16)); /* P1.23 MISO0, P1.24 MOSI0 */
#endif
/* PCLK_SSP0=CCLK */
LPC_SC->PCLKSEL1 &= ~(3<<10); /* PCLKSP0 = CCLK/4 (18MHz) */
LPC_SC->PCLKSEL1 |= (1<<10); /* PCLKSP0 = CCLK (72MHz) */
LPC_SSP0->CR0 = 0x0007; /* 8Bit, CPOL=0, CPHA=0 */
LPC_SSP0->CR1 = 0x0002; /* SSP0 enable, master */
LPC17xx_SPI_SetSpeed (SPI_SPEED_400kHz);
/* wait for busy gone */
while( LPC_SSP0->SR & ( 1 << SSPSR_BSY ) );
/* drain SPI RX FIFO */
while( LPC_SSP0->SR & ( 1 << SSPSR_RNE ) )
{
dummy = LPC_SSP0->DR;
}
}
/* Close SSP0 */
void LPC17xx_SPI_DeInit( void )
{
// disable SPI
LPC_SSP0->CR1 = 0;
#if 0
// Pins to GPIO
LPC_PINCON->PINSEL0 &= ~(3UL<<30);
LPC_PINCON->PINSEL1 &= ~((3<<2) | (3<<4));
#else
LPC_PINCON->PINSEL3 &= ~(3UL<<8); /* P1.20 cleared */
LPC_PINCON->PINSEL3 &= ~((3<<14) | (3<<16)); /* P1.23, P1.24 cleared */
#endif
// disable SSP power
LPC_SC->PCONP &= ~(1 << 21);
}
/* Set a SSP0 clock speed to desired value. */
void LPC17xx_SPI_SetSpeed (uint8_t speed)
{
speed &= 0xFE;
if ( speed < 2 ) {
speed = 2 ;
}
LPC_SSP0->CPSR = speed;
}
/* SSEL: low */
void LPC17xx_SPI_Select ()
{
#if 0
LPC_GPIO0->FIOPIN &= ~(1<<16);
#else
LPC_GPIO1->FIOPIN &= ~(1<<21); /* SSEL is GPIO, set to high. */
#endif
}
/* SSEL: high */
void LPC17xx_SPI_DeSelect ()
{
#if 0
LPC_GPIO0->FIOPIN |= (1<<16);
#else
LPC_GPIO1->FIOPIN |= (1<<21); /* SSEL is GPIO, set to high. */
#endif
}
/* Send one byte then recv one byte of response. */
static uint8_t LPC17xx_SPI_SendRecvByte (uint8_t byte_s)
{
uint8_t byte_r;
LPC_SSP0->DR = byte_s;
while (LPC_SSP0->SR & (1 << SSPSR_BSY) /*BSY*/); /* Wait for transfer to finish */
byte_r = LPC_SSP0->DR;
return byte_r; /* Return received value */
}
/* Send one byte */
void LPC17xx_SPI_SendByte (uint8_t data)
{
LPC17xx_SPI_SendRecvByte (data);
}
/* Recv one byte */
uint8_t LPC17xx_SPI_RecvByte ()
{
return LPC17xx_SPI_SendRecvByte (0xFF);
}
/* Release SSP0 */
void LPC17xx_SPI_Release (void)
{
LPC17xx_SPI_DeSelect ();
LPC17xx_SPI_RecvByte ();
}
#if USE_FIFO
/* on LPC17xx the FIFOs have 8 elements which each can hold up to 16 bits */
#define FIFO_ELEM 8
/* Receive btr (must be multiple of 4) bytes of data and store in buff. */
void LPC17xx_SPI_RecvBlock_FIFO (uint8_t *buff, uint32_t btr)
{
uint32_t hwtr, startcnt, i, rec;
hwtr = btr/2; /* byte number in unit of short */
if ( btr < FIFO_ELEM ) {
startcnt = hwtr;
} else {
startcnt = FIFO_ELEM;
}
LPC_SSP0 -> CR0 |= 0x0f; /* DSS to 16 bit */
for ( i = startcnt; i; i-- ) {
LPC_SSP0 -> DR = 0xffff; /* fill TX FIFO, prepare clk for receive */
}
do {
while ( !(LPC_SSP0->SR & ( 1 << SSPSR_RNE ) ) ) {
// wait for data in RX FIFO (RNE set)
}
rec = LPC_SSP0->DR;
if ( i < ( hwtr - startcnt ) ) {
LPC_SSP0->DR = 0xffff; /* fill TX FIFO, prepare clk for receive */
}
*buff++ = (uint8_t)(rec>>8);
*buff++ = (uint8_t)(rec);
i++;
} while ( i < hwtr );
LPC_SSP0->CR0 &= ~0x08; /* DSS to 8 bit */
}
/* Send 512 bytes of data block (stored in buff). */
void LPC17xx_SPI_SendBlock_FIFO (const uint8_t *buff)
{
uint32_t cnt;
uint16_t data;
LPC_SSP0->CR0 |= 0x0f; /* DSS to 16 bit */
/* fill the FIFO unless it is full */
for ( cnt = 0; cnt < ( 512 / 2 ); cnt++ )
{
/* wait for TX FIFO not full (TNF) */
while ( !( LPC_SSP0->SR & ( 1 << SSPSR_TNF ) ) );
data = (*buff++) << 8;
data |= *buff++;
LPC_SSP0->DR = data;
}
/* wait for BSY gone */
while ( LPC_SSP0->SR & ( 1 << SSPSR_BSY ) );
/* drain receive FIFO */
while ( LPC_SSP0->SR & ( 1 << SSPSR_RNE ) ) {
data = LPC_SSP0->DR;
}
LPC_SSP0->CR0 &= ~0x08; /* DSS to 8 bit */
}
#endif /* USE_FIFO */