mirror of
https://github.com/adrcs/ip400.git
synced 2025-07-01 17:51:18 +03:00
387 lines
11 KiB
C
387 lines
11 KiB
C
/*---------------------------------------------------------------------------
|
|
Project: NucleoCC2
|
|
|
|
File Name: spi.c
|
|
|
|
Author: Martin, VE6VH
|
|
|
|
Description: SPI task. responds to commands from the host, implementing a quasi-
|
|
memory device
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version, provided this copyright notice
|
|
is included.
|
|
|
|
Copyright (c) Alberta Digital Radio Communications Society
|
|
All rights reserved.
|
|
|
|
Revision History:
|
|
|
|
---------------------------------------------------------------------------*/
|
|
#include <cmsis_os2.h>
|
|
#include <FreeRTOS.h>
|
|
#include <semphr.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
|
|
#include <config.h>
|
|
#include "types.h"
|
|
#include "main.h"
|
|
#include "spi.h"
|
|
#include "dataq.h"
|
|
#include "tasks.h"
|
|
|
|
#define SPI_MAX_TIME 200 // 200 ms max no activity timeout
|
|
#define NO_SPI_TIMEOUT (SPI_MAX_TIME/SPI_TASK_SCHED) // timeout in schedule ticks
|
|
|
|
// transmitter states
|
|
enum {
|
|
SPITXIDLE=0, // idle
|
|
SPITXFRAG // next fragment
|
|
};
|
|
|
|
// receiver states
|
|
enum {
|
|
SPIRXIDLE=0, // idle
|
|
SPIRXFRAG // next fragment
|
|
};
|
|
|
|
uint8_t SPITxState; // transmitter state
|
|
uint8_t SPIRxState; // receiver state
|
|
|
|
// frame queue
|
|
FRAME_QUEUE spiTxQueue; // queue for outbound
|
|
|
|
// frame buffers
|
|
#if USE_BUFFER_RAM
|
|
static SPI_BUFFER spiTxBuffer __attribute__((section("BUFFERS"), aligned(4)));
|
|
static SPI_BUFFER spiRxBuffer __attribute__((section("BUFFERS"), aligned(4)));
|
|
static uint8_t rxFrameBuffer[PAYLOAD_MAX] __attribute__((section("BUFFERS"), aligned(4)));
|
|
#else
|
|
static SPI_BUFFER spiTxBuffer;
|
|
static SPI_BUFFER spiRxBuffer;
|
|
static uint8_t rxFrameBuffer[PAYLOAD_MAX];
|
|
#endif
|
|
|
|
uint8_t SPI_State; // current state
|
|
HAL_StatusTypeDef spiXfer; // last transfer status
|
|
BOOL spiExchangeComplete; // spi exchange has been completed
|
|
BOOL spiErrorOccurred;
|
|
BOOL spiActive;
|
|
uint16_t spiActivityTimer; // no activity timer
|
|
|
|
// select the correct SPI
|
|
#if _BOARD_TYPE == NUCLEO_BOARD
|
|
#define hSPI hspi1
|
|
extern SPI_HandleTypeDef hspi1; // spi handle
|
|
#else
|
|
#if __INCLUDE_SPI
|
|
#define hSPI hspi3
|
|
extern SPI_HandleTypeDef hspi3; // spi handle
|
|
#else
|
|
#define hSPI NULL // not implemented
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
// validate an inbound frame
|
|
BOOL isIP400Frame(uint8_t *eye);
|
|
|
|
/*
|
|
* place a frame on the queue frame content is copied
|
|
* to alloc'd memory
|
|
*/
|
|
BOOL EnqueSPIFrame(void *ip400frame)
|
|
{
|
|
IP400_FRAME *qFrame, *SrcFrame = (IP400_FRAME *)ip400frame;
|
|
uint8_t *frameBuffer;
|
|
|
|
// spi is not running...
|
|
if(!spiActive)
|
|
return FALSE;
|
|
|
|
// allocate an IP400 frame
|
|
if((qFrame=malloc(sizeof(IP400_FRAME)))== NULL)
|
|
return FALSE;
|
|
memcpy(qFrame, SrcFrame, sizeof(IP400_FRAME));
|
|
|
|
int16_t hopCount = qFrame->flagfld.flags.hop_count;
|
|
int16_t hTblSize = hopCount * sizeof(HOPTABLE);
|
|
|
|
// hop table is in the first part of the payload
|
|
// alloc enough for both
|
|
if((frameBuffer=malloc(SrcFrame->length+hTblSize)) == NULL) {
|
|
free(qFrame);
|
|
return FALSE;
|
|
}
|
|
|
|
if(hopCount) {
|
|
memcpy(frameBuffer, qFrame->hopTable, hTblSize);
|
|
frameBuffer += hTblSize;
|
|
}
|
|
|
|
memcpy(frameBuffer, (uint8_t *)SrcFrame->buf, SrcFrame->length);
|
|
qFrame->buf = frameBuffer;
|
|
qFrame->length = SrcFrame->length + hTblSize;
|
|
|
|
if(!enqueFrame(&spiTxQueue, qFrame))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* clean up any queued frames
|
|
*/
|
|
void EmptySPIFrameQ(void)
|
|
{
|
|
while(dequeFrame(&spiTxQueue) != NULL);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Initialize the task
|
|
*/
|
|
void SPI_Task_init(void)
|
|
{
|
|
// create the rx completed semaphore
|
|
spiExchangeComplete = FALSE;
|
|
spiErrorOccurred = FALSE;
|
|
|
|
spiTxBuffer.spiData.hdr.eye[0] = 'I';
|
|
spiTxBuffer.spiData.hdr.eye[1] = 'P';
|
|
spiTxBuffer.spiData.hdr.eye[2] = '4';
|
|
spiTxBuffer.spiData.hdr.eye[3] = 'C';
|
|
|
|
spiTxBuffer.spiData.hdr.status = NO_DATA;
|
|
spiRxBuffer.spiData.hdr.status = NO_DATA;
|
|
|
|
SPITxState = SPITXIDLE;
|
|
SPIRxState = SPIRXIDLE;
|
|
|
|
// tx (outbound) frame queue
|
|
spiTxQueue.q_forw = &spiTxQueue;
|
|
spiTxQueue.q_back = &spiTxQueue;
|
|
|
|
spiActive = FALSE; // no activity yet
|
|
spiActivityTimer = 0;
|
|
|
|
#if __INCLUDE_SPI
|
|
// start the ball rolling..
|
|
if((spiXfer = HAL_SPI_TransmitReceive_DMA(&hSPI, spiTxBuffer.rawData, spiRxBuffer.rawData, SPI_RAW_LEN)) != HAL_OK)
|
|
spiErrorOccurred = TRUE;
|
|
#endif
|
|
|
|
}
|
|
|
|
// execute the task
|
|
void SPI_Task_Exec(void)
|
|
{
|
|
static IP400_FRAME *txFrame;
|
|
static uint16_t txSegLength, txPayloadRoom, hopSize;
|
|
static uint8_t *prxData, *ptxData, nHops;
|
|
static uint16_t rxSegLen;
|
|
|
|
#if __INCLUDE_SPI
|
|
// check the status first: repost Rx if an error occurred and it is now ready
|
|
if(spiErrorOccurred) {
|
|
if(hSPI.State == HAL_SPI_STATE_READY) {
|
|
if((spiXfer = HAL_SPI_TransmitReceive_DMA(&hSPI, spiTxBuffer.rawData, spiRxBuffer.rawData, SPI_RAW_LEN)) == HAL_OK) {
|
|
spiErrorOccurred = FALSE;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Here we wait for an exchange to be completed
|
|
* If there is no activity for NO_SPI_TIMEOUT, then
|
|
* the other end is probably dead, so clean up
|
|
* any pending frames. If SPI is not enabled,
|
|
* the queue will also be cleaned up
|
|
*/
|
|
if(!spiExchangeComplete) {
|
|
spiActivityTimer += 1;
|
|
if(spiActivityTimer >= NO_SPI_TIMEOUT) {
|
|
EmptySPIFrameQ();
|
|
spiActive = FALSE;
|
|
spiActivityTimer = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
spiExchangeComplete = FALSE; // reset exchange done
|
|
spiActive = TRUE; // indicate that the SPI is active..
|
|
spiActivityTimer = 0; // reset no activity timer
|
|
|
|
// scope trigger on nucleo board
|
|
#if _BOARD_TYPE==NUCLEO_BOARD // board type in use
|
|
HAL_GPIO_TogglePin(SCOPE_GPIO_Port, SCOPE_Pin);
|
|
#endif
|
|
|
|
/*
|
|
* process an outbound frame
|
|
* Fragment it if longer that 500 bytes
|
|
*/
|
|
switch(SPITxState) {
|
|
|
|
case SPITXIDLE:
|
|
if((txFrame=dequeFrame(&spiTxQueue)) == NULL) {
|
|
spiTxBuffer.spiData.hdr.status = NO_DATA;
|
|
break;
|
|
}
|
|
txSegLength = txFrame->length;
|
|
nHops = txFrame->flagfld.flags.hop_count;
|
|
hopSize = (uint16_t)nHops * sizeof(HOPTABLE);
|
|
txPayloadRoom = SPI_BUFFER_LEN - hopSize;
|
|
spiTxBuffer.spiData.hdr.status = SINGLE_FRAME;
|
|
|
|
// fragment the frame if needed
|
|
if(txFrame->length > txPayloadRoom) {
|
|
txSegLength = txPayloadRoom;
|
|
spiTxBuffer.spiData.hdr.status = FRAGMENT;
|
|
SPITxState = SPITXFRAG;
|
|
}
|
|
|
|
// copy the address fields
|
|
memcpy(&spiTxBuffer.spiData.hdr.fromCall, txFrame->source.callbytes.bytes, N_CALL);
|
|
memcpy(&spiTxBuffer.spiData.hdr.fromIP, txFrame->source.vpnBytes.vpn, N_IPBYTES);
|
|
|
|
memcpy(spiTxBuffer.spiData.hdr.toCall, txFrame->dest.callbytes.bytes, N_CALL);
|
|
memcpy(&spiTxBuffer.spiData.hdr.toIP, txFrame->dest.vpnBytes.vpn, N_IPBYTES);
|
|
|
|
// flag fields
|
|
spiTxBuffer.spiData.hdr.coding = txFrame->flagfld.flags.coding;
|
|
spiTxBuffer.spiData.hdr.hopCount = txFrame->flagfld.flags.hop_count;
|
|
spiTxBuffer.spiData.hdr.flags = (uint8_t)((txFrame->flagfld.allflags >> 8) & 0xFF);
|
|
|
|
ptxData = spiTxBuffer.spiData.buffer;
|
|
|
|
// hop table
|
|
if(txFrame->flagfld.flags.hoptable) {
|
|
memcpy(ptxData, txFrame->hopTable, hopSize);
|
|
ptxData += hopSize;
|
|
free(txFrame->hopTable);
|
|
}
|
|
|
|
// and now the data
|
|
memcpy(ptxData, txFrame->buf, txSegLength);
|
|
spiTxBuffer.spiData.hdr.offset_hi = spiTxBuffer.spiData.hdr.offset_lo = 0;
|
|
spiTxBuffer.spiData.hdr.length_hi = ((txSegLength + hopSize) >> 8);
|
|
spiTxBuffer.spiData.hdr.length_lo = ((txSegLength + hopSize) & 0xFF);
|
|
|
|
// release memory if only a single frame
|
|
if(spiTxBuffer.spiData.hdr.status == SINGLE_FRAME)
|
|
{
|
|
free(txFrame->buf);
|
|
free(txFrame);
|
|
}
|
|
break;
|
|
|
|
case SPITXFRAG:
|
|
uint16_t offset = ((uint16_t)(spiTxBuffer.spiData.hdr.offset_hi) << 8) + (uint16_t)spiTxBuffer.spiData.hdr.offset_lo;
|
|
uint16_t prevLen = ((uint16_t)(spiTxBuffer.spiData.hdr.length_hi) << 8) + (uint16_t)spiTxBuffer.spiData.hdr.length_lo;
|
|
offset += prevLen;
|
|
txFrame->length -= prevLen;
|
|
txSegLength = txFrame->length;
|
|
if(txFrame->length > SPI_BUFFER_LEN) {
|
|
spiTxBuffer.spiData.hdr.status = FRAGMENT;
|
|
txSegLength = SPI_BUFFER_LEN;
|
|
} else {
|
|
spiTxBuffer.spiData.hdr.status = LAST_FRAGMENT;
|
|
SPITxState = SPITXIDLE;
|
|
}
|
|
|
|
// send the next fragment
|
|
void *fragAddr = txFrame->buf + offset;
|
|
memcpy(spiTxBuffer.spiData.buffer, fragAddr, txSegLength);
|
|
spiTxBuffer.spiData.hdr.offset_hi = (offset >> 8);
|
|
spiTxBuffer.spiData.hdr.offset_lo = (offset & 0xff);
|
|
spiTxBuffer.spiData.hdr.length_hi = (txSegLength >> 8);
|
|
spiTxBuffer.spiData.hdr.length_lo = (txSegLength & 0xFF);
|
|
|
|
// done with frame
|
|
if(spiTxBuffer.spiData.hdr.status == LAST_FRAGMENT)
|
|
{
|
|
free(txFrame->buf);
|
|
free(txFrame);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Inbound frame. Reassemble fragments if needed...
|
|
*/
|
|
|
|
switch(SPIRxState) {
|
|
|
|
case SPIRXIDLE:
|
|
spiFrameStatus rstat = spiRxBuffer.spiData.hdr.status;
|
|
if((rstat == NO_DATA) || (rstat >= NUM_STATS))
|
|
break;
|
|
|
|
if(!isIP400Frame(spiRxBuffer.spiData.hdr.eye))
|
|
break;
|
|
|
|
rxSegLen = (spiRxBuffer.spiData.hdr.length_hi << 8) + spiRxBuffer.spiData.hdr.length_lo;
|
|
if(rstat != SINGLE_FRAME) {
|
|
prxData = rxFrameBuffer + (spiRxBuffer.spiData.hdr.offset_hi << 8) + spiRxBuffer.spiData.hdr.offset_lo;
|
|
memcpy(prxData, spiRxBuffer.spiData.buffer, rxSegLen);
|
|
SPIRxState = SPIRXFRAG;
|
|
} else {
|
|
SendSPIFrame(&spiRxBuffer.spiData.hdr, spiRxBuffer.spiData.buffer, rxSegLen);
|
|
}
|
|
break;
|
|
|
|
case SPIRXFRAG:
|
|
uint8_t fragStat = spiRxBuffer.spiData.hdr.status;
|
|
uint16_t offset = (spiTxBuffer.spiData.hdr.offset_hi << 8) + spiTxBuffer.spiData.hdr.offset_lo;
|
|
prxData = rxFrameBuffer + offset;
|
|
rxSegLen = (spiRxBuffer.spiData.hdr.length_hi << 8) + spiRxBuffer.spiData.hdr.length_lo;
|
|
memcpy(prxData, spiRxBuffer.spiData.buffer, rxSegLen);
|
|
|
|
if(fragStat == LAST_FRAGMENT) {
|
|
uint16_t frameLen = offset + rxSegLen;
|
|
spiRxBuffer.spiData.hdr.length_hi = (frameLen >> 8);
|
|
spiRxBuffer.spiData.hdr.length_lo = (frameLen & 0xFF);
|
|
// process frame for tx here...
|
|
SPIRxState = SPIRXIDLE; // placeholder
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// test an incoming frame
|
|
BOOL isIP400Frame(uint8_t *eye)
|
|
{
|
|
if((eye[0] != 'I') || (eye[1] != 'P'))
|
|
return FALSE;
|
|
|
|
if((eye[2] != '4') || (eye[3] != 'C'))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#if __INCLUDE_SPI
|
|
// rx done callback
|
|
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef * hspi)
|
|
{
|
|
// set data valid and start a new transfer
|
|
if(hspi->State == HAL_SPI_STATE_READY) {
|
|
spiExchangeComplete = TRUE;
|
|
spiXfer = HAL_SPI_TransmitReceive_DMA(&hSPI, spiTxBuffer.rawData, spiRxBuffer.rawData, SPI_RAW_LEN);
|
|
if(spiXfer != HAL_OK)
|
|
spiErrorOccurred = TRUE;
|
|
return;
|
|
}
|
|
spiErrorOccurred = TRUE; // spi not ready
|
|
}
|
|
#endif
|