/* * Driver utilities for ATmega M1/C1 controllers, based on FreeRTOS. * Copyright (c) 2022 Sven Puga. */ #include #include #include #include #include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "../lib/buffers.h" #include "../spi.h" /***************************************************************************/ #define _SPI_MODE_DISABLED 0 #define _SPI_MODE_SLAVE 1 #define _SPI_MODE_MASTER 2 static volatile uint8_t spiMode = _SPI_MODE_DISABLED; static volatile SemaphoreHandle_t spiMutex = NULL; static volatile StaticSemaphore_t spiMutexStatic; #ifndef SPI_CONFIG_BUFFER_SIZE #define SPI_CONFIG_BUFFER_SIZE 8 #endif typedef struct spiData_t { spiTransaction_t *transaction; spiConfig_t config; TaskHandle_t taskHandle; uint32_t flags; } spiData_t; static volatile spiData_t _spiData[SPI_CONFIG_BUFFER_SIZE]; static volatile circularBuffer_t spiBuffer = CIRCULAR_BUFFER(_spiData, SPI_CONFIG_BUFFER_SIZE, spiData_t); typedef struct spiActiveData_t { spiTransaction_t *transaction; uint8_t *out; uint8_t *in; size_t len; TaskHandle_t taskHandle; uint32_t flags; } spiActiveData_t; static spiActiveData_t spiActiveData; #ifndef SPI_CONFIG_PORT_DELAY #define SPI_CONFIG_PORT_DELAY portMAX_DELAY #endif #define _SPI_DDR_MOSI DDRB #define _SPI_P_MOSI PB1 #define _SPI_DDR_MISO DDRB #define _SPI_P_MISO PB0 #define _SPI_DDR_SCK DDRB #define _SPI_P_SCK PB7 #define _SPI_PIN_SS PIND #define _SPI_P_SS PD3 #define _SPI_DDR_MOSI_A DDRD #define _SPI_P_MOSI_A PD3 #define _SPI_DDR_MISO_A DDRD #define _SPI_P_MISO_A PD2 #define _SPI_DDR_SCK_A DDRD #define _SPI_P_SCK_A PD4 #define _SPI_PIN_SS_A PINC #define _SPI_P_SS_A PC1 /***************************************************************************/ #define _SPI_SET_PINOUT(x) do { \ if (x == SPI_PINOUT_ALTERNATE) \ MCUCR |= (1<transaction, .out = data->transaction->out, .in = data->transaction->in, .len = data->transaction->len, .taskHandle = data->taskHandle, .flags = data->flags }; if (spiMode == _SPI_MODE_MASTER) spiActiveData.transaction->status = _SPI_TRANSACTION_STATUS_ACTIVE | (_SPI_TRANSACTION_STATUS_RUNNING); else spiActiveData.transaction->status = _SPI_TRANSACTION_STATUS_ACTIVE; if (spiMode == _SPI_MODE_MASTER) // must be set here because of master-lost possibility _SPI_SETUP(data->config, 1); else _SPI_SETUP(data->config, 0); void (*fcnStart)(void) = spiActiveData.transaction->fcnStart; if (fcnStart != NULL) fcnStart(); if (spiActiveData.out != NULL) _SPI_ENABLE_AND_START(*spiActiveData.out); else _SPI_ENABLE_AND_START(0); spiActiveData.out++; spiActiveData.len--; } } #undef _SPI_SETUP #undef _SPI_ENABLE_AND_START error_t spi_run(spiTransaction_t *transaction, spiConfig_t config) { const uint32_t eFlags = 1; //error_t e = spi_runAsync(transaction, config, eFlags); volatile error_t e = spi_runAsync(transaction, config, eFlags); if (e == SPI_ERROR_SUCCESS) { if (xTaskNotifyWait(pdFALSE, eFlags, NULL, SPI_CONFIG_PORT_DELAY) != pdPASS) e = SPI_ERROR_WAIT_TIMEOUT; } return e; } static inline error_t spi_mutexTake() { if (spiMutex == NULL) spiMutex = xSemaphoreCreateMutexStatic((StaticSemaphore_t*)&spiMutexStatic); if (xSemaphoreTake(spiMutex, SPI_CONFIG_PORT_DELAY) != pdTRUE) return SPI_ERROR_MUTEX_TAKE_FAILED; else return SPI_ERROR_SUCCESS; } static inline error_t spi_mutexGive() { if (xSemaphoreGive(spiMutex) != pdTRUE) return SPI_ERROR_MUTEX_GIVE_FAILED; else return SPI_ERROR_SUCCESS; } #define _SPI_IS_ACTIVE() ( SPCR & (1<len == 0) e = SPI_ERROR_SIZE_ZERO; else { spiData_t *data = circularBufferNext((circularBuffer_t*)&spiBuffer); if (data == NULL) e = SPI_ERROR_BUFFER_FULL; else { transaction->status = 0; data->transaction = transaction; data->config = config; data->taskHandle = xTaskGetCurrentTaskHandle(); data->flags = eFlags; taskENTER_CRITICAL(); if (!_SPI_IS_ACTIVE()) spi_startNew(); taskEXIT_CRITICAL(); } } //_SPI_MUTEX_GIVE(dataMutex, e); ON_ERROR_RETURN(spi_mutexGive()); return e; } #undef _SPI_IS_ACTIVE #undef _SPI_MUTEX_TAKE #undef _SPI_MUTEX_GIVE #define _SPI_DISABLE() do { \ SPCR = 0; \ } while(0) static void spi_stop() { _SPI_DISABLE(); _SPI_RESET_STATUS(); } #undef _SPI_RESET_STATUS #define _SPI_READ_DATA(d) do { \ d = SPDR; \ } while(0) #define _SPI_WRITE_DATA(d) do { \ SPDR = d; \ } while(0) #define _SPI_IS_WRITE_COLLISION() ( SPSR & (1<status = _SPI_TRANSACTION_STATUS_FINISHED | _SPI_TRANSACTION_STATUS_WRITE_COLLISION; finished = true; } else if (_SPI_IS_MASTER_LOST(spiMode)) { spiActiveData.transaction->status = _SPI_TRANSACTION_STATUS_FINISHED | _SPI_TRANSACTION_STATUS_MASTER_LOST; finished = true; } else if (spiActiveData.len > 0) { void (*fcnUpdate)(void) = spiActiveData.transaction->fcnUpdate; if (fcnUpdate != NULL) fcnUpdate(); if (spiActiveData.out != NULL) { _SPI_WRITE_DATA(*spiActiveData.out); spiActiveData.out++; } else _SPI_WRITE_DATA(0); if (spiActiveData.in != NULL) { _SPI_READ_DATA(*spiActiveData.in); spiActiveData.in++; } spiActiveData.len--; finished = false; } else { if (spiActiveData.in != NULL) _SPI_READ_DATA(*spiActiveData.in); spiActiveData.transaction->status = _SPI_TRANSACTION_STATUS_FINISHED; finished = true; } if (finished) { void (*fcnStop)(void) = spiActiveData.transaction->fcnStop; if (fcnStop != NULL) fcnStop(); _SPI_DISABLE(); xTaskNotifyFromISR(spiActiveData.taskHandle, spiActiveData.flags, eSetBits, NULL); spi_startNew(); } } #undef _SPI_READ_DATA #undef _SPI_WRITE_DATA #undef _SPI_IS_WRITE_COLLISION #undef _SPI_IS_MASTER_LOST #undef _SPI_DISABLE