|
|
|
@ -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], |
|
|
|
@ -136,6 +140,80 @@ void UADC_ReportEndOfStream(Unit *unit) |
|
|
|
|
scheduleJob(&j); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void handle_httc(Unit *unit, bool tc) |
|
|
|
|
{ |
|
|
|
|
struct priv *priv = unit->data; |
|
|
|
|
uint16_t start = priv->stream_startpos; |
|
|
|
|
uint16_t end; |
|
|
|
|
const bool ht = !tc; |
|
|
|
|
|
|
|
|
|
const bool m_trigd = priv->opmode == ADC_OPMODE_TRIGD; |
|
|
|
|
const bool m_stream = priv->opmode == ADC_OPMODE_STREAM; |
|
|
|
|
const bool m_fixcpt = priv->opmode == ADC_OPMODE_BLCAP; |
|
|
|
|
|
|
|
|
|
if (ht) { |
|
|
|
|
// dbg("HT");
|
|
|
|
|
end = (uint16_t) (priv->dma_buffer_itemcount / 2); |
|
|
|
|
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// dbg("TC");
|
|
|
|
|
end = (uint16_t) priv->dma_buffer_itemcount; |
|
|
|
|
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ht == tc) { |
|
|
|
|
// This shouldn't happen - looks like we missed the TC flag
|
|
|
|
|
dbg("!! %d -> %d", (int) start, (int) end); |
|
|
|
|
// TODO we could try to catch up. for now, just take what is easy to grab and hope it doesnt matter
|
|
|
|
|
if (end == 64) start = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (start != end) { |
|
|
|
|
uint32_t sgcount = (end - start) / priv->nb_channels; |
|
|
|
|
|
|
|
|
|
if (m_trigd || m_fixcpt) { |
|
|
|
|
sgcount = MIN(priv->trig_stream_remain, sgcount); |
|
|
|
|
priv->trig_stream_remain -= sgcount; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool close = !m_stream && priv->trig_stream_remain == 0; |
|
|
|
|
|
|
|
|
|
Job j = { |
|
|
|
|
.unit = unit, |
|
|
|
|
.data1 = start, |
|
|
|
|
.data2 = sgcount * priv->nb_channels, |
|
|
|
|
.data3 = (uint32_t) close, |
|
|
|
|
.cb = UADC_JobSendBlockChunk |
|
|
|
|
}; |
|
|
|
|
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");
|
|
|
|
|
// If auto-arm enabled, we need to re-arm again.
|
|
|
|
|
// However, EOS irq is disabled during the capture.
|
|
|
|
|
// We have to wait for the next EOS interrupt to occur.
|
|
|
|
|
// TODO verify if keeping the EOS irq enabled during capture has significant performance penalty. If not, we can leave it enabled.
|
|
|
|
|
UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// dbg("start==end, skip this irq");
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (tc) { |
|
|
|
|
priv->stream_startpos = 0; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
priv->stream_startpos = end; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void UADC_DMA_Handler(void *arg) |
|
|
|
|
{ |
|
|
|
|
Unit *unit = arg; |
|
|
|
@ -144,6 +222,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)) { |
|
|
|
@ -158,59 +243,17 @@ void UADC_DMA_Handler(void *arg) |
|
|
|
|
|
|
|
|
|
if (m_trigd || m_stream || m_fixcpt) { |
|
|
|
|
if (ht || tc) { |
|
|
|
|
const uint16_t start = priv->stream_startpos; |
|
|
|
|
uint16_t end; |
|
|
|
|
|
|
|
|
|
if (ht) { |
|
|
|
|
// dbg("HT");
|
|
|
|
|
end = (uint16_t) (priv->dma_buffer_itemcount / 2); |
|
|
|
|
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// dbg("TC");
|
|
|
|
|
end = (uint16_t) priv->dma_buffer_itemcount; |
|
|
|
|
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// dbg("start %d, end %d", (int)start, (int)end);
|
|
|
|
|
assert_param(start <= end); |
|
|
|
|
|
|
|
|
|
if (start != end) { |
|
|
|
|
uint32_t sgcount = (end - start) / priv->nb_channels; |
|
|
|
|
|
|
|
|
|
if (m_trigd || m_fixcpt) { |
|
|
|
|
sgcount = MIN(priv->trig_stream_remain, sgcount); |
|
|
|
|
priv->trig_stream_remain -= sgcount; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool close = !m_stream && priv->trig_stream_remain == 0; |
|
|
|
|
|
|
|
|
|
Job j = { |
|
|
|
|
.unit = unit, |
|
|
|
|
.data1 = start, |
|
|
|
|
.data2 = sgcount * priv->nb_channels, |
|
|
|
|
.data3 = (uint32_t) close, |
|
|
|
|
.cb = UADC_JobSendBlockChunk |
|
|
|
|
}; |
|
|
|
|
scheduleJob(&j); |
|
|
|
|
|
|
|
|
|
if (close) { |
|
|
|
|
// dbg("End of capture");
|
|
|
|
|
// If auto-arm enabled, we need to re-arm again.
|
|
|
|
|
// However, EOS irq is disabled during the capture.
|
|
|
|
|
// We have to wait for the next EOS interrupt to occur.
|
|
|
|
|
// TODO verify if keeping the EOS irq enabled during capture has significant performance penalty. If not, we can leave it enabled.
|
|
|
|
|
UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE); |
|
|
|
|
if (ht && tc) { |
|
|
|
|
uint16_t half = (uint16_t) (priv->dma_buffer_itemcount / 2); |
|
|
|
|
if (priv->stream_startpos > half) { |
|
|
|
|
handle_httc(unit, true); |
|
|
|
|
handle_httc(unit, false); |
|
|
|
|
} else { |
|
|
|
|
handle_httc(unit, false); |
|
|
|
|
handle_httc(unit, true); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// dbg("start==end, skip this irq");
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (tc) { |
|
|
|
|
priv->stream_startpos = 0; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
priv->stream_startpos = end; |
|
|
|
|
handle_httc(unit, tc); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
@ -235,18 +278,21 @@ void UADC_DMA_Handler(void *arg) |
|
|
|
|
|
|
|
|
|
void UADC_ADC_EOS_Handler(void *arg) |
|
|
|
|
{ |
|
|
|
|
uint64_t timestamp = PTIM_GetMicrotime(); |
|
|
|
|
Unit *unit = arg; |
|
|
|
|
|
|
|
|
|
assert_param(unit); |
|
|
|
|
struct priv *priv = unit->data; |
|
|
|
|
assert_param(priv); |
|
|
|
|
|
|
|
|
|
// Normally
|
|
|
|
|
uint64_t timestamp = 0; |
|
|
|
|
if (priv->opmode == ADC_OPMODE_ARMED) timestamp = PTIM_GetMicrotime(); |
|
|
|
|
|
|
|
|
|
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); // XXX this could be changed to reading it from the DR instead
|
|
|
|
|
|
|
|
|
|
uint32_t sample_pos; |
|
|
|
|
if (dmapos == 0) { |
|
|
|
@ -257,53 +303,51 @@ void UADC_ADC_EOS_Handler(void *arg) |
|
|
|
|
sample_pos -= priv->nb_channels; |
|
|
|
|
|
|
|
|
|
int cnt = 0; // index of the sample within the group
|
|
|
|
|
for (uint32_t i = 0; i < 18; i++) { |
|
|
|
|
if (priv->extended_channels_mask & (1 << i)) { |
|
|
|
|
|
|
|
|
|
const bool can_average = priv->real_frequency_int < UADC_MAX_FREQ_FOR_AVERAGING; |
|
|
|
|
const uint32_t channels_mask = priv->extended_channels_mask; |
|
|
|
|
|
|
|
|
|
for (uint8_t i = 0; i < 18; i++) { |
|
|
|
|
if (channels_mask & (1 << i)) { |
|
|
|
|
uint16_t val = priv->dma_buffer[sample_pos+cnt]; |
|
|
|
|
cnt++; |
|
|
|
|
|
|
|
|
|
priv->averaging_bins[i] = |
|
|
|
|
priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) + |
|
|
|
|
((float) val) * priv->avg_factor_as_float; |
|
|
|
|
if (can_average) { |
|
|
|
|
priv->averaging_bins[i] = |
|
|
|
|
priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) + |
|
|
|
|
((float) val) * priv->avg_factor_as_float; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
priv->last_samples[i] = val; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (i == priv->trigger_source) { |
|
|
|
|
if (priv->opmode == ADC_OPMODE_ARMED) { |
|
|
|
|
// dbg("Trig line level %d", (int)val);
|
|
|
|
|
|
|
|
|
|
bool trigd = false; |
|
|
|
|
uint8_t edge_type = 0; |
|
|
|
|
if (priv->trig_prev_level < priv->trig_level && val >= priv->trig_level) { |
|
|
|
|
// dbg("******** Rising edge");
|
|
|
|
|
// Rising edge
|
|
|
|
|
trigd = (bool) (priv->trig_edge & 0b01); |
|
|
|
|
edge_type = 1; |
|
|
|
|
} |
|
|
|
|
else if (priv->trig_prev_level > priv->trig_level && val <= priv->trig_level) { |
|
|
|
|
// dbg("******** Falling edge");
|
|
|
|
|
// Falling edge
|
|
|
|
|
trigd = (bool) (priv->trig_edge & 0b10); |
|
|
|
|
edge_type = 2; |
|
|
|
|
} |
|
|
|
|
if (priv->opmode == ADC_OPMODE_ARMED) { |
|
|
|
|
uint16_t val = priv->last_samples[priv->trigger_source]; |
|
|
|
|
|
|
|
|
|
if (trigd) { |
|
|
|
|
UADC_HandleTrigger(unit, edge_type, timestamp); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (priv->opmode == ADC_OPMODE_REARM_PENDING) { |
|
|
|
|
if (!priv->auto_rearm) { |
|
|
|
|
// It looks like the flag was cleared by DISARM before we got a new sample.
|
|
|
|
|
// Let's just switch to IDLE
|
|
|
|
|
UADC_SwitchMode(unit, ADC_OPMODE_IDLE); |
|
|
|
|
} else { |
|
|
|
|
// Re-arming for a new trigger
|
|
|
|
|
UADC_SwitchMode(unit, ADC_OPMODE_ARMED); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// dbg("Trig line level %d", (int)val);
|
|
|
|
|
if ((priv->trig_prev_level < priv->trig_level) && val >= priv->trig_level && (bool) (priv->trig_edge & 0b01)) { |
|
|
|
|
// dbg("******** Rising edge");
|
|
|
|
|
// Rising edge
|
|
|
|
|
UADC_HandleTrigger(unit, 1, timestamp); |
|
|
|
|
} |
|
|
|
|
else if ((priv->trig_prev_level > priv->trig_level) && val <= priv->trig_level && (bool) (priv->trig_edge & 0b10)) { |
|
|
|
|
// dbg("******** Falling edge");
|
|
|
|
|
// Falling edge
|
|
|
|
|
UADC_HandleTrigger(unit, 2, timestamp); |
|
|
|
|
} |
|
|
|
|
priv->trig_prev_level = val; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
priv->trig_prev_level = val; |
|
|
|
|
} |
|
|
|
|
// auto-rearm was waiting for the next sample
|
|
|
|
|
if (priv->opmode == ADC_OPMODE_REARM_PENDING) { |
|
|
|
|
if (!priv->auto_rearm) { |
|
|
|
|
// It looks like the flag was cleared by DISARM before we got a new sample.
|
|
|
|
|
// Let's just switch to IDLE
|
|
|
|
|
UADC_SwitchMode(unit, ADC_OPMODE_IDLE); |
|
|
|
|
} else { |
|
|
|
|
// Re-arming for a new trigger
|
|
|
|
|
UADC_SwitchMode(unit, ADC_OPMODE_ARMED); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -313,6 +357,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 +368,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 +394,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 +409,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 +423,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 +436,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 +462,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 +496,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 +509,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 +518,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 +549,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 +564,5 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) |
|
|
|
|
LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// the actual switch
|
|
|
|
|
priv->opmode = new_mode; |
|
|
|
|
} |
|
|
|
|