crashtest-r0ket/firmware/core/ssp/ssp.c

339 lines
11 KiB
C
Raw Permalink Blame History

/**************************************************************************/
/*!
@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
*/
/**************************************************************************/
void sspSend (uint8_t portNum, const 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;
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;
}
/**************************************************************************/
/*!
@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;
}