diff --git a/firmware/PowerDistributionUnit_Mk1/core/inc/LSOM_S_Pins.h b/firmware/PowerDistributionUnit_Mk1/core/inc/LSOM_S_Pins.h index 12fb4cf..68813dc 100644 --- a/firmware/PowerDistributionUnit_Mk1/core/inc/LSOM_S_Pins.h +++ b/firmware/PowerDistributionUnit_Mk1/core/inc/LSOM_S_Pins.h @@ -28,8 +28,9 @@ #define LSOM_12_PORT GPIOB #define LSOM_12_PIN GPIO_PIN_12 -#define LSOM_13_PORT GPIOD -#define LSOM_13_PIN GPIO_PIN_2 +// differs from sheet? +#define LSOM_13_PORT GPIOC +#define LSOM_13_PIN GPIO_PIN_12 #define LSOM_14_PORT GPIOB #define LSOM_14_PIN GPIO_PIN_14 diff --git a/firmware/PowerDistributionUnit_Mk1/core/inc/PDU_Mk1_HSSControl.h b/firmware/PowerDistributionUnit_Mk1/core/inc/PDU_Mk1_HSSControl.h new file mode 100644 index 0000000..ae66636 --- /dev/null +++ b/firmware/PowerDistributionUnit_Mk1/core/inc/PDU_Mk1_HSSControl.h @@ -0,0 +1,320 @@ +// PDU_Mk1_HSSControl.h +// ---------------------------------------------------------------------------- +// Stuff for controlling the high-side switches on BBPDU. + +#pragma once + +// INCLUDES ------------------------------------------------------------------- + +#include "stm32xx_hal.h" + +// BBPDU peripherals +#include "PDU_Mk1.h" +#include "PDU_Mk1_Pins.h" +#include "PDU_Mk1_OutputConfig.h" + +// drivers +#include "ShiftRegister_SPI.h" + +// DEFINES AND ENUMS ---------------------------------------------------------- + +typedef enum { + PDU_OUTPUT_0, + PDU_OUTPUT_1, + PDU_OUTPUT_2, + PDU_OUTPUT_3, + PDU_OUTPUT_4, + PDU_OUTPUT_5, + PDU_OUTPUT_6, + PDU_OUTPUT_7, + PDU_OUTPUT_8, + PDU_OUTPUT_9, + PDU_OUTPUT_10, + PDU_OUTPUT_11, + PDU_OUTPUT_12, + PDU_OUTPUT_13, + PDU_OUTPUT_14, + PDU_OUTPUT_15, + PDU_OUTPUT_INVALID, +} HSSControl_Channel_t; + +typedef enum { // Desired action to output channel EN state... + HSSCONTROL_EN_NOCHANGE, // performs no change + HSSCONTROL_EN_OFF, // turns off the output channel + HSSCONTROL_EN_ON, // turns on the output channel + HSSCONTROL_EN_TOGGLE, // toggles the output channel +} HSSControl_EnState_t; + +typedef enum { // Desired action to output channel LATCH state... + HSSCONTROL_LATCH_NOCHANGE, // performs no change + HSSCONTROL_UNLATCHFAULT_AUTORETRY, // enables HSS auto-retry (LATCH = 0) + HSSCONTROL_LATCHFAULT_STAYOFF, // disables HSS auto-retry, faults are latched (LATCH = 1) +} HSSControl_LatchState_t; + +typedef enum { // Describes fault state of each output channel... + HSSCONTROL_NOFAULT, // no fault, everything's dandy + HSSCONTROL_FAULT_HSS_TRIP, // HSS tripped (detected via ST pin through GPIO expander) + // TPS27SA08-Q1 current limit is fixed at 20 A + + HSSCONTROL_FAULT_SOFTWARE_TRIP, // Current sensing detected current over limit + // Current limits to be set in PDU_Mk1_OutputConfig.h (TODO) +} HSSControl_FaultState_t; + +#define HSSCONTROL_STATE_NUM_BYTES 4 + +#define HSSCONTROL_MASK_EN 0xAAAAAAAA +#define HSSCONTROL_MASK_LATCH 0x55555555 +// control group 3 +#define HSSCONTROL_LSHIFT_12EN 7 +#define HSSCONTROL_MASK_12EN (1<= PDU_OUTPUT_INVALID) + { + return false; + } + + switch(action) + { + case HSSCONTROL_EN_NOCHANGE: + break; + case HSSCONTROL_EN_OFF: + HSS_state &= ~(HSSCONTROL_EN_MASKS[ch]); + break; + case HSSCONTROL_EN_ON: + HSS_state |= HSSCONTROL_EN_MASKS[ch]; + break; + case HSSCONTROL_EN_TOGGLE: + HSS_state ^= HSSCONTROL_EN_MASKS[ch]; + break; + default: + // invalid action + return false; + } + + return true; +} + +bool PDU_Mk1_HSSControl_WriteOutputEN_Ch(HSSControl_Channel_t ch, HSSControl_EnState_t action) +{ + if(PDU_Mk1_HSSControl_WriteHSSEnField(ch, action) != true) + { + return false; + } + + PDU_Mk1_HSSControl_FilterFaultedOutputENs(); + + return PDU_Mk1_HSSControl_UpdateHSSShiftRegs(); +} + +bool PDU_Mk1_HSSControl_WriteOutputEN_All(HSSControl_EnState_t actions[]) +{ + uint32_t original_HSS_state = HSS_state; + + for(uint8_t ch = 0; ch < PDU_MK1_NUM_CHANNELS; ch++) + { + if(PDU_Mk1_HSSControl_WriteHSSEnField(ch, actions[ch]) != true) + { + HSS_state = original_HSS_state; + + return false; + } + } + + PDU_Mk1_HSSControl_FilterFaultedOutputENs(); + + return PDU_Mk1_HSSControl_UpdateHSSShiftRegs(); +} + +bool PDU_Mk1_HSSControl_AllOn() +{ + HSS_state |= HSSCONTROL_MASK_EN; + printf("HSS State\n"); + printf("%ld\n\n", (uint32_t) HSS_state); + + PDU_Mk1_HSSControl_FilterFaultedOutputENs(); + + return PDU_Mk1_HSSControl_UpdateHSSShiftRegs(); +} + +bool PDU_Mk1_HSSControl_AllOff() +{ + HSS_state &= (~HSSCONTROL_MASK_EN); + + PDU_Mk1_HSSControl_FilterFaultedOutputENs(); + + return PDU_Mk1_HSSControl_UpdateHSSShiftRegs(); +} + +bool PDU_Mk1_HSSControl_CritOnly() +{ + HSS_state &= (~HSSCONTROL_MASK_EN); + HSS_state |= HSSCONTROL_CRITICAL_MASK; + + PDU_Mk1_HSSControl_FilterFaultedOutputENs(); + + return PDU_Mk1_HSSControl_UpdateHSSShiftRegs(); +} + +inline void PDU_Mk1_HSSControl_FilterFaultedOutputENs() +{ + for(uint8_t ch = 0; ch < PDU_MK1_NUM_CHANNELS; ch++) + { + if(HSS_fault_state[ch] != HSSCONTROL_NOFAULT) + { + HSS_state &= ~(HSSCONTROL_EN_MASKS[ch]); + } + } +} + +bool PDU_Mk1_HSSControl_WriteHSSLatchField(HSSControl_Channel_t ch, HSSControl_LatchState_t latch) +{ + if(ch >= PDU_OUTPUT_INVALID) + { + return false; + } + + switch(latch) + { + case HSSCONTROL_LATCH_NOCHANGE: + break; + case HSSCONTROL_UNLATCHFAULT_AUTORETRY: + HSS_state &= ~(HSSCONTROL_LATCH_MASKS[ch]); + break; + case HSSCONTROL_LATCHFAULT_STAYOFF: + HSS_state |= HSSCONTROL_LATCH_MASKS[ch]; + break; + default: + // invalid action + return false; + } + + return true; +} + +bool PDU_Mk1_HSSControl_WriteLatch_Ch(HSSControl_Channel_t ch, HSSControl_LatchState_t latch) +{ + if(PDU_Mk1_HSSControl_WriteHSSLatchField(ch, latch) != true) + { + return false; + } + + return PDU_Mk1_HSSControl_UpdateHSSShiftRegs(); +} + +bool PDU_Mk1_HSSControl_WriteLatch_All(HSSControl_LatchState_t latch[]) +{ + uint32_t original_HSS_state = HSS_state; + + for(uint8_t ch = 0; ch < PDU_MK1_NUM_CHANNELS; ch++) + { + if(PDU_Mk1_HSSControl_WriteHSSLatchField(ch, latch[ch]) != true) + { + HSS_state = original_HSS_state; + + return false; + } + } + + return PDU_Mk1_HSSControl_UpdateHSSShiftRegs(); +} + +inline bool PDU_Mk1_HSSControl_OutputFaultRetry_Ch(HSSControl_Channel_t ch) +{ + return PDU_Mk1_HSSControl_WriteLatch_Ch(ch, HSSCONTROL_UNLATCHFAULT_AUTORETRY); +} + +bool PDU_Mk1_HSSControl_OutputFaultRetry_AllFaulted() +{ + uint32_t original_HSS_state = HSS_state; + + for(uint8_t ch = 0; ch < PDU_MK1_NUM_CHANNELS; ch++) + { + if(HSS_fault_state[ch] != HSSCONTROL_NOFAULT) + { + if(PDU_Mk1_HSSControl_WriteHSSLatchField(ch, HSSCONTROL_UNLATCHFAULT_AUTORETRY) != true) + { + HSS_state = original_HSS_state; + + return false; + } + } + } + + return PDU_Mk1_HSSControl_UpdateHSSShiftRegs(); +} + +inline bool PDU_Mk1_HSSControl_OutputFaultRelatch_Ch(HSSControl_Channel_t ch) +{ + return PDU_Mk1_HSSControl_WriteLatch_Ch(ch, HSSCONTROL_LATCHFAULT_STAYOFF); +} + +bool PDU_Mk1_HSSControl_OutputFaultRelatch_AllFaulted() +{ + uint32_t original_HSS_state = HSS_state; + + for(uint8_t ch = 0; ch < PDU_MK1_NUM_CHANNELS; ch++) + { + if(HSS_fault_state[ch] != HSSCONTROL_NOFAULT) + { + if(PDU_Mk1_HSSControl_WriteHSSLatchField(ch, HSSCONTROL_LATCHFAULT_STAYOFF) != true) + { + HSS_state = original_HSS_state; + + return false; + } + } + } + + return PDU_Mk1_HSSControl_UpdateHSSShiftRegs(); +} \ No newline at end of file diff --git a/firmware/PowerDistributionUnit_Mk1/test/src/HSSControl.c b/firmware/PowerDistributionUnit_Mk1/test/src/HSSControl.c new file mode 100644 index 0000000..8a3fe50 --- /dev/null +++ b/firmware/PowerDistributionUnit_Mk1/test/src/HSSControl.c @@ -0,0 +1,293 @@ +// HSSControl.c +// ---------------------------------------------------------------------------- +// Tests HSS control via shift register for output enable and fault latching. + +// INCLUDES ------------------------------------------------------------------- + +#include "stm32xx_hal.h" +// stm32xx_hal.h contains includes for RTOS stuff. +#include +#include + +// BBPDU Peripherals +#include "PDU_Mk1_Pins.h" +#include "PDU_Mk1_SPI.h" +#include "PDU_Mk1_GPIO.h" +#include "PDU_Mk1_UART.h" + +// drivers +#include "ShiftRegister_SPI.h" +#include "PDU_Mk1_HSSControl.h" + +// DEFINES -------------------------------------------------------------------- + +#define TASKPRIORITY_INIT tskIDLE_PRIORITY + 2 +#define TASKSTACKSIZE_INIT configMINIMAL_STACK_SIZE+1500 + +#define TASKPRIORITY_BLINK tskIDLE_PRIORITY + 2 +#define TASKSTACKSIZE_BLINK configMINIMAL_STACK_SIZE + +#define TASKPRIORITY_SR_WRITE tskIDLE_PRIORITY + 2 +#define TASKSTACKSIZE_SR_WRITE configMINIMAL_STACK_SIZE+1000 + +#define INTERVAL_BLINK_MS 500 +#define INTERVAL_BLINK_ERROR_MS 50 +#define INTERVAL_SR_WRITE_MS 60000 + +// DECLARATIONS --------------------------------------------------------------- + +SPI_HandleTypeDef hspi1; +SPI_HandleTypeDef hspi2; +SPI_HandleTypeDef hspi3; + +SemaphoreHandle_t spi1_mutex; // Mutex to prevent simultaneous SPI access +StaticSemaphore_t spi1_mutex_buffer; // Static buffer for mutex allocation + +SemaphoreHandle_t spi1_done_sem; // Semaphore to signal SPI IT completion +StaticSemaphore_t spi1_done_sem_buffer; // Static buffer for completion semaphore + +SemaphoreHandle_t spi2_mutex; // Mutex to prevent simultaneous SPI access +StaticSemaphore_t spi2_mutex_buffer; // Static buffer for mutex allocation + +SemaphoreHandle_t spi2_done_sem; // Semaphore to signal SPI IT completion +StaticSemaphore_t spi2_done_sem_buffer; // Static buffer for completion semaphore + +SemaphoreHandle_t spi3_mutex; // Mutex to prevent simultaneous SPI access +StaticSemaphore_t spi3_mutex_buffer; // Static buffer for mutex allocation + +SemaphoreHandle_t spi3_done_sem; // Semaphore to signal SPI IT completion +StaticSemaphore_t spi3_done_sem_buffer; // Static buffer for completion semaphore + +TaskHandle_t init_task; +StaticTask_t init_task_buffer; +StackType_t init_task_stack[TASKSTACKSIZE_INIT]; + +TaskHandle_t blink_task; +StaticTask_t blink_task_buffer; +StackType_t blink_task_stack[TASKSTACKSIZE_BLINK]; + +TaskHandle_t sr_write_task; +StaticTask_t sr_write_task_buffer; +StackType_t sr_write_task_stack[TASKSTACKSIZE_SR_WRITE]; + +extern HSSControl_FaultState_t HSS_fault_state[PDU_MK1_NUM_CHANNELS]; + +// TASKS ---------------------------------------------------------------------- + +// initialize stuff +void Init_Task(void *argument) +{ + printf("Starting Initialization...\n"); + + PDU_Mk1_GPIO_Init(); + + if(PDU_Mk1_SPI2_HSS_SR_Init() != true) + { + printf("FAIL:SPI_INIT\n"); + Error_Handler(); + } + + if(PDU_Mk1_UART_Printf_Init() != true) + { + printf("FAIL:UART_PRINTF_INIT\n"); + Error_Handler(); + } + + if(SPI_RTOS_Mutex_Semaphore_Setup(&spi2_mutex, &spi2_mutex_buffer, &spi2_done_sem, &spi2_done_sem_buffer) != true) + { + printf("FAIL:MUTEX_SEMAPHORE_INIT\n"); + Error_Handler(); + } + + // SPI dummy send because CLK pin initializes high even when configured low for some reason + if(SPI_Init_Dummy_Send(&hspi2, spi2_mutex, spi2_done_sem) != true) + { + printf("FAIL:SPI_INIT_DUMMY_SEND\n"); + Error_Handler(); + } + + PDU_Mk1_HSSControl_Init(); + + printf("Initialization complete.\n"); + + // get this party started + vTaskResume(blink_task); + vTaskResume(sr_write_task); + // task kills itself + vTaskDelete(NULL); +} + +// blink LED +void Blink_Task(void *argument) +{ + for(;;) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + + vTaskDelay(pdMS_TO_TICKS(INTERVAL_BLINK_MS)); + } +} + +// read conversion from ADC and print to serial +void SR_Write_Task(void *argument) +{ + HSS_fault_state[7] = HSSCONTROL_FAULT_HSS_TRIP; + + for(;;) + { + // [0] [15] + // expected EN: 0010 0011 0100 0010 (note: depends on PDU_Mk1_OutputConfig.h) + PDU_Mk1_HSSControl_CritOnly(); + + // expected LATCH: 1111 1111 1111 1111 + + vTaskDelay(pdMS_TO_TICKS(INTERVAL_SR_WRITE_MS)); + + // expected EN: 1111 1111 1111 1111 + PDU_Mk1_HSSControl_AllOn(); + + // expected LATCH: 1111 0111 1011 1111 + PDU_Mk1_HSSControl_WriteLatch_Ch(PDU_OUTPUT_2, HSSCONTROL_LATCH_NOCHANGE); + PDU_Mk1_HSSControl_WriteLatch_Ch(PDU_OUTPUT_4, HSSCONTROL_UNLATCHFAULT_AUTORETRY); + PDU_Mk1_HSSControl_WriteLatch_Ch(PDU_OUTPUT_6, HSSCONTROL_LATCHFAULT_STAYOFF); + PDU_Mk1_HSSControl_WriteLatch_Ch(PDU_OUTPUT_9, HSSCONTROL_UNLATCHFAULT_AUTORETRY); + + vTaskDelay(pdMS_TO_TICKS(INTERVAL_SR_WRITE_MS)); + + // expected EN: 0000 0000 0000 0000 + PDU_Mk1_HSSControl_AllOff(); + + // expected LATCH: 1111 1101 1011 1111 + PDU_Mk1_HSSControl_WriteLatch_Ch(PDU_OUTPUT_2, HSSCONTROL_LATCH_NOCHANGE); + PDU_Mk1_HSSControl_WriteLatch_Ch(PDU_OUTPUT_4, HSSCONTROL_LATCHFAULT_STAYOFF); + PDU_Mk1_HSSControl_WriteLatch_Ch(PDU_OUTPUT_6, HSSCONTROL_UNLATCHFAULT_AUTORETRY); + PDU_Mk1_HSSControl_WriteLatch_Ch(PDU_OUTPUT_9, HSSCONTROL_LATCH_NOCHANGE); + + vTaskDelay(pdMS_TO_TICKS(INTERVAL_SR_WRITE_MS)); + + // expected EN: 0100 0000 1010 0001 + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_1, HSSCONTROL_EN_ON); + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_8, HSSCONTROL_EN_ON); + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_10, HSSCONTROL_EN_TOGGLE); + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_11, HSSCONTROL_EN_OFF); + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_15, HSSCONTROL_EN_ON); + + // expected LATCH: 1111 1100 1010 1111 + PDU_Mk1_HSSControl_OutputFaultRetry_Ch(PDU_OUTPUT_11); + PDU_Mk1_HSSControl_OutputFaultRelatch_Ch(PDU_OUTPUT_12); + PDU_Mk1_HSSControl_OutputFaultRetry_AllFaulted(); + + vTaskDelay(pdMS_TO_TICKS(INTERVAL_SR_WRITE_MS)); + + // expected EN: 0000 0000 0111 0001 + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_1, HSSCONTROL_EN_OFF); + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_8, HSSCONTROL_EN_TOGGLE); + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_9, HSSCONTROL_EN_TOGGLE); + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_11, HSSCONTROL_EN_ON); + PDU_Mk1_HSSControl_WriteOutputEN_Ch(PDU_OUTPUT_15, HSSCONTROL_EN_NOCHANGE); + + // expected LATCH: 1111 1101 1011 0111 + PDU_Mk1_HSSControl_OutputFaultRelatch_Ch(PDU_OUTPUT_11); + PDU_Mk1_HSSControl_OutputFaultRetry_Ch(PDU_OUTPUT_12); + PDU_Mk1_HSSControl_OutputFaultRelatch_AllFaulted(); + + vTaskDelay(pdMS_TO_TICKS(INTERVAL_SR_WRITE_MS)); + + // expected EN: 1010 0101 1010 1100 + HSSControl_EnState_t en_actions[PDU_MK1_NUM_CHANNELS] = { + HSSCONTROL_EN_ON, + HSSCONTROL_EN_NOCHANGE, + HSSCONTROL_EN_TOGGLE, + HSSCONTROL_EN_NOCHANGE, + HSSCONTROL_EN_OFF, + HSSCONTROL_EN_ON, + HSSCONTROL_EN_OFF, + HSSCONTROL_EN_TOGGLE, + HSSCONTROL_EN_TOGGLE, + HSSCONTROL_EN_OFF, + HSSCONTROL_EN_ON, + HSSCONTROL_EN_TOGGLE, + HSSCONTROL_EN_ON, + HSSCONTROL_EN_ON, + HSSCONTROL_EN_OFF, + HSSCONTROL_EN_OFF + }; + PDU_Mk1_HSSControl_WriteOutputEN_All(en_actions); + + // expected LATCH: 1000 1101 1101 0001 + HSSControl_LatchState_t latch_states[PDU_MK1_NUM_CHANNELS] = { + HSSCONTROL_LATCH_NOCHANGE, + HSSCONTROL_UNLATCHFAULT_AUTORETRY, + HSSCONTROL_UNLATCHFAULT_AUTORETRY, + HSSCONTROL_UNLATCHFAULT_AUTORETRY, + HSSCONTROL_LATCH_NOCHANGE, + HSSCONTROL_LATCH_NOCHANGE, + HSSCONTROL_UNLATCHFAULT_AUTORETRY, + HSSCONTROL_LATCHFAULT_STAYOFF, + HSSCONTROL_LATCHFAULT_STAYOFF, + HSSCONTROL_LATCHFAULT_STAYOFF, + HSSCONTROL_UNLATCHFAULT_AUTORETRY, + HSSCONTROL_LATCHFAULT_STAYOFF, + HSSCONTROL_UNLATCHFAULT_AUTORETRY, + HSSCONTROL_UNLATCHFAULT_AUTORETRY, + HSSCONTROL_UNLATCHFAULT_AUTORETRY, + HSSCONTROL_LATCHFAULT_STAYOFF + }; + PDU_Mk1_HSSControl_WriteLatch_All(latch_states); + + vTaskDelay(pdMS_TO_TICKS(INTERVAL_SR_WRITE_MS)); + } +} + +// MAIN ----------------------------------------------------------------------- + +int main() +{ + HAL_Init(); + SystemClock_Config(); + + init_task = xTaskCreateStatic(Init_Task, + "Init Task", + TASKSTACKSIZE_INIT, + NULL, + TASKPRIORITY_INIT, + init_task_stack, + &init_task_buffer + ); + + blink_task = xTaskCreateStatic(Blink_Task, + "Blink Task", + TASKSTACKSIZE_BLINK, + NULL, + TASKPRIORITY_BLINK, + blink_task_stack, + &blink_task_buffer + ); + + sr_write_task = xTaskCreateStatic(SR_Write_Task, + "SR Write Task", + TASKSTACKSIZE_SR_WRITE, + NULL, + TASKPRIORITY_SR_WRITE, + sr_write_task_stack, + &sr_write_task_buffer + ); + + vTaskSuspend(blink_task); + vTaskSuspend(sr_write_task); + vTaskStartScheduler(); + + while(1) {} +} + +// ERROR HANDLER -------------------------------------------------------------- + +void Error_Handler(void) +{ + __disable_irq(); + while(1) + { + HAL_GPIO_TogglePin(LED_PORT, LED_PIN); + vTaskDelay(pdMS_TO_TICKS(INTERVAL_BLINK_ERROR_MS)); + } +} diff --git a/firmware/driver/inc/ShiftRegister_SPI.h b/firmware/driver/inc/ShiftRegister_SPI.h new file mode 100644 index 0000000..7e24c99 --- /dev/null +++ b/firmware/driver/inc/ShiftRegister_SPI.h @@ -0,0 +1,44 @@ +// ShiftRegister_SPI.h +// ---------------------------------------------------------------------------- + +#pragma once + +#include "stm32xx_hal.h" + +#define SR_SPI_MUTEX_DELAY_TICKS portMAX_DELAY +#define SR_SPI_TRANSMISSION_DELAY_TICKS pdMS_TO_TICKS(100) +#define SR_SPI_EN_DELAY_TICKS pdMS_TO_TICKS(1) +#define SR_SPI_INITIAL_ALL_OFF_DELAY_TICKS pdMS_TO_TICKS(1) + +typedef enum { + SR_SPI_😒, // ShiftRegister_SPI sad + SR_SPI_πŸ™‚, // ShiftRegister_SPI happy + SR_SPI_πŸ•·οΈ, // ShiftRegister_SPI SPI mutex timeout + SR_SPI_πŸ•ΈοΈ, // ShiftRegister_SPI_ SPI done semaphore timeout +} ShiftRegister_SPI_Status_t; + +typedef struct { + SPI_HandleTypeDef* spi; // STM32 HAL SPI handle + GPIO_TypeDef* en_port; // enables SPI CLK to reach SR + uint16_t en_pin; // like CS, but active-high + + GPIO_TypeDef* out_clk_port; // out clk moves SR contents to output reg + uint16_t out_clk_pin; + + GPIO_TypeDef* all_off_port; // turns off all SR outputs + uint16_t all_off_pin; + + SemaphoreHandle_t spi_mutex; // Mutex to prevent simultaenous SPI access + SemaphoreHandle_t spi_done_sem; // Semaphore to signal SPI transmission complete + + uint8_t* data; // pointer to SR values + uint8_t num_bytes; +} ShiftRegister_SPI_HandleTypeDef; + +ShiftRegister_SPI_Status_t SR_SPI_Init(ShiftRegister_SPI_HandleTypeDef* sr); + +ShiftRegister_SPI_Status_t SR_SPI_SetRegs(ShiftRegister_SPI_HandleTypeDef* sr); + +void SR_SPI_Assert_AllOff(ShiftRegister_SPI_HandleTypeDef* sr); + +void SR_SPI_Deassert_AllOff(ShiftRegister_SPI_HandleTypeDef* sr); diff --git a/firmware/driver/src/ShiftRegister_SPI.c b/firmware/driver/src/ShiftRegister_SPI.c new file mode 100644 index 0000000..d73b7c4 --- /dev/null +++ b/firmware/driver/src/ShiftRegister_SPI.c @@ -0,0 +1,84 @@ +// ShiftRegister_SPI.c + +#include "ShiftRegister_SPI.h" + +// FUNCTION DEFINITIONS ------------------------------------------------------- + +ShiftRegister_SPI_Status_t SR_SPI_Init(ShiftRegister_SPI_HandleTypeDef* sr) +{ + // validate SPI configured correctly + if(sr->spi == NULL) + { + return SR_SPI_😒; + } + // validate SPI RTOS stuff initialized correctly + if(sr->spi_mutex == NULL || sr->spi_done_sem == NULL) + { + return SR_SPI_😒; + } + // validate data is pointing somewhere + if(sr->data == NULL || sr->num_bytes == 0) + { + return SR_SPI_😒; + } + + SR_SPI_Assert_AllOff(sr); + vTaskDelay(SR_SPI_INITIAL_ALL_OFF_DELAY_TICKS); + SR_SPI_Deassert_AllOff(sr); + + return SR_SPI_SetRegs(sr); +} + +ShiftRegister_SPI_Status_t SR_SPI_SetRegs(ShiftRegister_SPI_HandleTypeDef* sr) +{ + // take SPI mutex + if(xSemaphoreTake(sr->spi_mutex, SR_SPI_MUTEX_DELAY_TICKS) != pdTRUE) + { + return SR_SPI_πŸ•·οΈ; + } + + // enable SPI CLK to reach SR + HAL_GPIO_WritePin(sr->en_port, sr->en_pin, 1); + vTaskDelay(pdMS_TO_TICKS(1)); + + // transmit data + if(HAL_SPI_Transmit_IT(sr->spi, sr->data, sr->num_bytes) != HAL_OK) + { + HAL_GPIO_WritePin(sr->en_port, sr->en_pin, 0); + xSemaphoreGive(sr->spi_mutex); + + return SR_SPI_😒; + } + + // take spi completion semaphore + if(xSemaphoreTake(sr->spi_done_sem, SR_SPI_TRANSMISSION_DELAY_TICKS) != pdTRUE) + { + HAL_SPI_Abort(sr->spi); + + HAL_GPIO_WritePin(sr->en_port, sr->en_pin, 0); + xSemaphoreGive(sr->spi_mutex); + + return SR_SPI_πŸ•ΈοΈ; + } + + // done with SPI + HAL_GPIO_WritePin(sr->en_port, sr->en_pin, 0); + xSemaphoreGive(sr->spi_mutex); + + // write output register with new values + HAL_GPIO_WritePin(sr->out_clk_port, sr->out_clk_pin, 1); + vTaskDelay(SR_SPI_EN_DELAY_TICKS); + HAL_GPIO_WritePin(sr->out_clk_port, sr->out_clk_pin, 0); + + return SR_SPI_πŸ™‚; +} + +inline void SR_SPI_Assert_AllOff(ShiftRegister_SPI_HandleTypeDef* sr) +{ + HAL_GPIO_WritePin(sr->all_off_port, sr->all_off_pin, 0); +} + +inline void SR_SPI_Deassert_AllOff(ShiftRegister_SPI_HandleTypeDef* sr) +{ + HAL_GPIO_WritePin(sr->all_off_port, sr->all_off_pin, 1); +}