Program Listing for File dm_fx_spi_proto.cpp¶
↰ Return to documentation for file (src/dm_fx_spi_proto.cpp)
// Copyright (c) 2020 Run Jump Labs LLC. All right reserved.
// This code is licensed under MIT license (see license.txt for details)
#include <SPI.h>
#include "dreammakerfx.h"
#include "dm_fx_spi_proto.h"
#ifndef DOXYGEN_SHOULD_SKIP_THIS
/************************************************************************
*
* SPI Protocol
*
***********************************************************************/
#define SPI_SPEED_HZ 8000000
#define MAX_SPI_BLOCK_SIZE (2048)
#define SPI_FIFO_SIZE (2048)
#define SPI_FIFO_MASK (SPI_FIFO_SIZE-1)
#define SPI_RX_FRAME_SIZE (SPI_DSP_STAT_FRAME_SIZE + 3)
#define SPI_RX_PAYLOAD_SIZE (SPI_DSP_STAT_FRAME_SIZE)
// Frame constants
#define FRAME_HEADER_1 (0x80FD)
#define FRAME_HEADER_2 (0x80FE)
#define FRAME_TERMINATOR (0x80FF)
// SPI Interface
uint16_t spi_tx_fifo[SPI_FIFO_SIZE];
uint16_t spi_rx_frame[SPI_RX_FRAME_SIZE];
// DSP status structure
DSP_STATUS dsp_status;
// Initialize SPI communications
uint16_t spi_tx_wr_ptr = 0;
uint16_t spi_tx_rd_ptr = 0;
int16_t spi_rx_wr_ptr = 0;
SPI_RX_STATE spi_rx_state = SPI_RX_WAITING;
uint16_t spi_service_last_millis = 0;
void spi_start(void) {
// Start SPI port
pinMode(SPI_SS_PIN, OUTPUT);
digitalWrite(SPI_SS_PIN, HIGH);
SPI.begin();
// Reset SPI frame in case we just updated firmware
spi_transmit_buffered_frames(true);
}
void spi_stop(void) {
// Stop SPI peripheral
SPI.end();
pinMode(PIN_DSP_SPI_FLASH_SELECT, INPUT);
}
void spi_fifo_reset(void) {
spi_tx_wr_ptr = 0;
spi_tx_rd_ptr = 0;
memset(spi_rx_frame, 0, sizeof(spi_rx_frame));
memset(&dsp_status, 0, sizeof(dsp_status));
}
static bool spi_fifo_push(uint16_t val) {
if (SPI_FIFO_MASK & (spi_tx_wr_ptr+1) == spi_tx_rd_ptr) {
DEBUG_MSG("Not enough room in FIFO", MSG_WARN);
return false;
}
spi_tx_fifo[spi_tx_wr_ptr++] = val;
spi_tx_wr_ptr &= SPI_FIFO_MASK;
return true;
}
void spi_fifo_push_emptry_frame(void) {
for (int i=0;i<SPI_RX_FRAME_SIZE;i++) {
spi_fifo_push(0);
}
}
static uint16_t spi_fifo_available_space(void) {
if (spi_tx_wr_ptr > spi_tx_rd_ptr) {
return SPI_FIFO_SIZE - (spi_tx_wr_ptr - spi_tx_rd_ptr);
}
else if (spi_tx_rd_ptr > spi_tx_wr_ptr) {
return (spi_tx_rd_ptr - spi_tx_wr_ptr) - 1;
}
else {
return SPI_FIFO_SIZE;
}
}
bool spi_fifo_insert_block(uint16_t * data, int size) {
#if 0
char msg[64];
sprintf(msg,"Inserting block of size %d", size);
DEBUG_MSG(msg, MSG_INFO);
#endif
// Check if block exceeds size
if (size > MAX_SPI_BLOCK_SIZE-3) {
DEBUG_MSG("SPI block size too big", MSG_ERROR);
display_error_status(ERROR_INTERNAL);
}
// Check if block will fit in the fifo
if (size > spi_fifo_available_space()) {
DEBUG_MSG("SPI FIFO full", MSG_ERROR);
display_error_status(ERROR_INTERNAL);
}
// Add frame to FIFO
// 1. Add frame headers
spi_fifo_push(FRAME_HEADER_1);
spi_fifo_push(FRAME_HEADER_2);
// 2. Add frame size
spi_fifo_push(size);
// 3. Add frame payload
for (int i=0;i<size;i++) {
spi_fifo_push(data[i]);
}
// 4. Add frame terminator
spi_fifo_push(FRAME_TERMINATOR);
}
void spi_process_received_frame(uint16_t * rx_frame) {
#if 0
Serial.println("-------");
for (int i=0;i<SPI_RX_PAYLOAD_SIZE;i++) {
Serial.println(rx_frame[i], HEX);
}
#endif
dsp_status.firmware_ver = rx_frame[SPI_DSP_STAT_FIRMWARE_MAJ] << 16;
dsp_status.firmware_ver |= rx_frame[SPI_DSP_STAT_FIRMWARE_MIN];
dsp_status.firmware_valid = true;
if (dsp_status.firmware_ver < 10000 || dsp_status.firmware_ver > 99999) {
dsp_status.firmware_valid = false;
}
dsp_status.loading_percentage = 100.0 * ((float) rx_frame[SPI_DSP_STAT_MIPS_PERCENT] * (1.0/65536.0));
dsp_status.amplitude = ((float) rx_frame[SPI_DSP_STAT_AMPLITUDE]) / 65536.0;
dsp_status.new_note = rx_frame[SPI_DSP_STAT_NEW_NOTE];
// Get system state
uint16_t sys_state = rx_frame[SPI_DSP_STAT_SYS_STATE];
dsp_status.state_flags = sys_state;
dsp_status.state_booted = ((sys_state & SYS_VALID) == SYS_VALID)?true:false;
dsp_status.state_initialized = (sys_state & SYS_INITIALIZED)?true:false;
dsp_status.state_lf_audio_running = (sys_state & SYS_LF_AUDIO)?true:false;
dsp_status.state_hf_audio_running = (sys_state & SYS_HF_AUDIO)?true:false;
dsp_status.state_canvas_running = (sys_state & SYS_CANVAS_OK)?true:false;
dsp_status.state_err_allocation = (sys_state & SYS_ERR_ALLOC)?true:false;
dsp_status.state_err_param = (sys_state & SYS_ERR_PARAM)?true:false;
dsp_status.state_err_corrupt = (sys_state & SYS_ERR_CRPT)?true:false;
dsp_status.state_err_other = (sys_state & SYS_ERR_OTHER)?true:false;
}
void spi_transmit_buffered_frames(bool reset_state) {
static uint8_t rx_frame_state = 0;
static uint16_t rx_frame_ptr = 0;
static uint16_t rx_frame_buf[16];
if (reset_state) {
rx_frame_state = SPI_RX_WAITING;
return;
}
// If there are no words in the fifo, return
if (spi_tx_wr_ptr == spi_tx_rd_ptr) {
return;
}
// If we last serviced the spi port less than 10 milliseconds ago, hold off
uint32_t now = millis();
if (spi_service_last_millis + 10 > now) {
return;
}
spi_service_last_millis = now;
// Begin SPI transaction
SPI.beginTransaction(SPISettings(SPI_SPEED_HZ, MSBFIRST, SPI_MODE0));
digitalWrite(SPI_SS_PIN, LOW);
uint16_t rx_word;
while (spi_tx_wr_ptr != spi_tx_rd_ptr) {
// SPI TX/RX operation
rx_word = SPI.transfer16(spi_tx_fifo[spi_tx_rd_ptr++]);
if (spi_tx_rd_ptr >= SPI_FIFO_SIZE) {
spi_tx_rd_ptr = 0;
}
//Serial.println(rx_word, HEX);
// SPI process received frame
if (spi_rx_state == SPI_RX_RECEIVING && rx_word == FRAME_TERMINATOR) {
spi_rx_state = SPI_RX_WAITING;
spi_process_received_frame(spi_rx_frame);
} else if (spi_rx_state == SPI_RX_RECEIVING) {
spi_rx_frame[spi_rx_wr_ptr++] = rx_word;
// if we've received a complete frame, set state to ready
if (spi_rx_wr_ptr >= SPI_RX_PAYLOAD_SIZE) {
spi_rx_wr_ptr = SPI_RX_PAYLOAD_SIZE - 1;
}
}
else if (spi_rx_state == SPI_RX_WAITING && rx_word == FRAME_HEADER_1) {
spi_rx_state = SPI_RX_HEADER_1_RX;
}
else if (spi_rx_state == SPI_RX_HEADER_1_RX && rx_word == FRAME_HEADER_2) {
spi_rx_state = SPI_RX_RECEIVING;
spi_rx_wr_ptr = 0;
}
}
// End transaction
digitalWrite(SPI_SS_PIN, HIGH);
SPI.endTransaction();
}
#endif // DOXYGEN_SHOULD_SKIP_THIS