This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
reprapfirmware-dc42/Libraries/SPI/spi_master.h
David Crocker aa7bac3cc0 Merged in Dan's thermocouple support
Merged in Dan's thermocouple support
Define serial port allocation in Pins_duet.h
Fixed some leaks of OutputBuffer objects under error conditions
The terminating newline character in a gcode response is now added in
HandleReply
2015-12-30 13:11:56 +00:00

539 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
// Request that a NPCS peripheral NOT be connected
#define CONFIG_SPI_NO_PERIPHERALS 0xffff
//! 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, uint32_t ul_cs_pin, uint32_t 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_