mirror of
https://github.com/jialexd/sdk-ameba-v4.0c_180328.git
synced 2024-11-24 23:14:18 +00:00
1311 lines
38 KiB
C
1311 lines
38 KiB
C
|
/** mbed Microcontroller Library
|
||
|
******************************************************************************
|
||
|
* @file spi_api.c
|
||
|
* @author
|
||
|
* @version V1.0.0
|
||
|
* @date 2016-08-01
|
||
|
* @brief This file provides mbed API for SPI.
|
||
|
******************************************************************************
|
||
|
* @attention
|
||
|
*
|
||
|
* This module is a confidential and proprietary property of RealTek and
|
||
|
* possession or use of this module requires written permission of RealTek.
|
||
|
*
|
||
|
* Copyright(c) 2016, Realtek Semiconductor Corporation. All rights reserved.
|
||
|
******************************************************************************
|
||
|
*/
|
||
|
#include "objects.h"
|
||
|
#include "spi_api.h"
|
||
|
#include "spi_ex_api.h"
|
||
|
#include "PinNames.h"
|
||
|
#include "pinmap.h"
|
||
|
|
||
|
typedef struct {
|
||
|
GDMA_InitTypeDef SSITxGdmaInitStruct;
|
||
|
GDMA_InitTypeDef SSIRxGdmaInitStruct;
|
||
|
IRQn_Type IrqNum;
|
||
|
SPI_TypeDef *spi_dev;
|
||
|
|
||
|
void (*RxCompCallback)(void *Para);
|
||
|
void *RxCompCbPara;
|
||
|
void (*TxCompCallback)(void *Para);
|
||
|
void *TxCompCbPara;
|
||
|
void (*TxIdleCallback)(void *Para);
|
||
|
void *TxIdleCbPara;
|
||
|
|
||
|
void *RxData;
|
||
|
void *TxData;
|
||
|
u32 RxLength;
|
||
|
u32 TxLength;
|
||
|
|
||
|
u32 Index;
|
||
|
u32 Role;
|
||
|
|
||
|
/* mbed var */
|
||
|
u32 dma_en;
|
||
|
}HAL_SSI_ADAPTOR, *PHAL_SSI_ADAPTOR;
|
||
|
|
||
|
HAL_SSI_ADAPTOR ssi_adapter_g[2];
|
||
|
SSI_InitTypeDef SSI_InitStruct[2];
|
||
|
|
||
|
void spi_flush_rx_fifo (spi_t *obj);
|
||
|
|
||
|
static void spi_tx_done_callback(VOID *spi_obj)
|
||
|
{
|
||
|
spi_t *obj = (spi_t *)spi_obj;
|
||
|
spi_irq_handler handler;
|
||
|
|
||
|
if (obj->state & SPI_STATE_TX_BUSY) {
|
||
|
obj->state &= ~SPI_STATE_TX_BUSY;
|
||
|
if (obj->irq_handler) {
|
||
|
handler = (spi_irq_handler)obj->irq_handler;
|
||
|
handler(obj->irq_id, SpiTxIrq);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void spi_rx_done_callback(VOID *spi_obj)
|
||
|
{
|
||
|
spi_t *obj = (spi_t *)spi_obj;
|
||
|
spi_irq_handler handler;
|
||
|
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
if (obj->irq_handler) {
|
||
|
handler = (spi_irq_handler)obj->irq_handler;
|
||
|
handler(obj->irq_id, SpiRxIrq);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Bus Idle: Real TX done, TX FIFO empty and bus shift all data out already
|
||
|
void spi_bus_tx_done_callback(VOID *spi_obj)
|
||
|
{
|
||
|
spi_t *obj = (spi_t *)spi_obj;
|
||
|
spi_irq_handler handler;
|
||
|
|
||
|
if (obj->bus_tx_done_handler) {
|
||
|
handler = (spi_irq_handler)obj->bus_tx_done_handler;
|
||
|
handler(obj->bus_tx_done_irq_id, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Set SPI interrupt bus tx done handler if needed.
|
||
|
* @param obj: spi object define in application software.
|
||
|
* @param handler: interrupt bus tx done callback function
|
||
|
* @param id: interrupt callback parameter
|
||
|
* @retval none
|
||
|
*/
|
||
|
void spi_bus_tx_done_irq_hook(spi_t *obj, spi_irq_handler handler, uint32_t id)
|
||
|
{
|
||
|
obj->bus_tx_done_handler = (u32)handler;
|
||
|
obj->bus_tx_done_irq_id = (u32)id;
|
||
|
}
|
||
|
|
||
|
static u32 ssi_interrupt(void *Adaptor)
|
||
|
{
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = (PHAL_SSI_ADAPTOR) Adaptor;
|
||
|
u32 InterruptStatus = SSI_GetIsr(ssi_adapter->spi_dev);
|
||
|
u32 DataFrameSize = SSI_GetDataFrameSize(ssi_adapter->spi_dev);
|
||
|
|
||
|
SSI_SetIsrClean(ssi_adapter->spi_dev, InterruptStatus);
|
||
|
|
||
|
if (InterruptStatus & (BIT_ISR_TXOIS | BIT_ISR_RXUIS | BIT_ISR_RXOIS | BIT_ISR_MSTIS)) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_INFO, "[INT] Tx/Rx Warning %x \n", InterruptStatus);
|
||
|
}
|
||
|
|
||
|
if ((InterruptStatus & BIT_ISR_RXFIS) ) {
|
||
|
u32 TransLen = 0;
|
||
|
|
||
|
TransLen = SSI_ReceiveData(ssi_adapter->spi_dev, ssi_adapter->RxData, ssi_adapter->RxLength);
|
||
|
ssi_adapter->RxLength -= TransLen;
|
||
|
if (DataFrameSize > 8) {
|
||
|
// 16~9 bits mode
|
||
|
ssi_adapter->RxData = (void*)(((u16*)ssi_adapter->RxData) + TransLen);
|
||
|
} else {
|
||
|
// 8~4 bits mode
|
||
|
ssi_adapter->RxData = (void*)(((u8*)ssi_adapter->RxData) + TransLen);
|
||
|
}
|
||
|
|
||
|
if (ssi_adapter->RxLength == 0) {
|
||
|
SSI_INTConfig(ssi_adapter->spi_dev, (BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM), DISABLE);
|
||
|
if (ssi_adapter->RxCompCallback != NULL) {
|
||
|
ssi_adapter->RxCompCallback(ssi_adapter->RxCompCbPara);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (InterruptStatus & BIT_ISR_TXEIS) {
|
||
|
u32 TransLen = 0;
|
||
|
volatile u32 bus_busy;
|
||
|
u32 i;
|
||
|
|
||
|
/* all data complete */
|
||
|
if (ssi_adapter->TxLength == 0) {
|
||
|
SSI_INTConfig(ssi_adapter->spi_dev, (BIT_IMR_TXOIM | BIT_IMR_TXEIM), DISABLE);
|
||
|
for (i=0;i<1000000;i++) {
|
||
|
bus_busy = SSI_Busy(ssi_adapter->spi_dev);
|
||
|
if (!bus_busy) {
|
||
|
break; // break the for loop
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If it's not a dummy TX for master read SPI, then call the TX_done callback
|
||
|
if (ssi_adapter->TxData != NULL) {
|
||
|
if (ssi_adapter->TxIdleCallback != NULL) {
|
||
|
ssi_adapter->TxIdleCallback(ssi_adapter->TxIdleCbPara);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
TransLen = SSI_SendData(ssi_adapter->spi_dev, ssi_adapter->TxData,
|
||
|
ssi_adapter->TxLength, ssi_adapter->Role);
|
||
|
|
||
|
ssi_adapter->TxLength -= TransLen;
|
||
|
if (DataFrameSize > 8) {
|
||
|
// 16~9 bits mode
|
||
|
ssi_adapter->TxData = (void*)(((u16*)ssi_adapter->TxData) + TransLen);
|
||
|
} else {
|
||
|
// 8~4 bits mode
|
||
|
ssi_adapter->TxData = (void*)(((u8*)ssi_adapter->TxData) + TransLen);
|
||
|
}
|
||
|
|
||
|
/* all data write into fifo */
|
||
|
if (ssi_adapter->TxLength == 0) {
|
||
|
SSI_INTConfig(ssi_adapter->spi_dev, (BIT_IMR_TXOIM), DISABLE);
|
||
|
// If it's not a dummy TX for master read SPI, then call the TX_done callback
|
||
|
if (ssi_adapter->TxData != NULL) {
|
||
|
if (ssi_adapter->TxCompCallback != NULL) {
|
||
|
ssi_adapter->TxCompCallback(ssi_adapter->TxCompCbPara);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static u32 ssi_int_read(void *Adapter, void *RxData, u32 Length)
|
||
|
{
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = (PHAL_SSI_ADAPTOR) Adapter;
|
||
|
u32 DataFrameSize = SSI_GetDataFrameSize(ssi_adapter->spi_dev);
|
||
|
|
||
|
assert_param(Length != 0);
|
||
|
|
||
|
/* As a Slave mode, if the peer(Master) side is power off, the BUSY flag is always on */
|
||
|
if (SSI_Busy(ssi_adapter->spi_dev)) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "ssi_int_read: SSI is busy\n");
|
||
|
return _FALSE;
|
||
|
}
|
||
|
|
||
|
if (DataFrameSize > 8) {
|
||
|
/* 16~9 bits mode */
|
||
|
ssi_adapter->RxLength = Length >> 1; // 2 bytes(16 bit) every transfer
|
||
|
} else {
|
||
|
/* 8~4 bits mode */
|
||
|
ssi_adapter->RxLength = Length; // 1 byte(8 bit) every transfer
|
||
|
}
|
||
|
|
||
|
ssi_adapter->RxData = RxData;
|
||
|
|
||
|
SSI_INTConfig(ssi_adapter->spi_dev, (BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM), ENABLE);
|
||
|
|
||
|
return _TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
static u32 ssi_int_write(void *Adapter, u8 *pTxData, u32 Length)
|
||
|
{
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = (PHAL_SSI_ADAPTOR) Adapter;
|
||
|
u32 DataFrameSize = SSI_GetDataFrameSize(ssi_adapter->spi_dev);
|
||
|
|
||
|
assert_param(Length != 0);
|
||
|
|
||
|
if (DataFrameSize > 8) {
|
||
|
/* 16~9 bits mode */
|
||
|
ssi_adapter->TxLength = Length >> 1; // 2 bytes(16 bit) every transfer
|
||
|
} else {
|
||
|
/* 8~4 bits mode */
|
||
|
ssi_adapter->TxLength = Length; // 1 byte(8 bit) every transfer
|
||
|
}
|
||
|
|
||
|
ssi_adapter->TxData = (void*)pTxData;
|
||
|
SSI_INTConfig(ssi_adapter->spi_dev, (BIT_IMR_TXOIM | BIT_IMR_TXEIM), ENABLE);
|
||
|
|
||
|
return _TRUE;
|
||
|
}
|
||
|
|
||
|
/* GDMA IRQ Handler */
|
||
|
static void ssi_dma_tx_irq(void *Data)
|
||
|
{
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = (PHAL_SSI_ADAPTOR) Data;
|
||
|
PGDMA_InitTypeDef GDMA_InitStruct;
|
||
|
|
||
|
GDMA_InitStruct = &ssi_adapter->SSITxGdmaInitStruct;
|
||
|
|
||
|
/* Clear Pending ISR */
|
||
|
GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
GDMA_Cmd(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, DISABLE);
|
||
|
|
||
|
/* Call user TX complete callback */
|
||
|
if (NULL != ssi_adapter->TxCompCallback) {
|
||
|
ssi_adapter->TxCompCallback(ssi_adapter->TxCompCbPara);
|
||
|
}
|
||
|
|
||
|
SSI_SetDmaEnable(ssi_adapter->spi_dev, DISABLE, BIT_SHIFT_DMACR_TDMAE);
|
||
|
|
||
|
GDMA_ChnlFree(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
|
||
|
ssi_adapter->dma_en &= ~SPI_DMA_TX_EN;
|
||
|
}
|
||
|
|
||
|
static void ssi_dma_rx_irq(void *Data)
|
||
|
{
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = (PHAL_SSI_ADAPTOR) Data;
|
||
|
PGDMA_InitTypeDef GDMA_InitStruct;
|
||
|
|
||
|
GDMA_InitStruct = &ssi_adapter->SSIRxGdmaInitStruct;
|
||
|
|
||
|
/* Clear Pending ISR */
|
||
|
GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
GDMA_Cmd(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, DISABLE);
|
||
|
|
||
|
/* Set SSI DMA Disable */
|
||
|
SSI_SetDmaEnable(ssi_adapter->spi_dev, DISABLE, BIT_SHIFT_DMACR_RDMAE);
|
||
|
|
||
|
/* Call user RX complete callback */
|
||
|
if (NULL != ssi_adapter->RxCompCallback) {
|
||
|
ssi_adapter->RxCompCallback(ssi_adapter->RxCompCbPara);
|
||
|
}
|
||
|
|
||
|
GDMA_ChnlFree(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
|
||
|
ssi_adapter->dma_en &= ~SPI_DMA_RX_EN;
|
||
|
}
|
||
|
|
||
|
static u32 ssi_dma_send(void *Adapter, u8 *pTxData, u32 Length)
|
||
|
{
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = (PHAL_SSI_ADAPTOR) Adapter;
|
||
|
u32 ret = _TRUE;
|
||
|
|
||
|
assert_param(Length != 0);
|
||
|
assert_param(pTxData != NULL);
|
||
|
|
||
|
if (ssi_adapter->dma_en & SPI_DMA_TX_EN) {
|
||
|
return _FALSE;
|
||
|
}
|
||
|
|
||
|
ssi_adapter->dma_en |= SPI_DMA_TX_EN;
|
||
|
ssi_adapter->TxLength = Length;
|
||
|
ssi_adapter->TxData = (void*)pTxData;
|
||
|
|
||
|
SSI_SetDmaEnable(ssi_adapter->spi_dev, ENABLE, BIT_SHIFT_DMACR_TDMAE);
|
||
|
|
||
|
ret = SSI_TXGDMA_Init(ssi_adapter->Index, &ssi_adapter->SSITxGdmaInitStruct,
|
||
|
ssi_adapter, (IRQ_FUN) ssi_dma_tx_irq,
|
||
|
pTxData, Length);
|
||
|
|
||
|
NVIC_SetPriority(GDMA_IrqNum[0][ssi_adapter->SSITxGdmaInitStruct.GDMA_ChNum], 10);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static u32 ssi_dma_recv(void *Adapter, u8 *pRxData, u32 Length)
|
||
|
{
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = (PHAL_SSI_ADAPTOR) Adapter;
|
||
|
u32 ret = _TRUE;
|
||
|
|
||
|
assert_param(Length != 0);
|
||
|
assert_param(pRxData != NULL);
|
||
|
|
||
|
if (ssi_adapter->dma_en & SPI_DMA_RX_EN) {
|
||
|
return _FALSE;
|
||
|
}
|
||
|
|
||
|
ssi_adapter->dma_en |= SPI_DMA_RX_EN;
|
||
|
ssi_adapter->RxLength = Length;
|
||
|
ssi_adapter->RxData = (void*)pRxData;
|
||
|
|
||
|
ret = SSI_RXGDMA_Init(ssi_adapter->Index, &ssi_adapter->SSIRxGdmaInitStruct,
|
||
|
ssi_adapter, (IRQ_FUN) ssi_dma_rx_irq,
|
||
|
pRxData, Length);
|
||
|
|
||
|
NVIC_SetPriority(GDMA_IrqNum[0][ssi_adapter->SSIRxGdmaInitStruct.GDMA_ChNum], 11);
|
||
|
|
||
|
/* Set SSI DMA Enable */
|
||
|
SSI_SetDmaEnable(ssi_adapter->spi_dev, ENABLE, BIT_SHIFT_DMACR_RDMAE);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static u32 spi_stop_recv(spi_t *obj)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
PGDMA_InitTypeDef GDMA_InitStruct = &ssi_adapter->SSIRxGdmaInitStruct;
|
||
|
u32 DMAStopAddr = 0;
|
||
|
u32 ReceivedCnt;
|
||
|
u8 DmaMode = 0;
|
||
|
|
||
|
SSI_INTConfig(ssi_adapter->spi_dev, (BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM), DISABLE);
|
||
|
|
||
|
if (ssi_adapter->dma_en & SPI_DMA_RX_EN) {
|
||
|
DmaMode=1;
|
||
|
/* Set SSI DMA Disable */
|
||
|
SSI_SetDmaEnable(ssi_adapter->spi_dev, DISABLE, BIT_SHIFT_DMACR_RDMAE);
|
||
|
|
||
|
/* Clear Pending ISR */
|
||
|
GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
GDMA_ChCleanAutoReload(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, CLEAN_RELOAD_SRC_DST);
|
||
|
GDMA_Cmd(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, DISABLE);
|
||
|
DMAStopAddr = GDMA_GetDstAddr(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
GDMA_ChnlFree(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
|
||
|
ReceivedCnt = DMAStopAddr - (u32)(ssi_adapter->RxData);
|
||
|
ssi_adapter->RxLength-= ReceivedCnt;
|
||
|
ssi_adapter->RxData = (u8 *)(ssi_adapter->RxData) + ReceivedCnt;
|
||
|
|
||
|
ssi_adapter->dma_en &= ~SPI_DMA_RX_EN;
|
||
|
}
|
||
|
|
||
|
if (ssi_adapter->RxLength > 0) {
|
||
|
ReceivedCnt = SSI_ReceiveData(ssi_adapter->spi_dev, ssi_adapter->RxData, ssi_adapter->RxLength);
|
||
|
if (DmaMode && (SSI_GetDataFrameSize(ssi_adapter->spi_dev) > 8))
|
||
|
ReceivedCnt=ReceivedCnt*2;
|
||
|
ssi_adapter->RxLength -= ReceivedCnt;
|
||
|
}
|
||
|
|
||
|
if (ssi_adapter->RxLength == 0) {
|
||
|
spi_flush_rx_fifo(obj);
|
||
|
}
|
||
|
|
||
|
obj->state &= ~ SPI_STATE_RX_BUSY;
|
||
|
|
||
|
return HAL_OK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Initializes the SPI device, include clock/function/interrupt/SPI registers.
|
||
|
* @param obj: spi object define in application software.
|
||
|
* @param mosi: MOSI PinName according to pinmux spec.
|
||
|
* @param miso: MISO PinName according to pinmux spec.
|
||
|
* @param sclk: SCLK PinName according to pinmux spec.
|
||
|
* @param ssel: CS PinName according to pinmux spec.
|
||
|
* @retval none
|
||
|
* @note must set obj->spi_index to MBED_SPI0 or MBED_SPI1 before using spi_init
|
||
|
*/
|
||
|
void spi_init (spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
|
||
|
if ((obj->spi_idx != MBED_SPI0) && (obj->spi_idx != MBED_SPI1)) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_ERROR, "spi_init: you should set spi_idx MBED_SPI0 or MBED_SPI1");
|
||
|
assert_param(0);
|
||
|
}
|
||
|
|
||
|
ssi_adapter->Index = spi_idx;
|
||
|
obj->sclk = (u8)sclk;
|
||
|
|
||
|
if (spi_idx == 1) {
|
||
|
ssi_adapter->Role = SSI_MASTER;
|
||
|
RCC_PeriphClockCmd(APBPeriph_SPI1, APBPeriph_SPI1_CLOCK, ENABLE);
|
||
|
Pinmux_Config(mosi, PINMUX_FUNCTION_SPIM);
|
||
|
Pinmux_Config(miso, PINMUX_FUNCTION_SPIM);
|
||
|
Pinmux_Config(sclk, PINMUX_FUNCTION_SPIM);
|
||
|
Pinmux_Config(ssel, PINMUX_FUNCTION_SPIM);
|
||
|
} else {
|
||
|
ssi_adapter->Role = SSI_SLAVE;
|
||
|
RCC_PeriphClockCmd(APBPeriph_SPI0, APBPeriph_SPI0_CLOCK, ENABLE);
|
||
|
Pinmux_Config(mosi, PINMUX_FUNCTION_SPIS);
|
||
|
Pinmux_Config(miso, PINMUX_FUNCTION_SPIS);
|
||
|
Pinmux_Config(sclk, PINMUX_FUNCTION_SPIS);
|
||
|
Pinmux_Config(ssel, PINMUX_FUNCTION_SPIS);
|
||
|
}
|
||
|
|
||
|
//TODO: Implement default setting structure.
|
||
|
SSI_StructInit(&SSI_InitStruct[spi_idx]);
|
||
|
if (spi_idx == 1) {
|
||
|
SSI_InitStruct[spi_idx].SPI_Role = SSI_MASTER;
|
||
|
} else {
|
||
|
PAD_PullCtrl(ssel, GPIO_PuPd_UP);
|
||
|
PAD_PullCtrl(sclk, GPIO_PuPd_UP);
|
||
|
}
|
||
|
|
||
|
ssi_adapter->spi_dev = SPI_DEV_TABLE[ssi_adapter->Index].SPIx;
|
||
|
ssi_adapter->IrqNum = SPI_DEV_TABLE[ssi_adapter->Index].IrqNum;
|
||
|
InterruptRegister((IRQ_FUN)ssi_interrupt, ssi_adapter->IrqNum, (u32)ssi_adapter, 10);
|
||
|
InterruptEn(ssi_adapter->IrqNum, 10);
|
||
|
|
||
|
SSI_Init(ssi_adapter->spi_dev, &SSI_InitStruct[spi_idx]);
|
||
|
|
||
|
ssi_adapter->TxCompCallback = spi_tx_done_callback;
|
||
|
ssi_adapter->TxCompCbPara = (void*)obj;
|
||
|
ssi_adapter->RxCompCallback = spi_rx_done_callback;
|
||
|
ssi_adapter->RxCompCbPara = (void*)obj;
|
||
|
ssi_adapter->TxIdleCallback = spi_bus_tx_done_callback;
|
||
|
ssi_adapter->TxIdleCbPara = (void*)obj;
|
||
|
obj->state = SPI_STATE_READY;
|
||
|
|
||
|
#ifdef CONFIG_GDMA_EN
|
||
|
ssi_adapter->dma_en = 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Deinitializes the SPI device, include interrupt/DMA/DISABLE SPI.
|
||
|
* @param obj: spi object define in application software.
|
||
|
* @retval none
|
||
|
*/
|
||
|
void spi_free (spi_t *obj)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
|
||
|
InterruptDis(ssi_adapter->IrqNum);
|
||
|
InterruptUnRegister(ssi_adapter->IrqNum);
|
||
|
|
||
|
SSI_INTConfig(ssi_adapter->spi_dev, (BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM), DISABLE);
|
||
|
|
||
|
if (ssi_adapter->dma_en & SPI_DMA_RX_EN) {
|
||
|
PGDMA_InitTypeDef GDMA_InitStruct = &ssi_adapter->SSIRxGdmaInitStruct;
|
||
|
|
||
|
/* Set SSI DMA Disable */
|
||
|
SSI_SetDmaEnable(ssi_adapter->spi_dev, DISABLE, BIT_SHIFT_DMACR_RDMAE);
|
||
|
|
||
|
/* Clear Pending ISR */
|
||
|
GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
GDMA_ChCleanAutoReload(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, CLEAN_RELOAD_SRC_DST);
|
||
|
GDMA_Cmd(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, DISABLE);
|
||
|
GDMA_ChnlFree(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
}
|
||
|
|
||
|
if (ssi_adapter->dma_en & SPI_DMA_TX_EN) {
|
||
|
PGDMA_InitTypeDef GDMA_InitStruct = &ssi_adapter->SSITxGdmaInitStruct;
|
||
|
|
||
|
/* Set SSI DMA Disable */
|
||
|
SSI_SetDmaEnable(ssi_adapter->spi_dev, DISABLE, BIT_SHIFT_DMACR_TDMAE);
|
||
|
|
||
|
/* Clear Pending ISR */
|
||
|
GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
GDMA_ChCleanAutoReload(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, CLEAN_RELOAD_SRC_DST);
|
||
|
GDMA_Cmd(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, DISABLE);
|
||
|
GDMA_ChnlFree(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum);
|
||
|
}
|
||
|
|
||
|
obj->state = 0;
|
||
|
|
||
|
SSI_Cmd(ssi_adapter->spi_dev, DISABLE);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Set SPI format,include DFS/Phase/Polarity.
|
||
|
* @param obj: spi object define in application software.
|
||
|
* @param bits: data frame size, 4-16 supported.
|
||
|
* @param mode: this parameter can be one of the following values:
|
||
|
* @arg 0 : [Polarity,Phase]=[0,0]
|
||
|
* @arg 1 : [Polarity,Phase]=[0,1]
|
||
|
* @arg 2 : [Polarity,Phase]=[1,0]
|
||
|
* @arg 3 : [Polarity,Phase]=[1,1]
|
||
|
* @param slave: this parameter can be one of the following values:
|
||
|
* @arg 0 : indicates role-master
|
||
|
* @arg 1 : indicates role-slave
|
||
|
* @retval none
|
||
|
*/
|
||
|
void spi_format (spi_t *obj, int bits, int mode, int slave)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
u32 SclkPhase;
|
||
|
u32 SclkPolarity;
|
||
|
u32 DataFrameSize = (bits - 1);
|
||
|
|
||
|
/*
|
||
|
* mode | POL PHA
|
||
|
* -----+--------
|
||
|
* 0 | 0 0
|
||
|
* 1 | 0 1
|
||
|
* 2 | 1 0
|
||
|
* 3 | 1 1
|
||
|
*
|
||
|
* SCPOL_INACTIVE_IS_LOW = 0,
|
||
|
* SCPOL_INACTIVE_IS_HIGH = 1
|
||
|
*
|
||
|
* SCPH_TOGGLES_IN_MIDDLE = 0,
|
||
|
* SCPH_TOGGLES_AT_START = 1
|
||
|
*/
|
||
|
switch (mode)
|
||
|
{
|
||
|
case 0:
|
||
|
SclkPolarity = SCPOL_INACTIVE_IS_LOW;
|
||
|
SclkPhase = SCPH_TOGGLES_IN_MIDDLE;
|
||
|
break;
|
||
|
case 1:
|
||
|
SclkPolarity = SCPOL_INACTIVE_IS_LOW;
|
||
|
SclkPhase = SCPH_TOGGLES_AT_START;
|
||
|
break;
|
||
|
case 2:
|
||
|
SclkPolarity = SCPOL_INACTIVE_IS_HIGH;
|
||
|
SclkPhase = SCPH_TOGGLES_IN_MIDDLE;
|
||
|
break;
|
||
|
case 3:
|
||
|
SclkPolarity = SCPOL_INACTIVE_IS_HIGH;
|
||
|
SclkPhase = SCPH_TOGGLES_AT_START;
|
||
|
break;
|
||
|
default: // same as 3
|
||
|
SclkPolarity = SCPOL_INACTIVE_IS_HIGH;
|
||
|
SclkPhase = SCPH_TOGGLES_AT_START;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (slave == 1) {
|
||
|
if (spi_idx == 0) {
|
||
|
ssi_adapter->Role = SSI_SLAVE;
|
||
|
} else {
|
||
|
assert_param(0);
|
||
|
}
|
||
|
} else {
|
||
|
if (spi_idx == 1) {
|
||
|
ssi_adapter->Role = SSI_MASTER;
|
||
|
} else {
|
||
|
assert_param(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SSI_SetSclkPhase(ssi_adapter->spi_dev, SclkPhase);
|
||
|
SSI_SetSclkPolarity(ssi_adapter->spi_dev, SclkPolarity);
|
||
|
SSI_SetDataFrameSize(ssi_adapter->spi_dev, DataFrameSize);
|
||
|
|
||
|
if (slave == 1) {
|
||
|
if (SclkPolarity == SCPOL_INACTIVE_IS_LOW) {
|
||
|
PAD_PullCtrl((u32)obj->sclk, GPIO_PuPd_DOWN);
|
||
|
}
|
||
|
else {
|
||
|
PAD_PullCtrl((u32)obj->sclk, GPIO_PuPd_UP);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Set SPI baudrate.
|
||
|
* @param obj: spi master object define in application software.
|
||
|
* @param hz: baudrate for SPI bus
|
||
|
* @retval none
|
||
|
* @note "hz" should be less or equal to half of the SPI IpClk
|
||
|
*/
|
||
|
void spi_frequency (spi_t *obj, int hz)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
|
||
|
SSI_SetBaud(ssi_adapter->spi_dev, (u32)hz, CPU_ClkGet(_FALSE)/2);
|
||
|
}
|
||
|
|
||
|
void spi_slave_select(spi_t *obj, ChipSelect slaveindex)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
|
||
|
if((ssi_adapter->Role == SSI_MASTER) && (spi_idx == 1)){
|
||
|
SSI_SetSlaveEnable(ssi_adapter->spi_dev,slaveindex);
|
||
|
} else {
|
||
|
assert_param(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static inline void ssi_write (spi_t *obj, int value)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
|
||
|
while (!SSI_Writeable(ssi_adapter->spi_dev));
|
||
|
SSI_WriteData(ssi_adapter->spi_dev, value);
|
||
|
}
|
||
|
|
||
|
static inline int ssi_read(spi_t *obj)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
|
||
|
while (!SSI_Readable(ssi_adapter->spi_dev));
|
||
|
return (int)SSI_ReadData(ssi_adapter->spi_dev);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Master send one frame use SPI.
|
||
|
* @param obj: spi master object define in application software.
|
||
|
* @param value: the data to transmit.
|
||
|
* @retval : data received from slave
|
||
|
*/
|
||
|
int spi_master_write (spi_t *obj, int value)
|
||
|
{
|
||
|
ssi_write(obj, value);
|
||
|
return ssi_read(obj);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Get slave readable && busy state.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @retval : slave Readable && Busy State
|
||
|
*/
|
||
|
int spi_slave_receive (spi_t *obj)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int Readable;
|
||
|
int Busy;
|
||
|
|
||
|
Readable = SSI_Readable(ssi_adapter->spi_dev);
|
||
|
Busy = (int)SSI_Busy(ssi_adapter->spi_dev);
|
||
|
return ((Readable && !Busy) ? 1 : 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Slave receive one frame use SPI.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @retval : data received from master
|
||
|
*/
|
||
|
int spi_slave_read (spi_t *obj)
|
||
|
{
|
||
|
return ssi_read(obj);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Slave send one frame use SPI.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @param value: the data to transmit.
|
||
|
* @retval none
|
||
|
*/
|
||
|
void spi_slave_write (spi_t *obj, int value)
|
||
|
{
|
||
|
ssi_write(obj, value);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Get SPI busy state.
|
||
|
* @param obj: spi object define in application software.
|
||
|
* @retval : current busy state
|
||
|
*/
|
||
|
int spi_busy (spi_t *obj)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
|
||
|
return (int)SSI_Busy(ssi_adapter->spi_dev);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief SPI device to flush rx fifo.
|
||
|
* @param obj: spi object define in application software.
|
||
|
* @retval none
|
||
|
*/
|
||
|
void spi_flush_rx_fifo (spi_t *obj)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
u32 rx_fifo_level;
|
||
|
u32 i;
|
||
|
|
||
|
while(SSI_Readable(ssi_adapter->spi_dev)){
|
||
|
rx_fifo_level = SSI_GetRxCount(ssi_adapter->spi_dev);
|
||
|
for(i=0;i<rx_fifo_level;i++) {
|
||
|
SSI_ReadData(ssi_adapter->spi_dev);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief slave recv target length data use interrupt mode.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be read.
|
||
|
* @retval : stream init status
|
||
|
*/
|
||
|
int32_t spi_slave_read_stream(spi_t *obj, char *rx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_RX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spis int rx: state(0x%x) is not ready\r\n",
|
||
|
obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
//DBG_PRINTF(MODULE_SPI, LEVEL_INFO, "rx_buffer addr: %X, length: %d\n", rx_buffer, length);
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
if ((ret = ssi_int_read(ssi_adapter, rx_buffer, length)) != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
}
|
||
|
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief slave send target length data use interrupt mode.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @param tx_buffer: buffer to be written to Tx FIFO.
|
||
|
* @param length: number of data bytes to be send.
|
||
|
* @retval : stream init status
|
||
|
*/
|
||
|
int32_t spi_slave_write_stream(spi_t *obj, char *tx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_TX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spis int tx: state(0x%x) is not ready\r\n",
|
||
|
obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_TX_BUSY;
|
||
|
if ((ret=ssi_int_write(ssi_adapter, (u8 *) tx_buffer, length)) != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_TX_BUSY;
|
||
|
}
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief master recv target length data use interrupt mode.
|
||
|
* @param obj: spi master object define in application software.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be read.
|
||
|
* @retval : stream init status
|
||
|
*/
|
||
|
int32_t spi_master_read_stream(spi_t *obj, char *rx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_RX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spim int rx: state(0x%x) is not ready\r\n",
|
||
|
obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
// wait bus idle
|
||
|
while(SSI_Busy(ssi_adapter->spi_dev));
|
||
|
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
if ((ret = ssi_int_read(ssi_adapter, rx_buffer, length)) == _TRUE) {
|
||
|
/* as Master mode, it need to push data to TX FIFO to generate clock out
|
||
|
then the slave can transmit data out */
|
||
|
// send some dummy data out
|
||
|
if ((ret=ssi_int_write(ssi_adapter, NULL, length)) != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
}
|
||
|
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief master send target length data use interrupt mode.
|
||
|
* @param obj: spi master object define in application software.
|
||
|
* @param tx_buffer: buffer to be written to Tx FIFO.
|
||
|
* @param length: number of data bytes to be send.
|
||
|
* @retval : stream init status
|
||
|
*/
|
||
|
int32_t spi_master_write_stream(spi_t *obj, char *tx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_TX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spim int tx: state(0x%x) is not ready\r\n",
|
||
|
obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_TX_BUSY;
|
||
|
/* as Master mode, sending data will receive data at sametime, so we need to
|
||
|
drop those received dummy data */
|
||
|
if ((ret=ssi_int_write(ssi_adapter, (u8 *) tx_buffer, length)) != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_TX_BUSY;
|
||
|
}
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief master send & recv target length data use interrupt mode.
|
||
|
* @param obj: spi master object define in application software.
|
||
|
* @param tx_buffer: buffer to be written to Tx FIFO.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be send & recv.
|
||
|
* @retval : stream init status
|
||
|
*/
|
||
|
int32_t spi_master_write_read_stream(spi_t *obj, char *tx_buffer,
|
||
|
char *rx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & (SPI_STATE_RX_BUSY|SPI_STATE_TX_BUSY)) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spim int trx: state(0x%x) is not ready\r\n",
|
||
|
obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
// wait bus idle
|
||
|
while(SSI_Busy(ssi_adapter->spi_dev));
|
||
|
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
/* as Master mode, sending data will receive data at sametime */
|
||
|
if ((ret = ssi_int_read(ssi_adapter, rx_buffer, length)) == _TRUE) {
|
||
|
obj->state |= SPI_STATE_TX_BUSY;
|
||
|
if ((ret=ssi_int_write(ssi_adapter, (u8 *) tx_buffer, length)) != _TRUE) {
|
||
|
obj->state &= ~(SPI_STATE_RX_BUSY|SPI_STATE_TX_BUSY);
|
||
|
// Disable RX IRQ
|
||
|
SSI_INTConfig(ssi_adapter->spi_dev, (BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM), DISABLE);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
obj->state &= ~(SPI_STATE_RX_BUSY);
|
||
|
}
|
||
|
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Set SPI interrupt handler if needed.
|
||
|
* @param obj: spi object define in application software.
|
||
|
* @param handler: interrupt callback function
|
||
|
* @param id: interrupt callback parameter
|
||
|
* @retval none
|
||
|
*/
|
||
|
void spi_irq_hook(spi_t *obj, spi_irq_handler handler, uint32_t id)
|
||
|
{
|
||
|
obj->irq_handler = (u32)handler;
|
||
|
obj->irq_id = (u32)id;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief slave recv target length data use DMA mode.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be read.
|
||
|
* @retval : stream init status
|
||
|
*/
|
||
|
int32_t spi_slave_read_stream_dma(spi_t *obj, char *rx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_RX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spim dma rx: state(0x%x) is not ready\r\n",
|
||
|
obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
ret = ssi_dma_recv(ssi_adapter, (u8 *) rx_buffer, length);
|
||
|
if (ret != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
}
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief slave send target length data use DMA mode.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @param tx_buffer: buffer to be written to Tx FIFO.
|
||
|
* @param length: number of data bytes to be send.
|
||
|
* @retval : stream init status
|
||
|
*/
|
||
|
int32_t spi_slave_write_stream_dma(spi_t *obj, char *tx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_TX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spis dma tx: state(0x%x) is not ready\r\n",
|
||
|
obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_TX_BUSY;
|
||
|
ret = ssi_dma_send(ssi_adapter, (u8 *) tx_buffer, length);
|
||
|
if (ret != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_TX_BUSY;
|
||
|
}
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
#define USE_DMA_WRITE_MASTER_READ 1
|
||
|
/**
|
||
|
* @brief master recv target length data use DMA mode.
|
||
|
* @param obj: spi master object define in application software.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be read.
|
||
|
* @retval : stream init status
|
||
|
* @note : DMA or Interrupt mode can be used to TX dummy data
|
||
|
*/
|
||
|
int32_t spi_master_read_stream_dma(spi_t *obj, char *rx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_RX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spim dma rx: state(0x%x) is not ready\r\n",
|
||
|
obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
ret = ssi_dma_recv(ssi_adapter, (u8 *) rx_buffer, length);
|
||
|
if (ret != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
}
|
||
|
|
||
|
// for master mode, we need to send data to generate clock out
|
||
|
#ifdef USE_DMA_WRITE_MASTER_READ
|
||
|
// TX DMA is on already, so use DMA to TX data
|
||
|
// Make the GDMA to use the rx_buffer too
|
||
|
|
||
|
ret = ssi_dma_send(ssi_adapter, (u8 *) rx_buffer, length);
|
||
|
if (ret != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
}
|
||
|
#else
|
||
|
// TX DMA isn't enabled, so we just use Interrupt mode to TX dummy data
|
||
|
if ((ret=ssi_int_write(ssi_adapter, NULL, length)) != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
}
|
||
|
#endif
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief master send target length data use DMA mode.
|
||
|
* @param obj: spi master object define in application software.
|
||
|
* @param tx_buffer: buffer to be written to Tx FIFO.
|
||
|
* @param length: number of data bytes to be send.
|
||
|
* @retval : stream init status
|
||
|
*/
|
||
|
int32_t spi_master_write_stream_dma(spi_t *obj, char *tx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_TX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spim dma tx: state(0x%x) is not ready\r\n",
|
||
|
obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_TX_BUSY;
|
||
|
ret = ssi_dma_send(ssi_adapter, (u8 *) tx_buffer, length);
|
||
|
if (ret != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_TX_BUSY;
|
||
|
}
|
||
|
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief master send & recv target length data use DMA mode.
|
||
|
* @param obj: spi master object define in application software.
|
||
|
* @param tx_buffer: buffer to be written to Tx FIFO.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be send & recv.
|
||
|
* @retval : stream init status
|
||
|
*/
|
||
|
int32_t spi_master_write_read_stream_dma(spi_t *obj, char *tx_buffer,
|
||
|
char *rx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int32_t ret;
|
||
|
|
||
|
if (obj->state & (SPI_STATE_RX_BUSY|SPI_STATE_TX_BUSY)) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spi_master_write_and_read_stream: state(0x%x) is not ready\r\n", obj->state);
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
ret = ssi_dma_recv(ssi_adapter, (u8 *) rx_buffer, length);
|
||
|
if (ret != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_TX_BUSY;
|
||
|
ret = ssi_dma_send(ssi_adapter, (u8 *) tx_buffer, length);
|
||
|
if (ret != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_TX_BUSY;
|
||
|
return HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
return (ret == _TRUE) ? HAL_OK : HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief slave recv target length data use DMA mode and timeout mechanism.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be read.
|
||
|
* @param timeout_ms: timeout waiting time.
|
||
|
* @retval : number of bytes read already
|
||
|
*/
|
||
|
int32_t spi_slave_read_stream_dma_timeout(spi_t *obj, char *rx_buffer, uint32_t length, uint32_t timeout_ms)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
int ret, timeout = 0;
|
||
|
uint32_t StartCount = 0;
|
||
|
|
||
|
assert_param(timeout_ms > 0);
|
||
|
|
||
|
if (obj->state & SPI_STATE_RX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spi_slave_read_stream_dma: state(0x%x) is not ready\r\n", obj->state);
|
||
|
return -HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
ret = ssi_dma_recv(ssi_adapter, (u8 *) rx_buffer, length);
|
||
|
if (ret != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
return -HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
StartCount = SYSTIMER_TickGet();
|
||
|
|
||
|
while (1) {
|
||
|
/* complete */
|
||
|
if ((obj->state & SPI_STATE_RX_BUSY) == 0) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* time out */
|
||
|
if (SYSTIMER_GetPassTime(StartCount) > timeout_ms) {
|
||
|
ret = spi_stop_recv(obj);
|
||
|
obj->state &= ~ SPI_STATE_RX_BUSY;
|
||
|
timeout = 1;
|
||
|
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_INFO, "Slave is timeout\n");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//if (SSI_GetDataFrameSize(SPIx) > 8){
|
||
|
// ssi_adapter->RxLength <<= 1;
|
||
|
//}
|
||
|
|
||
|
if(timeout)
|
||
|
return (length - ssi_adapter->RxLength);
|
||
|
else
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief slave recv target length data use DMA mode and stop if the spi bus is idle.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be read.
|
||
|
* @retval : number of bytes read already
|
||
|
*/
|
||
|
int32_t spi_slave_read_stream_dma_terminate(spi_t *obj, char *rx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
SPI_TypeDef *SPIx = SPI_DEV_TABLE[spi_idx].SPIx;
|
||
|
int ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_RX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spi_slave_read_stream_dma: state(0x%x) is not ready\r\n", obj->state);
|
||
|
return -HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
ret = ssi_dma_recv(ssi_adapter, (u8 *) rx_buffer, length);
|
||
|
if (ret != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
return -HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
while(obj->state & SPI_STATE_RX_BUSY){
|
||
|
if(SSI_Busy(SPIx) == 0){
|
||
|
ret = spi_stop_recv(obj);
|
||
|
goto EndOfDMACS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EndOfDMACS:
|
||
|
if((obj->state & SPI_STATE_RX_BUSY) != 0){
|
||
|
obj->state &= ~ SPI_STATE_RX_BUSY;
|
||
|
}
|
||
|
|
||
|
//if (SSI_GetDataFrameSize(SPIx) > 8) {
|
||
|
// ssi_adapter->RxLength <<= 1;
|
||
|
//}
|
||
|
|
||
|
return (length - ssi_adapter->RxLength);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Slave device to flush tx fifo.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @note : It will discard all data in both tx fifo and rx fifo
|
||
|
*/
|
||
|
void spi_slave_flush_fifo(spi_t *obj)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
SPI_TypeDef *SPIx = SPI_DEV_TABLE[spi_idx].SPIx;
|
||
|
|
||
|
SSI_Cmd(SPIx, DISABLE);
|
||
|
SSI_Cmd(SPIx, ENABLE);
|
||
|
|
||
|
obj->state &= ~SPI_STATE_TX_BUSY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief slave recv target length data use interrupt mode and timeout mechanism.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be read.
|
||
|
* @param timeout_ms: timeout waiting time.
|
||
|
* @retval : number of bytes read already
|
||
|
*/
|
||
|
int32_t spi_slave_read_stream_timeout(spi_t *obj, char *rx_buffer, uint32_t length, uint32_t timeout_ms)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
SPI_TypeDef *SPIx = SPI_DEV_TABLE[spi_idx].SPIx;
|
||
|
uint32_t ret;
|
||
|
uint32_t timeout = 0;
|
||
|
uint32_t StartCount = 0;
|
||
|
|
||
|
assert_param(timeout_ms > 0);
|
||
|
|
||
|
if (obj->state & SPI_STATE_RX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spi_slave_read_stream: state(0x%x) is not ready\r\n", obj->state);
|
||
|
return -HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
if ((ret = ssi_int_read(ssi_adapter, rx_buffer, length)) != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
return -HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
|
||
|
StartCount = SYSTIMER_TickGet();
|
||
|
while (1) {
|
||
|
/* complete */
|
||
|
if ((obj->state & SPI_STATE_RX_BUSY) == 0) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* time out */
|
||
|
if (SYSTIMER_GetPassTime(StartCount) > timeout_ms) {
|
||
|
ret = spi_stop_recv(obj);
|
||
|
obj->state &= ~ SPI_STATE_RX_BUSY;
|
||
|
timeout = 1;
|
||
|
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_INFO, "Slave is timeout\n");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (SSI_GetDataFrameSize(SPIx) > 8){
|
||
|
ssi_adapter->RxLength <<= 1;
|
||
|
}
|
||
|
|
||
|
if(timeout)
|
||
|
return (length - ssi_adapter->RxLength);
|
||
|
else
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief slave recv target length data use interrupt mode and stop if the spi bus is idle.
|
||
|
* @param obj: spi slave object define in application software.
|
||
|
* @param rx_buffer: buffer to save data read from SPI FIFO.
|
||
|
* @param length: number of data bytes to be read.
|
||
|
* @retval : number of bytes read already
|
||
|
*/
|
||
|
int32_t spi_slave_read_stream_terminate(spi_t *obj, char *rx_buffer, uint32_t length)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
PHAL_SSI_ADAPTOR ssi_adapter = &ssi_adapter_g[spi_idx];
|
||
|
SPI_TypeDef *SPIx = SPI_DEV_TABLE[spi_idx].SPIx;
|
||
|
int ret;
|
||
|
|
||
|
if (obj->state & SPI_STATE_RX_BUSY) {
|
||
|
DBG_PRINTF(MODULE_SPI, LEVEL_WARN, "spi_slave_read_stream_dma: state(0x%x) is not ready\r\n", obj->state);
|
||
|
return -HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
obj->state |= SPI_STATE_RX_BUSY;
|
||
|
if ((ret = ssi_int_read(ssi_adapter, rx_buffer, length)) != _TRUE) {
|
||
|
obj->state &= ~SPI_STATE_RX_BUSY;
|
||
|
return -HAL_BUSY;
|
||
|
}
|
||
|
|
||
|
|
||
|
while(obj->state & SPI_STATE_RX_BUSY) {
|
||
|
if(SSI_Busy(SPIx) == 0){
|
||
|
ret = spi_stop_recv(obj);
|
||
|
goto EndOfCS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EndOfCS:
|
||
|
if((obj->state & SPI_STATE_RX_BUSY) != 0){
|
||
|
obj->state &= ~ SPI_STATE_RX_BUSY;
|
||
|
}
|
||
|
|
||
|
if (SSI_GetDataFrameSize(SPIx) > 8) {
|
||
|
ssi_adapter->RxLength <<= 1;
|
||
|
}
|
||
|
|
||
|
return (length - ssi_adapter->RxLength);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Open SPI device clock.
|
||
|
* @param obj: spi object define in application software.
|
||
|
* @retval none
|
||
|
*/
|
||
|
void spi_enable(spi_t *obj)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
|
||
|
if (spi_idx == 1) {
|
||
|
RCC_PeriphClockCmd(APBPeriph_SPI1, APBPeriph_SPI1_CLOCK, ENABLE);
|
||
|
} else {
|
||
|
RCC_PeriphClockCmd(APBPeriph_SPI0, APBPeriph_SPI0_CLOCK, ENABLE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Close SPI device clock.
|
||
|
* @param obj: spi object define in application software.
|
||
|
* @retval none
|
||
|
*/
|
||
|
void spi_disable(spi_t *obj)
|
||
|
{
|
||
|
uint8_t spi_idx = obj->spi_idx &0x0F;
|
||
|
|
||
|
if (spi_idx == 1) {
|
||
|
RCC_PeriphClockCmd(APBPeriph_SPI1, APBPeriph_SPI1_CLOCK, DISABLE);
|
||
|
} else {
|
||
|
RCC_PeriphClockCmd(APBPeriph_SPI0, APBPeriph_SPI0_CLOCK, DISABLE);
|
||
|
}
|
||
|
}
|