mirror of
https://github.com/MarlinFirmware/Marlin.git
synced 2024-11-25 21:07:32 +00:00
444 lines
14 KiB
C
444 lines
14 KiB
C
/**********************************************************************
|
|
* $Id$ lpc17xx_spi.c 2010-05-21
|
|
*//**
|
|
* @file lpc17xx_spi.c
|
|
* @brief Contains all functions support for SPI firmware library on LPC17xx
|
|
* @version 2.0
|
|
* @date 21. May. 2010
|
|
* @author NXP MCU SW Application Team
|
|
*
|
|
* Copyright(C) 2010, NXP Semiconductor
|
|
* All rights reserved.
|
|
*
|
|
***********************************************************************
|
|
* Software that is described herein is for illustrative purposes only
|
|
* which provides customers with programming information regarding the
|
|
* products. This software is supplied "AS IS" without any warranties.
|
|
* NXP Semiconductors assumes no responsibility or liability for the
|
|
* use of the software, conveys no license or title under any patent,
|
|
* copyright, or mask work right to the product. NXP Semiconductors
|
|
* reserves the right to make changes in the software without
|
|
* notification. NXP Semiconductors also make no representation or
|
|
* warranty that such application will be suitable for the specified
|
|
* use without further testing or modification.
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
* documentation is hereby granted, under NXP Semiconductors'
|
|
* relevant copyright in the software, without fee, provided that it
|
|
* is used in conjunction with NXP Semiconductors microcontrollers. This
|
|
* copyright, permission, and disclaimer notice must appear in all copies of
|
|
* this code.
|
|
**********************************************************************/
|
|
|
|
/* Peripheral group ----------------------------------------------------------- */
|
|
/** @addtogroup SPI
|
|
* @{
|
|
*/
|
|
|
|
/* Includes ------------------------------------------------------------------- */
|
|
#include "lpc17xx_spi.h"
|
|
#include "lpc17xx_clkpwr.h"
|
|
|
|
/* If this source file built with example, the LPC17xx FW library configuration
|
|
* file in each example directory ("lpc17xx_libcfg.h") must be included,
|
|
* otherwise the default FW library configuration file must be included instead
|
|
*/
|
|
#ifdef __BUILD_WITH_EXAMPLE__
|
|
#include "lpc17xx_libcfg.h"
|
|
#else
|
|
#include "lpc17xx_libcfg_default.h"
|
|
#endif /* __BUILD_WITH_EXAMPLE__ */
|
|
|
|
#ifdef _SPI
|
|
|
|
|
|
/* Public Functions ----------------------------------------------------------- */
|
|
/** @addtogroup SPI_Public_Functions
|
|
* @{
|
|
*/
|
|
|
|
/*********************************************************************//**
|
|
* @brief Setup clock rate for SPI device
|
|
* @param[in] SPIx SPI peripheral definition, should be LPC_SPI
|
|
* @param[in] target_clock : clock of SPI (Hz)
|
|
* @return None
|
|
***********************************************************************/
|
|
void SPI_SetClock (LPC_SPI_TypeDef *SPIx, uint32_t target_clock)
|
|
{
|
|
uint32_t spi_pclk;
|
|
uint32_t prescale, temp;
|
|
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
|
|
if (SPIx == LPC_SPI){
|
|
spi_pclk = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_SPI);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
prescale = 8;
|
|
// Find closest clock to target clock
|
|
while (1){
|
|
temp = target_clock * prescale;
|
|
if (temp >= spi_pclk){
|
|
break;
|
|
}
|
|
prescale += 2;
|
|
if(prescale >= 254){
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Write to register
|
|
SPIx->SPCCR = SPI_SPCCR_COUNTER(prescale);
|
|
}
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief De-initializes the SPIx peripheral registers to their
|
|
* default reset values.
|
|
* @param[in] SPIx SPI peripheral selected, should be LPC_SPI
|
|
* @return None
|
|
**********************************************************************/
|
|
void SPI_DeInit(LPC_SPI_TypeDef *SPIx)
|
|
{
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
|
|
if (SPIx == LPC_SPI){
|
|
/* Set up clock and power for SPI module */
|
|
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCSPI, DISABLE);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Get data bit size per transfer
|
|
* @param[in] SPIx SPI peripheral selected, should be LPC_SPI
|
|
* @return number of bit per transfer, could be 8-16
|
|
**********************************************************************/
|
|
uint8_t SPI_GetDataSize (LPC_SPI_TypeDef *SPIx)
|
|
{
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
return ((SPIx->SPCR)>>8 & 0xF);
|
|
}
|
|
|
|
/********************************************************************//**
|
|
* @brief Initializes the SPIx peripheral according to the specified
|
|
* parameters in the UART_ConfigStruct.
|
|
* @param[in] SPIx SPI peripheral selected, should be LPC_SPI
|
|
* @param[in] SPI_ConfigStruct Pointer to a SPI_CFG_Type structure
|
|
* that contains the configuration information for the
|
|
* specified SPI peripheral.
|
|
* @return None
|
|
*********************************************************************/
|
|
void SPI_Init(LPC_SPI_TypeDef *SPIx, SPI_CFG_Type *SPI_ConfigStruct)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
|
|
if(SPIx == LPC_SPI){
|
|
/* Set up clock and power for UART module */
|
|
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCSPI, ENABLE);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
// Configure SPI, interrupt is disable as default
|
|
tmp = ((SPI_ConfigStruct->CPHA) | (SPI_ConfigStruct->CPOL) \
|
|
| (SPI_ConfigStruct->DataOrder) | (SPI_ConfigStruct->Databit) \
|
|
| (SPI_ConfigStruct->Mode) | SPI_SPCR_BIT_EN) & SPI_SPCR_BITMASK;
|
|
// write back to SPI control register
|
|
SPIx->SPCR = tmp;
|
|
|
|
// Set clock rate for SPI peripheral
|
|
SPI_SetClock(SPIx, SPI_ConfigStruct->ClockRate);
|
|
|
|
// If interrupt flag is set, Write '1' to Clear interrupt flag
|
|
if (SPIx->SPINT & SPI_SPINT_INTFLAG){
|
|
SPIx->SPINT = SPI_SPINT_INTFLAG;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************//**
|
|
* @brief Fills each SPI_InitStruct member with its default value:
|
|
* - CPHA = SPI_CPHA_FIRST
|
|
* - CPOL = SPI_CPOL_HI
|
|
* - ClockRate = 1000000
|
|
* - DataOrder = SPI_DATA_MSB_FIRST
|
|
* - Databit = SPI_DATABIT_8
|
|
* - Mode = SPI_MASTER_MODE
|
|
* @param[in] SPI_InitStruct Pointer to a SPI_CFG_Type structure
|
|
* which will be initialized.
|
|
* @return None
|
|
*******************************************************************************/
|
|
void SPI_ConfigStructInit(SPI_CFG_Type *SPI_InitStruct)
|
|
{
|
|
SPI_InitStruct->CPHA = SPI_CPHA_FIRST;
|
|
SPI_InitStruct->CPOL = SPI_CPOL_HI;
|
|
SPI_InitStruct->ClockRate = 1000000;
|
|
SPI_InitStruct->DataOrder = SPI_DATA_MSB_FIRST;
|
|
SPI_InitStruct->Databit = SPI_DATABIT_8;
|
|
SPI_InitStruct->Mode = SPI_MASTER_MODE;
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief Transmit a single data through SPIx peripheral
|
|
* @param[in] SPIx SPI peripheral selected, should be LPC_SPI
|
|
* @param[in] Data Data to transmit (must be 16 or 8-bit long,
|
|
* this depend on SPI data bit number configured)
|
|
* @return none
|
|
**********************************************************************/
|
|
void SPI_SendData(LPC_SPI_TypeDef* SPIx, uint16_t Data)
|
|
{
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
|
|
SPIx->SPDR = Data & SPI_SPDR_BITMASK;
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
* @brief Receive a single data from SPIx peripheral
|
|
* @param[in] SPIx SPI peripheral selected, should be LPC_SPI
|
|
* @return Data received (16-bit long)
|
|
**********************************************************************/
|
|
uint16_t SPI_ReceiveData(LPC_SPI_TypeDef* SPIx)
|
|
{
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
|
|
return ((uint16_t) (SPIx->SPDR & SPI_SPDR_BITMASK));
|
|
}
|
|
|
|
/*********************************************************************//**
|
|
* @brief SPI Read write data function
|
|
* @param[in] SPIx Pointer to SPI peripheral, should be LPC_SPI
|
|
* @param[in] dataCfg Pointer to a SPI_DATA_SETUP_Type structure that
|
|
* contains specified information about transmit
|
|
* data configuration.
|
|
* @param[in] xfType Transfer type, should be:
|
|
* - SPI_TRANSFER_POLLING: Polling mode
|
|
* - SPI_TRANSFER_INTERRUPT: Interrupt mode
|
|
* @return Actual Data length has been transferred in polling mode.
|
|
* In interrupt mode, always return (0)
|
|
* Return (-1) if error.
|
|
* Note: This function can be used in both master and slave mode.
|
|
***********************************************************************/
|
|
int32_t SPI_ReadWrite (LPC_SPI_TypeDef *SPIx, SPI_DATA_SETUP_Type *dataCfg, \
|
|
SPI_TRANSFER_Type xfType)
|
|
{
|
|
uint8_t *rdata8;
|
|
uint8_t *wdata8;
|
|
uint16_t *rdata16;
|
|
uint16_t *wdata16;
|
|
uint32_t stat;
|
|
uint32_t temp;
|
|
uint8_t dataword;
|
|
|
|
//read for empty buffer
|
|
temp = SPIx->SPDR;
|
|
//dummy to clear status
|
|
temp = SPIx->SPSR;
|
|
dataCfg->counter = 0;
|
|
dataCfg->status = 0;
|
|
|
|
if(SPI_GetDataSize (SPIx) == 8)
|
|
dataword = 0;
|
|
else dataword = 1;
|
|
if (xfType == SPI_TRANSFER_POLLING){
|
|
|
|
if (dataword == 0){
|
|
rdata8 = (uint8_t *)dataCfg->rx_data;
|
|
wdata8 = (uint8_t *)dataCfg->tx_data;
|
|
} else {
|
|
rdata16 = (uint16_t *)dataCfg->rx_data;
|
|
wdata16 = (uint16_t *)dataCfg->tx_data;
|
|
}
|
|
|
|
while(dataCfg->counter < dataCfg->length)
|
|
{
|
|
// Write data to buffer
|
|
if(dataCfg->tx_data == NULL){
|
|
if (dataword == 0){
|
|
SPI_SendData(SPIx, 0xFF);
|
|
} else {
|
|
SPI_SendData(SPIx, 0xFFFF);
|
|
}
|
|
} else {
|
|
if (dataword == 0){
|
|
SPI_SendData(SPIx, *wdata8);
|
|
wdata8++;
|
|
} else {
|
|
SPI_SendData(SPIx, *wdata16);
|
|
wdata16++;
|
|
}
|
|
}
|
|
// Wait for transfer complete
|
|
while (!((stat = SPIx->SPSR) & SPI_SPSR_SPIF));
|
|
// Check for error
|
|
if (stat & (SPI_SPSR_ABRT | SPI_SPSR_MODF | SPI_SPSR_ROVR | SPI_SPSR_WCOL)){
|
|
// save status
|
|
dataCfg->status = stat | SPI_STAT_ERROR;
|
|
return (dataCfg->counter);
|
|
}
|
|
// Read data from SPI dat
|
|
temp = (uint32_t) SPI_ReceiveData(SPIx);
|
|
|
|
// Store data to destination
|
|
if (dataCfg->rx_data != NULL)
|
|
{
|
|
if (dataword == 0){
|
|
*(rdata8) = (uint8_t) temp;
|
|
rdata8++;
|
|
} else {
|
|
*(rdata16) = (uint16_t) temp;
|
|
rdata16++;
|
|
}
|
|
}
|
|
// Increase counter
|
|
if (dataword == 0){
|
|
dataCfg->counter++;
|
|
} else {
|
|
dataCfg->counter += 2;
|
|
}
|
|
}
|
|
|
|
// Return length of actual data transferred
|
|
// save status
|
|
dataCfg->status = stat | SPI_STAT_DONE;
|
|
return (dataCfg->counter);
|
|
}
|
|
// Interrupt mode
|
|
else {
|
|
|
|
// Check if interrupt flag is already set
|
|
if(SPIx->SPINT & SPI_SPINT_INTFLAG){
|
|
SPIx->SPINT = SPI_SPINT_INTFLAG;
|
|
}
|
|
if (dataCfg->counter < dataCfg->length){
|
|
// Write data to buffer
|
|
if(dataCfg->tx_data == NULL){
|
|
if (dataword == 0){
|
|
SPI_SendData(SPIx, 0xFF);
|
|
} else {
|
|
SPI_SendData(SPIx, 0xFFFF);
|
|
}
|
|
} else {
|
|
if (dataword == 0){
|
|
SPI_SendData(SPIx, (*(uint8_t *)dataCfg->tx_data));
|
|
} else {
|
|
SPI_SendData(SPIx, (*(uint16_t *)dataCfg->tx_data));
|
|
}
|
|
}
|
|
SPI_IntCmd(SPIx, ENABLE);
|
|
} else {
|
|
// Save status
|
|
dataCfg->status = SPI_STAT_DONE;
|
|
}
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
|
|
/********************************************************************//**
|
|
* @brief Enable or disable SPIx interrupt.
|
|
* @param[in] SPIx SPI peripheral selected, should be LPC_SPI
|
|
* @param[in] NewState New state of specified UART interrupt type,
|
|
* should be:
|
|
* - ENALBE: Enable this SPI interrupt.
|
|
* - DISALBE: Disable this SPI interrupt.
|
|
* @return None
|
|
*********************************************************************/
|
|
void SPI_IntCmd(LPC_SPI_TypeDef *SPIx, FunctionalState NewState)
|
|
{
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
CHECK_PARAM(PARAM_FUNCTIONALSTATE(NewState));
|
|
|
|
if (NewState == ENABLE)
|
|
{
|
|
SPIx->SPCR |= SPI_SPCR_SPIE;
|
|
}
|
|
else
|
|
{
|
|
SPIx->SPCR &= (~SPI_SPCR_SPIE) & SPI_SPCR_BITMASK;
|
|
}
|
|
}
|
|
|
|
|
|
/********************************************************************//**
|
|
* @brief Checks whether the SPI interrupt flag is set or not.
|
|
* @param[in] SPIx SPI peripheral selected, should be LPC_SPI
|
|
* @return The new state of SPI Interrupt Flag (SET or RESET)
|
|
*********************************************************************/
|
|
IntStatus SPI_GetIntStatus (LPC_SPI_TypeDef *SPIx)
|
|
{
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
|
|
return ((SPIx->SPINT & SPI_SPINT_INTFLAG) ? SET : RESET);
|
|
}
|
|
|
|
/********************************************************************//**
|
|
* @brief Clear SPI interrupt flag.
|
|
* @param[in] SPIx SPI peripheral selected, should be LPC_SPI
|
|
* @return None
|
|
*********************************************************************/
|
|
void SPI_ClearIntPending(LPC_SPI_TypeDef *SPIx)
|
|
{
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
|
|
SPIx->SPINT = SPI_SPINT_INTFLAG;
|
|
}
|
|
|
|
/********************************************************************//**
|
|
* @brief Get current value of SPI Status register in SPIx peripheral.
|
|
* @param[in] SPIx SPI peripheral selected, should be LPC_SPI
|
|
* @return Current value of SPI Status register in SPI peripheral.
|
|
* Note: The return value of this function must be used with
|
|
* SPI_CheckStatus() to determine current flag status
|
|
* corresponding to each SPI status type. Because some flags in
|
|
* SPI Status register will be cleared after reading, the next reading
|
|
* SPI Status register could not be correct. So this function used to
|
|
* read SPI status register in one time only, then the return value
|
|
* used to check all flags.
|
|
*********************************************************************/
|
|
uint32_t SPI_GetStatus(LPC_SPI_TypeDef* SPIx)
|
|
{
|
|
CHECK_PARAM(PARAM_SPIx(SPIx));
|
|
|
|
return (SPIx->SPSR & SPI_SPSR_BITMASK);
|
|
}
|
|
|
|
/********************************************************************//**
|
|
* @brief Checks whether the specified SPI Status flag is set or not
|
|
* via inputSPIStatus parameter.
|
|
* @param[in] inputSPIStatus Value to check status of each flag type.
|
|
* This value is the return value from SPI_GetStatus().
|
|
* @param[in] SPIStatus Specifies the SPI status flag to check,
|
|
* should be one of the following:
|
|
- SPI_STAT_ABRT: Slave abort.
|
|
- SPI_STAT_MODF: Mode fault.
|
|
- SPI_STAT_ROVR: Read overrun.
|
|
- SPI_STAT_WCOL: Write collision.
|
|
- SPI_STAT_SPIF: SPI transfer complete.
|
|
* @return The new state of SPIStatus (SET or RESET)
|
|
*********************************************************************/
|
|
FlagStatus SPI_CheckStatus (uint32_t inputSPIStatus, uint8_t SPIStatus)
|
|
{
|
|
CHECK_PARAM(PARAM_SPI_STAT(SPIStatus));
|
|
|
|
return ((inputSPIStatus & SPIStatus) ? SET : RESET);
|
|
}
|
|
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
#endif /* _SPI */
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/* --------------------------------- End Of File ------------------------------ */
|