Aaron Fischer
f24858d162
This step seems bold, but is saves us so much hassle. Even better, we have a reliable codebase, with all the dependencies (and their versions) we need in order to build the project. If a library got an update, we can replace it inplace if the code is still compatible.
444 lines
10 KiB
C
444 lines
10 KiB
C
/* This source file is part of the ATMEL AVR-UC3-SoftwareFramework-1.7.0 Release */
|
|
|
|
/*This file is prepared for Doxygen automatic documentation generation.*/
|
|
/*! \file *********************************************************************
|
|
*
|
|
* \brief SPI driver for AVR32 UC3.
|
|
*
|
|
* This file defines a useful set of functions for the SPI interface on AVR32
|
|
* devices.
|
|
*
|
|
* - Compiler: IAR EWAVR32 and GNU GCC for AVR32
|
|
* - Supported devices: All AVR32 devices with an SPI module can be used.
|
|
* - AppNote:
|
|
*
|
|
* \author Atmel Corporation: http://www.atmel.com \n
|
|
* Support and FAQ: http://support.atmel.no/
|
|
*
|
|
******************************************************************************/
|
|
|
|
/* Copyright (c) 2009 Atmel Corporation. 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. The name of Atmel may not be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* 4. This software may only be redistributed and used in connection with an Atmel
|
|
* AVR product.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
|
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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 "spi.h"
|
|
|
|
#ifdef FREERTOS_USED
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "semphr.h"
|
|
|
|
#endif
|
|
|
|
|
|
/*! \name SPI Writable Bit-Field Registers
|
|
*/
|
|
//! @{
|
|
|
|
typedef union
|
|
{
|
|
unsigned long cr;
|
|
avr32_spi_cr_t CR;
|
|
} u_avr32_spi_cr_t;
|
|
|
|
typedef union
|
|
{
|
|
unsigned long mr;
|
|
avr32_spi_mr_t MR;
|
|
} u_avr32_spi_mr_t;
|
|
|
|
typedef union
|
|
{
|
|
unsigned long tdr;
|
|
avr32_spi_tdr_t TDR;
|
|
} u_avr32_spi_tdr_t;
|
|
|
|
typedef union
|
|
{
|
|
unsigned long ier;
|
|
avr32_spi_ier_t IER;
|
|
} u_avr32_spi_ier_t;
|
|
|
|
typedef union
|
|
{
|
|
unsigned long idr;
|
|
avr32_spi_idr_t IDR;
|
|
} u_avr32_spi_idr_t;
|
|
|
|
typedef union
|
|
{
|
|
unsigned long csr;
|
|
avr32_spi_csr0_t CSR;
|
|
} u_avr32_spi_csr_t;
|
|
|
|
//! @}
|
|
|
|
|
|
#ifdef FREERTOS_USED
|
|
|
|
//! The SPI mutex.
|
|
static xSemaphoreHandle xSPIMutex;
|
|
|
|
#endif
|
|
|
|
|
|
/*! \brief Calculates the baudrate divider.
|
|
*
|
|
* \param options Pointer to a structure containing initialization options for
|
|
* an SPI channel.
|
|
* \param pba_hz SPI module input clock frequency (PBA clock, Hz).
|
|
*
|
|
* \return Divider or error code.
|
|
* \retval >=0 Success.
|
|
* \retval <0 Error.
|
|
*/
|
|
static int getBaudDiv(const spi_options_t *options, unsigned int pba_hz)
|
|
{
|
|
int baudDiv = (pba_hz + options->baudrate / 2) / options->baudrate;
|
|
|
|
if (baudDiv <= 0 || baudDiv > 255) {
|
|
return -1;
|
|
}
|
|
|
|
return baudDiv;
|
|
}
|
|
|
|
|
|
void spi_reset(volatile avr32_spi_t *spi)
|
|
{
|
|
spi->cr = AVR32_SPI_CR_SWRST_MASK;
|
|
}
|
|
|
|
|
|
spi_status_t spi_initSlave(volatile avr32_spi_t *spi,
|
|
unsigned char bits,
|
|
unsigned char spi_mode)
|
|
{
|
|
if (spi_mode > 3 ||
|
|
bits < 8 || bits > 16) {
|
|
return SPI_ERROR_ARGUMENT;
|
|
}
|
|
|
|
// Reset.
|
|
spi->cr = AVR32_SPI_CR_SWRST_MASK;
|
|
|
|
// Will use CSR0 offsets; these are the same for CSR0 to CSR3.
|
|
spi->csr0 = ((spi_mode >> 1) << AVR32_SPI_CSR0_CPOL_OFFSET) |
|
|
(((spi_mode & 0x1) ^ 0x1) << AVR32_SPI_CSR0_NCPHA_OFFSET) |
|
|
((bits - 8) << AVR32_SPI_CSR0_BITS_OFFSET);
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
spi_status_t spi_initTest(volatile avr32_spi_t *spi)
|
|
{
|
|
// Reset.
|
|
spi->cr = AVR32_SPI_CR_SWRST_MASK;
|
|
spi->mr |= AVR32_SPI_MR_MSTR_MASK | // Master Mode.
|
|
AVR32_SPI_MR_LLB_MASK; // Local Loopback.
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
spi_status_t spi_initMaster(volatile avr32_spi_t *spi, const spi_options_t *options)
|
|
{
|
|
u_avr32_spi_mr_t u_avr32_spi_mr;
|
|
|
|
if (options->modfdis > 1) {
|
|
return SPI_ERROR_ARGUMENT;
|
|
}
|
|
|
|
// Reset.
|
|
spi->cr = AVR32_SPI_CR_SWRST_MASK;
|
|
|
|
// Master Mode.
|
|
u_avr32_spi_mr.mr = spi->mr;
|
|
u_avr32_spi_mr.MR.mstr = 1;
|
|
u_avr32_spi_mr.MR.modfdis = options->modfdis;
|
|
u_avr32_spi_mr.MR.llb = 0;
|
|
u_avr32_spi_mr.MR.pcs = (1 << AVR32_SPI_MR_PCS_SIZE) - 1;
|
|
spi->mr = u_avr32_spi_mr.mr;
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
spi_status_t spi_selectionMode(volatile avr32_spi_t *spi,
|
|
unsigned char variable_ps,
|
|
unsigned char pcs_decode,
|
|
unsigned char delay)
|
|
{
|
|
u_avr32_spi_mr_t u_avr32_spi_mr;
|
|
|
|
if (variable_ps > 1 ||
|
|
pcs_decode > 1) {
|
|
return SPI_ERROR_ARGUMENT;
|
|
}
|
|
|
|
u_avr32_spi_mr.mr = spi->mr;
|
|
u_avr32_spi_mr.MR.ps = variable_ps;
|
|
u_avr32_spi_mr.MR.pcsdec = pcs_decode;
|
|
u_avr32_spi_mr.MR.dlybcs = delay;
|
|
spi->mr = u_avr32_spi_mr.mr;
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
spi_status_t spi_selectChip(volatile avr32_spi_t *spi, unsigned char chip)
|
|
{
|
|
#ifdef FREERTOS_USED
|
|
while (pdFALSE == xSemaphoreTake(xSPIMutex, 20));
|
|
#endif
|
|
|
|
// Assert all lines; no peripheral is selected.
|
|
spi->mr |= AVR32_SPI_MR_PCS_MASK;
|
|
|
|
if (spi->mr & AVR32_SPI_MR_PCSDEC_MASK) {
|
|
// The signal is decoded; allow up to 15 chips.
|
|
if (chip > 14) {
|
|
return SPI_ERROR_ARGUMENT;
|
|
}
|
|
|
|
spi->mr &= ~AVR32_SPI_MR_PCS_MASK | (chip << AVR32_SPI_MR_PCS_OFFSET);
|
|
} else {
|
|
if (chip > 3) {
|
|
return SPI_ERROR_ARGUMENT;
|
|
}
|
|
|
|
spi->mr &= ~(1 << (AVR32_SPI_MR_PCS_OFFSET + chip));
|
|
}
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
spi_status_t spi_unselectChip(volatile avr32_spi_t *spi, unsigned char chip)
|
|
{
|
|
unsigned int timeout = SPI_TIMEOUT;
|
|
|
|
while (!(spi->sr & AVR32_SPI_SR_TXEMPTY_MASK)) {
|
|
if (!timeout--) {
|
|
return SPI_ERROR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
// Assert all lines; no peripheral is selected.
|
|
spi->mr |= AVR32_SPI_MR_PCS_MASK;
|
|
|
|
// Last transfer, so deassert the current NPCS if CSAAT is set.
|
|
spi->cr = AVR32_SPI_CR_LASTXFER_MASK;
|
|
|
|
#ifdef FREERTOS_USED
|
|
xSemaphoreGive(xSPIMutex);
|
|
#endif
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
spi_status_t spi_setupChipReg(volatile avr32_spi_t *spi,
|
|
const spi_options_t *options,
|
|
unsigned int pba_hz)
|
|
{
|
|
u_avr32_spi_csr_t u_avr32_spi_csr;
|
|
|
|
if (options->spi_mode > 3 ||
|
|
options->stay_act > 1 ||
|
|
options->bits < 8 || options->bits > 16) {
|
|
return SPI_ERROR_ARGUMENT;
|
|
}
|
|
|
|
int baudDiv = getBaudDiv(options, pba_hz);
|
|
|
|
if (baudDiv < 0) {
|
|
return SPI_ERROR_ARGUMENT;
|
|
}
|
|
|
|
// Will use CSR0 offsets; these are the same for CSR0 to CSR3.
|
|
u_avr32_spi_csr.csr = 0;
|
|
u_avr32_spi_csr.CSR.cpol = options->spi_mode >> 1;
|
|
u_avr32_spi_csr.CSR.ncpha = (options->spi_mode & 0x1) ^ 0x1;
|
|
u_avr32_spi_csr.CSR.csaat = options->stay_act;
|
|
u_avr32_spi_csr.CSR.bits = options->bits - 8;
|
|
u_avr32_spi_csr.CSR.scbr = baudDiv;
|
|
u_avr32_spi_csr.CSR.dlybs = options->spck_delay;
|
|
u_avr32_spi_csr.CSR.dlybct = options->trans_delay;
|
|
|
|
switch(options->reg) {
|
|
case 0:
|
|
spi->csr0 = u_avr32_spi_csr.csr;
|
|
break;
|
|
case 1:
|
|
spi->csr1 = u_avr32_spi_csr.csr;
|
|
break;
|
|
case 2:
|
|
spi->csr2 = u_avr32_spi_csr.csr;
|
|
break;
|
|
case 3:
|
|
spi->csr3 = u_avr32_spi_csr.csr;
|
|
break;
|
|
default:
|
|
return SPI_ERROR_ARGUMENT;
|
|
}
|
|
|
|
#ifdef FREERTOS_USED
|
|
if (!xSPIMutex)
|
|
{
|
|
// Create the SPI mutex.
|
|
vSemaphoreCreateBinary(xSPIMutex);
|
|
if (!xSPIMutex)
|
|
{
|
|
while(1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
void spi_enable(volatile avr32_spi_t *spi)
|
|
{
|
|
spi->cr = AVR32_SPI_CR_SPIEN_MASK;
|
|
}
|
|
|
|
|
|
void spi_disable(volatile avr32_spi_t *spi)
|
|
{
|
|
spi->cr = AVR32_SPI_CR_SPIDIS_MASK;
|
|
}
|
|
|
|
|
|
int spi_is_enabled(volatile avr32_spi_t *spi)
|
|
{
|
|
return (spi->sr & AVR32_SPI_SR_SPIENS_MASK) != 0;
|
|
}
|
|
|
|
|
|
inline unsigned char spi_writeRegisterEmptyCheck(volatile avr32_spi_t *spi)
|
|
{
|
|
return ((spi->sr & AVR32_SPI_SR_TDRE_MASK) != 0);
|
|
}
|
|
|
|
|
|
inline spi_status_t spi_write(volatile avr32_spi_t *spi, unsigned short data)
|
|
{
|
|
unsigned int timeout = SPI_TIMEOUT;
|
|
|
|
while (!(spi->sr & AVR32_SPI_SR_TDRE_MASK)) {
|
|
if (!timeout--) {
|
|
return SPI_ERROR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
spi->tdr = data << AVR32_SPI_TDR_TD_OFFSET;
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
spi_status_t spi_variableSlaveWrite(volatile avr32_spi_t *spi, unsigned short data,
|
|
unsigned char pcs, unsigned char lastxfer)
|
|
{
|
|
unsigned int timeout = SPI_TIMEOUT;
|
|
|
|
if (pcs > 14 || lastxfer > 1) {
|
|
return SPI_ERROR_ARGUMENT;
|
|
}
|
|
|
|
while (!(spi->sr & AVR32_SPI_SR_TDRE_MASK)) {
|
|
if (!timeout--) {
|
|
return SPI_ERROR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
spi->tdr = (data << AVR32_SPI_TDR_TD_OFFSET) |
|
|
(pcs << AVR32_SPI_TDR_PCS_OFFSET) |
|
|
(lastxfer << AVR32_SPI_TDR_LASTXFER_OFFSET);
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
inline unsigned char spi_writeEndCheck(volatile avr32_spi_t *spi)
|
|
{
|
|
return ((spi->sr & AVR32_SPI_SR_TXEMPTY_MASK) != 0);
|
|
}
|
|
|
|
|
|
unsigned char spi_readRegisterFullCheck(volatile avr32_spi_t *spi)
|
|
{
|
|
return ((spi->sr & AVR32_SPI_SR_RDRF_MASK) != 0);
|
|
}
|
|
|
|
|
|
inline spi_status_t spi_read(volatile avr32_spi_t *spi, unsigned short *data)
|
|
{
|
|
unsigned int timeout = SPI_TIMEOUT;
|
|
|
|
while ((spi->sr & (AVR32_SPI_SR_RDRF_MASK | AVR32_SPI_SR_TXEMPTY_MASK)) !=
|
|
(AVR32_SPI_SR_RDRF_MASK | AVR32_SPI_SR_TXEMPTY_MASK)) {
|
|
if (!timeout--) {
|
|
return SPI_ERROR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
*data = spi->rdr >> AVR32_SPI_RDR_RD_OFFSET;
|
|
|
|
return SPI_OK;
|
|
}
|
|
|
|
|
|
unsigned char spi_getStatus(volatile avr32_spi_t *spi)
|
|
{
|
|
spi_status_t ret = SPI_OK;
|
|
unsigned long sr = spi->sr;
|
|
|
|
if (sr & AVR32_SPI_SR_OVRES_MASK) {
|
|
ret = SPI_ERROR_OVERRUN;
|
|
}
|
|
|
|
if (sr & AVR32_SPI_SR_MODF_MASK) {
|
|
ret += SPI_ERROR_MODE_FAULT;
|
|
}
|
|
|
|
if (ret == (SPI_ERROR_OVERRUN + SPI_ERROR_MODE_FAULT)) {
|
|
return SPI_ERROR_OVERRUN_AND_MODE_FAULT;
|
|
}
|
|
else if (ret > 0) {
|
|
return ret;
|
|
} else {
|
|
return SPI_OK;
|
|
}
|
|
}
|