diff --git a/include/globals.h b/include/globals.h index 78df4a95..d946b2df 100644 --- a/include/globals.h +++ b/include/globals.h @@ -66,10 +66,6 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask; #include "lorawan.h" #endif -#ifdef HAS_SPI -#include "spisend.h" -#endif - #ifdef HAS_DISPLAY #include "display.h" #endif diff --git a/include/spisend.h b/include/spisend.h deleted file mode 100644 index 750a3c20..00000000 --- a/include/spisend.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _SPISEND_H -#define _SPISEND_H - -extern TaskHandle_t SpiTask; -extern QueueHandle_t SPISendQueue; - -void spi_loop(void *pvParameters); - -#endif \ No newline at end of file diff --git a/include/spislave.h b/include/spislave.h new file mode 100644 index 00000000..526203b9 --- /dev/null +++ b/include/spislave.h @@ -0,0 +1,36 @@ +/* + +//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\ + +Copyright 2018 Christian Ambach + + 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 diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 081bb556..d8a0490f 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -4,6 +4,7 @@ // Basic config #include "cyclic.h" #include "rcommand.h" +#include "spislave.h" // Local logging tag static const char TAG[] = "main"; @@ -26,9 +27,9 @@ void doHousekeeping() { #ifdef HAS_GPS ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask)); #endif -#ifdef HAS_SPI - ESP_LOGD(TAG, "Spiloop %d bytes left", uxTaskGetStackHighWaterMark(SpiTask)); -#endif + + spi_housekeeping(); + #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) ESP_LOGD(TAG, "LEDloop %d bytes left", uxTaskGetStackHighWaterMark(ledLoopTask)); #endif diff --git a/src/hal/generic.h b/src/hal/generic.h index 7e2256df..a8a5555d 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -7,6 +7,11 @@ #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 +// 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_sx1272_radio 1 // select LoRa chip diff --git a/src/hal/lopy4.h b/src/hal/lopy4.h index 79e80a36..6b93baad 100644 --- a/src/hal/lopy4.h +++ b/src/hal/lopy4.h @@ -7,6 +7,12 @@ #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 +// 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 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) diff --git a/src/main.cpp b/src/main.cpp index af5fdd4c..de80e718 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -48,6 +48,7 @@ ESP32 hardware timers // Basic Config #include "main.h" +#include "spislave.h" configData_t cfg; // struct holds current device configuration char display_line6[16], display_line7[16]; // display buffers @@ -197,14 +198,8 @@ void setup() { // initialize SPI #ifdef HAS_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 + assert(spi_init() == ESP_OK); #ifdef VENDORFILTER strcat_P(features, " OUIFLT"); @@ -312,17 +307,6 @@ void setup() { 1); // CPU core #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 ESP_LOGI(TAG, "Starting IRQ Handler..."); xTaskCreatePinnedToCore(irqHandler, // task function diff --git a/src/senddata.cpp b/src/senddata.cpp index ee293485..89657114 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -1,5 +1,6 @@ // Basic Config #include "globals.h" +#include "spislave.h" // put data to send in RTos Queues used for transmit over channels Lora and SPI 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()); #endif -// enqueue message in SPI send queue -#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 + spi_enqueuedata(port, &SendBuffer); // clear counter if not in cumulative counter mode if ((port == COUNTERPORT) && (cfg.countermode != 1)) { @@ -66,7 +62,6 @@ void flushQueues() { #ifdef HAS_LORA xQueueReset(LoraSendQueue); #endif -#ifdef HAS_SPI - xQueueReset(SPISendQueue); -#endif + + spi_queuereset(); } \ No newline at end of file diff --git a/src/spisend.cpp b/src/spisend.cpp deleted file mode 100644 index 57da9287..00000000 --- a/src/spisend.cpp +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/spislave.cpp b/src/spislave.cpp new file mode 100644 index 00000000..7cd8d729 --- /dev/null +++ b/src/spislave.cpp @@ -0,0 +1,152 @@ +/* + +//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\ + +Copyright 2018 Christian Ambach + + 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 +#include +#include + +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 +} \ No newline at end of file