Fixed three issues with the SPI driver for the LPC1549 platform:

* The TXDATCTL register was used even if only the control signals were
  modified which caused extra data to be transmitted.

* The RXDAT register does not only contain the received data, but also
  control information in bits 16 to 20. The old code did not mask out
  the control information and in rare cases that would cause the returned
  data to include too much information (i.e. received 0xaa as data but the
  function returned 0x300aa).

* The LPC1549 uses a Switch Matric (SWM) to allow any pin to have any
  function. This is not used in the old code which simply assigned
  the first instance of the SPI class to SPI0 and the second instance to
  SPI1. The third instance would result in a call to error().

  This behaviour is not at all working with real world examples where the
  SPI bus contains more than two peripherals. The third peripheral would
  cause the platform to end up in error().

  The solution is to modify the get_available_spi() function to first see
  if the MISO/MOSI/SCLK and SSEL pins are already configured for use as
  either SPI0 or SPI1. If the exact same pins are already used then the
  SPIx will be reused. If one or more pins are different then another
  SPIx will be used (or if both are alredy in use then error()). With this
  change it is now possible to do this:

  MyFlash      f(D11,D12,D13);      // Will use SPI0
  MyTemp       t(D11,D12,D13);      // Will use SPI0
  SDFileSystem s(D11,D12,D13,"sd"); // Will use SPI0
  MyDisplay    d(D11,D12,D13);      // Will use SPI0

  The old/existing code would have resulted in this

  MyFlash      f(D11,D12,D13);      // Will use SPI0
  MyTemp       t(D11,D12,D13);      // Will use SPI1
  SDFileSystem s(D11,D12,D13,"sd"); // error()
  MyDisplay    d(D11,D12,D13);      // Will never be called
pull/413/head
Anders Lindvall 2014-07-23 10:15:14 +02:00
parent 6663575abc
commit ea510ce162
1 changed files with 68 additions and 12 deletions

View File

@ -43,13 +43,69 @@ static const SWM_Map SWM_SPI_MISO[] = {
// bit flags for used SPIs
static unsigned char spi_used = 0;
static int get_available_spi(void)
static int get_available_spi(PinName mosi, PinName miso, PinName sclk, PinName ssel)
{
int i;
for (i=0; i<2; i++) {
if ((spi_used & (1 << i)) == 0)
return i;
if (spi_used == 0) {
return 0; // The first user
}
const SWM_Map *swm;
uint32_t regVal;
// Investigate if same pins as the used SPI0/1 - to be able to reuse it
for (int spi_n = 0; spi_n < 2; spi_n++) {
if (spi_used & (1<<spi_n)) {
if (sclk != NC) {
swm = &SWM_SPI_SCLK[spi_n];
regVal = LPC_SWM->PINASSIGN[swm->n] & (0xFF << swm->offset);
if (regVal != (sclk << swm->offset)) {
// Existing pin is not the same as the one we want
continue;
}
}
if (mosi != NC) {
swm = &SWM_SPI_MOSI[spi_n];
regVal = LPC_SWM->PINASSIGN[swm->n] & (0xFF << swm->offset);
if (regVal != (mosi << swm->offset)) {
// Existing pin is not the same as the one we want
continue;
}
}
if (miso != NC) {
swm = &SWM_SPI_MISO[spi_n];
regVal = LPC_SWM->PINASSIGN[swm->n] & (0xFF << swm->offset);
if (regVal != (miso << swm->offset)) {
// Existing pin is not the same as the one we want
continue;
}
}
if (ssel != NC) {
swm = &SWM_SPI_SSEL[spi_n];
regVal = LPC_SWM->PINASSIGN[swm->n] & (0xFF << swm->offset);
if (regVal != (ssel << swm->offset)) {
// Existing pin is not the same as the one we want
continue;
}
}
// The pins for the currently used SPIx are the same as the
// ones we want so we will reuse it
return spi_n;
}
}
// None of the existing SPIx pin setups match the pins we want
// so the last hope is to select one unused SPIx
if ((spi_used & 1) == 0) {
return 0;
} else if ((spi_used & 2) == 0) {
return 1;
}
// No matching setup and no free SPIx
return -1;
}
@ -58,7 +114,7 @@ static inline void spi_enable(spi_t *obj);
void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
{
int spi_n = get_available_spi();
int spi_n = get_available_spi(mosi, miso, sclk, ssel);
if (spi_n == -1) {
error("No available SPI");
}
@ -138,10 +194,10 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
obj->spi->CFG = tmp;
// select frame length
tmp = obj->spi->TXDATCTL;
tmp = obj->spi->TXCTL;
tmp &= ~(0xf << 24);
tmp |= (LEN << 24);
obj->spi->TXDATCTL = tmp;
obj->spi->TXCTL = tmp;
spi_enable(obj);
}
@ -181,14 +237,14 @@ static inline void spi_write(spi_t *obj, int value)
{
while (!spi_writeable(obj));
// end of transfer
obj->spi->TXDATCTL |= (1 << 20);
obj->spi->TXDAT = value;
obj->spi->TXCTL |= (1 << 20);
obj->spi->TXDAT = (value & 0xffff);
}
static inline int spi_read(spi_t *obj)
{
while (!spi_readable(obj));
return obj->spi->RXDAT;
return obj->spi->RXDAT & 0xffff; // Only the lower 16 bits contain data
}
int spi_busy(spi_t *obj)
@ -210,7 +266,7 @@ int spi_slave_receive(spi_t *obj)
int spi_slave_read(spi_t *obj)
{
return obj->spi->RXDAT;
return obj->spi->RXDAT & 0xffff; // Only the lower 16 bits contain data
}
void spi_slave_write(spi_t *obj, int value)