add SPI slave support

Set up SPI slave transactions for entries in the SPI send queue.
Add a header to each SPI datagram that includes a CRC16,
the type and the size of the message that follows.

Does not act on received bytes (yet).

Signed-off-by: Christian Ambach <christian.ambach@deutschebahn.com>
This commit is contained in:
Christian Ambach 2018-09-11 16:15:39 +02:00
parent bf3516bf45
commit a3750ef01b
10 changed files with 209 additions and 73 deletions

View File

@ -66,10 +66,6 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
#include "lorawan.h" #include "lorawan.h"
#endif #endif
#ifdef HAS_SPI
#include "spisend.h"
#endif
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
#include "display.h" #include "display.h"
#endif #endif

View File

@ -1,9 +0,0 @@
#ifndef _SPISEND_H
#define _SPISEND_H
extern TaskHandle_t SpiTask;
extern QueueHandle_t SPISendQueue;
void spi_loop(void *pvParameters);
#endif

36
include/spislave.h Normal file
View File

@ -0,0 +1,36 @@
/*
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
Copyright 2018 Christian Ambach <christian.ambach@deutschebahn.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
NOTICE:
Parts of the source files in this repository are made available under different
licenses. Refer to LICENSE.txt file in repository for more details.
*/
#ifndef _SPISLAVE_H
#define _SPISLAVE_H
#include "globals.h"
esp_err_t spi_init();
void spi_enqueuedata(uint8_t messageType, MessageBuffer_t *message);
void spi_queuereset();
void spi_housekeeping();
#endif // _SPISLAVE_H

View File

@ -4,6 +4,7 @@
// Basic config // Basic config
#include "cyclic.h" #include "cyclic.h"
#include "rcommand.h" #include "rcommand.h"
#include "spislave.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
@ -26,9 +27,9 @@ void doHousekeeping() {
#ifdef HAS_GPS #ifdef HAS_GPS
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask)); ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
#endif #endif
#ifdef HAS_SPI
ESP_LOGD(TAG, "Spiloop %d bytes left", uxTaskGetStackHighWaterMark(SpiTask)); spi_housekeeping();
#endif
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
ESP_LOGD(TAG, "LEDloop %d bytes left", uxTaskGetStackHighWaterMark(ledLoopTask)); ESP_LOGD(TAG, "LEDloop %d bytes left", uxTaskGetStackHighWaterMark(ledLoopTask));
#endif #endif

View File

@ -7,6 +7,11 @@
#define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI #define HAS_SPI 1 // comment out if device shall not send data via SPI
// pin definitions for SPI slave interface
#define SPI_MOSI GPIO_NUM_23
#define SPI_MISO GPIO_NUM_19
#define SPI_SCLK GPIO_NUM_18
#define SPI_CS GPIO_NUM_5
#define CFG_sx1276_radio 1 // select LoRa chip #define CFG_sx1276_radio 1 // select LoRa chip
//#define CFG_sx1272_radio 1 // select LoRa chip //#define CFG_sx1272_radio 1 // select LoRa chip

View File

@ -7,6 +7,12 @@
#define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI #define HAS_SPI 1 // comment out if device shall not send data via SPI
// pin definitions for SPI slave interface
#define SPI_MOSI GPIO_NUM_22
#define SPI_MISO GPIO_NUM_33
#define SPI_SCLK GPIO_NUM_26
#define SPI_CS GPIO_NUM_36
#define CFG_sx1276_radio 1 #define CFG_sx1276_radio 1
//#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED //#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 (P2) #define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 (P2)

View File

@ -48,6 +48,7 @@ ESP32 hardware timers
// Basic Config // Basic Config
#include "main.h" #include "main.h"
#include "spislave.h"
configData_t cfg; // struct holds current device configuration configData_t cfg; // struct holds current device configuration
char display_line6[16], display_line7[16]; // display buffers char display_line6[16], display_line7[16]; // display buffers
@ -197,14 +198,8 @@ void setup() {
// initialize SPI // initialize SPI
#ifdef HAS_SPI #ifdef HAS_SPI
strcat_P(features, " SPI"); strcat_P(features, " SPI");
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (SPISendQueue == 0) {
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
exit(0);
} else
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
#endif #endif
assert(spi_init() == ESP_OK);
#ifdef VENDORFILTER #ifdef VENDORFILTER
strcat_P(features, " OUIFLT"); strcat_P(features, " OUIFLT");
@ -312,17 +307,6 @@ void setup() {
1); // CPU core 1); // CPU core
#endif #endif
#ifdef HAS_SPI
ESP_LOGI(TAG, "Starting SPIloop...");
xTaskCreatePinnedToCore(spi_loop, // task function
"spiloop", // name of task
2048, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
&SpiTask, // task handle
0); // CPU core
#endif
// start state machine // start state machine
ESP_LOGI(TAG, "Starting IRQ Handler..."); ESP_LOGI(TAG, "Starting IRQ Handler...");
xTaskCreatePinnedToCore(irqHandler, // task function xTaskCreatePinnedToCore(irqHandler, // task function

View File

@ -1,5 +1,6 @@
// Basic Config // Basic Config
#include "globals.h" #include "globals.h"
#include "spislave.h"
// put data to send in RTos Queues used for transmit over channels Lora and SPI // put data to send in RTos Queues used for transmit over channels Lora and SPI
void SendData(uint8_t port) { void SendData(uint8_t port) {
@ -19,12 +20,7 @@ void SendData(uint8_t port) {
ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", payload.getSize()); ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", payload.getSize());
#endif #endif
// enqueue message in SPI send queue spi_enqueuedata(port, &SendBuffer);
#ifdef HAS_SPI
if (xQueueSendToBack(SPISendQueue, (void *)&SendBuffer, (TickType_t)0) ==
pdTRUE)
ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", payload.getSize());
#endif
// clear counter if not in cumulative counter mode // clear counter if not in cumulative counter mode
if ((port == COUNTERPORT) && (cfg.countermode != 1)) { if ((port == COUNTERPORT) && (cfg.countermode != 1)) {
@ -66,7 +62,6 @@ void flushQueues() {
#ifdef HAS_LORA #ifdef HAS_LORA
xQueueReset(LoraSendQueue); xQueueReset(LoraSendQueue);
#endif #endif
#ifdef HAS_SPI
xQueueReset(SPISendQueue); spi_queuereset();
#endif
} }

View File

@ -1,30 +0,0 @@
#ifdef HAS_SPI
#include "globals.h"
// Local logging tag
static const char TAG[] = "main";
MessageBuffer_t SendBuffer;
QueueHandle_t SPISendQueue;
TaskHandle_t SpiTask;
// SPI feed Task
void spi_loop(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
while (1) {
if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize);
}
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
} // end of infinite loop
vTaskDelete(NULL); // shoud never be reached
} // spi_loop()
#endif // HAS_SPI

152
src/spislave.cpp Normal file
View File

@ -0,0 +1,152 @@
/*
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
Copyright 2018 Christian Ambach <christian.ambach@deutschebahn.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
NOTICE:
Parts of the source files in this repository are made available under different
licenses. Refer to LICENSE.txt file in repository for more details.
*/
#include "spislave.h"
#include <driver/spi_slave.h>
#include <sys/param.h>
#include <rom/crc.h>
static const char TAG[] = __FILE__;
#define HEADER_SIZE 4
// SPI transaction size needs to be at least 8 bytes and dividable by 4, see
// https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/spi_slave.html
#define BUFFER_SIZE \
(MAX(8, HEADER_SIZE + PAYLOAD_BUFFER_SIZE) + \
(4 - MAX(8, HEADER_SIZE + PAYLOAD_BUFFER_SIZE) % 4))
DMA_ATTR uint8_t txbuf[BUFFER_SIZE];
DMA_ATTR uint8_t rxbuf[BUFFER_SIZE];
QueueHandle_t SPISendQueue;
TaskHandle_t spiTask;
void spi_slave_task(void *param) {
while (1) {
MessageBuffer_t msg;
size_t transaction_size;
memset(txbuf, 0, sizeof(txbuf));
memset(rxbuf, 0, sizeof(rxbuf));
if (xQueueReceive(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
continue;
}
uint8_t *messageType = txbuf + 2;
*messageType = msg.MessagePort;
uint8_t *messageSize = txbuf + 3;
*messageSize = msg.MessageSize;
memcpy(txbuf + HEADER_SIZE, &msg.Message, msg.MessageSize);
transaction_size = HEADER_SIZE + msg.MessageSize;
transaction_size += (4 - transaction_size % 4);
uint16_t *crc = (uint16_t *)txbuf;
*crc = crc16_be(0, messageType, msg.MessageSize + HEADER_SIZE - 2);
spi_slave_transaction_t spi_transaction = {0};
spi_transaction.length = transaction_size * 8;
spi_transaction.tx_buffer = txbuf;
spi_transaction.rx_buffer = rxbuf;
ESP_LOGI(TAG, "Prepared SPI transaction for %zu bytes", transaction_size);
ESP_LOG_BUFFER_HEXDUMP(TAG, txbuf, transaction_size, ESP_LOG_DEBUG);
esp_err_t ret =
spi_slave_transmit(HSPI_HOST, &spi_transaction, portMAX_DELAY);
ESP_LOG_BUFFER_HEXDUMP(TAG, rxbuf, transaction_size, ESP_LOG_DEBUG);
ESP_LOGI(TAG, "Transaction finished with size %zu bits",
spi_transaction.trans_len);
}
}
esp_err_t spi_init() {
#ifndef HAS_SPI
return ESP_OK;
#else
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (SPISendQueue == 0) {
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
return ESP_FAIL;
}
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
spi_bus_config_t spi_bus_cfg = {.mosi_io_num = SPI_MOSI,
.miso_io_num = SPI_MISO,
.sclk_io_num = SPI_SCLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 0,
.flags = 0};
spi_slave_interface_config_t spi_slv_cfg = {.spics_io_num = SPI_CS,
.flags = 0,
.queue_size = 1,
.mode = 0,
.post_setup_cb = NULL,
.post_trans_cb = NULL};
gpio_set_pull_mode(SPI_MOSI, GPIO_PULLUP_ONLY);
gpio_set_pull_mode(SPI_SCLK, GPIO_PULLUP_ONLY);
gpio_set_pull_mode(SPI_CS, GPIO_PULLUP_ONLY);
ESP_LOGI(TAG, "Starting SPIloop...");
xTaskCreate(spi_slave_task, "spiloop", 4096, (void *)NULL, 2, &spiTask);
esp_err_t ret =
spi_slave_initialize(HSPI_HOST, &spi_bus_cfg, &spi_slv_cfg, 1);
return ret;
#endif
}
void spi_enqueuedata(uint8_t messageType, MessageBuffer_t *message) {
// enqueue message in SPI send queue
#ifdef HAS_SPI
BaseType_t ret =
xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0);
if (ret == pdTRUE) {
ESP_LOGI(TAG, "%d bytes enqueued for SPI consumption",
message->MessageSize);
} else {
ESP_LOGW(TAG, "SPI sendqueue is full");
}
#endif
}
void spi_queuereset(void) {
#ifdef HAS_SPI
xQueueReset(SPISendQueue);
#endif
}
void spi_housekeeping(void) {
#ifdef HAS_SPI
ESP_LOGD(TAG, "spiloop %d bytes left", uxTaskGetStackHighWaterMark(spiTask));
#endif
}