mirror of
https://github.com/oopuuu/zTC1.git
synced 2025-12-17 23:48:13 +08:00
476 lines
14 KiB
C
476 lines
14 KiB
C
/**
|
|
******************************************************************************
|
|
* @file platform_spi.c
|
|
* @author William Xu
|
|
* @version V1.0.0
|
|
* @date 05-May-2014
|
|
* @brief This file provide SPI driver functions.
|
|
******************************************************************************
|
|
* UNPUBLISHED PROPRIETARY SOURCE CODE
|
|
* Copyright (c) 2016 MXCHIP Inc.
|
|
*
|
|
* The contents of this file may not be disclosed to third parties, copied or
|
|
* duplicated in any form, in whole or in part, without the prior written
|
|
* permission of MXCHIP Corporation.
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "platform.h"
|
|
#include "platform_config.h"
|
|
#include "platform_peripheral.h"
|
|
#include "debug.h"
|
|
|
|
/******************************************************
|
|
* Constants
|
|
******************************************************/
|
|
#define MAX_NUM_SPI_PRESCALERS (8)
|
|
|
|
/******************************************************
|
|
* Enumerations
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Type Definitions
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Structures
|
|
******************************************************/
|
|
typedef struct
|
|
{
|
|
uint16_t factor;
|
|
uint16_t prescaler_value;
|
|
} spi_baudrate_division_mapping_t;
|
|
|
|
/******************************************************
|
|
* Static Function Declarations
|
|
******************************************************/
|
|
|
|
static OSStatus calculate_prescaler( uint32_t speed, uint16_t* prescaler );
|
|
static uint16_t spi_transfer ( const platform_spi_t* spi, uint16_t data );
|
|
static OSStatus spi_dma_transfer ( const platform_spi_t* spi, const platform_spi_config_t* config );
|
|
static void spi_dma_config ( const platform_spi_t* spi, const platform_spi_message_segment_t* message );
|
|
|
|
/******************************************************
|
|
* Variables Definitions
|
|
******************************************************/
|
|
static const spi_baudrate_division_mapping_t spi_baudrate_prescalers[MAX_NUM_SPI_PRESCALERS] =
|
|
{
|
|
{ 2, SPI_BaudRatePrescaler_2 },
|
|
{ 4, SPI_BaudRatePrescaler_4 },
|
|
{ 8, SPI_BaudRatePrescaler_8 },
|
|
{ 16, SPI_BaudRatePrescaler_16 },
|
|
{ 32, SPI_BaudRatePrescaler_32 },
|
|
{ 64, SPI_BaudRatePrescaler_64 },
|
|
{ 128, SPI_BaudRatePrescaler_128 },
|
|
{ 256, SPI_BaudRatePrescaler_256 },
|
|
};
|
|
|
|
/******************************************************
|
|
* Function Definitions
|
|
******************************************************/
|
|
uint8_t platform_spi_get_port_number( platform_spi_port_t* spi )
|
|
{
|
|
if ( spi == SPI1 )
|
|
{
|
|
return 0;
|
|
}
|
|
else if ( spi == SPI2 )
|
|
{
|
|
return 1;
|
|
}
|
|
else if ( spi == SPI3 )
|
|
{
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
return 0xFF;
|
|
}
|
|
}
|
|
|
|
static uint32_t get_dma_irq_status( DMA_Stream_TypeDef* stream )
|
|
{
|
|
if ( stream <= DMA1_Stream3 )
|
|
{
|
|
return DMA1->LISR;
|
|
}
|
|
else if ( stream <= DMA1_Stream7 )
|
|
{
|
|
return DMA1->HISR;
|
|
}
|
|
else if ( stream <= DMA2_Stream3 )
|
|
{
|
|
return DMA2->LISR;
|
|
}
|
|
else
|
|
{
|
|
return DMA2->HISR;
|
|
}
|
|
}
|
|
|
|
OSStatus platform_spi_init( platform_spi_driver_t* driver, const platform_spi_t* peripheral, const platform_spi_config_t* config )
|
|
{
|
|
SPI_InitTypeDef spi_init;
|
|
OSStatus err;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( ( driver != NULL ) && ( peripheral != NULL ) && ( config != NULL ), exit, err = kParamErr);
|
|
|
|
/* Calculate prescaler */
|
|
err = calculate_prescaler( config->speed, &spi_init.SPI_BaudRatePrescaler );
|
|
require_noerr(err, exit);
|
|
|
|
/* Configure data-width */
|
|
if ( config->bits == 8 )
|
|
{
|
|
spi_init.SPI_DataSize = SPI_DataSize_8b;
|
|
}
|
|
else if ( config->bits == 16 )
|
|
{
|
|
require_action( !(config->mode & SPI_USE_DMA), exit, err = kUnsupportedErr);
|
|
spi_init.SPI_DataSize = SPI_DataSize_16b;
|
|
}
|
|
else
|
|
{
|
|
err = kUnsupportedErr;
|
|
goto exit;
|
|
}
|
|
|
|
/* Configure MSB or LSB */
|
|
if ( config->mode & SPI_MSB_FIRST )
|
|
{
|
|
spi_init.SPI_FirstBit = SPI_FirstBit_MSB;
|
|
}
|
|
else
|
|
{
|
|
spi_init.SPI_FirstBit = SPI_FirstBit_LSB;
|
|
}
|
|
|
|
/* Configure mode CPHA and CPOL */
|
|
if ( config->mode & SPI_CLOCK_IDLE_HIGH )
|
|
{
|
|
spi_init.SPI_CPOL = SPI_CPOL_High;
|
|
}
|
|
else
|
|
{
|
|
spi_init.SPI_CPOL = SPI_CPOL_Low;
|
|
}
|
|
|
|
if ( config->mode & SPI_CLOCK_RISING_EDGE )
|
|
{
|
|
spi_init.SPI_CPHA = ( config->mode & SPI_CLOCK_IDLE_HIGH ) ? SPI_CPHA_2Edge : SPI_CPHA_1Edge;
|
|
}
|
|
else
|
|
{
|
|
spi_init.SPI_CPHA = ( config->mode & SPI_CLOCK_IDLE_HIGH ) ? SPI_CPHA_1Edge : SPI_CPHA_2Edge;
|
|
}
|
|
|
|
driver->peripheral = (platform_spi_t *)peripheral;
|
|
|
|
/* Init SPI GPIOs */
|
|
platform_gpio_set_alternate_function( peripheral->pin_clock->port, peripheral->pin_clock->pin_number, GPIO_OType_PP, GPIO_PuPd_NOPULL, peripheral->gpio_af );
|
|
platform_gpio_set_alternate_function( peripheral->pin_mosi->port, peripheral->pin_mosi->pin_number, GPIO_OType_PP, GPIO_PuPd_NOPULL, peripheral->gpio_af );
|
|
platform_gpio_set_alternate_function( peripheral->pin_miso->port, peripheral->pin_miso->pin_number, GPIO_OType_PP, GPIO_PuPd_UP, peripheral->gpio_af );
|
|
|
|
/* Init the chip select GPIO */
|
|
platform_gpio_init( config->chip_select, OUTPUT_PUSH_PULL );
|
|
platform_gpio_output_high( config->chip_select );
|
|
|
|
|
|
/* Enable SPI peripheral clock */
|
|
(peripheral->peripheral_clock_func)( peripheral->peripheral_clock_reg, ENABLE );
|
|
(peripheral->peripheral_clock_func)( peripheral->peripheral_clock_reg, ENABLE );
|
|
|
|
SPI_I2S_DeInit( peripheral->port );
|
|
|
|
spi_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
|
spi_init.SPI_Mode = SPI_Mode_Master;
|
|
spi_init.SPI_NSS = SPI_NSS_Soft;
|
|
spi_init.SPI_CRCPolynomial = 0x7; /* reset value */
|
|
SPI_CalculateCRC( peripheral->port, DISABLE );
|
|
|
|
/* Init and enable SPI */
|
|
SPI_Init( peripheral->port, &spi_init );
|
|
|
|
SPI_I2S_DMACmd( peripheral->port, SPI_I2S_DMAReq_Rx, DISABLE );
|
|
SPI_I2S_DMACmd( peripheral->port, SPI_I2S_DMAReq_Tx, DISABLE );
|
|
|
|
SPI_Cmd ( peripheral->port, ENABLE );
|
|
|
|
if ( config->mode & SPI_USE_DMA ){
|
|
DMA_DeInit( peripheral->rx_dma.stream );
|
|
DMA_DeInit( peripheral->tx_dma.stream );
|
|
|
|
if ( peripheral->tx_dma.controller == DMA1 )
|
|
{
|
|
RCC->AHB1ENR |= RCC_AHB1Periph_DMA1;
|
|
}
|
|
else
|
|
{
|
|
RCC->AHB1ENR |= RCC_AHB1Periph_DMA2;
|
|
}
|
|
|
|
if ( peripheral->rx_dma.controller == DMA1 )
|
|
{
|
|
RCC->AHB1ENR |= RCC_AHB1Periph_DMA1;
|
|
}
|
|
else
|
|
{
|
|
RCC->AHB1ENR |= RCC_AHB1Periph_DMA2;
|
|
}
|
|
SPI_I2S_DMACmd( peripheral->port, SPI_I2S_DMAReq_Rx, ENABLE );
|
|
SPI_I2S_DMACmd( peripheral->port, SPI_I2S_DMAReq_Tx, ENABLE );
|
|
}
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable();
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
OSStatus platform_spi_deinit( platform_spi_driver_t* driver )
|
|
{
|
|
UNUSED_PARAMETER( driver );
|
|
/* TODO: unimplemented */
|
|
return kUnsupportedErr;
|
|
}
|
|
|
|
|
|
OSStatus platform_spi_transfer( platform_spi_driver_t* driver, const platform_spi_config_t* config, const platform_spi_message_segment_t* segments, uint16_t number_of_segments )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
uint32_t count = 0;
|
|
uint16_t i;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( ( driver != NULL ) && ( config != NULL ) && ( segments != NULL ) && ( number_of_segments != 0 ), exit, err = kParamErr);
|
|
|
|
/* Activate chip select */
|
|
platform_gpio_output_low( config->chip_select );
|
|
|
|
for ( i = 0; i < number_of_segments; i++ )
|
|
{
|
|
/* Check if we are using DMA */
|
|
if ( config->mode & SPI_USE_DMA )
|
|
{
|
|
if( segments[ i ].length != 0){
|
|
//platform_log( "length: %d, i:%d", segments[ i ].length, i );
|
|
|
|
spi_dma_config( driver->peripheral, &segments[ i ] );
|
|
|
|
err = spi_dma_transfer( driver->peripheral, config );
|
|
require_noerr(err, cleanup_transfer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
count = segments[i].length;
|
|
|
|
/* in interrupt-less mode */
|
|
if ( config->bits == 8 )
|
|
{
|
|
const uint8_t* send_ptr = ( const uint8_t* )segments[i].tx_buffer;
|
|
uint8_t* rcv_ptr = ( uint8_t* )segments[i].rx_buffer;
|
|
|
|
while ( count-- )
|
|
{
|
|
uint16_t data = 0xFF;
|
|
|
|
if ( send_ptr != NULL )
|
|
{
|
|
data = *send_ptr++;
|
|
}
|
|
|
|
data = spi_transfer( driver->peripheral, data );
|
|
|
|
if ( rcv_ptr != NULL )
|
|
{
|
|
*rcv_ptr++ = (uint8_t)data;
|
|
}
|
|
}
|
|
}
|
|
else if ( config->bits == 16 )
|
|
{
|
|
const uint16_t* send_ptr = (const uint16_t *) segments[i].tx_buffer;
|
|
uint16_t* rcv_ptr = (uint16_t *) segments[i].rx_buffer;
|
|
|
|
/* Check that the message length is a multiple of 2 */
|
|
|
|
require_action_quiet( ( count % 2 ) == 0, cleanup_transfer, err = kSizeErr);
|
|
|
|
/* Transmit/receive data stream, 16-bit at time */
|
|
while ( count != 0 )
|
|
{
|
|
uint16_t data = 0xFFFF;
|
|
|
|
if ( send_ptr != NULL )
|
|
{
|
|
data = *send_ptr++;
|
|
}
|
|
|
|
data = spi_transfer( driver->peripheral, data );
|
|
|
|
if ( rcv_ptr != NULL )
|
|
{
|
|
*rcv_ptr++ = data;
|
|
}
|
|
|
|
count -= 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cleanup_transfer:
|
|
/* Deassert chip select */
|
|
platform_gpio_output_high( config->chip_select );
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable( );
|
|
return err;
|
|
}
|
|
|
|
static uint16_t spi_transfer( const platform_spi_t* spi, uint16_t data )
|
|
{
|
|
/* Wait until the transmit buffer is empty */
|
|
while ( SPI_I2S_GetFlagStatus( spi->port, SPI_I2S_FLAG_TXE ) == RESET )
|
|
{
|
|
}
|
|
|
|
/* Send the byte */
|
|
SPI_I2S_SendData( spi->port, data );
|
|
|
|
/* Wait until a data is received */
|
|
while ( SPI_I2S_GetFlagStatus( spi->port, SPI_I2S_FLAG_RXNE ) == RESET )
|
|
{
|
|
}
|
|
|
|
/* Get the received data */
|
|
return SPI_I2S_ReceiveData( spi->port );
|
|
}
|
|
|
|
|
|
static OSStatus calculate_prescaler( uint32_t speed, uint16_t* prescaler )
|
|
{
|
|
uint8_t i;
|
|
OSStatus err = kNoErr;
|
|
|
|
require_action_quiet( prescaler != NULL, exit, err = kParamErr);
|
|
|
|
for( i = 0 ; i < MAX_NUM_SPI_PRESCALERS ; i++ )
|
|
{
|
|
if( ( 100000000 / spi_baudrate_prescalers[i].factor ) <= speed )
|
|
{
|
|
*prescaler = spi_baudrate_prescalers[i].prescaler_value;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static OSStatus spi_dma_transfer( const platform_spi_t* spi, const platform_spi_config_t* config )
|
|
{
|
|
/* Enable dma channels that have just been configured */
|
|
DMA_Cmd( spi->rx_dma.stream, ENABLE );
|
|
DMA_Cmd( spi->tx_dma.stream, ENABLE );
|
|
|
|
/* Wait for DMA to complete */
|
|
/* TODO: This should wait on a semaphore that is triggered from an IRQ */
|
|
while ( ( get_dma_irq_status( spi->rx_dma.stream ) & spi->rx_dma.complete_flags ) == 0 )
|
|
{
|
|
}
|
|
|
|
return kNoErr;
|
|
}
|
|
|
|
static void spi_dma_config( const platform_spi_t* spi, const platform_spi_message_segment_t* message )
|
|
{
|
|
DMA_InitTypeDef dma_init;
|
|
static uint8_t dummy = 0xFF;
|
|
|
|
/* Setup DMA for SPI TX if it is enabled */
|
|
DMA_DeInit( spi->tx_dma.stream );
|
|
|
|
/* Setup DMA stream for TX */
|
|
dma_init.DMA_Channel = spi->tx_dma.channel;
|
|
dma_init.DMA_PeripheralBaseAddr = ( uint32_t )&spi->port->DR;
|
|
dma_init.DMA_DIR = DMA_DIR_MemoryToPeripheral;
|
|
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
|
dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
|
dma_init.DMA_BufferSize = message->length;
|
|
dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
|
dma_init.DMA_Mode = DMA_Mode_Normal;
|
|
dma_init.DMA_Priority = DMA_Priority_VeryHigh;
|
|
dma_init.DMA_FIFOMode = DMA_FIFOMode_Disable;
|
|
dma_init.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
|
|
dma_init.DMA_MemoryBurst = DMA_MemoryBurst_Single;
|
|
dma_init.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
|
|
|
|
if ( message->tx_buffer != NULL )
|
|
{
|
|
dma_init.DMA_Memory0BaseAddr = ( uint32_t )message->tx_buffer;
|
|
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
|
}
|
|
else
|
|
{
|
|
dma_init.DMA_Memory0BaseAddr = ( uint32_t )(&dummy);
|
|
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
|
|
}
|
|
|
|
DMA_Init( spi->tx_dma.stream, &dma_init );
|
|
|
|
/* Activate SPI DMA mode for transmission */
|
|
|
|
|
|
/* TODO: Init TX DMA finished semaphore */
|
|
|
|
/* Setup DMA for SPI RX stream */
|
|
DMA_DeInit( spi->rx_dma.stream );
|
|
dma_init.DMA_Channel = spi->rx_dma.channel;
|
|
dma_init.DMA_PeripheralBaseAddr = ( uint32_t )&spi->port->DR;
|
|
dma_init.DMA_DIR = DMA_DIR_PeripheralToMemory;
|
|
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
|
dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
|
dma_init.DMA_BufferSize = message->length;
|
|
dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
|
dma_init.DMA_Mode = DMA_Mode_Normal;
|
|
dma_init.DMA_Priority = DMA_Priority_VeryHigh;
|
|
dma_init.DMA_FIFOMode = DMA_FIFOMode_Disable;
|
|
dma_init.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
|
|
dma_init.DMA_MemoryBurst = DMA_MemoryBurst_Single;
|
|
dma_init.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
|
|
if ( message->rx_buffer != NULL )
|
|
{
|
|
dma_init.DMA_Memory0BaseAddr = (uint32_t)message->rx_buffer;
|
|
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
|
}
|
|
else
|
|
{
|
|
dma_init.DMA_Memory0BaseAddr = (uint32_t)&dummy;
|
|
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
|
|
}
|
|
|
|
/* Init and activate RX DMA channel */
|
|
DMA_Init( spi->rx_dma.stream, &dma_init );
|
|
|
|
|
|
/* TODO: Init RX DMA finish semaphore */
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|