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" | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAS_SPI | ||||
| #include "spisend.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifdef HAS_DISPLAY | ||||
| #include "display.h" | ||||
| #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
 | ||||
| #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 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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)
 | ||||
|  | ||||
							
								
								
									
										20
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								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
 | ||||
|  | ||||
| @ -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(); | ||||
| } | ||||
| @ -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