
Added timeout to output buffers destined for USB Fixed bugs in thermocouple code Reallocated thermocouple pin numbers Made Roland mill and inkjet support conditional and normally disabled Fixed issue with thermocouple code messing up the timekeeping system, which was suspected of causing the network to be unreliable Added support for M577 (thanks chrishamm)
536 lines
13 KiB
C
536 lines
13 KiB
C
// ASF 3.27.0
|
|
|
|
/**
|
|
* \file
|
|
*
|
|
* \brief SPI master common service for SAM.
|
|
*
|
|
* Copyright (c) 2011-2015 Atmel Corporation. All rights reserved.
|
|
*
|
|
* \asf_license_start
|
|
*
|
|
* \page License
|
|
*
|
|
* 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 microcontroller 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.
|
|
*
|
|
* \asf_license_stop
|
|
*
|
|
*/
|
|
/**
|
|
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
|
|
*/
|
|
|
|
#ifndef _SPI_MASTER_H_
|
|
#define _SPI_MASTER_H_
|
|
|
|
#include "compiler.h"
|
|
#include "status_codes.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
//! Default Configuration of SPI Master Delay BCS
|
|
#ifndef CONFIG_SPI_MASTER_DELAY_BCS
|
|
#define CONFIG_SPI_MASTER_DELAY_BCS 0
|
|
#endif
|
|
|
|
//! Default Configuration of SPI Master Bits per Transfer Definition
|
|
#ifndef CONFIG_SPI_MASTER_BITS_PER_TRANSFER
|
|
#define CONFIG_SPI_MASTER_BITS_PER_TRANSFER SPI_CSR_BITS_8_BIT
|
|
#endif
|
|
|
|
//! Default Configuration of SPI Master Delay BCT
|
|
#ifndef CONFIG_SPI_MASTER_DELAY_BCT
|
|
#define CONFIG_SPI_MASTER_DELAY_BCT 0
|
|
#endif
|
|
|
|
//! Default Configuration of SPI Master Delay BS
|
|
#ifndef CONFIG_SPI_MASTER_DELAY_BS
|
|
#define CONFIG_SPI_MASTER_DELAY_BS 0
|
|
#endif
|
|
|
|
//! Default Configuration of SPI Master Dummy Field
|
|
#ifndef CONFIG_SPI_MASTER_DUMMY
|
|
#define CONFIG_SPI_MASTER_DUMMY 0xFF
|
|
#endif
|
|
//! @}
|
|
|
|
#if defined(USE_SAM3X_DMAC)
|
|
#include "dmac.h"
|
|
#include "pmc.h"
|
|
#endif
|
|
|
|
/**
|
|
* \brief Clock phase.
|
|
*/
|
|
#define SPI_CPHA (1 << 0)
|
|
|
|
/**
|
|
* \brief Clock polarity.
|
|
*/
|
|
#define SPI_CPOL (1 << 1)
|
|
|
|
/**
|
|
* \brief SPI mode 0.
|
|
*/
|
|
#define SPI_MODE_0 0
|
|
/**
|
|
* \brief SPI mode 1.
|
|
*/
|
|
#define SPI_MODE_1 (SPI_CPHA)
|
|
/**
|
|
* \brief SPI mode 2.
|
|
*/
|
|
#define SPI_MODE_2 (SPI_CPOL)
|
|
/**
|
|
* \brief SPI mode 3.
|
|
*/
|
|
#define SPI_MODE_3 (SPI_CPOL | SPI_CPHA)
|
|
|
|
/** Time-out value (number of attempts). */
|
|
#define SPI_TIMEOUT 15000
|
|
|
|
/** Status codes used by the SPI driver. */
|
|
typedef enum
|
|
{
|
|
SPI_ERROR = -1,
|
|
SPI_OK = 0,
|
|
SPI_ERROR_TIMEOUT = 1,
|
|
SPI_ERROR_ARGUMENT,
|
|
SPI_ERROR_OVERRUN,
|
|
SPI_ERROR_MODE_FAULT,
|
|
SPI_ERROR_OVERRUN_AND_MODE_FAULT
|
|
} spi_status_t;
|
|
|
|
/** SPI Chip Select behavior modes while transferring. */
|
|
typedef enum spi_cs_behavior {
|
|
/** CS does not rise until a new transfer is requested on different chip
|
|
select. */
|
|
SPI_CS_KEEP_LOW = SPI_CSR_CSAAT,
|
|
/** CS rises if there is no more data to transfer. */
|
|
SPI_CS_RISE_NO_TX = 0,
|
|
/** CS is de-asserted systematically during a time DLYBCS. */
|
|
SPI_CS_RISE_FORCED = SPI_CSR_CSNAAT
|
|
} spi_cs_behavior_t;
|
|
|
|
#ifndef SPI_TYPE_DEFS
|
|
#define SPI_TYPE_DEFS
|
|
//! SPI Flags Definition
|
|
typedef uint8_t spi_flags_t;
|
|
|
|
//! Board SPI Select Id Definition
|
|
typedef uint32_t board_spi_select_id_t;
|
|
#endif
|
|
|
|
//! \brief Polled SPI device definition.
|
|
struct spi_device {
|
|
//! Board specific select id
|
|
board_spi_select_id_t id;
|
|
uint8_t cs;
|
|
uint8_t bits;
|
|
};
|
|
|
|
void spi_set_peripheral_chip_select_value(Spi *p_spi, uint32_t ul_value);
|
|
void spi_set_delay_between_chip_select(Spi *p_spi, uint32_t ul_delay);
|
|
void spi_set_baudrate_div(Spi *p_spi, uint32_t ul_pcs_ch,
|
|
uint8_t uc_baudrate_divider);
|
|
void spi_set_transfer_delay(Spi *p_spi, uint32_t ul_pcs_ch,
|
|
uint8_t uc_dlybs, uint8_t uc_dlybct);
|
|
|
|
|
|
/** \brief Initialize the SPI in master mode.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
*
|
|
*/
|
|
extern void spi_master_init(Spi *p_spi, int ul_cs_pin, int ul_npcs_pin);
|
|
|
|
/**
|
|
* \brief Set up an SPI device.
|
|
*
|
|
* The returned device descriptor structure must be passed to the driver
|
|
* whenever that device should be used as current slave device.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
* \param device Pointer to SPI device struct that should be initialized.
|
|
* \param flags SPI configuration flags. Common flags for all
|
|
* implementations are the SPI modes SPI_MODE_0 ...
|
|
* SPI_MODE_3.
|
|
* \param baud_rate Baud rate for communication with slave device in Hz.
|
|
* \param sel_id Board specific select id.
|
|
*/
|
|
extern void spi_master_setup_device(Spi *p_spi, const struct spi_device *device,
|
|
spi_flags_t flags, uint32_t baud_rate, board_spi_select_id_t sel_id);
|
|
|
|
/**
|
|
* \brief Select the given device on the SPI bus.
|
|
*
|
|
* Set device specific setting and call board chip select.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
* \param device SPI device.
|
|
*
|
|
*/
|
|
extern void spi_select_device(Spi *p_spi, const struct spi_device *device);
|
|
|
|
/**
|
|
* \brief Deselect the given device on the SPI bus.
|
|
*
|
|
* Call board chip deselect.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
* \param device SPI device.
|
|
*
|
|
* \pre SPI device must be selected with spi_select_device() first.
|
|
*/
|
|
extern void spi_deselect_device(Spi *p_spi, const struct spi_device *device);
|
|
|
|
/**
|
|
* \brief Send a sequence of bytes to an SPI device.
|
|
*
|
|
* Received bytes on the SPI bus are discarded.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
* \param data Data buffer to write.
|
|
* \param len Length of data to be written.
|
|
*
|
|
* \pre SPI device must be selected with spi_select_device() first.
|
|
*/
|
|
extern status_code_t spi_write_packet(Spi *p_spi, const uint8_t *data,
|
|
size_t len);
|
|
|
|
/**
|
|
* \brief Check if all transmissions are ready.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*
|
|
* \retval 1 if transmissions are complete.
|
|
* \retval 0 if transmissions are not complete.
|
|
*/
|
|
static inline uint32_t spi_is_tx_ready(Spi *p_spi)
|
|
{
|
|
if (p_spi->SPI_SR & SPI_SR_TDRE) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Check if all transmissions are complete.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*
|
|
* \retval 1 if transmissions are complete.
|
|
* \retval 0 if transmissions are not complete.
|
|
*/
|
|
static inline uint32_t spi_is_tx_empty(Spi *p_spi)
|
|
{
|
|
if (p_spi->SPI_SR & SPI_SR_TXEMPTY) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Check if all receptions are ready.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*
|
|
* \return 1 if the SPI Receiver is ready, otherwise 0.
|
|
*/
|
|
static inline uint32_t spi_is_rx_ready(Spi *p_spi)
|
|
{
|
|
if ((p_spi->SPI_SR & (SPI_SR_RDRF | SPI_SR_TXEMPTY))
|
|
== (SPI_SR_RDRF | SPI_SR_TXEMPTY)) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/** \brief Get one data to a SPI peripheral.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
* \return The data byte
|
|
*
|
|
*/
|
|
static inline status_code_t spi_get(Spi *p_spi, uint8_t *b)
|
|
{
|
|
// wait for transmit register empty
|
|
uint32_t timeout = SPI_TIMEOUT;
|
|
while (!spi_is_tx_ready(p_spi)) {
|
|
if (--timeout == 0) {
|
|
return ERR_TMO;
|
|
}
|
|
}
|
|
|
|
// write dummy byte with address and end transmission flag
|
|
p_spi->SPI_TDR = 0x000000FF | SPI_TDR_LASTXFER;
|
|
|
|
// wait for receive register
|
|
timeout = SPI_TIMEOUT;
|
|
while (!spi_is_rx_ready(p_spi)) {
|
|
if (--timeout == 0) {
|
|
return ERR_TMO;
|
|
}
|
|
}
|
|
|
|
// get byte from receive register
|
|
*b = (uint8_t)p_spi->SPI_RDR;
|
|
|
|
return STATUS_OK;
|
|
|
|
// return (p_spi->SPI_RDR & SPI_RDR_RD_Msk);
|
|
}
|
|
|
|
/** \brief Receive one byte from an SPI device.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
* \param data Data to read.
|
|
*
|
|
*/
|
|
#define spi_read_single(p,d) spi_get(p,d)
|
|
//static inline status_code_t spi_read_single(Spi *p_spi, uint8_t *data)
|
|
//{
|
|
// return spi_get(p_spi, data);
|
|
//}
|
|
|
|
/**
|
|
* \brief Receive a sequence of bytes from an SPI device.
|
|
*
|
|
* All bytes sent out on SPI bus are sent as value 0xff.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
* \param data Data buffer to read.
|
|
* \param len Length of data to be read.
|
|
*
|
|
* \pre SPI device must be selected with spi_select_device() first.
|
|
*/
|
|
extern status_code_t spi_read_packet(Spi *p_spi, uint8_t *data, size_t len);
|
|
|
|
/**
|
|
* \brief Put one data to a SPI peripheral.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
* \param data The data byte to be loaded
|
|
*
|
|
*/
|
|
static inline status_code_t spi_put(Spi *p_spi, uint8_t data)
|
|
{
|
|
// p_spi->SPI_TDR = SPI_TDR_TD(data);
|
|
|
|
// wait for transmit register empty
|
|
uint32_t timeout = SPI_TIMEOUT;
|
|
while (!spi_is_tx_ready(p_spi)) {
|
|
if (!timeout--)
|
|
{
|
|
return ERR_TMO;
|
|
}
|
|
}
|
|
|
|
// write byte with address and end transmission flag
|
|
p_spi->SPI_TDR = (uint32_t)data | SPI_TDR_LASTXFER;
|
|
|
|
// wait for receive register
|
|
timeout = SPI_TIMEOUT;
|
|
while (!spi_is_rx_ready(p_spi)) {
|
|
if (--timeout == 0) {
|
|
return ERR_TMO;
|
|
}
|
|
}
|
|
|
|
// clear status
|
|
p_spi->SPI_RDR;
|
|
|
|
return STATUS_OK;
|
|
}
|
|
|
|
|
|
/** \brief Write one byte to an SPI device.
|
|
*
|
|
* \param p_spi Base address of the SPI instance.
|
|
* \param data Data to write.
|
|
*
|
|
*/
|
|
#define spi_write_single(p,d) spi_put(p,d)
|
|
//static inline status_code_t spi_write_single(Spi *p_spi, uint8_t data)
|
|
//{
|
|
// return spi_put(p_spi, (uint16_t)data);
|
|
//}
|
|
|
|
/**
|
|
* \brief Test if the SPI is enabled.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*
|
|
* \return 1 if the SPI is enabled, otherwise 0.
|
|
*/
|
|
static inline uint32_t spi_is_enabled(Spi *p_spi)
|
|
{
|
|
if (p_spi->SPI_SR & SPI_SR_SPIENS) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Enable SPI.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*/
|
|
static inline void spi_enable(Spi *p_spi)
|
|
{
|
|
p_spi->SPI_CR = SPI_CR_SPIEN;
|
|
}
|
|
|
|
/**
|
|
* \brief Disable SPI.
|
|
*
|
|
* \note CS is de-asserted, which indicates that the last data is done, and user
|
|
* should check TX_EMPTY before disabling SPI.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*/
|
|
static inline void spi_disable(Spi *p_spi)
|
|
{
|
|
p_spi->SPI_CR = SPI_CR_SPIDIS;
|
|
}
|
|
|
|
/**
|
|
* \brief Reset SPI and set it to Slave mode.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*/
|
|
static inline void spi_reset(Spi *p_spi)
|
|
{
|
|
p_spi->SPI_CR = SPI_CR_SWRST;
|
|
}
|
|
|
|
/**
|
|
* \brief Set SPI to Master mode.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*/
|
|
static inline void spi_set_master_mode(Spi *p_spi)
|
|
{
|
|
p_spi->SPI_MR |= SPI_MR_MSTR;
|
|
}
|
|
|
|
/**
|
|
* \brief Disable Mode Fault Detection.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*/
|
|
static inline void spi_disable_mode_fault_detect(Spi *p_spi)
|
|
{
|
|
p_spi->SPI_MR |= SPI_MR_MODFDIS;
|
|
}
|
|
|
|
/**
|
|
* \brief Disable loopback mode.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*/
|
|
static inline void spi_disable_loopback(Spi *p_spi)
|
|
{
|
|
p_spi->SPI_MR &= (~SPI_MR_LLB);
|
|
}
|
|
|
|
/**
|
|
* \brief Set Fixed Peripheral Select.
|
|
* Peripheral Chip Select is controlled by SPI_MR.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*/
|
|
static inline void spi_set_fixed_peripheral_select(Spi *p_spi)
|
|
{
|
|
p_spi->SPI_MR &= (~SPI_MR_PS);
|
|
}
|
|
|
|
/**
|
|
* \brief Disable Peripheral Select Decode.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*/
|
|
static inline void spi_disable_peripheral_select_decode(Spi *p_spi)
|
|
{
|
|
p_spi->SPI_MR &= (~SPI_MR_PCSDEC);
|
|
}
|
|
|
|
/**
|
|
* \brief Get Peripheral Select Decode mode.
|
|
*
|
|
* \param p_spi Pointer to an SPI instance.
|
|
*
|
|
* \return 1 for decode mode, 0 for direct mode.
|
|
*/
|
|
static inline uint32_t spi_get_peripheral_select_decode_setting(Spi *p_spi)
|
|
{
|
|
if (p_spi->SPI_MR & SPI_MR_PCSDEC) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#if defined(USE_SAM3X_DMAC)
|
|
|
|
#define SPI_TX_IDX 1 // DMAC HW interface id for SPI TX (Table 22-2. DMA Controller)
|
|
#define SPI_RX_IDX 2 // DMAC HW interface id for SPI RX (Table 22-2. DMA Controller)
|
|
|
|
void spi_start_transmit_dma(Dmac *p_dmac, Spi *p_spi, uint32_t ul_num,
|
|
const void *src, uint32_t nb_bytes);
|
|
|
|
void spi_start_receive_dma(Dmac *p_dmac, Spi *p_spi, uint32_t ul_num,
|
|
const void *dest, uint32_t nb_bytes);
|
|
|
|
static inline uint32_t spi_get_peripheral_chip_select_value(Spi *p_spi)
|
|
{
|
|
// return 3 & ((p_spi->SPI_MR & SPI_MR_PCS_Msk) >> SPI_MR_PCS_Pos);
|
|
return 3 & (p_spi->SPI_MR >> SPI_MR_PCS_Pos);
|
|
}
|
|
|
|
static inline uint32_t spi_get_clock_divisor(Spi *p_spi)
|
|
{
|
|
uint32_t ul_pcs_ch = spi_get_peripheral_chip_select_value(p_spi);
|
|
if (ul_pcs_ch > 3) return 255;
|
|
return (p_spi->SPI_CSR[ul_pcs_ch] & SPI_CSR_SCBR_Msk) >> SPI_CSR_SCBR_Pos;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // _SPI_MASTER_H_
|