Files
zTC1/mico-os/platform/MCU/STM32F4xx/peripherals/platform_spi.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 */
}