You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

476 lines
15 KiB

/*
* upstream_spi.c
*
* Created on: 24/07/2015
* Author: Robert Fisk
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "downstream_interface_def.h"
#include "downstream_spi.h"
#include "downstream_statemachine.h"
#include "gd32vf103.h"
#include "gd32vf103v_eval.h"
//SPI_HandleTypeDef Hspi1;
DownstreamPacketTypeDef DownstreamPacket0;
DownstreamPacketTypeDef DownstreamPacket1;
DownstreamPacketTypeDef* CurrentWorkingPacket;
DownstreamPacketTypeDef* NextTxPacket = NULL;
InterfaceStateTypeDef DownstreamInterfaceState = DOWNSTREAM_INTERFACE_IDLE;
FreePacketCallbackTypeDef PendingFreePacketCallback = NULL; //Indicates someone is waiting for a packet buffer to become available
SpiPacketReceivedCallbackTypeDef ReceivePacketCallback = NULL; //Indicates someone is waiting for a received packet
uint32_t TemporaryIncomingPacketLength = 0;
uint8_t SpiInterruptCompleted = 0;
StatusTypeDef Downstream_CheckPreparePacketReception(void);
void Downstream_PrepareReceivePacketSize(DownstreamPacketTypeDef* freePacket);
void Downstream_InitSPI(void)
{
DownstreamPacket0.Busy = NOT_BUSY;
DownstreamPacket1.Busy = NOT_BUSY;
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_DMA0);
rcu_periph_clock_enable(RCU_SPI0);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 |GPIO_PIN_6| GPIO_PIN_7);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
dma_parameter_struct dma_init_struct;
/* SPI0 transmit dma config:DMA0-DMA_CH2 */
dma_deinit(DMA0, DMA_CH2);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI0);
dma_init_struct.memory_addr = (uint32_t)spi0_send_array;
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_LOW;
dma_init_struct.number = ARRAYSIZE;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init(DMA0, DMA_CH2, &dma_init_struct);
/* configure DMA mode */
dma_circulation_disable(DMA0, DMA_CH2);
dma_memory_to_memory_disable(DMA0, DMA_CH2);
dma_interrupt_enable(DMA0, DMA_CH2, DMA_INT_FTF);
/* SPI0 receive dma config:DMA0-DMA_CH1 */
dma_deinit(DMA0, DMA_CH1);
dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI0);
dma_init_struct.memory_addr = (uint32_t)spi0_receive_array;
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH1, &dma_init_struct);
/* configure DMA mode */
dma_circulation_disable(DMA0, DMA_CH1);
dma_memory_to_memory_disable(DMA0, DMA_CH1);
dma_interrupt_enable(DMA0, DMA_CH1, DMA_INT_FTF);
spi_parameter_struct spi_init_struct;
/* deinitilize SPI and the parameters */
spi_i2s_deinit(SPI0);
spi_struct_para_init(&spi_init_struct);
/* SPI0 parameter config */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_struct.device_mode = SPI_SLAVE;
spi_init_struct.frame_size = SPI_FRAMESIZE_16BIT;
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init_struct.prescale = SPI_PSC_2;
spi_init_struct.endian = SPI_ENDIAN_LSB;
spi_init(SPI0, &spi_init_struct);
spi_crc_polynomial_set(SPI0,7);
spi_enable(SPI0);
/* DMA channel enable */
dma_channel_enable(DMA0, DMA_CH1);
dma_channel_enable(DMA0, DMA_CH2);
/* SPI DMA enable */
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
spi_dma_enable(SPI0, SPI_DMA_RECEIVE);
eclic_irq_enable(DMA1_Channel0_IRQn,1,0);
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_TBE);
}
//Used by downstream state machine and USB host classes.
StatusTypeDef Downstream_GetFreePacket(FreePacketCallbackTypeDef callback)
{
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return STATUS_ERROR;
}
//Do we already have a queued callback?
if (PendingFreePacketCallback != NULL)
{
DOWNSTREAM_SPI_FREAKOUT;
return STATUS_ERROR;
}
//Check if there is a free buffer now
if (DownstreamPacket0.Busy == NOT_BUSY)
{
DownstreamPacket0.Busy = BUSY;
callback(&DownstreamPacket0);
return STATUS_OK;
}
if (DownstreamPacket1.Busy == NOT_BUSY)
{
DownstreamPacket1.Busy = BUSY;
callback(&DownstreamPacket1);
return STATUS_OK;
}
//Otherwise save requested address for when a buffer becomes free in the future
PendingFreePacketCallback = callback;
return STATUS_OK;
}
DownstreamPacketTypeDef* Downstream_GetFreePacketImmediately(void)
{
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return NULL;
}
//We are expecting a free buffer now
if (DownstreamPacket0.Busy == NOT_BUSY)
{
DownstreamPacket0.Busy = BUSY;
return &DownstreamPacket0;
}
if (DownstreamPacket1.Busy == NOT_BUSY)
{
DownstreamPacket1.Busy = BUSY;
return &DownstreamPacket1;
}
//Should not happen:
DOWNSTREAM_SPI_FREAKOUT;
return NULL;
}
//Used by Downstream state machine and USB host classes.
void Downstream_ReleasePacket(DownstreamPacketTypeDef* packetToRelease)
{
FreePacketCallbackTypeDef tempCallback;
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return;
}
if ((packetToRelease != &DownstreamPacket0) &&
(packetToRelease != &DownstreamPacket1))
{
DOWNSTREAM_SPI_FREAKOUT;
return;
}
if (PendingFreePacketCallback != NULL)
{
tempCallback = PendingFreePacketCallback; //In extreme situations, running this callback can trigger another request for a free packet,
PendingFreePacketCallback = NULL; //thereby causing GetFreePacket to freak out. So we need to clear the callback indicator first.
tempCallback(packetToRelease);
}
else
{
packetToRelease->Busy = NOT_BUSY;
}
}
//Used by Downstream state machine and USB classes.
//Ok to call when idle or transmitting.
//Not OK to call when receiving or awaiting reception.
StatusTypeDef Downstream_ReceivePacket(SpiPacketReceivedCallbackTypeDef callback)
{
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return STATUS_ERROR;
}
if ((DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_SIZE_WAIT) ||
(DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_PACKET_WAIT) ||
(ReceivePacketCallback != NULL))
{
DOWNSTREAM_SPI_FREAKOUT;
return STATUS_ERROR;
}
ReceivePacketCallback = callback;
return Downstream_CheckPreparePacketReception();
}
//Internal use only
StatusTypeDef Downstream_CheckPreparePacketReception(void)
{
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return STATUS_ERROR;
}
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_IDLE)
{
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_RX_SIZE_WAIT;
return Downstream_GetFreePacket(Downstream_PrepareReceivePacketSize);
}
return STATUS_OK;
}
//Internal use only
void Downstream_PrepareReceivePacketSize(DownstreamPacketTypeDef* freePacket)
{
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return;
}
if (DownstreamInterfaceState != DOWNSTREAM_INTERFACE_RX_SIZE_WAIT)
{
DOWNSTREAM_SPI_FREAKOUT;
return;
}
CurrentWorkingPacket = freePacket;
CurrentWorkingPacket->Length16 = 0;
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
(uint8_t*)&CurrentWorkingPacket->Length16,
(uint8_t*)&CurrentWorkingPacket->Length16,
2) != HAL_OK) //We only need to read one word, but the peripheral library freaks out...
{
DOWNSTREAM_SPI_FREAKOUT;
return;
}
UPSTREAM_TX_REQUEST_ASSERT;
}
//Used by Downstream state machine and USB classes.
//Call when idle or transmitting.
//It doesn't make sense to call when receiving or awaiting reception.
StatusTypeDef Downstream_TransmitPacket(DownstreamPacketTypeDef* packetToWrite)
{
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return STATUS_ERROR;
}
//Sanity checks
if ((packetToWrite != &DownstreamPacket0) &&
(packetToWrite != &DownstreamPacket1))
{
DOWNSTREAM_SPI_FREAKOUT;
return STATUS_ERROR;
}
if ((packetToWrite->Busy != BUSY) ||
(packetToWrite->Length16 < DOWNSTREAM_PACKET_LEN_MIN_16) ||
(packetToWrite->Length16 > DOWNSTREAM_PACKET_LEN_16))
{
DOWNSTREAM_SPI_FREAKOUT;
return STATUS_ERROR;
}
if (NextTxPacket != NULL)
{
DOWNSTREAM_SPI_FREAKOUT;
return STATUS_ERROR;
}
switch (DownstreamInterfaceState)
{
case DOWNSTREAM_INTERFACE_TX_SIZE_WAIT:
case DOWNSTREAM_INTERFACE_TX_PACKET_WAIT:
NextTxPacket = packetToWrite;
break;
case DOWNSTREAM_INTERFACE_IDLE:
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_TX_SIZE_WAIT;
CurrentWorkingPacket = packetToWrite;
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
(uint8_t*)&CurrentWorkingPacket->Length16,
(uint8_t*)&TemporaryIncomingPacketLength,
2) != HAL_OK) //We only need to write one word, but the peripheral library freaks out...
{
DOWNSTREAM_SPI_FREAKOUT;
return STATUS_ERROR;
}
UPSTREAM_TX_REQUEST_ASSERT;
break;
default:
DOWNSTREAM_SPI_FREAKOUT;
return STATUS_ERROR;
}
return STATUS_OK;
}
//Do stuff at main loop priority after SPI transaction is complete
void Downstream_SPIProcess(void)
{
SpiPacketReceivedCallbackTypeDef tempPacketCallback;
if (SpiInterruptCompleted == 0)
{
return;
}
SpiInterruptCompleted = 0;
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return;
}
//Finished transmitting packet size
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_TX_SIZE_WAIT)
{
if ((uint16_t)TemporaryIncomingPacketLength != 0)
{
//Currently we just freak out if Upstream sends us an unexpected command.
//Theoretically we could reset our downstream state machine and accept the new command...
DOWNSTREAM_SPI_FREAKOUT;
return;
}
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_TX_PACKET_WAIT;
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
&CurrentWorkingPacket->CommandClass,
&CurrentWorkingPacket->CommandClass,
((CurrentWorkingPacket->Length16 < 2) ? 2 : CurrentWorkingPacket->Length16)) != HAL_OK)
{
DOWNSTREAM_SPI_FREAKOUT;
return;
}
UPSTREAM_TX_REQUEST_ASSERT;
return;
}
//Finished transmitting packet body
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_TX_PACKET_WAIT)
{
Downstream_ReleasePacket(CurrentWorkingPacket);
if (NextTxPacket != NULL)
{
//NextTxPacket has already passed the checks in Downstream_TransmitPacket.
//So we just need to pass it to HAL_SPI_Transmit_DMA.
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_TX_SIZE_WAIT;
CurrentWorkingPacket = NextTxPacket;
NextTxPacket = NULL;
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
(uint8_t*)&CurrentWorkingPacket->Length16,
(uint8_t*)&TemporaryIncomingPacketLength,
2) != HAL_OK) //We only need to write one word, but the peripheral library freaks out...
{
DOWNSTREAM_SPI_FREAKOUT;
return;
}
UPSTREAM_TX_REQUEST_ASSERT;
return;
}
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_IDLE;
if (ReceivePacketCallback != NULL)
{
Downstream_CheckPreparePacketReception();
}
return;
}
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_SIZE_WAIT)
{
if ((CurrentWorkingPacket->Length16 < DOWNSTREAM_PACKET_LEN_MIN_16) ||
(CurrentWorkingPacket->Length16 > DOWNSTREAM_PACKET_LEN_16))
{
DOWNSTREAM_SPI_FREAKOUT;
return;
}
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_RX_PACKET_WAIT;
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
&CurrentWorkingPacket->CommandClass,
&CurrentWorkingPacket->CommandClass,
((CurrentWorkingPacket->Length16 < 2) ? 2 : CurrentWorkingPacket->Length16)) != HAL_OK)
{
DOWNSTREAM_SPI_FREAKOUT;
return;
}
UPSTREAM_TX_REQUEST_ASSERT;
return;
}
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_PACKET_WAIT)
{
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_IDLE;
if (ReceivePacketCallback == NULL)
{
DOWNSTREAM_SPI_FREAKOUT;
return;
}
//Packet processor may want to receive another packet immediately,
//so clear ReceivePacketCallback before the call.
//It is the callback's responsibility to release the packet buffer we are passing to it!
tempPacketCallback = ReceivePacketCallback;
ReceivePacketCallback = NULL;
tempPacketCallback(CurrentWorkingPacket);
return;
}
//case default:
DOWNSTREAM_SPI_FREAKOUT;
}
//Called at the end of the SPI TxRx DMA transfer,
//at DMA2 interrupt priority. Assume *hspi points to our hspi1.
//We use TxRx to send our reply packet to check if Upstream was trying
//to send us a packet at the same time.
//We also TxRx our packet body because the SPI silicon is buggy at the end of
//a transmit-only DMA transfer with CRC! (it does not clear RXNE flag on request)
void DMA_SPI_TxRxCpltCallback(void)
{
UPSTREAM_TX_REQUEST_DEASSERT;
SpiInterruptCompleted = 1;
}
//Something bad happened! Possibly CRC error...
/*void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
DOWNSTREAM_SPI_FREAKOUT;
}*/