diff --git a/platform/hw_utils.c b/platform/hw_utils.c index c082ca4..eacf7b7 100644 --- a/platform/hw_utils.c +++ b/platform/hw_utils.c @@ -86,7 +86,7 @@ bool parse_port_name(const char *value, char *targetName) } /** Parse a list of pin numbers with ranges and commans/semicolons to a bitmask */ -uint16_t parse_pinmask(const char *value, bool *suc) +uint32_t parse_pinmask(const char *value, bool *suc) { uint32_t bits = 0; uint32_t acu = 0; @@ -116,7 +116,10 @@ uint16_t parse_pinmask(const char *value, bool *suc) rangestart = swp; } - for(uint32_t i=rangestart; i<=acu; i++) { + if (rangestart > 31) rangestart = 31; + if (acu > 31) acu = 31; + + for(uint32_t i=rangestart; i <= acu; i++) { bits |= 1< 0xFFFF) *suc = false; - return (uint16_t) bits; + return bits; } /** Convert a pin bitmask to the ASCII format understood by str_parse_pinmask() */ -char * pinmask2str(uint16_t pins, char *buffer) +char * pinmask2str(uint32_t pins, char *buffer) { char *b = buffer; uint32_t start = 0; @@ -152,13 +155,13 @@ char * pinmask2str(uint16_t pins, char *buffer) return buffer; } - for (int32_t i = 15; i >= -1; i--) { + for (int32_t i = 31; i >= -1; i--) { bool bit; if (i == -1) { bit = false; } else { - bit = 0 != (pins & 0x8000); + bit = 0 != (pins & 0x80000000); pins <<= 1; } @@ -189,7 +192,7 @@ char * pinmask2str(uint16_t pins, char *buffer) return buffer; } -char * pinmask2str_up(uint16_t pins, char *buffer) +char * pinmask2str_up(uint32_t pins, char *buffer) { char *b = buffer; uint32_t start = 0; @@ -202,10 +205,10 @@ char * pinmask2str_up(uint16_t pins, char *buffer) return buffer; } - for (int32_t i = 0; i <= 16; i++) { + for (int32_t i = 0; i <= 32; i++) { bool bit; - if (i == 16) { + if (i == 32) { bit = false; } else { bit = 0 != (pins & 1); @@ -240,11 +243,11 @@ char * pinmask2str_up(uint16_t pins, char *buffer) } /** Spread packed port pins using a mask */ -uint16_t pinmask_spread(uint16_t packed, uint16_t mask) +uint32_t pinmask_spread(uint32_t packed, uint32_t mask) { - uint16_t result = 0; - uint16_t poke = 1; - for (int i = 0; i<16; i++) { + uint32_t result = 0; + uint32_t poke = 1; + for (int i = 0; i<32; i++) { if (mask & (1<dma_buffer_itemcount - (priv)->DMA_CHx->CNDTR) +#define DMA_POS(priv) ((priv)->buf_itemcount - (priv)->DMA_CHx->CNDTR) static void UADC_JobSendBlockChunk(Job *job) { @@ -25,7 +25,7 @@ static void UADC_JobSendBlockChunk(Job *job) TF_Msg msg = { .frame_id = priv->stream_frame_id, - .len = (TF_LEN) (1 + count*sizeof(uint16_t)), + .len = (TF_LEN) (1 /*seq*/ + count * sizeof(uint16_t)), .type = type, }; @@ -49,56 +49,43 @@ static void UADC_JobSendTriggerCaptureHeader(Job *job) .unit = unit, .type = EVT_CAPT_START, .timestamp = job->timestamp, - .length = (priv->pretrig_len+1)*priv->nb_channels*sizeof(uint16_t) + 2 /*pretrig len*/ + 1 /*edge*/ + 1 /* seq */ + .length = (priv->pretrig_len+1) * priv->nb_channels * sizeof(uint16_t) + 4 /*pretrig len*/ + 1 /*edge*/ + 1 /* seq */ }; - uint16_t index_trigd = (uint16_t) job->data1; + uint32_t index_trigd = job->data1; uint8_t edge = (uint8_t) job->data2; 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); { // preamble uint8_t buf[4]; PayloadBuilder pb = pb_start(buf, 4, NULL); - pb_u16(&pb, priv->pretrig_len); + pb_u32(&pb, priv->pretrig_len); pb_u8(&pb, edge); pb_u8(&pb, priv->stream_serial++); // This is the serial counter for the first chunk - // (containing the pre-trigger, or empty if no pretrig configured) + // (containing the pre-trigger, or empty if no pretrig configured) EventReport_PB(&pb); if (priv->pretrig_len > 0) { // pretrig - uint16_t pretrig_remain = (uint16_t) ((priv->pretrig_len + 1) * priv->nb_channels); // +1 because we want pretrig 0 to exactly start with the triggering sample + uint32_t pretrig_remain = (priv->pretrig_len + 1) * priv->nb_channels; // +1 because we want pretrig 0 to exactly start with the triggering sample - assert_param(index_trigd <= priv->dma_buffer_itemcount); + assert_param(index_trigd <= priv->buf_itemcount); // this is one past the last entry of the triggering capture group if (pretrig_remain > index_trigd) { // used items in the wrap-around part of the buffer - uint16_t items_from_end = pretrig_remain - index_trigd; - assert_param(priv->dma_buffer_itemcount - items_from_end >= index_trigd); + uint32_t items_from_end = pretrig_remain - index_trigd; + assert_param(priv->buf_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 -// ); - - EventReport_Data( - (uint8_t *) &priv->dma_buffer[priv->dma_buffer_itemcount - - items_from_end], - items_from_end * sizeof(uint16_t)); + EventReport_Data((uint8_t *) &priv->dma_buffer[priv->buf_itemcount - items_from_end], + items_from_end * sizeof(uint16_t)); assert_param(items_from_end <= pretrig_remain); pretrig_remain -= items_from_end; } -// 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], pretrig_remain * sizeof(uint16_t)); @@ -109,9 +96,6 @@ static void UADC_JobSendTriggerCaptureHeader(Job *job) static void UADC_JobSendEndOfStreamMsg(Job *job) { - Unit *unit = job->unit; - struct priv *priv = unit->data; - TF_Msg msg = { .type = EVT_CAPT_DONE, .frame_id = (TF_ID) job->data1 @@ -134,8 +118,8 @@ void UADC_ReportEndOfStream(Unit *unit) static void handle_httc(Unit *unit, bool tc) { struct priv *priv = unit->data; - uint16_t start = priv->stream_startpos; - uint16_t end; + uint32_t start = priv->stream_startpos; + uint32_t end; const bool ht = !tc; const bool m_trigd = priv->opmode == ADC_OPMODE_TRIGD; @@ -143,11 +127,11 @@ static void handle_httc(Unit *unit, bool tc) const bool m_fixcpt = priv->opmode == ADC_OPMODE_BLCAP; if (ht) { - end = (uint16_t) (priv->dma_buffer_itemcount / 2); + end = (priv->buf_itemcount / 2); LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); } else { - end = (uint16_t) priv->dma_buffer_itemcount; + end = priv->buf_itemcount; LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); } @@ -162,7 +146,7 @@ static void handle_httc(Unit *unit, bool tc) bool close = !m_stream && priv->trig_stream_remain == 0; if ((tc && priv->tc_pending) || (ht && priv->ht_pending)) { - dbg("(!) DMA not handled in time, abort capture"); + dbg("(!) ADC DMA not handled in time, abort capture"); UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN); return; } @@ -184,7 +168,6 @@ static void handle_httc(Unit *unit, bool tc) 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"); UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN); return; } @@ -232,7 +215,7 @@ void UADC_DMA_Handler(void *arg) if (m_trigd || m_stream || m_fixcpt) { if (ht || tc) { if (ht && tc) { - uint16_t half = (uint16_t) (priv->dma_buffer_itemcount / 2); + const uint32_t half = (uint32_t) (priv->buf_itemcount / 2); if (priv->stream_startpos > half) { handle_httc(unit, true); // TC handle_httc(unit, false); // HT @@ -246,7 +229,7 @@ void UADC_DMA_Handler(void *arg) } } else { // This shouldn't happen, the interrupt should be disabled in this opmode - dbg("(!) not streaming, DMA IT should be disabled"); + dbg("(!) not streaming, ADC DMA IT should be disabled"); if (ht) { LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); @@ -258,7 +241,7 @@ void UADC_DMA_Handler(void *arg) if (te) { // this shouldn't happen - error - dbg("ADC DMA TE!"); + adc_dbg("ADC DMA TE!"); LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum); } } @@ -277,12 +260,12 @@ void UADC_ADC_EOS_Handler(void *arg) if (priv->opmode == ADC_OPMODE_UNINIT) return; // Wait for the DMA to complete copying the last sample - uint16_t dmapos; - 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 dmapos; + hw_wait_while((dmapos = 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) { - sample_pos = (uint32_t) (priv->dma_buffer_itemcount); + sample_pos = (uint32_t) (priv->buf_itemcount); } else { sample_pos = dmapos; } @@ -291,11 +274,11 @@ void UADC_ADC_EOS_Handler(void *arg) int cnt = 0; // index of the sample within the group const bool can_average = priv->real_frequency_int < UADC_MAX_FREQ_FOR_AVERAGING; - const uint32_t channels_mask = priv->extended_channels_mask; + const uint32_t channels_mask = priv->channels_mask; for (uint8_t i = 0; i < 18; i++) { if (channels_mask & (1 << i)) { - uint16_t val = priv->dma_buffer[sample_pos+cnt]; + const uint16_t val = priv->dma_buffer[sample_pos+cnt]; cnt++; if (can_average) { @@ -309,16 +292,13 @@ void UADC_ADC_EOS_Handler(void *arg) } if (priv->opmode == ADC_OPMODE_ARMED) { - uint16_t val = priv->last_samples[priv->trigger_source]; + const uint16_t val = priv->last_samples[priv->trigger_source]; -// 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); } @@ -344,7 +324,7 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) if (priv->opmode == ADC_OPMODE_UNINIT) return; if (priv->trig_holdoff != 0 && priv->trig_holdoff_remain > 0) { -// dbg("Trig discarded due to holdoff."); + // Trig discarded due to holdoff return; } @@ -355,12 +335,10 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) unit->_tick_cnt = 1; } - priv->stream_startpos = (uint16_t) DMA_POS(priv); + priv->stream_startpos = DMA_POS(priv); priv->trig_stream_remain = priv->trig_len; priv->stream_serial = 0; -// dbg("Trigger condition hit, edge=%d, startpos %d", edge_type, (int)priv->stream_startpos); - Job j = { .unit = unit, .timestamp = timestamp, @@ -379,7 +357,7 @@ void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id) if (priv->opmode == ADC_OPMODE_UNINIT) return; priv->stream_frame_id = frame_id; - priv->stream_startpos = (uint16_t) DMA_POS(priv); + priv->stream_startpos = DMA_POS(priv); priv->trig_stream_remain = len; priv->stream_serial = 0; UADC_SwitchMode(unit, ADC_OPMODE_BLCAP); @@ -392,7 +370,7 @@ void UADC_StartStream(Unit *unit, TF_ID frame_id) if (priv->opmode == ADC_OPMODE_UNINIT) return; priv->stream_frame_id = frame_id; - priv->stream_startpos = (uint16_t) DMA_POS(priv); + priv->stream_startpos = DMA_POS(priv); priv->stream_serial = 0; UADC_SwitchMode(unit, ADC_OPMODE_STREAM); } @@ -414,7 +392,8 @@ void UADC_updateTick(Unit *unit) // Recover from shutdown after a delay if (priv->opmode == ADC_OPMODE_EMERGENCY_SHUTDOWN) { - dbg("Recovering from emergency shutdown"); + adc_dbg("ADC recovering from emergency shutdown"); + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); LL_TIM_EnableCounter(priv->TIMx); UADC_ReportEndOfStream(unit); @@ -446,7 +425,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) priv->opmode = ADC_OPMODE_UNINIT; if (new_mode == ADC_OPMODE_UNINIT) { -// dbg("ADC switch -> UNINIT"); + adc_dbg("ADC switch -> UNINIT"); // Stop the DMA, timer and disable ADC - this is called before tearing down the unit LL_TIM_DisableCounter(priv->TIMx); @@ -454,17 +433,14 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) if (LL_ADC_IsEnabled(priv->ADCx)) { // Cancel ongoing conversion if (LL_ADC_REG_IsConversionOngoing(priv->ADCx)) { -// dbg("Stopping ADC conv"); LL_ADC_REG_StopConversion(priv->ADCx); hw_wait_while(LL_ADC_REG_IsStopConversionOngoing(priv->ADCx), 100); } LL_ADC_Disable(priv->ADCx); -// dbg("Disabling ADC"); hw_wait_while(LL_ADC_IsDisableOngoing(priv->ADCx), 100); } -// dbg("Disabling DMA"); LL_DMA_DisableChannel(priv->DMAx, priv->dma_chnum); LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); @@ -496,6 +472,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) } } else if (new_mode == ADC_OPMODE_EMERGENCY_SHUTDOWN) { + adc_dbg("ADC switch -> EMERGENCY_STOP"); // 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. @@ -515,7 +492,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) unit->_tick_cnt = 250; // 1-off } else if (new_mode == ADC_OPMODE_ARMED) { -// dbg("ADC switch -> ARMED"); + adc_dbg("ADC switch -> ARMED"); assert_param(old_mode == ADC_OPMODE_IDLE || old_mode == ADC_OPMODE_REARM_PENDING); // avoid firing immediately by the value jumping across the scale @@ -525,7 +502,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) new_mode == ADC_OPMODE_STREAM || new_mode == ADC_OPMODE_BLCAP) { -// dbg("ADC switch -> TRIG'D / STREAM / BLOCK"); + adc_dbg("ADC switch -> CAPTURE"); assert_param(old_mode == ADC_OPMODE_ARMED || old_mode == ADC_OPMODE_IDLE); // during the capture, we disallow direct readout and averaging to reduce overhead diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index eb3d3f7..99315fb 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -15,13 +15,11 @@ error_t UADC_preInit(Unit *unit) struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv)); if (priv == NULL) return E_OUT_OF_MEM; - priv->channels = 1; // PA0 - priv->enable_tsense = false; - priv->enable_vref = false; - priv->sample_time = 0b010; // 13.5c - priv->frequency = 1000; - priv->buffer_size = 512; - priv->averaging_factor = 500; + priv->cfg.channels = 1<<16; // Tsense by default - always available, easy testing + priv->cfg.sample_time = 0b010; // 13.5c + priv->cfg.frequency = 1000; + priv->cfg.buffer_size = 256; + priv->cfg.averaging_factor = 500; priv->opmode = ADC_OPMODE_UNINIT; @@ -41,7 +39,7 @@ error_t UADC_SetSampleRate(Unit *unit, uint32_t hertz) dbg("Failed to resolve timer params."); return E_BAD_VALUE; } - dbg("Frequency error %d ppm, presc %d, count %d", + adc_dbg("Frequency error %d ppm, presc %d, count %d", (int) lrintf(1000000.0f * ((priv->real_frequency - hertz) / (float) hertz)), (int) presc, (int) count); @@ -77,8 +75,10 @@ error_t UADC_init(Unit *unit) { // Claim and configure all analog pins priv->nb_channels = 0; - for (uint8_t i = 0; i < 16; i++) { - if (priv->channels & (1 << i)) { + for (uint8_t i = 0; i <= UADC_MAX_CHANNEL; i++) { + if (priv->cfg.channels & (1 << i)) { + priv->nb_channels++; + char c; uint8_t num; if (i <= 7) { @@ -89,9 +89,11 @@ error_t UADC_init(Unit *unit) c = 'B'; num = (uint8_t) (i - 8); } - else { + else if (i <= 15) { c = 'C'; num = (uint8_t) (i - 10); + } else { + break; } TRY(rsc_claim_pin(unit, c, num)); @@ -101,18 +103,16 @@ error_t UADC_init(Unit *unit) LL_GPIO_SetPinPull(port, ll_pin, LL_GPIO_PULL_NO); LL_GPIO_SetPinMode(port, ll_pin, LL_GPIO_MODE_ANALOG); - priv->nb_channels++; } } - if (priv->enable_tsense) priv->nb_channels++; - if (priv->enable_vref) priv->nb_channels++; if (priv->nb_channels == 0) { - dbg("!! Need at least 1 channel"); + dbg("Need at least 1 channel"); return E_BAD_CONFIG; } - if (priv->buffer_size < priv->nb_channels*2*2) { + // ensure some minimal space is available + if (priv->cfg.buffer_size < priv->nb_channels * 2) { dbg("Insufficient buf size"); return E_BAD_CONFIG; } @@ -127,22 +127,10 @@ error_t UADC_init(Unit *unit) } // ------------------- CONFIGURE THE TIMER -------------------------- - dbg("Setting up TIMER"); + adc_dbg("Setting up TIMER"); { - TRY(UADC_SetSampleRate(unit, priv->frequency)); -// // Find suitable timer values -// uint16_t presc; -// uint32_t count; -// float real_freq; -// if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count, &real_freq)) { -// dbg("Failed to resolve timer params."); -// return E_BAD_VALUE; -// } -// dbg("Frequency error %d ppm, presc %d, count %d", -// (int) lrintf(1000000.0f * ((real_freq - priv->frequency) / (float)priv->frequency)), (int) presc, (int) count); -// -// LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1)); -// LL_TIM_SetAutoReload(priv->TIMx, count - 1); + TRY(UADC_SetSampleRate(unit, priv->cfg.frequency)); + LL_TIM_EnableARRPreload(priv->TIMx); LL_TIM_EnableUpdateEvent(priv->TIMx); LL_TIM_SetTriggerOutput(priv->TIMx, LL_TIM_TRGO_UPDATE); @@ -150,53 +138,46 @@ error_t UADC_init(Unit *unit) } // --------------------- CONFIGURE THE ADC --------------------------- - dbg("Setting up ADC"); + adc_dbg("Setting up ADC"); { // Calibrate the ADC - dbg("Wait for calib"); + adc_dbg("Wait for calib"); LL_ADC_StartCalibration(priv->ADCx); while (LL_ADC_IsCalibrationOnGoing(priv->ADCx)) {} - dbg("ADC calibrated."); + adc_dbg("ADC calibrated."); - { - uint32_t mask = 0; - if (priv->enable_vref) mask |= LL_ADC_PATH_INTERNAL_VREFINT; - if (priv->enable_tsense) mask |= LL_ADC_PATH_INTERNAL_TEMPSENSOR; - LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, mask); - } + // Let's just enable the internal channels always - makes toggling them on-line easier + LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, LL_ADC_PATH_INTERNAL_VREFINT | LL_ADC_PATH_INTERNAL_TEMPSENSOR); LL_ADC_SetDataAlignment(priv->ADCx, LL_ADC_DATA_ALIGN_RIGHT); LL_ADC_SetResolution(priv->ADCx, LL_ADC_RESOLUTION_12B); LL_ADC_REG_SetDMATransfer(priv->ADCx, LL_ADC_REG_DMA_TRANSFER_UNLIMITED); // configure channels - priv->extended_channels_mask = priv->channels; - if (priv->enable_tsense) priv->extended_channels_mask |= (1<<16); - if (priv->enable_vref) priv->extended_channels_mask |= (1<<17); + priv->channels_mask = priv->cfg.channels; - priv->ADCx->CHSELR = priv->extended_channels_mask; + priv->ADCx->CHSELR = priv->channels_mask; LL_ADC_REG_SetTriggerSource(priv->ADCx, LL_ADC_REG_TRIG_EXT_TIM15_TRGO); - LL_ADC_SetSamplingTimeCommonChannels(priv->ADCx, LL_ADC_SAMPLETIMES[priv->sample_time]); + LL_ADC_SetSamplingTimeCommonChannels(priv->ADCx, LL_ADC_SAMPLETIMES[priv->cfg.sample_time]); -// LL_ADC_Enable(priv->ADCx); + // will be enabled when switching to INIT mode } // --------------------- CONFIGURE DMA ------------------------------- - dbg("Setting up DMA"); + adc_dbg("Setting up DMA"); { - // The length must be a 2*multiple of the number of channels, in bytes - uint16_t itemcount = (uint16_t) ((priv->nb_channels) * (uint16_t) (priv->buffer_size / (2 * priv->nb_channels))); - if (itemcount % 2 == 1) itemcount -= priv->nb_channels; - priv->dma_buffer_itemcount = itemcount; - - dbg("DMA item count is %d (%d bytes), There are %d 2-byte samples per group.", - priv->dma_buffer_itemcount, - priv->dma_buffer_itemcount*sizeof(uint16_t), + uint32_t itemcount = priv->nb_channels * (priv->cfg.buffer_size / (priv->nb_channels)); + if (itemcount % 2 == 1) itemcount -= priv->nb_channels; // ensure the count is even + priv->buf_itemcount = itemcount; + + adc_dbg("DMA item count is %d (%d bytes), There are %d samples per group.", + priv->buf_itemcount, + priv->buf_itemcount * sizeof(uint16_t), priv->nb_channels); - priv->dma_buffer = calloc_ck(priv->dma_buffer_itemcount, sizeof(uint16_t)); + priv->dma_buffer = calloc_ck(priv->buf_itemcount, sizeof(uint16_t)); if (NULL == priv->dma_buffer) return E_OUT_OF_MEM; assert_param(((uint32_t) priv->dma_buffer & 3) == 0); // must be aligned @@ -217,28 +198,23 @@ error_t UADC_init(Unit *unit) init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; assert_param(SUCCESS == LL_DMA_Init(priv->DMAx, priv->dma_chnum, &init)); - - // Interrupt on transfer 1/2 and complete - // We will capture the first and second half and send it while the other half is being filled. -// LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum); -// LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); } LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum); } // prepare the avg factor float for the ISR - if (priv->averaging_factor > 1000) priv->averaging_factor = 1000; // normalize - priv->avg_factor_as_float = priv->averaging_factor/1000.0f; + if (priv->cfg.averaging_factor > 1000) priv->cfg.averaging_factor = 1000; // normalize + priv->avg_factor_as_float = priv->cfg.averaging_factor/1000.0f; - dbg("ADC peripherals configured."); + adc_dbg("ADC peripherals configured."); irqd_attach(priv->DMA_CHx, UADC_DMA_Handler, unit); irqd_attach(priv->ADCx, UADC_ADC_EOS_Handler, unit); - dbg("irqs attached"); + adc_dbg("irqs attached"); UADC_SwitchMode(unit, ADC_OPMODE_IDLE); - dbg("ADC done"); + adc_dbg("ADC done"); return E_SUCCESS; } diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index 12dcdab..88efcee 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -11,8 +11,13 @@ #include "unit_base.h" +//#define adc_dbg(...) dbg(##__VA_ARGS__) +#define adc_dbg(...) do {} while(0) + #define UADC_MAX_FREQ_FOR_AVERAGING 20000 +#define UADC_MAX_CHANNEL 17 + enum uadc_opmode { ADC_OPMODE_UNINIT, //!< Not yet switched to any mode ADC_OPMODE_IDLE, //!< Idle. Allows immediate value readout and averaging. @@ -33,41 +38,46 @@ enum uadc_event { /** Private data structure */ struct priv { - // settings - uint16_t channels; //!< bit flags (will be recorded in order 0-15) - bool enable_tsense; //!< append a signal from the temperature channel (voltage proportional to Tj) - bool enable_vref; //!< append a signal from the internal voltage reference - uint8_t sample_time; //!< 0-7 (corresponds to 1.5-239.5 cycles) - time for the sampling capacitor to charge - uint32_t frequency; //!< Timer frequency in Hz. Note: not all frequencies can be achieved accurately - uint16_t buffer_size; //!< Buffer size in bytes (count 2 bytes per channel per measurement) - faster sampling freq needs bigger buffer - uint16_t averaging_factor; //!< Exponential averaging factor 0-1000 - - // internal state - float real_frequency; - uint32_t real_frequency_int; - uint32_t extended_channels_mask; //!< channels bitfield including tsense and vref - float avg_factor_as_float; + struct { + uint32_t channels; //!< bit flags (will be recorded in order 0-15) + uint8_t sample_time; //!< 0-7 (corresponds to 1.5-239.5 cycles) - time for the sampling capacitor to charge + uint32_t frequency; //!< Timer frequency in Hz. Note: not all frequencies can be achieved accurately + uint32_t buffer_size; //!< Buffer size in bytes (count 2 bytes per channel per measurement) - faster sampling freq needs bigger buffer + uint16_t averaging_factor; //!< Exponential averaging factor 0-1000 + } cfg; + + // Peripherals ADC_TypeDef *ADCx; //!< The ADC peripheral used ADC_Common_TypeDef *ADCx_Common; //!< The ADC common control block TIM_TypeDef *TIMx; //!< ADC timing timer instance DMA_TypeDef *DMAx; //!< DMA isnatnce used uint8_t dma_chnum; //!< DMA channel number DMA_Channel_TypeDef *DMA_CHx; //!< DMA channel instance - uint16_t *dma_buffer; //!< malloc'd buffer for the samples - uint8_t nb_channels; //!< nbr of enabled adc channels - uint16_t dma_buffer_itemcount; //!< real size of the buffer in samples (adjusted to fit 2x whole multiple of sample group) + // Live config + float real_frequency; + uint32_t real_frequency_int; + uint32_t channels_mask; //!< channels bitfield including tsense and vref + float avg_factor_as_float; + + uint16_t *dma_buffer; //!< malloc'd buffer for the samples + uint8_t nb_channels; //!< nbr of enabled adc channels + uint32_t buf_itemcount; //!< real size of the buffer in samples (adjusted to fit 2x whole multiple of sample group) + + // Trigger state uint32_t trig_stream_remain; //!< Counter of samples remaining to be sent in the post-trigger stream uint16_t trig_holdoff_remain; //!< Tmp counter for the currently active hold-off uint16_t trig_prev_level; //!< Value of the previous sample, used to detect trigger edge - uint16_t stream_startpos; //!< Byte offset in the DMA buffer where the next capture for a stream should start. + uint32_t stream_startpos; //!< Byte offset in the DMA buffer where the next capture for a stream should start. //!< Updated in TH/TC and on trigger (after the preceding data is sent as a pretrig buffer) + enum uadc_opmode opmode; //!< OpMode (state machine state) float averaging_bins[18]; //!< Averaging buffers, enough space to accommodate all channels (16 external + 2 internal) uint16_t last_samples[18]; //!< If averaging is disabled, the last captured sample is stored here. + // Trigger config uint8_t trigger_source; //!< number of the pin selected as a trigger source - uint16_t pretrig_len; //!< Pre-trigger length, nbr of historical samples to report when trigger occurs + uint32_t pretrig_len; //!< Pre-trigger length, nbr of historical samples to report when trigger occurs uint32_t trig_len; //!< Trigger length, nbr of samples to report AFTER a trigger occurs uint16_t trig_level; //!< Triggering level in LSB uint8_t trig_edge; //!< Which edge we want to trigger on. 1-rising, 2-falling, 3-both diff --git a/units/adc/_adc_settings.c b/units/adc/_adc_settings.c index e723c19..3ab8f3e 100644 --- a/units/adc/_adc_settings.c +++ b/units/adc/_adc_settings.c @@ -16,18 +16,11 @@ void UADC_loadBinary(Unit *unit, PayloadParser *pp) uint8_t version = pp_u8(pp); (void)version; - priv->channels = pp_u16(pp); - priv->enable_tsense = pp_bool(pp); - priv->enable_vref = pp_bool(pp); - priv->sample_time = pp_u8(pp); - priv->frequency = pp_u32(pp); - - if (version >= 1) { - priv->buffer_size = pp_u16(pp); - } - if (version >= 2) { - priv->averaging_factor = pp_u16(pp); - } + priv->cfg.channels = pp_u32(pp); + priv->cfg.sample_time = pp_u8(pp); + priv->cfg.frequency = pp_u32(pp); + priv->cfg.buffer_size = pp_u32(pp); + priv->cfg.averaging_factor = pp_u16(pp); } /** Write to a binary buffer for storing in Flash */ @@ -35,15 +28,13 @@ void UADC_writeBinary(Unit *unit, PayloadBuilder *pb) { struct priv *priv = unit->data; - pb_u8(pb, 2); // version + pb_u8(pb, 0); // version - pb_u16(pb, priv->channels); - pb_bool(pb, priv->enable_tsense); - pb_bool(pb, priv->enable_vref); - pb_u8(pb, priv->sample_time); - pb_u32(pb, priv->frequency); - pb_u16(pb, priv->buffer_size); - pb_u16(pb, priv->averaging_factor); + pb_u32(pb, priv->cfg.channels); + pb_u8(pb, priv->cfg.sample_time); + pb_u32(pb, priv->cfg.frequency); + pb_u32(pb, priv->cfg.buffer_size); + pb_u16(pb, priv->cfg.averaging_factor); } // ------------------------------------------------------------------------ @@ -55,27 +46,21 @@ error_t UADC_loadIni(Unit *unit, const char *key, const char *value) struct priv *priv = unit->data; if (streq(key, "channels")) { - priv->channels = parse_pinmask(value, &suc); - } - else if (streq(key, "enable_tsense")) { - priv->enable_tsense = str_parse_yn(value, &suc); - } - else if (streq(key, "enable_vref")) { - priv->enable_vref = str_parse_yn(value, &suc); + priv->cfg.channels = parse_pinmask(value, &suc); } else if (streq(key, "sample_time")) { - priv->sample_time = (uint8_t) avr_atoi(value); - if (priv->sample_time > 7) return E_BAD_VALUE; + priv->cfg.sample_time = (uint8_t) avr_atoi(value); + if (priv->cfg.sample_time > 7) return E_BAD_VALUE; } else if (streq(key, "frequency")) { - priv->frequency = (uint32_t) avr_atoi(value); + priv->cfg.frequency = (uint32_t) avr_atoi(value); } else if (streq(key, "buffer_size")) { - priv->buffer_size = (uint16_t) avr_atoi(value); + priv->cfg.buffer_size = (uint32_t) avr_atoi(value); } else if (streq(key, "avg_factor")) { - priv->averaging_factor = (uint16_t) avr_atoi(value); - if (priv->averaging_factor > 1000) return E_BAD_VALUE; + priv->cfg.averaging_factor = (uint16_t) avr_atoi(value); + if (priv->cfg.averaging_factor > 1000) return E_BAD_VALUE; } else { return E_BAD_KEY; @@ -91,32 +76,29 @@ void UADC_writeIni(Unit *unit, IniWriter *iw) struct priv *priv = unit->data; iw_comment(iw, "Enabled channels, comma separated"); - iw_comment(iw, "0-7 = A0-A7, 8-9 = B0-B1, 10-15 = C0-C5"); - iw_entry(iw, "channels", "%s", pinmask2str_up(priv->channels, unit_tmp512)); - - iw_comment(iw, "Enable Tsense channel (#16)"); - iw_entry(iw, "enable_tsense", str_yn(priv->enable_tsense)); - - iw_comment(iw, "Enable Vref channel (#17)"); - iw_entry(iw, "enable_vref", str_yn(priv->enable_vref)); + iw_comment(iw, " 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17"); + iw_comment(iw, "A0 A1 A2 A3 A4 A5 A6 A7 B0 B1 C0 C1 C2 C3 C4 C5 Tsens Vref"); + iw_entry(iw, "channels", "%s", pinmask2str_up(priv->cfg.channels, unit_tmp512)); iw_cmt_newline(iw); iw_comment(iw, "Sampling time (0-7)"); - iw_entry(iw, "sample_time", "%d", (int)priv->sample_time); + iw_entry(iw, "sample_time", "%d", (int)priv->cfg.sample_time); iw_comment(iw, "Sampling frequency (Hz)"); - iw_entry(iw, "frequency", "%d", (int)priv->frequency); + iw_entry(iw, "frequency", "%d", (int)priv->cfg.frequency); - iw_comment(iw, "Sample buffer size (bytes, 2 per channels per sample)"); - iw_comment(iw, "- a report is sent when 1/2 of the circular buffer is filled"); - iw_comment(iw, "- the buffer is shared by all channels"); - iw_comment(iw, "- insufficient buffer size can lead to data loss"); - iw_entry(iw, "buffer_size", "%d", (int)priv->buffer_size); + iw_cmt_newline(iw); + iw_comment(iw, "Sample buffer size"); + iw_comment(iw, "- shared by all enabled channels"); + iw_comment(iw, "- defines the maximum pre-trigger size (divide by # of channels)"); + iw_comment(iw, "- captured data is sent in half-buffer chunks"); + iw_comment(iw, "- buffer overrun aborts the data capture"); + iw_entry(iw, "buffer_size", "%d", (int)priv->cfg.buffer_size); iw_cmt_newline(iw); iw_comment(iw, "Exponential averaging coefficient (permil, range 0-1000 ~ 0.000-1.000)"); iw_comment(iw, "- used formula: y[t]=(1-k)*y[t-1]+k*u[t]"); - iw_comment(iw, "- available only for direct readout (i.e. not used in block capture)"); - iw_entry(iw, "avg_factor", "%d", priv->averaging_factor); + iw_comment(iw, "- not available when a capture is running"); + iw_entry(iw, "avg_factor", "%d", priv->cfg.averaging_factor); } diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index 715b7bc..e42b06d 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -10,7 +10,7 @@ // ------------------------------------------------------------------------ -enum TplCmd_ { +enum AdcCmd_ { CMD_READ_RAW = 0, CMD_READ_SMOOTHED = 1, @@ -44,7 +44,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P */ case CMD_GET_ENABLED_CHANNELS: for (uint8_t i = 0; i < 18; i++) { - if (priv->extended_channels_mask & (1 << i)) { + if (priv->channels_mask & (1 << i)) { pb_u8(&pb, i); } } @@ -91,7 +91,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P } for (uint8_t i = 0; i < 18; i++) { - if (priv->extended_channels_mask & (1 << i)) { + if (priv->channels_mask & (1 << i)) { pb_u16(&pb, priv->last_samples[i]); } } @@ -113,7 +113,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P } for (uint8_t i = 0; i < 18; i++) { - if (priv->extended_channels_mask & (1 << i)) { + if (priv->channels_mask & (1 << i)) { pb_float(&pb, priv->averaging_bins[i]); } } @@ -133,7 +133,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * u8(bool) - auto re-arm after firing and completing the capture */ case CMD_SETUP_TRIGGER: - dbg("> Setup trigger"); + adc_dbg("> Setup trigger"); if (priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED && priv->opmode != ADC_OPMODE_REARM_PENDING) { @@ -144,17 +144,17 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P const uint8_t source = pp_u8(pp); const uint16_t level = pp_u16(pp); const uint8_t edge = pp_u8(pp); - const uint16_t pretrig = pp_u16(pp); + const uint32_t pretrig = pp_u32(pp); const uint32_t count = pp_u32(pp); const uint16_t holdoff = pp_u16(pp); const bool auto_rearm = pp_bool(pp); - if (source > 17) { + if (source > UADC_MAX_CHANNEL) { com_respond_str(MSG_ERROR, frame_id, "Invalid trig source"); return E_FAILURE; } - if (0 == (priv->extended_channels_mask & (1 << source))) { + if (0 == (priv->channels_mask & (1 << source))) { com_respond_str(MSG_ERROR, frame_id, "Channel not enabled"); return E_FAILURE; } @@ -170,7 +170,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P } // XXX the max size may be too much - const uint16_t max_pretrig = (priv->dma_buffer_itemcount / priv->nb_channels); + const uint32_t max_pretrig = (priv->buf_itemcount / priv->nb_channels); if (pretrig > max_pretrig) { com_respond_snprintf(frame_id, MSG_ERROR, "Pretrig too large (max %d)", (int) max_pretrig); @@ -192,7 +192,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Arm (permissible only if idle and the trigger is configured) */ case CMD_ARM: - dbg("> Arm"); + adc_dbg("> Arm"); uint8_t sticky = pp_u8(pp); if(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_REARM_PENDING) { @@ -222,7 +222,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Switches to idle. */ case CMD_DISARM: - dbg("> Disarm"); + adc_dbg("> Disarm"); priv->auto_rearm = false; @@ -245,7 +245,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Abort any ongoing capture and dis-arm. */ case CMD_ABORT:; - dbg("> Abort capture"); + adc_dbg("> Abort capture"); TRY(UU_ADC_AbortCapture(unit)); return E_SUCCESS; @@ -254,7 +254,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * The reported edge will be 0b11, here meaning "manual trigger" */ case CMD_FORCE_TRIGGER: - dbg("> Force trigger"); + adc_dbg("> Force trigger"); // This is similar to block capture, but includes the pre-trig buffer and has fixed size based on trigger config // FORCE is useful for checking if the trigger is set up correctly if (priv->opmode != ADC_OPMODE_ARMED && @@ -276,7 +276,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * u32 - sample count (for each channel) */ case CMD_BLOCK_CAPTURE: - dbg("> Block cpt"); + adc_dbg("> Block cpt"); if (priv->opmode != ADC_OPMODE_ARMED && priv->opmode != ADC_OPMODE_REARM_PENDING && priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; @@ -291,7 +291,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * The stream can be terminated by the stop command. */ case CMD_STREAM_START: - dbg("> Stream ON"); + adc_dbg("> Stream ON"); if (priv->opmode != ADC_OPMODE_ARMED && priv->opmode != ADC_OPMODE_REARM_PENDING && priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; @@ -303,7 +303,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Stop a stream. */ case CMD_STREAM_STOP: - dbg("> Stream OFF"); + adc_dbg("> Stream OFF"); if (priv->opmode != ADC_OPMODE_STREAM) { com_respond_str(MSG_ERROR, frame_id, "Not streaming"); return E_FAILURE;