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:
parent
bf3516bf45
commit
a3750ef01b
@ -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
|
||||||
|
@ -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
36
include/spislave.h
Normal 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
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
20
src/main.cpp
20
src/main.cpp
@ -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
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
@ -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
152
src/spislave.cpp
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user