diff --git a/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c b/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c index 7d05557..dca206d 100644 --- a/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c +++ b/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c @@ -112,7 +112,7 @@ __ALIGN_BEGIN uint8_t USBD_MSC_CDC_CfgFSDesc[USBD_MSC_CDC_CONFIG_DESC_SIZ] __AL 0x03, /* bmAttributes: Interrupt */ LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: TODO: 2?*/ HIBYTE(CDC_CMD_PACKET_SIZE), - 0xFF, /* bInterval: TODO was 0x10?*/ + 0x10, //0xFF, /* bInterval: TODO was 0x10?*/ /********** CDC Data Class Interface Descriptor ***********/ /*75*/ 0x09, /* bLength: Endpoint Descriptor size */ diff --git a/framework/unit_registry.c b/framework/unit_registry.c index bb86421..23d2ae8 100644 --- a/framework/unit_registry.c +++ b/framework/unit_registry.c @@ -612,14 +612,17 @@ void ureg_tick_units(void) UlistEntry *li = ulist_head; while (li != NULL) { Unit *const pUnit = &li->unit; - if (pUnit && pUnit->data && pUnit->status == E_SUCCESS && pUnit->tick_interval > 0) { + if (pUnit && pUnit->data && pUnit->status == E_SUCCESS && (pUnit->tick_interval > 0 || pUnit->_tick_cnt > 0)) { + if (pUnit->_tick_cnt > 0) { + pUnit->_tick_cnt--; // check for 0 allows one-off timers + } + if (pUnit->_tick_cnt == 0) { if (pUnit->driver->updateTick) { pUnit->driver->updateTick(pUnit); } pUnit->_tick_cnt = pUnit->tick_interval; } - pUnit->_tick_cnt--; } li = li->next; } diff --git a/platform/plat_compat.h b/platform/plat_compat.h index bd6d7b3..994fe3e 100644 --- a/platform/plat_compat.h +++ b/platform/plat_compat.h @@ -24,7 +24,7 @@ #define FLASH_SAVE_BUF_LEN 128 // Malloc'd buffer for saving to flash #define MSG_QUE_SLOT_SIZE 64 // FIXME this should be possible to lower, but there's some bug with bulk transfer / INI parser -#define RX_QUE_CAPACITY 16 // TinyFrame rx queue size (64 bytes each) +#define RX_QUE_CAPACITY 32 // TinyFrame rx queue size (64 bytes each) #define TF_MAX_PAYLOAD_RX 512 // TF max Rx payload #define TF_SENDBUF_LEN 64 // TF transmit buffer (can be less than a full frame) diff --git a/tasks/task_msg.c b/tasks/task_msg.c index e58c260..c3a8b91 100644 --- a/tasks/task_msg.c +++ b/tasks/task_msg.c @@ -8,7 +8,7 @@ volatile uint32_t msgQueHighWaterMark = 0; -static void que_safe_post(struct rx_sched_combined_que_item *slot) +static bool que_safe_post(struct rx_sched_combined_que_item *slot) { uint32_t count = 0; assert_param(slot != NULL); @@ -18,7 +18,7 @@ static void que_safe_post(struct rx_sched_combined_que_item *slot) BaseType_t status = xQueueSendFromISR(queMsgJobHandle, slot, &xHigherPriorityTaskWoken); if (pdPASS != status) { dbg("! Que post from ISR failed"); - return; + return false; } #if USE_STACK_MONITOR @@ -30,7 +30,7 @@ static void que_safe_post(struct rx_sched_combined_que_item *slot) BaseType_t status = xQueueSend(queMsgJobHandle, slot, MSG_QUE_POST_TIMEOUT); if (pdPASS != status) { dbg("! Que post failed"); - return; + return false; } #if USE_STACK_MONITOR @@ -41,6 +41,8 @@ static void que_safe_post(struct rx_sched_combined_que_item *slot) #if USE_STACK_MONITOR msgQueHighWaterMark = MAX(msgQueHighWaterMark, count); #endif + + return true; } /** @@ -48,7 +50,7 @@ static void que_safe_post(struct rx_sched_combined_que_item *slot) * * @param callback - the callback function */ -void scheduleJob(Job *job) +bool scheduleJob(Job *job) { assert_param(job->cb != NULL); @@ -57,7 +59,7 @@ void scheduleJob(Job *job) .job = *job, // copy content of the struct }; - que_safe_post(&slot); + return que_safe_post(&slot); } /** @@ -112,7 +114,10 @@ void rxQuePostMsg(uint8_t *buf, uint32_t len) slot.msg.len = len > MSG_QUE_SLOT_SIZE ? MSG_QUE_SLOT_SIZE : len; memcpy(slot.msg.data, buf, slot.msg.len); - que_safe_post(&slot); + if (!que_safe_post(&slot)) { + dbg("rxQuePostMsg fail!"); + break; + } len -= slot.msg.len; buf += slot.msg.len; diff --git a/tasks/task_msg.h b/tasks/task_msg.h index 3b99d6a..f0a9990 100644 --- a/tasks/task_msg.h +++ b/tasks/task_msg.h @@ -25,7 +25,7 @@ void TaskMsgJob(const void *argument); * * @param job */ -void scheduleJob(Job *job); +bool scheduleJob(Job *job); /** * Add a message to the queue. This always takes the entire slot (64 bytes) or multiple diff --git a/units/adc/_adc_api.c b/units/adc/_adc_api.c index 872d3a1..b91ce37 100644 --- a/units/adc/_adc_api.c +++ b/units/adc/_adc_api.c @@ -9,3 +9,21 @@ #define ADC_INTERNAL #include "_adc_internal.h" +error_t UU_ADC_AbortCapture(Unit *unit) +{ + CHECK_TYPE(unit, &UNIT_ADC); + struct priv *priv = unit->data; + + enum uadc_opmode old_opmode = priv->opmode; + + priv->auto_rearm = false; + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + + if (old_opmode == ADC_OPMODE_BLCAP || + old_opmode == ADC_OPMODE_STREAM || + old_opmode == ADC_OPMODE_TRIGD) { + UADC_ReportEndOfStream(unit); + } + + return E_SUCCESS; +} diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index 586b55a..d101831 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -4,12 +4,16 @@ #include "platform.h" #include "unit_base.h" +#include "unit_adc.h" #define ADC_INTERNAL #include "_adc_internal.h" #define DMA_POS(priv) ((priv)->dma_buffer_itemcount - (priv)->DMA_CHx->CNDTR) +volatile bool emergency = false; +//#define CRUMB() if(emergency) trap("crumb") + static void UADC_JobSendBlockChunk(Job *job) { Unit *unit = job->unit; @@ -21,7 +25,7 @@ static void UADC_JobSendBlockChunk(Job *job) uint32_t count = job->data2; bool close = (bool) job->data3; - dbg("Send indices [%d -> %d)", (int)start, (int)(start+count)); +// dbg("Send indices [%d -> %d)", (int)start, (int)(start+count)); TF_TYPE type = close ? EVT_CAPT_DONE : EVT_CAPT_MORE; @@ -58,7 +62,7 @@ static void UADC_JobSendTriggerCaptureHeader(Job *job) EventReport_Start(&er); priv->stream_frame_id = er.sent_msg_id; - dbg("Sending TRIG HEADER with id %d (idx %d)", (int)er.sent_msg_id, (int)index_trigd); +// dbg("Sending TRIG HEADER with id %d (idx %d)", (int)er.sent_msg_id, (int)index_trigd); { // preamble uint8_t buf[4]; @@ -81,10 +85,10 @@ static void UADC_JobSendTriggerCaptureHeader(Job *job) uint16_t items_from_end = pretrig_remain - index_trigd; assert_param(priv->dma_buffer_itemcount - items_from_end >= index_trigd); - dbg("Pretrig wraparound part: start %d, len %d", - (int) (priv->dma_buffer_itemcount - items_from_end), - (int) items_from_end - ); +// dbg("Pretrig wraparound part: start %d, len %d", +// (int) (priv->dma_buffer_itemcount - items_from_end), +// (int) items_from_end +// ); EventReport_Data( (uint8_t *) &priv->dma_buffer[priv->dma_buffer_itemcount - @@ -95,10 +99,10 @@ static void UADC_JobSendTriggerCaptureHeader(Job *job) pretrig_remain -= items_from_end; } - dbg("Pretrig front part: start %d, len %d", - (int) (index_trigd - pretrig_remain), - (int) pretrig_remain - ); +// dbg("Pretrig front part: start %d, len %d", +// (int) (index_trigd - pretrig_remain), +// (int) pretrig_remain +// ); assert_param(pretrig_remain <= index_trigd); EventReport_Data((uint8_t *) &priv->dma_buffer[index_trigd - pretrig_remain], @@ -144,6 +148,13 @@ void UADC_DMA_Handler(void *arg) struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) { + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); + LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum); + return; + } + const uint32_t isrsnapshot = priv->DMAx->ISR; if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_chnum)) { @@ -192,7 +203,13 @@ void UADC_DMA_Handler(void *arg) .data3 = (uint32_t) close, .cb = UADC_JobSendBlockChunk }; - scheduleJob(&j); + if (!scheduleJob(&j)) { + // Abort if we can't queue - the stream would tear and we'd hog the system with error messages + dbg("(!) Buffers overflow, abort capture"); + emergency = true; + UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN); + return; + } if (close) { // dbg("End of capture"); @@ -243,10 +260,11 @@ void UADC_ADC_EOS_Handler(void *arg) assert_param(priv); LL_ADC_ClearFlag_EOS(priv->ADCx); + if (priv->opmode == ADC_OPMODE_UNINIT) return; // Wait for the DMA to complete copying the last sample uint16_t dmapos; - while ((dmapos = (uint16_t) DMA_POS(priv)) % priv->nb_channels != 0); + hw_wait_while((dmapos = (uint16_t) DMA_POS(priv)) % priv->nb_channels != 0, 100); uint32_t sample_pos; if (dmapos == 0) { @@ -313,6 +331,7 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) return; if (priv->trig_holdoff != 0 && priv->trig_holdoff_remain > 0) { // dbg("Trig discarded due to holdoff."); @@ -323,7 +342,7 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) priv->trig_holdoff_remain = priv->trig_holdoff; // Start the tick unit->tick_interval = 1; - unit->_tick_cnt = 0; + unit->_tick_cnt = 1; } priv->stream_startpos = (uint16_t) DMA_POS(priv); @@ -349,6 +368,7 @@ void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id) assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) return; priv->stream_frame_id = frame_id; priv->stream_startpos = (uint16_t) DMA_POS(priv); @@ -363,11 +383,11 @@ void UADC_StartStream(Unit *unit, TF_ID frame_id) assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) return; priv->stream_frame_id = frame_id; priv->stream_startpos = (uint16_t) DMA_POS(priv); priv->stream_serial = 0; -// dbg("Start streaming."); UADC_SwitchMode(unit, ADC_OPMODE_STREAM); } @@ -377,8 +397,8 @@ void UADC_StopStream(Unit *unit) assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) return; -// dbg("Stop stream."); UADC_ReportEndOfStream(unit); UADC_SwitchMode(unit, ADC_OPMODE_IDLE); } @@ -390,6 +410,16 @@ void UADC_updateTick(Unit *unit) struct priv *priv = unit->data; assert_param(priv); + // Recover from shutdown after a delay + if (priv->opmode == ADC_OPMODE_EMERGENCY_SHUTDOWN) { + dbg("Recovering from emergency shutdown"); + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + LL_TIM_EnableCounter(priv->TIMx); + UADC_ReportEndOfStream(unit); + unit->tick_interval = 0; + return; + } + if (priv->trig_holdoff_remain > 0) { priv->trig_holdoff_remain--; @@ -406,10 +436,14 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) struct priv *priv = unit->data; assert_param(priv); - if (new_mode == priv->opmode) return; // nothing to do + const enum uadc_opmode old_mode = priv->opmode; + + if (new_mode == old_mode) return; // nothing to do // if un-itied, can go only to IDLE - assert_param((priv->opmode != ADC_OPMODE_UNINIT) || (new_mode == ADC_OPMODE_IDLE)); + assert_param((old_mode != ADC_OPMODE_UNINIT) || (new_mode == ADC_OPMODE_IDLE)); + + priv->opmode = ADC_OPMODE_UNINIT; if (new_mode == ADC_OPMODE_UNINIT) { // dbg("ADC switch -> UNINIT"); @@ -436,11 +470,12 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); } else if (new_mode == ADC_OPMODE_IDLE || new_mode == ADC_OPMODE_REARM_PENDING) { -// dbg("ADC switch -> IDLE or IDLE/REARM_PENDING"); // IDLE and ARMED are identical with the exception that the trigger condition is not checked // ARMED can be only entered from IDLE, thus we do the init only here. // In IDLE, we don't need the DMA interrupts + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); @@ -448,7 +483,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_ADC_ClearFlag_EOS(priv->ADCx); LL_ADC_EnableIT_EOS(priv->ADCx); - if (priv->opmode == ADC_OPMODE_UNINIT) { + if (old_mode == ADC_OPMODE_UNINIT) { // Nothing is started yet - this is the only way to leave UNINIT LL_ADC_Enable(priv->ADCx); LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum); @@ -457,9 +492,28 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_ADC_REG_StartConversion(priv->ADCx); } } + else if (new_mode == ADC_OPMODE_EMERGENCY_SHUTDOWN) { + // Emergency shutdown is used when the job queue overflows and the stream is torn + // This however doesn't help in the case when user sets such a high frequency + // that the whole app becomes unresponsive due to the completion ISR, need to verify the value manually. + + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); + LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); + + LL_TIM_DisableCounter(priv->TIMx); + UADC_SetSampleRate(unit, 10000); // fallback to a known safe value + + LL_ADC_ClearFlag_EOS(priv->ADCx); + LL_ADC_DisableIT_EOS(priv->ADCx); + + unit->tick_interval = 0; + unit->_tick_cnt = 250; // 1-off + } else if (new_mode == ADC_OPMODE_ARMED) { // dbg("ADC switch -> ARMED"); - assert_param(priv->opmode == ADC_OPMODE_IDLE || priv->opmode == ADC_OPMODE_REARM_PENDING); + assert_param(old_mode == ADC_OPMODE_IDLE || old_mode == ADC_OPMODE_REARM_PENDING); // avoid firing immediately by the value jumping across the scale priv->trig_prev_level = priv->last_samples[priv->trigger_source]; @@ -469,7 +523,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) new_mode == ADC_OPMODE_BLCAP) { // dbg("ADC switch -> TRIG'D / STREAM / BLOCK"); - assert_param(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_IDLE); + assert_param(old_mode == ADC_OPMODE_ARMED || old_mode == ADC_OPMODE_IDLE); // during the capture, we disallow direct readout and averaging to reduce overhead LL_ADC_DisableIT_EOS(priv->ADCx); @@ -484,6 +538,6 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); } - // the actual switch + dbg("Now setting the new opmode"); priv->opmode = new_mode; } diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index b46e3a9..fe00843 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -19,6 +19,7 @@ enum uadc_opmode { ADC_OPMODE_TRIGD, //!< Triggered, sending pre-trigger and streaming captured data. ADC_OPMODE_BLCAP, //!< Capture of fixed length without a trigger ADC_OPMODE_STREAM, //!< Unlimited capture + ADC_OPMODE_EMERGENCY_SHUTDOWN, //!< Used when the buffers overrun to safely transition to IDLE after a delay }; enum uadc_event { diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index f6ddb7c..0f8cb83 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -240,18 +240,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P */ case CMD_ABORT:; dbg("> Abort capture"); - { - enum uadc_opmode old_opmode = priv->opmode; - - priv->auto_rearm = false; - UADC_SwitchMode(unit, ADC_OPMODE_IDLE); - - if (old_opmode == ADC_OPMODE_BLCAP || - old_opmode == ADC_OPMODE_STREAM || - old_opmode == ADC_OPMODE_TRIGD) { - UADC_ReportEndOfStream(unit); - } - } + TRY(UU_ADC_AbortCapture(unit)); return E_SUCCESS; /** diff --git a/units/adc/unit_adc.h b/units/adc/unit_adc.h index 84d48ba..1ee3501 100644 --- a/units/adc/unit_adc.h +++ b/units/adc/unit_adc.h @@ -11,6 +11,6 @@ extern const UnitDriver UNIT_ADC; -// UU_ prototypes +error_t UU_ADC_AbortCapture(Unit *unit); #endif //U_TPL_H diff --git a/units/usart/unit_usart.c b/units/usart/unit_usart.c index 985074a..e8aca86 100644 --- a/units/usart/unit_usart.c +++ b/units/usart/unit_usart.c @@ -65,7 +65,7 @@ void UUSART_DMA_HandleRxFromIRQ(Unit *unit, uint16_t endpos) .data2 = count, .cb = UUSART_SendReceivedDataToMaster }; - scheduleJob(&j); + scheduleJob(&j); // TODO disable unit on failure // Move the read cursor, wrap around if needed if (endpos == UUSART_RXBUF_LEN) endpos = 0;