2011-05-15 11:27:49 +00:00
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@file ssp.c
|
|
|
|
|
@author K. Townsend (microBuilder.eu)
|
|
|
|
|
@date 22 March 2010
|
|
|
|
|
@version 0.10
|
|
|
|
|
|
|
|
|
|
@section DESCRIPTION
|
|
|
|
|
|
|
|
|
|
Generic code for SSP/SPI communications. By default, the SSP block
|
|
|
|
|
is initialised in SPI master mode.
|
|
|
|
|
|
|
|
|
|
@section Example
|
|
|
|
|
|
|
|
|
|
@code
|
|
|
|
|
#include "core/cpu/cpu.h"
|
|
|
|
|
#include "core/ssp/ssp.h"
|
|
|
|
|
...
|
|
|
|
|
cpuInit();
|
|
|
|
|
sspInit(0, sspClockPolarity_High, sspClockPhase_RisingEdge);
|
|
|
|
|
...
|
|
|
|
|
uint8_t request[SSP_FIFOSIZE];
|
|
|
|
|
uint8_t response[SSP_FIFOSIZE];
|
|
|
|
|
|
|
|
|
|
// Send 0x9C to the slave device and wait for a response
|
|
|
|
|
request[0] = 0x80 | 0x1C;
|
|
|
|
|
// Toggle the select pin
|
|
|
|
|
ssp0Select();
|
|
|
|
|
// Send 1 byte from the request buffer
|
|
|
|
|
sspSend(0, (uint8_t *)&request, 1);
|
|
|
|
|
// Receive 1 byte into the response buffer
|
|
|
|
|
sspReceive(0, (uint8_t *)&response, 1);
|
|
|
|
|
// Reset the select pin
|
|
|
|
|
ssp0Deselect();
|
|
|
|
|
// Print the results
|
|
|
|
|
debug_printf("Ox%x ", response[0]);
|
|
|
|
|
@endcode
|
|
|
|
|
|
|
|
|
|
@section LICENSE
|
|
|
|
|
|
|
|
|
|
Software License Agreement (BSD License)
|
|
|
|
|
|
|
|
|
|
Copyright (c) 2010, microBuilder SARL
|
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
|
3. Neither the name of the copyright holders nor the
|
|
|
|
|
names of its contributors may be used to endorse or promote products
|
|
|
|
|
derived from this software without specific prior written permission.
|
|
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
|
|
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
|
|
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
#include "ssp.h"
|
|
|
|
|
#include "core/gpio/gpio.h"
|
|
|
|
|
|
|
|
|
|
/* Statistics for all interrupts */
|
|
|
|
|
volatile uint32_t interruptRxStat = 0;
|
|
|
|
|
volatile uint32_t interruptOverRunStat = 0;
|
|
|
|
|
volatile uint32_t interruptRxTimeoutStat = 0;
|
|
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief SSP0 interrupt handler for SPI communication
|
|
|
|
|
|
|
|
|
|
The algorithm is, if RXFIFO is at least half full,
|
|
|
|
|
start receive until it's empty; if TXFIFO is at least
|
|
|
|
|
half empty, start transmit until it's full.
|
|
|
|
|
This will maximize the use of both FIFOs and performance.
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
void SSP_IRQHandler (void)
|
|
|
|
|
{
|
|
|
|
|
uint32_t regValue;
|
|
|
|
|
|
|
|
|
|
regValue = SSP_SSP0MIS;
|
|
|
|
|
|
|
|
|
|
/* Check for overrun interrupt */
|
|
|
|
|
if ( regValue & SSP_SSP0MIS_RORMIS_FRMRCVD )
|
|
|
|
|
{
|
|
|
|
|
interruptOverRunStat++;
|
|
|
|
|
SSP_SSP0ICR = SSP_SSP0ICR_RORIC_CLEAR; // Clear interrupt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check for timeout interrupt */
|
|
|
|
|
if ( regValue & SSP_SSP0MIS_RTMIS_NOTEMPTY )
|
|
|
|
|
{
|
|
|
|
|
interruptRxTimeoutStat++;
|
|
|
|
|
SSP_SSP0ICR = SSP_SSP0ICR_RTIC_CLEAR; // Clear interrupt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check if Rx buffer is at least half-full */
|
|
|
|
|
if ( regValue & SSP_SSP0MIS_RXMIS_HALFFULL )
|
|
|
|
|
{
|
|
|
|
|
// ToDo: Receive until it's empty
|
|
|
|
|
interruptRxStat++;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Initialises the SSP0 port
|
|
|
|
|
|
|
|
|
|
By default, SSP0 is set to SPI frame-format with 8-bit data. Pin 2.11
|
|
|
|
|
is routed to serve as serial clock (SCK), and SSEL (0.2) is set to
|
|
|
|
|
GPIO to allow manual control of when the SPI port is enabled or
|
|
|
|
|
disabled. Overrun and timeout interrupts are both enabled.
|
|
|
|
|
|
|
|
|
|
@param[in] portNum
|
|
|
|
|
The SPI port to use (0..1)
|
|
|
|
|
@param[in] polarity
|
|
|
|
|
Indicates whether the clock should be held high
|
|
|
|
|
(sspClockPolarity_High) or low (sspClockPolarity_Low)
|
|
|
|
|
when inactive.
|
|
|
|
|
@param[in] phase
|
|
|
|
|
Indicates whether new bits start on the leading
|
|
|
|
|
(sspClockPhase_RisingEdge) or falling
|
|
|
|
|
(sspClockPhase_FallingEdge) edge of clock transitions.
|
|
|
|
|
|
|
|
|
|
@note sspSelect() and sspDeselect() macros have been defined in
|
|
|
|
|
ssp.h to control the SSEL line without having to know the
|
|
|
|
|
specific pin being used.
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
void sspInit (uint8_t portNum, sspClockPolarity_t polarity, sspClockPhase_t phase)
|
|
|
|
|
{
|
|
|
|
|
gpioInit();
|
|
|
|
|
|
|
|
|
|
if (portNum == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Reset SSP */
|
|
|
|
|
SCB_PRESETCTRL &= ~SCB_PRESETCTRL_SSP0_MASK;
|
|
|
|
|
SCB_PRESETCTRL |= SCB_PRESETCTRL_SSP0_RESETDISABLED;
|
|
|
|
|
|
|
|
|
|
/* Enable AHB clock to the SSP domain. */
|
|
|
|
|
SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_SSP0);
|
|
|
|
|
|
|
|
|
|
/* Divide by 1 (SSPCLKDIV also enables to SSP CLK) */
|
|
|
|
|
SCB_SSP0CLKDIV = SCB_SSP0CLKDIV_DIV1;
|
|
|
|
|
|
|
|
|
|
/* Set P0.8 to SSP MISO */
|
|
|
|
|
IOCON_PIO0_8 &= ~IOCON_PIO0_8_FUNC_MASK;
|
|
|
|
|
IOCON_PIO0_8 |= IOCON_PIO0_8_FUNC_MISO0;
|
|
|
|
|
|
|
|
|
|
/* Set P0.9 to SSP MOSI */
|
|
|
|
|
IOCON_PIO0_9 &= ~IOCON_PIO0_9_FUNC_MASK;
|
|
|
|
|
IOCON_PIO0_9 |= IOCON_PIO0_9_FUNC_MOSI0;
|
|
|
|
|
|
|
|
|
|
/* Set 2.11 to SSP SCK (0.6 and 0.10 can also be used) */
|
|
|
|
|
#ifdef CFG_SSP0_SCKPIN_2_11
|
|
|
|
|
IOCON_SCKLOC = IOCON_SCKLOC_SCKPIN_PIO2_11;
|
|
|
|
|
IOCON_PIO2_11 = IOCON_PIO2_11_FUNC_SCK0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Set 0.6 to SSP SCK (2.11 and 0.10 can also be used) */
|
|
|
|
|
#ifdef CFG_SSP0_SCKPIN_0_6
|
|
|
|
|
IOCON_SCKLOC = IOCON_SCKLOC_SCKPIN_PIO0_6;
|
|
|
|
|
IOCON_PIO0_6 = IOCON_PIO0_6_FUNC_SCK;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Set P0.2/SSEL to GPIO output and high */
|
|
|
|
|
IOCON_PIO0_2 &= ~IOCON_PIO0_2_FUNC_MASK;
|
|
|
|
|
IOCON_PIO0_2 |= IOCON_PIO0_2_FUNC_GPIO;
|
|
|
|
|
gpioSetDir(SSP0_CSPORT, SSP0_CSPIN, 1);
|
|
|
|
|
gpioSetValue(SSP0_CSPORT, SSP0_CSPIN, 1);
|
|
|
|
|
gpioSetPullup(&IOCON_PIO0_2, gpioPullupMode_Inactive); // Board has external pull-up
|
|
|
|
|
|
|
|
|
|
/* If SSP0CLKDIV = DIV1 -- (PCLK / (CPSDVSR <20> [SCR+1])) = (72,000,000 / (2 x [8 + 1])) = 4.0 MHz */
|
|
|
|
|
uint32_t configReg = ( SSP_SSP0CR0_DSS_8BIT // Data size = 8-bit
|
|
|
|
|
| SSP_SSP0CR0_FRF_SPI // Frame format = SPI
|
|
|
|
|
| SSP_SSP0CR0_SCR_8); // Serial clock rate = 8
|
|
|
|
|
|
|
|
|
|
// Set clock polarity
|
|
|
|
|
if (polarity == sspClockPolarity_High)
|
|
|
|
|
configReg |= SSP_SSP0CR0_CPOL_HIGH; // Clock polarity = High between frames
|
|
|
|
|
else
|
|
|
|
|
configReg &= ~SSP_SSP0CR0_CPOL_MASK; // Clock polarity = Low between frames
|
|
|
|
|
|
|
|
|
|
// Set edge transition
|
|
|
|
|
if (phase == sspClockPhase_FallingEdge)
|
|
|
|
|
configReg |= SSP_SSP0CR0_CPHA_SECOND; // Clock out phase = Trailing edge clock transition
|
|
|
|
|
else
|
|
|
|
|
configReg &= ~SSP_SSP0CR0_CPHA_MASK; // Clock out phase = Leading edge clock transition
|
|
|
|
|
|
|
|
|
|
// Assign config values to SSP0CR0
|
|
|
|
|
SSP_SSP0CR0 = configReg;
|
|
|
|
|
|
|
|
|
|
/* Clock prescale register must be even and at least 2 in master mode */
|
|
|
|
|
SSP_SSP0CPSR = SSP_SSP0CPSR_CPSDVSR_DIV2;
|
|
|
|
|
|
|
|
|
|
/* Clear the Rx FIFO */
|
|
|
|
|
uint8_t i, Dummy=Dummy;
|
|
|
|
|
for ( i = 0; i < SSP_FIFOSIZE; i++ )
|
|
|
|
|
{
|
|
|
|
|
Dummy = SSP_SSP0DR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Enable the SSP Interrupt */
|
|
|
|
|
NVIC_EnableIRQ(SSP_IRQn);
|
|
|
|
|
|
|
|
|
|
/* Set SSPINMS registers to enable interrupts
|
|
|
|
|
* enable all error related interrupts */
|
|
|
|
|
SSP_SSP0IMSC = ( SSP_SSP0IMSC_RORIM_ENBL // Enable overrun interrupt
|
|
|
|
|
| SSP_SSP0IMSC_RTIM_ENBL); // Enable timeout interrupt
|
|
|
|
|
|
|
|
|
|
/* Enable device and set it to master mode, no loopback */
|
|
|
|
|
SSP_SSP0CR1 = SSP_SSP0CR1_SSE_ENABLED | SSP_SSP0CR1_MS_MASTER | SSP_SSP0CR1_LBM_NORMAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Sends a block of data to the SSP0 port
|
|
|
|
|
|
|
|
|
|
@param[in] portNum
|
|
|
|
|
The SPI port to use (0..1)
|
|
|
|
|
@param[in] buf
|
|
|
|
|
Pointer to the data buffer
|
|
|
|
|
@param[in] length
|
|
|
|
|
Block length of the data buffer
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2011-07-17 20:08:27 +00:00
|
|
|
|
void sspSend (uint8_t portNum, const uint8_t *buf, uint32_t length)
|
2011-05-15 11:27:49 +00:00
|
|
|
|
{
|
|
|
|
|
uint32_t i;
|
|
|
|
|
uint8_t Dummy = Dummy;
|
|
|
|
|
|
|
|
|
|
if (portNum == 0)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
/* Move on only if NOT busy and TX FIFO not full. */
|
|
|
|
|
while ((SSP_SSP0SR & (SSP_SSP0SR_TNF_NOTFULL | SSP_SSP0SR_BSY_BUSY)) != SSP_SSP0SR_TNF_NOTFULL);
|
|
|
|
|
SSP_SSP0DR = *buf;
|
|
|
|
|
buf++;
|
|
|
|
|
|
|
|
|
|
while ( (SSP_SSP0SR & (SSP_SSP0SR_BSY_BUSY|SSP_SSP0SR_RNE_NOTEMPTY)) != SSP_SSP0SR_RNE_NOTEMPTY );
|
|
|
|
|
/* Whenever a byte is written, MISO FIFO counter increments, Clear FIFO
|
|
|
|
|
on MISO. Otherwise, when SSP0Receive() is called, previous data byte
|
|
|
|
|
is left in the FIFO. */
|
|
|
|
|
Dummy = SSP_SSP0DR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Receives a block of data from the SSP0 port
|
|
|
|
|
|
|
|
|
|
@param[in] portNum
|
|
|
|
|
The SPI port to use (0..1)
|
|
|
|
|
@param[in] buf
|
|
|
|
|
Pointer to the data buffer
|
|
|
|
|
@param[in] length
|
|
|
|
|
Block length of the data buffer
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
void sspReceive(uint8_t portNum, uint8_t *buf, uint32_t length)
|
|
|
|
|
{
|
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
|
|
if (portNum == 0)
|
|
|
|
|
{
|
|
|
|
|
for ( i = 0; i < length; i++ )
|
|
|
|
|
{
|
|
|
|
|
/* As long as the receive FIFO is not empty, data can be received. */
|
|
|
|
|
SSP_SSP0DR = 0xFF;
|
|
|
|
|
|
|
|
|
|
/* Wait until the Busy bit is cleared */
|
|
|
|
|
while ( (SSP_SSP0SR & (SSP_SSP0SR_BSY_BUSY|SSP_SSP0SR_RNE_NOTEMPTY)) != SSP_SSP0SR_RNE_NOTEMPTY );
|
|
|
|
|
|
|
|
|
|
*buf = SSP_SSP0DR;
|
|
|
|
|
buf++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-06 00:54:15 +00:00
|
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Sends a block of data to the SSP0 port and receives the
|
|
|
|
|
answer back into the same buffer.
|
|
|
|
|
|
|
|
|
|
@param[in] portNum
|
|
|
|
|
The SPI port to use (0..1)
|
|
|
|
|
@param[in] buf
|
|
|
|
|
Pointer to the data buffer
|
|
|
|
|
@param[in] length
|
|
|
|
|
Block length of the data buffer
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
void sspSendReceive(uint8_t portNum, uint8_t *buf, uint32_t length)
|
|
|
|
|
{
|
|
|
|
|
uint32_t i;
|
|
|
|
|
uint8_t Dummy = Dummy;
|
|
|
|
|
|
|
|
|
|
if (portNum == 0)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
/* Move on only if NOT busy and TX FIFO not full. */
|
|
|
|
|
while ((SSP_SSP0SR & (SSP_SSP0SR_TNF_NOTFULL | SSP_SSP0SR_BSY_BUSY)) != SSP_SSP0SR_TNF_NOTFULL);
|
|
|
|
|
SSP_SSP0DR = *buf;
|
|
|
|
|
|
|
|
|
|
while ( (SSP_SSP0SR & (SSP_SSP0SR_BSY_BUSY|SSP_SSP0SR_RNE_NOTEMPTY)) != SSP_SSP0SR_RNE_NOTEMPTY );
|
|
|
|
|
/* Whenever a byte is written, MISO FIFO counter increments, Clear FIFO
|
|
|
|
|
on MISO. Otherwise, when SSP0Receive() is called, previous data byte
|
|
|
|
|
is left in the FIFO. */
|
|
|
|
|
*buf = SSP_SSP0DR;
|
|
|
|
|
buf++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|