/* * upstream_spi.c * * Created on: 21/06/2015 * Author: Robert Fisk */ #include #include #include "stm32f4xx_hal.h" #include "usbd_def.h" #include "board_config.h" SPI_HandleTypeDef Hspi1; UpstreamPacketTypeDef UpstreamPacket0; UpstreamPacketTypeDef UpstreamPacket1; UpstreamPacketTypeDef* CurrentWorkingPacket; UpstreamPacketTypeDef* NextTxPacket; //Indicates we have a pending TX packet InterfaceStateTypeDef UpstreamInterfaceState; FreePacketCallbackTypeDef PendingFreePacketCallback; //Indicates someone is waiting for a packet buffer to become available SpiPacketReceivedCallbackTypeDef ReceivePacketCallback; //Indicates someone is waiting for a received packet uint8_t SentCommandClass; uint8_t SentCommand; static void SPI1_Init(void); static HAL_StatusTypeDef Upstream_CheckBeginPacketReception(void); static void Upstream_BeginPacketReception(UpstreamPacketTypeDef* freePacket); void Upstream_InitSPI(void) { UpstreamInterfaceState = UPSTREAM_INTERFACE_RESET; SPI1_Init(); UpstreamPacket0.Busy = NOT_BUSY; UpstreamPacket1.Busy = NOT_BUSY; NextTxPacket = NULL; PendingFreePacketCallback = NULL; ReceivePacketCallback = NULL; //Todo: check connection to downstream, await client USB insertion while (!DOWNSTREAM_TX_OK_ACTIVE); UpstreamInterfaceState = UPSTREAM_INTERFACE_IDLE; } void SPI1_Init(void) { Hspi1.Instance = SPI1; Hspi1.State = HAL_SPI_STATE_RESET; Hspi1.Init.Mode = SPI_MODE_MASTER; Hspi1.Init.Direction = SPI_DIRECTION_2LINES; Hspi1.Init.DataSize = SPI_DATASIZE_8BIT; Hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; Hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; Hspi1.Init.NSS = SPI_NSS_SOFT; Hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; //42MHz APB2 / 32 = 1.3Mbaud Hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; Hspi1.Init.TIMode = SPI_TIMODE_DISABLED; Hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_ENABLED; Hspi1.Init.CRCPolynomial = SPI_CRC_DEFAULTPOLYNOMIAL; HAL_SPI_Init(&Hspi1); } //Used by USB interface classes, and by our internal RX code. HAL_StatusTypeDef Upstream_GetFreePacket(FreePacketCallbackTypeDef callback) { //Sanity checks if ((UpstreamInterfaceState < UPSTREAM_INTERFACE_IDLE) || (UpstreamInterfaceState > UPSTREAM_INTERFACE_RX_PACKET)) { SPI_INTERFACE_FREAKOUT_HAL_ERROR; } //Do we already have a queued callback? if (PendingFreePacketCallback != NULL) { SPI_INTERFACE_FREAKOUT_HAL_ERROR; } //Check if there is a free buffer now if (UpstreamPacket0.Busy == NOT_BUSY) { UpstreamPacket0.Busy = BUSY; callback(&UpstreamPacket0); return HAL_OK; } if (UpstreamPacket1.Busy == NOT_BUSY) { UpstreamPacket1.Busy = BUSY; callback(&UpstreamPacket1); return HAL_OK; } //Otherwise save requested address for when a buffer becomes free in the future PendingFreePacketCallback = callback; return HAL_OK; } UpstreamPacketTypeDef* Upstream_GetFreePacketImmediately(void) { //Sanity checks if ((UpstreamInterfaceState < UPSTREAM_INTERFACE_IDLE) || (UpstreamInterfaceState > UPSTREAM_INTERFACE_RX_PACKET)) { SPI_INTERFACE_FREAKOUT_HAL_ERROR; } //We are expecting a free buffer now if (UpstreamPacket0.Busy == NOT_BUSY) { UpstreamPacket0.Busy = BUSY; return &UpstreamPacket0; } if (UpstreamPacket1.Busy == NOT_BUSY) { UpstreamPacket1.Busy = BUSY; return &UpstreamPacket1; } //Should not happen: SPI_INTERFACE_FREAKOUT_HAL_ERROR; } //Used by USB interface classes, and by our internal RX code. void Upstream_ReleasePacket(UpstreamPacketTypeDef* packetToRelease) { FreePacketCallbackTypeDef tempCallback; if ((packetToRelease != &UpstreamPacket0) && (packetToRelease != &UpstreamPacket1)) { SPI_INTERFACE_FREAKOUT_HAL_ERROR; } 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 USB interface classes only. //OK to call when still transmitting another packet. //Not OK to call when receiving or waiting for downstream reply. HAL_StatusTypeDef Upstream_SendPacket(UpstreamPacketTypeDef* packetToWrite) { //Sanity checks if ((packetToWrite != &UpstreamPacket0) && (packetToWrite != &UpstreamPacket1)) { SPI_INTERFACE_FREAKOUT_HAL_ERROR; } if ((packetToWrite->Busy != BUSY) || (packetToWrite->Length < UPSTREAM_PACKET_LEN_MIN) || (packetToWrite->Length > UPSTREAM_PACKET_LEN)) { SPI_INTERFACE_FREAKOUT_HAL_ERROR; } switch (UpstreamInterfaceState) { case UPSTREAM_INTERFACE_TX_SIZE_WAIT: case UPSTREAM_INTERFACE_TX_SIZE: case UPSTREAM_INTERFACE_TX_PACKET_WAIT: case UPSTREAM_INTERFACE_TX_PACKET: if (NextTxPacket != NULL) { SPI_INTERFACE_FREAKOUT_HAL_ERROR; } NextTxPacket = packetToWrite; break; case UPSTREAM_INTERFACE_RX_SIZE_WAIT: case UPSTREAM_INTERFACE_RX_SIZE: case UPSTREAM_INTERFACE_RX_PACKET_WAIT: case UPSTREAM_INTERFACE_RX_PACKET: //We can't let the size/packet sequence get out of sync. SPI_INTERFACE_FREAKOUT_HAL_ERROR; case UPSTREAM_INTERFACE_IDLE: UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_SIZE_WAIT; CurrentWorkingPacket = packetToWrite; SentCommandClass = CurrentWorkingPacket->CommandClass; SentCommand = CurrentWorkingPacket->Command; if (DOWNSTREAM_TX_OK_ACTIVE) { Upstream_TxOkInterrupt(); //Manually trigger edge interrupt processing if the line was already asserted } break; default: SPI_INTERFACE_FREAKOUT_HAL_ERROR; } return HAL_OK; } //Called at the end of the SPI TX DMA transfer, //at DMA2 interrupt priority. Assume *hspi points to our hspi1. void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { SPI1_NSS_DEASSERT; if ((UpstreamInterfaceState != UPSTREAM_INTERFACE_TX_SIZE) && (UpstreamInterfaceState != UPSTREAM_INTERFACE_TX_PACKET)) { SPI_INTERFACE_FREAKOUT_VOID; } if (UpstreamInterfaceState == UPSTREAM_INTERFACE_TX_SIZE) { UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_PACKET_WAIT; if (DOWNSTREAM_TX_OK_ACTIVE) { Upstream_TxOkInterrupt(); } return; } if (UpstreamInterfaceState == UPSTREAM_INTERFACE_TX_PACKET) { if ((PendingFreePacketCallback != NULL) && (NextTxPacket == NULL)) { //SPI_INTERFACE_FREAKOUT_VOID; ///////////////////////////////////////! } Upstream_ReleasePacket(CurrentWorkingPacket); if (NextTxPacket != NULL) { //NextTxPacket has already passed the checks in SendUpstreamPacket. //So we just need to pass it to HAL_SPI_Transmit_DMA. UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_SIZE_WAIT; CurrentWorkingPacket = NextTxPacket; NextTxPacket = NULL; if (DOWNSTREAM_TX_OK_ACTIVE) { Upstream_TxOkInterrupt(); } return; } UpstreamInterfaceState = UPSTREAM_INTERFACE_IDLE; if (ReceivePacketCallback != NULL) { Upstream_CheckBeginPacketReception(); } } } //Used by USB interface classes. //Ok to call when idle or transmitting. //Not OK to call when receiving or waiting for downstream reply. HAL_StatusTypeDef Upstream_GetPacket(SpiPacketReceivedCallbackTypeDef callback) { if (ReceivePacketCallback != NULL) { SPI_INTERFACE_FREAKOUT_HAL_ERROR; } ReceivePacketCallback = callback; return Upstream_CheckBeginPacketReception(); } //Internal use only. HAL_StatusTypeDef Upstream_CheckBeginPacketReception(void) { if ((UpstreamInterfaceState < UPSTREAM_INTERFACE_IDLE) || (UpstreamInterfaceState > UPSTREAM_INTERFACE_RX_SIZE_WAIT)) { SPI_INTERFACE_FREAKOUT_HAL_ERROR; } if (UpstreamInterfaceState == UPSTREAM_INTERFACE_IDLE) { UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_SIZE_WAIT; } if (UpstreamInterfaceState == UPSTREAM_INTERFACE_RX_SIZE_WAIT) { if (DOWNSTREAM_TX_OK_ACTIVE) { //UpstreamTxOkInterrupt(); Upstream_GetFreePacket(Upstream_BeginPacketReception); //Take a shortcut here :) } } return HAL_OK; } //This is called by EXTI3 falling edge interrupt, //indicating that downstream is ready for next transaction. void Upstream_TxOkInterrupt(void) { switch (UpstreamInterfaceState) { case UPSTREAM_INTERFACE_TX_SIZE_WAIT: UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_SIZE; SPI1_NSS_ASSERT; if (HAL_SPI_Transmit_DMA(&Hspi1, (uint8_t*)&CurrentWorkingPacket->Length, 2) != HAL_OK) { SPI_INTERFACE_FREAKOUT_VOID; } break; case UPSTREAM_INTERFACE_TX_PACKET_WAIT: UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_PACKET; SPI1_NSS_ASSERT; if ((HAL_SPI_Transmit_DMA(&Hspi1, &CurrentWorkingPacket->CommandClass, CurrentWorkingPacket->Length)) != HAL_OK) { SPI_INTERFACE_FREAKOUT_VOID; } break; case UPSTREAM_INTERFACE_RX_SIZE_WAIT: Upstream_GetFreePacket(Upstream_BeginPacketReception); break; case UPSTREAM_INTERFACE_RX_PACKET_WAIT: UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_PACKET; SPI1_NSS_ASSERT; if ((HAL_SPI_Receive_DMA(&Hspi1, &CurrentWorkingPacket->CommandClass, (CurrentWorkingPacket->Length + 1))) != HAL_OK) //"When the CRC feature is enabled the pData Length must be Size + 1" { SPI_INTERFACE_FREAKOUT_VOID; } break; default: SPI_INTERFACE_FREAKOUT_VOID; } } //Internal use only. //Called when we want to receive downstream packet, and a packet buffer has become free. void Upstream_BeginPacketReception(UpstreamPacketTypeDef* freePacket) { if (UpstreamInterfaceState != UPSTREAM_INTERFACE_RX_SIZE_WAIT) { SPI_INTERFACE_FREAKOUT_VOID; } UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_SIZE; CurrentWorkingPacket = freePacket; CurrentWorkingPacket->Length = 0; //Our RX buffer is used by HAL_SPI_Receive_DMA as dummy TX data, we set Length to 0 so downstream will know this is a dummy packet. SPI1_NSS_ASSERT; if (HAL_SPI_Receive_DMA(&Hspi1, (uint8_t*)&CurrentWorkingPacket->Length, (2 + 1)) != HAL_OK) //"When the CRC feature is enabled the pData Length must be Size + 1" { SPI_INTERFACE_FREAKOUT_VOID; } } //Called at the end of the SPI TX DMA transfer, //at DMA2 interrupt priority. Assume *hspi points to our hspi1. void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { SpiPacketReceivedCallbackTypeDef tempPacketCallback; SPI1_NSS_DEASSERT; if ((UpstreamInterfaceState != UPSTREAM_INTERFACE_RX_SIZE) && (UpstreamInterfaceState != UPSTREAM_INTERFACE_RX_PACKET)) { SPI_INTERFACE_FREAKOUT_VOID; } if (UpstreamInterfaceState == UPSTREAM_INTERFACE_RX_SIZE) { if ((CurrentWorkingPacket->Length < UPSTREAM_PACKET_LEN_MIN) || (CurrentWorkingPacket->Length > UPSTREAM_PACKET_LEN)) { SPI_INTERFACE_FREAKOUT_VOID; } UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_PACKET_WAIT; if (DOWNSTREAM_TX_OK_ACTIVE) { Upstream_TxOkInterrupt(); } return; } if (UpstreamInterfaceState == UPSTREAM_INTERFACE_RX_PACKET) { UpstreamInterfaceState = UPSTREAM_INTERFACE_IDLE; if ((SentCommandClass != (CurrentWorkingPacket->CommandClass & COMMAND_CLASS_MASK)) || (SentCommand != CurrentWorkingPacket->Command)) { SPI_INTERFACE_FREAKOUT_VOID; } if (ReceivePacketCallback == NULL) { SPI_INTERFACE_FREAKOUT_VOID; } //USB interface 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); } } //Something bad happened! Possibly CRC error... void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) { SPI_INTERFACE_FREAKOUT_VOID; }