#include "plsr_platform.h" #include "plsr.h" #ifdef PLSR_HOST_TEST #include static uint8_t PlsrHostPulseActive[4]; static uint32_t PlsrHostFrequency[4]; static uint32_t PlsrHostQueuedFrequency[4]; static uint8_t PlsrHostUpdatePending[4]; static uint8_t PlsrHostInputs[2]; static uint8_t PlsrHostSelectedPulse; static uint8_t PlsrHostDirectionLevel; static uint8_t PlsrHostEmitPulseOnCriticalEntry; static uint8_t PlsrHostEmitPulseOnCriticalExit; static uint8_t PlsrHostLatchPulseOnCriticalEntry; static uint8_t PlsrHostCriticalEntriesToSkip; static uint8_t PlsrHostFailNextStart; static uint8_t PlsrHostFailNextFrequencyAtUpdate; static PLSR_PERSIST_PAYLOAD PlsrHostPersistentPayload; static uint8_t PlsrHostPersistentValid; static uint32_t PlsrHostSaveCount; static void PlsrHostLatchPulse(uint8_t pulseOutput) { if ((pulseOutput <= 3U) && (PlsrHostPulseActive[pulseOutput] != 0U)) { PlsrHostFrequency[pulseOutput] = PlsrHostQueuedFrequency[pulseOutput]; PlsrHostUpdatePending[pulseOutput] = 1U; } } static void PlsrHostServicePendingPulse(uint8_t pulseOutput) { if ((pulseOutput <= 3U) && (PlsrHostUpdatePending[pulseOutput] != 0U)) { PlsrHostUpdatePending[pulseOutput] = 0U; PlsrPulseTimerIrq(pulseOutput); } } uint8_t PlsrPlatformInit(void) { (void)memset(PlsrHostPulseActive, 0, sizeof(PlsrHostPulseActive)); (void)memset(PlsrHostFrequency, 0, sizeof(PlsrHostFrequency)); (void)memset(PlsrHostQueuedFrequency, 0, sizeof(PlsrHostQueuedFrequency)); (void)memset(PlsrHostUpdatePending, 0, sizeof(PlsrHostUpdatePending)); PlsrHostSelectedPulse = 0U; PlsrHostDirectionLevel = 0U; PlsrHostEmitPulseOnCriticalEntry = 0U; PlsrHostEmitPulseOnCriticalExit = 0U; PlsrHostLatchPulseOnCriticalEntry = 0U; PlsrHostCriticalEntriesToSkip = 0U; PlsrHostFailNextStart = 0U; PlsrHostFailNextFrequencyAtUpdate = 0U; return 1U; } uint8_t PlsrPlatformPrepare(uint8_t pulseOutput, uint8_t directionOutput, uint8_t directionLevel) { uint8_t index; (void)directionOutput; if (PlsrHostFailNextStart != 0U) { PlsrHostFailNextStart = 0U; return 0U; } if ((pulseOutput > 3U) || (directionOutput > 3U)) { return 0U; } for (index = 0U; index < 4U; index++) { PlsrHostPulseActive[index] = 0U; PlsrHostFrequency[index] = 0UL; PlsrHostQueuedFrequency[index] = 0UL; PlsrHostUpdatePending[index] = 0U; } PlsrHostSelectedPulse = pulseOutput; PlsrHostDirectionLevel = (directionLevel != 0U) ? 1U : 0U; return 1U; } uint8_t PlsrPlatformStartPulse(uint8_t pulseOutput, uint32_t firstFrequencyHz, uint32_t queuedFrequencyHz, uint32_t *actualFirstFrequencyHz, uint32_t *actualQueuedFrequencyHz) { if ((pulseOutput > 3U) || (firstFrequencyHz == 0UL) || (firstFrequencyHz > PLSR_FREQUENCY_MAX_HZ) || (queuedFrequencyHz == 0UL) || (queuedFrequencyHz > PLSR_FREQUENCY_MAX_HZ) || (actualFirstFrequencyHz == NULL) || (actualQueuedFrequencyHz == NULL)) { return 0U; } PlsrHostPulseActive[pulseOutput] = 1U; PlsrHostFrequency[pulseOutput] = firstFrequencyHz; PlsrHostQueuedFrequency[pulseOutput] = queuedFrequencyHz; PlsrHostUpdatePending[pulseOutput] = 0U; PlsrHostSelectedPulse = pulseOutput; *actualFirstFrequencyHz = firstFrequencyHz; *actualQueuedFrequencyHz = queuedFrequencyHz; return 1U; } uint8_t PlsrPlatformQueueFrequency(uint8_t pulseOutput, uint32_t frequencyHz, uint32_t *actualFrequencyHz) { if (PlsrHostFailNextFrequencyAtUpdate != 0U) { PlsrHostFailNextFrequencyAtUpdate = 0U; return 0U; } if ((pulseOutput > 3U) || (frequencyHz == 0UL) || (frequencyHz > PLSR_FREQUENCY_MAX_HZ) || (actualFrequencyHz == NULL) || (PlsrHostPulseActive[pulseOutput] == 0U)) { return 0U; } PlsrHostQueuedFrequency[pulseOutput] = frequencyHz; *actualFrequencyHz = frequencyHz; return 1U; } void PlsrPlatformDrainPendingPulse(uint8_t pulseOutput) { PlsrHostServicePendingPulse(pulseOutput); } uint32_t PlsrPlatformActiveFrequency(uint8_t pulseOutput) { return (pulseOutput <= 3U) ? PlsrHostFrequency[pulseOutput] : 0UL; } void PlsrPlatformStopPulse(uint8_t pulseOutput) { if (pulseOutput <= 3U) { PlsrHostPulseActive[pulseOutput] = 0U; PlsrHostFrequency[pulseOutput] = 0UL; PlsrHostQueuedFrequency[pulseOutput] = 0UL; PlsrHostUpdatePending[pulseOutput] = 0U; } } uint8_t PlsrPlatformReadInput(uint8_t inputSelection) { return (inputSelection <= 1U) ? PlsrHostInputs[inputSelection] : 0U; } uint8_t PlsrPlatformLoad(PLSR_PERSIST_PAYLOAD *payload) { if ((payload == NULL) || (PlsrHostPersistentValid == 0U)) { return 0U; } *payload = PlsrHostPersistentPayload; return 1U; } uint8_t PlsrPlatformSave(const PLSR_PERSIST_PAYLOAD *payload) { if (payload == NULL) { return 0U; } PlsrHostPersistentPayload = *payload; PlsrHostPersistentValid = 1U; PlsrHostSaveCount++; return 1U; } void PlsrPlatformCheckpointConfig(const PLSR_CONFIG *config) { if (config != NULL) { PlsrHostPersistentPayload.config = *config; PlsrHostPersistentValid = 1U; } } void PlsrPlatformCheckpointPosition(int32_t position, uint8_t positionValid, uint8_t wasBusy) { PlsrHostPersistentPayload.position = position; PlsrHostPersistentPayload.positionValid = positionValid; PlsrHostPersistentPayload.wasBusy = wasBusy; PlsrHostPersistentPayload.reserved = 0U; } uint32_t PlsrPlatformEnterCritical(void) { if (PlsrHostEmitPulseOnCriticalEntry != 0U) { if (PlsrHostCriticalEntriesToSkip != 0U) { PlsrHostCriticalEntriesToSkip--; } else { PlsrHostEmitPulseOnCriticalEntry = 0U; PlsrHostLatchPulse(PlsrHostSelectedPulse); PlsrHostServicePendingPulse(PlsrHostSelectedPulse); } } if (PlsrHostLatchPulseOnCriticalEntry != 0U) { PlsrHostLatchPulseOnCriticalEntry = 0U; PlsrHostLatchPulse(PlsrHostSelectedPulse); } return 0UL; } void PlsrPlatformExitCritical(uint32_t state) { (void)state; if (PlsrHostEmitPulseOnCriticalExit != 0U) { PlsrHostEmitPulseOnCriticalExit = 0U; PlsrHostLatchPulse(PlsrHostSelectedPulse); } PlsrHostServicePendingPulse(PlsrHostSelectedPulse); } void PlsrTestSetInput(uint8_t inputSelection, uint8_t level) { if (inputSelection <= 1U) { PlsrHostInputs[inputSelection] = (level != 0U) ? 1U : 0U; } } void PlsrTestEmitPulses(uint32_t pulseCount) { while ((pulseCount != 0UL) && (PlsrHostPulseActive[PlsrHostSelectedPulse] != 0U)) { PlsrHostLatchPulse(PlsrHostSelectedPulse); PlsrHostServicePendingPulse(PlsrHostSelectedPulse); pulseCount--; } } void PlsrTestEmitPulseOnCriticalEntry(void) { PlsrHostCriticalEntriesToSkip = 0U; PlsrHostEmitPulseOnCriticalEntry = 1U; } void PlsrTestEmitPulseAfterCriticalEntries(uint8_t entriesToSkip) { PlsrHostCriticalEntriesToSkip = entriesToSkip; PlsrHostEmitPulseOnCriticalEntry = 1U; } void PlsrTestEmitPulseOnCriticalExit(void) { PlsrHostEmitPulseOnCriticalExit = 1U; } void PlsrTestLatchPulseOnCriticalEntry(void) { PlsrHostLatchPulseOnCriticalEntry = 1U; } void PlsrTestServicePendingPulse(void) { PlsrHostServicePendingPulse(PlsrHostSelectedPulse); } void PlsrTestFailNextStart(void) { PlsrHostFailNextStart = 1U; } void PlsrTestFailNextFrequencyAtUpdate(void) { PlsrHostFailNextFrequencyAtUpdate = 1U; } uint8_t PlsrTestPulseIsActive(void) { return PlsrHostPulseActive[PlsrHostSelectedPulse]; } uint32_t PlsrTestOutputFrequency(void) { return PlsrHostFrequency[PlsrHostSelectedPulse]; } uint32_t PlsrTestQueuedFrequency(void) { return PlsrHostQueuedFrequency[PlsrHostSelectedPulse]; } uint8_t PlsrTestDirectionLevel(void) { return PlsrHostDirectionLevel; } void PlsrTestClearPersistentStorage(void) { (void)memset(&PlsrHostPersistentPayload, 0, sizeof(PlsrHostPersistentPayload)); (void)memset(PlsrHostInputs, 0, sizeof(PlsrHostInputs)); PlsrHostPersistentValid = 0U; PlsrHostSaveCount = 0UL; } void PlsrTestResetSaveCount(void) { PlsrHostSaveCount = 0UL; } uint32_t PlsrTestSaveCount(void) { return PlsrHostSaveCount; } #else #include "stm32f4xx_hal.h" #include #include #define PLSR_ENABLE_IRQ_CYCLE_DIAG (0U) #define PLSR_FLASH_SLOT_A_ADDRESS (0x080C0000UL) #define PLSR_FLASH_SLOT_B_ADDRESS (0x080E0000UL) #define PLSR_FLASH_MAGIC (0x50534C52UL) #define PLSR_FLASH_VERSION (2U) #define PLSR_BACKUP_CONFIG_ADDRESS (BKPSRAM_BASE + 0x0100UL) #define PLSR_BACKUP_POSITION_ADDRESS (BKPSRAM_BASE + 0x0200UL) #define PLSR_BACKUP_CONFIG_MAGIC (0x50434647UL) #define PLSR_BACKUP_POSITION_MAGIC (0x50504F53UL) typedef struct { TIM_TypeDef *timer; GPIO_TypeDef *port; uint16_t pin; uint8_t pinIndex; uint8_t alternate; IRQn_Type irq; uint32_t timerClockHz; } PLSR_TIMER_MAP; typedef struct { GPIO_TypeDef *port; uint16_t pin; } PLSR_GPIO_MAP; typedef struct { uint32_t prescaler; uint32_t period; uint32_t compare; uint32_t actualFrequencyHz; } PLSR_TIMER_SETTING; typedef struct { uint32_t magic; uint16_t version; uint16_t payloadSize; uint32_t generation; PLSR_PERSIST_PAYLOAD payload; uint32_t crc32; } PLSR_FLASH_RECORD; typedef struct { uint32_t magic; PLSR_CONFIG config; uint32_t crc32; } PLSR_BACKUP_CONFIG_RECORD; typedef struct { uint32_t magic; uint32_t generation; int32_t position; uint8_t positionValid; uint8_t wasBusy; uint16_t reserved; uint32_t crc32; } PLSR_BACKUP_POSITION_RECORD; static const PLSR_TIMER_MAP PlsrTimerMap[4] = { {TIM10, GPIOF, GPIO_PIN_6, 6U, GPIO_AF3_TIM10, TIM1_UP_TIM10_IRQn, 168000000UL}, {TIM13, GPIOF, GPIO_PIN_8, 8U, GPIO_AF9_TIM13, TIM8_UP_TIM13_IRQn, 84000000UL}, {TIM11, GPIOF, GPIO_PIN_7, 7U, GPIO_AF3_TIM11, TIM1_TRG_COM_TIM11_IRQn, 168000000UL}, {TIM14, GPIOF, GPIO_PIN_9, 9U, GPIO_AF9_TIM14, TIM8_TRG_COM_TIM14_IRQn, 84000000UL} }; static const PLSR_GPIO_MAP PlsrDirectionMap[4] = { {GPIOH, GPIO_PIN_9}, {GPIOH, GPIO_PIN_8}, {GPIOH, GPIO_PIN_7}, {GPIOH, GPIO_PIN_6} }; static PLSR_FLASH_RECORD PlsrFlashRecordBuffer; static uint32_t PlsrBackupPositionGeneration; static uint32_t PlsrTimerActiveFrequencyHz[4]; static uint32_t PlsrTimerQueuedFrequencyHz[4]; static uint32_t PlsrTimerQueueGeneration[4]; static void PlsrHandleTimerIrq(uint8_t pulseOutput); #if PLSR_ENABLE_IRQ_CYCLE_DIAG volatile uint32_t PlsrIrqCount[4]; volatile uint32_t PlsrIrqLastCycles[4]; volatile uint32_t PlsrIrqMaxCycles[4]; #endif static uint32_t PlsrCrc32(const void *data, uint32_t length) { const uint8_t *bytes = (const uint8_t *)data; uint32_t crc = 0xFFFFFFFFUL; uint32_t index; uint8_t bit; for (index = 0UL; index < length; index++) { crc ^= bytes[index]; for (bit = 0U; bit < 8U; bit++) { crc = ((crc & 1UL) != 0UL) ? ((crc >> 1U) ^ 0xEDB88320UL) : (crc >> 1U); } } return ~crc; } static uint8_t PlsrGenerationIsNewer(uint32_t first, uint32_t second) { return ((int32_t)(first - second) > 0) ? 1U : 0U; } static uint32_t PlsrFlashRecordCrc(const PLSR_FLASH_RECORD *record) { const uint8_t *start = (const uint8_t *)&record->version; uint32_t length = (uint32_t)(offsetof(PLSR_FLASH_RECORD, crc32) - offsetof(PLSR_FLASH_RECORD, version)); return PlsrCrc32(start, length); } static uint8_t PlsrFlashRecordIsValid(const PLSR_FLASH_RECORD *record) { return ((record->magic == PLSR_FLASH_MAGIC) && (record->version == PLSR_FLASH_VERSION) && (record->payloadSize == sizeof(PLSR_PERSIST_PAYLOAD)) && (record->crc32 == PlsrFlashRecordCrc(record))) ? 1U : 0U; } static uint8_t PlsrBackupConfigIsValid( const PLSR_BACKUP_CONFIG_RECORD *record) { return ((record->magic == PLSR_BACKUP_CONFIG_MAGIC) && (record->crc32 == PlsrCrc32(&record->config, sizeof(record->config)))) ? 1U : 0U; } static uint8_t PlsrBackupPositionIsValid( const PLSR_BACKUP_POSITION_RECORD *record) { uint32_t crc = PlsrCrc32(&record->generation, sizeof(record->generation) + sizeof(record->position) + sizeof(record->positionValid) + sizeof(record->wasBusy) + sizeof(record->reserved)); return ((record->magic == PLSR_BACKUP_POSITION_MAGIC) && (record->crc32 == crc)) ? 1U : 0U; } static const PLSR_BACKUP_POSITION_RECORD *PlsrNewestBackupPosition(void) { const PLSR_BACKUP_POSITION_RECORD *slots = (const PLSR_BACKUP_POSITION_RECORD *)PLSR_BACKUP_POSITION_ADDRESS; uint8_t validA = PlsrBackupPositionIsValid(&slots[0]); uint8_t validB = PlsrBackupPositionIsValid(&slots[1]); if ((validA == 0U) && (validB == 0U)) { return NULL; } if (validA == 0U) { return &slots[1]; } if (validB == 0U) { return &slots[0]; } return (PlsrGenerationIsNewer(slots[1].generation, slots[0].generation) != 0U) ? &slots[1] : &slots[0]; } static void PlsrTimerStop(TIM_TypeDef *timer) { timer->DIER &= ~TIM_DIER_UIE; timer->CR1 &= ~TIM_CR1_CEN; timer->CCER &= ~TIM_CCER_CC1E; timer->SR = ~TIM_SR_UIF; } static void PlsrTimerInitialize(TIM_TypeDef *timer) { timer->CR1 = TIM_CR1_ARPE | TIM_CR1_URS; timer->CR2 = 0UL; timer->SMCR = 0UL; timer->DIER = 0UL; timer->CCMR1 = TIM_CCMR1_OC1PE | (6UL << TIM_CCMR1_OC1M_Pos); timer->CCER = 0UL; timer->PSC = 0UL; timer->ARR = 999UL; timer->CCR1 = 500UL; timer->CNT = 0UL; timer->EGR = TIM_EGR_UG; timer->SR = 0UL; } static void PlsrPulsePinHoldIdle(uint8_t pulseOutput) { const PLSR_TIMER_MAP *map = &PlsrTimerMap[pulseOutput]; GPIO_InitTypeDef gpio; HAL_GPIO_WritePin(map->port, map->pin, GPIO_PIN_SET); gpio.Pin = map->pin; gpio.Mode = GPIO_MODE_OUTPUT_PP; gpio.Pull = GPIO_NOPULL; gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; gpio.Alternate = 0U; HAL_GPIO_Init(map->port, &gpio); } static void PlsrPulsePinCaptureIdle(uint8_t pulseOutput) { const PLSR_TIMER_MAP *map = &PlsrTimerMap[pulseOutput]; uint32_t shift = (uint32_t)map->pinIndex * 2UL; uint32_t mode = map->port->MODER; /* The update IRQ occurs while PWM is high; switch to GPIO high first. */ map->port->BSRR = map->pin; mode &= ~(3UL << shift); mode |= 1UL << shift; map->port->MODER = mode; __DSB(); } static void PlsrPulsePinRelease(uint8_t pulseOutput) { const PLSR_TIMER_MAP *map = &PlsrTimerMap[pulseOutput]; GPIO_InitTypeDef gpio; gpio.Pin = map->pin; gpio.Mode = GPIO_MODE_AF_PP; gpio.Pull = GPIO_NOPULL; gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; gpio.Alternate = map->alternate; HAL_GPIO_Init(map->port, &gpio); __DSB(); } static uint8_t PlsrTimerCalculate(uint8_t pulseOutput, uint32_t frequencyHz, PLSR_TIMER_SETTING *setting) { const PLSR_TIMER_MAP *map; uint32_t prescalerDivider; uint32_t denominator; uint32_t periodCounts; if ((pulseOutput > 3U) || (frequencyHz == 0UL) || (frequencyHz > PLSR_FREQUENCY_MAX_HZ) || (setting == NULL)) { return 0U; } map = &PlsrTimerMap[pulseOutput]; prescalerDivider = (((map->timerClockHz - 1UL) / frequencyHz) >> 16U) + 1UL; if (prescalerDivider > 65536UL) { return 0U; } denominator = prescalerDivider * frequencyHz; periodCounts = (map->timerClockHz + denominator / 2UL) / denominator; if (periodCounts < 2UL) { periodCounts = 2UL; } if (periodCounts > 65536UL) { periodCounts = 65536UL; } setting->prescaler = prescalerDivider - 1UL; setting->period = periodCounts - 1UL; setting->compare = periodCounts / 2UL; setting->actualFrequencyHz = map->timerClockHz / (prescalerDivider * periodCounts); return 1U; } static void PlsrTimerWriteSetting(TIM_TypeDef *timer, const PLSR_TIMER_SETTING *setting) { timer->PSC = setting->prescaler; timer->ARR = setting->period; timer->CCR1 = setting->compare; } uint8_t PlsrPlatformInit(void) { GPIO_InitTypeDef gpio; uint8_t index; const PLSR_BACKUP_POSITION_RECORD *positionRecord; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_TIM10_CLK_ENABLE(); __HAL_RCC_TIM11_CLK_ENABLE(); __HAL_RCC_TIM13_CLK_ENABLE(); __HAL_RCC_TIM14_CLK_ENABLE(); __HAL_RCC_PWR_CLK_ENABLE(); HAL_PWR_EnableBkUpAccess(); __HAL_RCC_BKPSRAM_CLK_ENABLE(); if (HAL_PWREx_EnableBkUpReg() != HAL_OK) { return 0U; } #if PLSR_ENABLE_IRQ_CYCLE_DIAG CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0UL; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; (void)memset((void *)PlsrIrqCount, 0, sizeof(PlsrIrqCount)); (void)memset((void *)PlsrIrqLastCycles, 0, sizeof(PlsrIrqLastCycles)); (void)memset((void *)PlsrIrqMaxCycles, 0, sizeof(PlsrIrqMaxCycles)); #endif HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET); gpio.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9; gpio.Mode = GPIO_MODE_OUTPUT_PP; gpio.Pull = GPIO_NOPULL; gpio.Speed = GPIO_SPEED_FREQ_HIGH; gpio.Alternate = 0U; HAL_GPIO_Init(GPIOH, &gpio); gpio.Mode = GPIO_MODE_INPUT; gpio.Pull = GPIO_NOPULL; gpio.Speed = GPIO_SPEED_FREQ_LOW; gpio.Alternate = 0U; gpio.Pin = GPIO_PIN_5; HAL_GPIO_Init(GPIOB, &gpio); gpio.Pin = GPIO_PIN_12; HAL_GPIO_Init(GPIOG, &gpio); for (index = 0U; index < 4U; index++) { PlsrTimerActiveFrequencyHz[index] = 0UL; PlsrTimerQueuedFrequencyHz[index] = 0UL; PlsrTimerQueueGeneration[index] = 0UL; PlsrTimerInitialize(PlsrTimerMap[index].timer); PlsrPulsePinHoldIdle(index); HAL_NVIC_SetPriority(PlsrTimerMap[index].irq, 1U, 0U); HAL_NVIC_EnableIRQ(PlsrTimerMap[index].irq); } positionRecord = PlsrNewestBackupPosition(); PlsrBackupPositionGeneration = (positionRecord == NULL) ? 0UL : positionRecord->generation; return 1U; } uint8_t PlsrPlatformPrepare(uint8_t pulseOutput, uint8_t directionOutput, uint8_t directionLevel) { uint8_t index; if ((pulseOutput > 3U) || (directionOutput > 3U)) { return 0U; } for (index = 0U; index < 4U; index++) { PlsrPulsePinHoldIdle(index); PlsrTimerStop(PlsrTimerMap[index].timer); HAL_GPIO_WritePin(PlsrDirectionMap[index].port, PlsrDirectionMap[index].pin, GPIO_PIN_SET); } if (directionLevel != 0U) { HAL_GPIO_WritePin(PlsrDirectionMap[directionOutput].port, PlsrDirectionMap[directionOutput].pin, GPIO_PIN_RESET); } return 1U; } uint8_t PlsrPlatformStartPulse(uint8_t pulseOutput, uint32_t firstFrequencyHz, uint32_t queuedFrequencyHz, uint32_t *actualFirstFrequencyHz, uint32_t *actualQueuedFrequencyHz) { TIM_TypeDef *timer; PLSR_TIMER_SETTING firstSetting; PLSR_TIMER_SETTING queuedSetting; if ((actualFirstFrequencyHz == NULL) || (actualQueuedFrequencyHz == NULL) || (PlsrTimerCalculate(pulseOutput, firstFrequencyHz, &firstSetting) == 0U) || (PlsrTimerCalculate(pulseOutput, queuedFrequencyHz, &queuedSetting) == 0U)) { return 0U; } timer = PlsrTimerMap[pulseOutput].timer; timer->DIER &= ~TIM_DIER_UIE; timer->CR1 &= ~TIM_CR1_CEN; timer->CCER &= ~TIM_CCER_CC1E; timer->CNT = 0UL; PlsrTimerWriteSetting(timer, &firstSetting); timer->EGR = TIM_EGR_UG; PlsrTimerWriteSetting(timer, &queuedSetting); timer->SR = 0UL; timer->CCER |= TIM_CCER_CC1E; __DSB(); timer->DIER |= TIM_DIER_UIE; PlsrPulsePinRelease(pulseOutput); timer->CR1 |= TIM_CR1_CEN; PlsrTimerActiveFrequencyHz[pulseOutput] = firstSetting.actualFrequencyHz; PlsrTimerQueuedFrequencyHz[pulseOutput] = queuedSetting.actualFrequencyHz; PlsrTimerQueueGeneration[pulseOutput]++; *actualFirstFrequencyHz = firstSetting.actualFrequencyHz; *actualQueuedFrequencyHz = queuedSetting.actualFrequencyHz; return 1U; } uint8_t PlsrPlatformQueueFrequency(uint8_t pulseOutput, uint32_t frequencyHz, uint32_t *actualFrequencyHz) { TIM_TypeDef *timer; PLSR_TIMER_SETTING setting; uint32_t counterBefore; uint32_t counterAfter; uint32_t counterFinal; uint32_t criticalState; uint32_t generationBefore; uint32_t ownGeneration; uint8_t wrappedWhileUpdatesDisabled; if ((pulseOutput > 3U) || (actualFrequencyHz == NULL) || (PlsrTimerCalculate(pulseOutput, frequencyHz, &setting) == 0U)) { return 0U; } timer = PlsrTimerMap[pulseOutput].timer; if ((timer->CR1 & TIM_CR1_CEN) == 0UL) { return 0U; } criticalState = PlsrPlatformEnterCritical(); generationBefore = PlsrTimerQueueGeneration[pulseOutput]; if ((timer->SR & TIM_SR_UIF) != 0UL) { PlsrHandleTimerIrq(pulseOutput); if (PlsrTimerQueueGeneration[pulseOutput] != generationBefore) { *actualFrequencyHz = PlsrTimerQueuedFrequencyHz[pulseOutput]; PlsrPlatformExitCritical(criticalState); return 1U; } if ((timer->CR1 & TIM_CR1_CEN) == 0UL) { PlsrPlatformExitCritical(criticalState); return 0U; } } /* UDIS blocks shadow transfers while the three preload registers are replaced. The counter and PWM output continue without interruption. */ counterBefore = timer->CNT; timer->CR1 |= TIM_CR1_UDIS; __DMB(); if ((timer->SR & TIM_SR_UIF) != 0UL) { timer->CR1 &= ~TIM_CR1_UDIS; PlsrHandleTimerIrq(pulseOutput); if ((PlsrTimerQueueGeneration[pulseOutput] != generationBefore) || ((timer->CR1 & TIM_CR1_CEN) == 0UL)) { uint8_t stillRunning = ((timer->CR1 & TIM_CR1_CEN) != 0UL) ? 1U : 0U; *actualFrequencyHz = PlsrTimerQueuedFrequencyHz[pulseOutput]; PlsrPlatformExitCritical(criticalState); return stillRunning; } counterBefore = timer->CNT; timer->CR1 |= TIM_CR1_UDIS; __DMB(); } PlsrTimerWriteSetting(timer, &setting); __DMB(); PlsrTimerQueuedFrequencyHz[pulseOutput] = setting.actualFrequencyHz; PlsrTimerQueueGeneration[pulseOutput]++; ownGeneration = PlsrTimerQueueGeneration[pulseOutput]; counterAfter = timer->CNT; timer->CR1 &= ~TIM_CR1_UDIS; __DMB(); counterFinal = timer->CNT; /* With UDIS set an overflow does not set UIF. A wrapped counter proves that its real output edge occurred, so account for that edge once. */ wrappedWhileUpdatesDisabled = ((counterAfter < counterBefore) || ((counterFinal < counterAfter) && ((timer->SR & TIM_SR_UIF) == 0UL))) ? 1U : 0U; if (wrappedWhileUpdatesDisabled != 0U) { PlsrPulseTimerIrq(pulseOutput); } else if ((timer->SR & TIM_SR_UIF) != 0UL) { PlsrHandleTimerIrq(pulseOutput); } *actualFrequencyHz = (PlsrTimerQueueGeneration[pulseOutput] == ownGeneration) ? setting.actualFrequencyHz : PlsrTimerQueuedFrequencyHz[pulseOutput]; PlsrPlatformExitCritical(criticalState); return 1U; } void PlsrPlatformDrainPendingPulse(uint8_t pulseOutput) { if (pulseOutput <= 3U) { PlsrHandleTimerIrq(pulseOutput); } } uint32_t PlsrPlatformActiveFrequency(uint8_t pulseOutput) { return (pulseOutput <= 3U) ? PlsrTimerActiveFrequencyHz[pulseOutput] : 0UL; } void PlsrPlatformStopPulse(uint8_t pulseOutput) { if (pulseOutput <= 3U) { PlsrPulsePinCaptureIdle(pulseOutput); PlsrTimerStop(PlsrTimerMap[pulseOutput].timer); PlsrTimerActiveFrequencyHz[pulseOutput] = 0UL; PlsrTimerQueuedFrequencyHz[pulseOutput] = 0UL; PlsrTimerQueueGeneration[pulseOutput]++; } } uint8_t PlsrPlatformReadInput(uint8_t inputSelection) { if (inputSelection == 0U) { return (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == GPIO_PIN_SET) ? 1U : 0U; } if (inputSelection == 1U) { return (HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_12) == GPIO_PIN_SET) ? 1U : 0U; } return 0U; } uint8_t PlsrPlatformLoad(PLSR_PERSIST_PAYLOAD *payload) { const PLSR_FLASH_RECORD *slotA = (const PLSR_FLASH_RECORD *)PLSR_FLASH_SLOT_A_ADDRESS; const PLSR_FLASH_RECORD *slotB = (const PLSR_FLASH_RECORD *)PLSR_FLASH_SLOT_B_ADDRESS; const PLSR_FLASH_RECORD *selected = NULL; const PLSR_BACKUP_CONFIG_RECORD *backupConfig = (const PLSR_BACKUP_CONFIG_RECORD *)PLSR_BACKUP_CONFIG_ADDRESS; const PLSR_BACKUP_POSITION_RECORD *backupPosition; uint8_t validA; uint8_t validB; uint8_t haveConfig = 0U; if (payload == NULL) { return 0U; } validA = PlsrFlashRecordIsValid(slotA); validB = PlsrFlashRecordIsValid(slotB); if ((validA != 0U) && (validB != 0U)) { selected = (PlsrGenerationIsNewer(slotB->generation, slotA->generation) != 0U) ? slotB : slotA; } else if (validA != 0U) { selected = slotA; } else if (validB != 0U) { selected = slotB; } if (selected != NULL) { *payload = selected->payload; haveConfig = 1U; } else { (void)memset(payload, 0, sizeof(*payload)); } if (PlsrBackupConfigIsValid(backupConfig) != 0U) { payload->config = backupConfig->config; haveConfig = 1U; } backupPosition = PlsrNewestBackupPosition(); if (backupPosition != NULL) { payload->position = backupPosition->position; payload->positionValid = backupPosition->positionValid; payload->wasBusy = backupPosition->wasBusy; } return haveConfig; } uint8_t PlsrPlatformSave(const PLSR_PERSIST_PAYLOAD *payload) { const PLSR_FLASH_RECORD *slotA = (const PLSR_FLASH_RECORD *)PLSR_FLASH_SLOT_A_ADDRESS; const PLSR_FLASH_RECORD *slotB = (const PLSR_FLASH_RECORD *)PLSR_FLASH_SLOT_B_ADDRESS; uint8_t validA; uint8_t validB; uint32_t newestGeneration = 0UL; uint32_t targetAddress; uint32_t targetSector; uint32_t sectorError; uint32_t index; uint32_t wordCount; const uint32_t *words; FLASH_EraseInitTypeDef erase; HAL_StatusTypeDef status = HAL_OK; if (payload == NULL) { return 0U; } validA = PlsrFlashRecordIsValid(slotA); validB = PlsrFlashRecordIsValid(slotB); if ((validA != 0U) && (validB != 0U)) { if (PlsrGenerationIsNewer(slotB->generation, slotA->generation) != 0U) { newestGeneration = slotB->generation; targetAddress = PLSR_FLASH_SLOT_A_ADDRESS; targetSector = FLASH_SECTOR_10; } else { newestGeneration = slotA->generation; targetAddress = PLSR_FLASH_SLOT_B_ADDRESS; targetSector = FLASH_SECTOR_11; } } else if (validA != 0U) { newestGeneration = slotA->generation; targetAddress = PLSR_FLASH_SLOT_B_ADDRESS; targetSector = FLASH_SECTOR_11; } else if (validB != 0U) { newestGeneration = slotB->generation; targetAddress = PLSR_FLASH_SLOT_A_ADDRESS; targetSector = FLASH_SECTOR_10; } else { targetAddress = PLSR_FLASH_SLOT_A_ADDRESS; targetSector = FLASH_SECTOR_10; } (void)memset(&PlsrFlashRecordBuffer, 0, sizeof(PlsrFlashRecordBuffer)); PlsrFlashRecordBuffer.magic = PLSR_FLASH_MAGIC; PlsrFlashRecordBuffer.version = PLSR_FLASH_VERSION; PlsrFlashRecordBuffer.payloadSize = sizeof(PLSR_PERSIST_PAYLOAD); PlsrFlashRecordBuffer.generation = newestGeneration + 1UL; PlsrFlashRecordBuffer.payload = *payload; PlsrFlashRecordBuffer.crc32 = PlsrFlashRecordCrc(&PlsrFlashRecordBuffer); if (HAL_FLASH_Unlock() != HAL_OK) { return 0U; } __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); erase.TypeErase = FLASH_TYPEERASE_SECTORS; erase.VoltageRange = FLASH_VOLTAGE_RANGE_3; erase.Sector = targetSector; erase.NbSectors = 1U; if (HAL_FLASHEx_Erase(&erase, §orError) != HAL_OK) { status = HAL_ERROR; } words = (const uint32_t *)&PlsrFlashRecordBuffer; wordCount = sizeof(PlsrFlashRecordBuffer) / sizeof(uint32_t); if (status == HAL_OK) { for (index = 1UL; index < wordCount; index++) { if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, targetAddress + index * 4UL, words[index]) != HAL_OK) { status = HAL_ERROR; break; } } } if ((status == HAL_OK) && (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, targetAddress, PLSR_FLASH_MAGIC) != HAL_OK)) { status = HAL_ERROR; } if (HAL_FLASH_Lock() != HAL_OK) { status = HAL_ERROR; } if ((status == HAL_OK) && (PlsrFlashRecordIsValid( (const PLSR_FLASH_RECORD *)targetAddress) != 0U)) { return 1U; } return 0U; } void PlsrPlatformCheckpointConfig(const PLSR_CONFIG *config) { PLSR_BACKUP_CONFIG_RECORD *record = (PLSR_BACKUP_CONFIG_RECORD *)PLSR_BACKUP_CONFIG_ADDRESS; if (config == NULL) { return; } record->magic = 0UL; record->config = *config; record->crc32 = PlsrCrc32(&record->config, sizeof(record->config)); __DMB(); record->magic = PLSR_BACKUP_CONFIG_MAGIC; __DMB(); } void PlsrPlatformCheckpointPosition(int32_t position, uint8_t positionValid, uint8_t wasBusy) { PLSR_BACKUP_POSITION_RECORD *slots = (PLSR_BACKUP_POSITION_RECORD *)PLSR_BACKUP_POSITION_ADDRESS; PLSR_BACKUP_POSITION_RECORD *record; PlsrBackupPositionGeneration++; record = &slots[PlsrBackupPositionGeneration & 1UL]; record->magic = 0UL; record->generation = PlsrBackupPositionGeneration; record->position = position; record->positionValid = (positionValid != 0U) ? 1U : 0U; record->wasBusy = (wasBusy != 0U) ? 1U : 0U; record->reserved = 0U; record->crc32 = PlsrCrc32(&record->generation, sizeof(record->generation) + sizeof(record->position) + sizeof(record->positionValid) + sizeof(record->wasBusy) + sizeof(record->reserved)); __DMB(); record->magic = PLSR_BACKUP_POSITION_MAGIC; __DMB(); } uint32_t PlsrPlatformEnterCritical(void) { uint32_t state = __get_PRIMASK(); __disable_irq(); __DMB(); return state; } void PlsrPlatformExitCritical(uint32_t state) { __DMB(); if (state == 0UL) { __enable_irq(); } } static void PlsrHandleTimerIrq(uint8_t pulseOutput) { TIM_TypeDef *timer = PlsrTimerMap[pulseOutput].timer; #if PLSR_ENABLE_IRQ_CYCLE_DIAG uint32_t startedAt; uint32_t elapsedCycles; #endif if (((timer->SR & TIM_SR_UIF) != 0UL) && ((timer->DIER & TIM_DIER_UIE) != 0UL)) { #if PLSR_ENABLE_IRQ_CYCLE_DIAG startedAt = DWT->CYCCNT; #endif timer->SR = ~TIM_SR_UIF; PlsrTimerActiveFrequencyHz[pulseOutput] = PlsrTimerQueuedFrequencyHz[pulseOutput]; PlsrPulseTimerIrq(pulseOutput); #if PLSR_ENABLE_IRQ_CYCLE_DIAG elapsedCycles = DWT->CYCCNT - startedAt; PlsrIrqCount[pulseOutput]++; PlsrIrqLastCycles[pulseOutput] = elapsedCycles; if (elapsedCycles > PlsrIrqMaxCycles[pulseOutput]) { PlsrIrqMaxCycles[pulseOutput] = elapsedCycles; } #endif } } void TIM1_UP_TIM10_IRQHandler(void) { PlsrHandleTimerIrq(0U); } void TIM8_UP_TIM13_IRQHandler(void) { PlsrHandleTimerIrq(1U); } void TIM1_TRG_COM_TIM11_IRQHandler(void) { PlsrHandleTimerIrq(2U); } void TIM8_TRG_COM_TIM14_IRQHandler(void) { PlsrHandleTimerIrq(3U); } #endif /* PLSR_HOST_TEST */