GEX core repository.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
gex-core/units/adc/_adc_core.c

271 lines
8.9 KiB

//
// Created by MightyPork on 2018/02/04.
//
#include "platform.h"
#include "unit_base.h"
#define ADC_INTERNAL
#include "_adc_internal.h"
void UADC_DMA_Handler(void *arg)
{
Unit *unit = arg;
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
const uint32_t isrsnapshot = priv->DMAx->ISR;
if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_chnum)) {
const bool tc = LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_chnum);
const bool ht = LL_DMA_IsActiveFlag_HT(isrsnapshot, priv->dma_chnum);
const bool te = LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_chnum);
// check what mode we're in
const bool m_trig = priv->opmode == ADC_OPMODE_TRIGD;
const bool m_stream = priv->opmode == ADC_OPMODE_STREAM;
const bool m_fixcpt = priv->opmode == ADC_OPMODE_FIXCAPT;
if (m_trig || 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);
}
assert_param(start < end);
uint32_t sgcount = (end - start) / priv->nb_channels;
if (m_trig || m_fixcpt) {
sgcount = MIN(priv->trig_stream_remain, sgcount);
priv->trig_stream_remain -= sgcount;
}
dbg("Would send %d groups (u16 offset %d -> %d)", (int)sgcount, (int)start, (int)(start+sgcount*priv->nb_channels));
// TODO send the data together with remaining count (used to detect end of transmission)
if (m_trig || m_fixcpt) {
if (priv->trig_stream_remain == 0) {
dbg("End of capture");
UADC_SwitchMode(unit, (priv->auto_rearm && m_trig) ? ADC_OPMODE_ARMED : ADC_OPMODE_IDLE);
}
}
if (end == priv->dma_buffer_itemcount) {
priv->stream_startpos = 0;
}
else {
priv->stream_startpos = end;
}
}
} else {
// This shouldn't happen, the interrupt should be disabled in this opmode
dbg("(!) not streaming, DMA IT should be disabled");
if (ht) {
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
}
else {
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
}
}
if (te) {
// this shouldn't happen - error
dbg("ADC DMA TE!");
LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum);
}
}
}
void UADC_ADC_EOS_Handler(void *arg)
{
uint64_t timestamp = PTIM_GetMicrotime();
Unit *unit = arg;
dbg("ADC EOS ISR hit");
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
// Wait for the DMA to complete copying the last sample
while (priv->DMA_CHx->CNDTR % priv->nb_channels != 0);
uint32_t sample_pos;
if (priv->DMA_CHx->CNDTR == 0) {
sample_pos = (uint32_t) (priv->dma_buffer_itemcount - 1);
} else {
sample_pos = priv->DMA_CHx->CNDTR;
}
sample_pos -= priv->nb_channels;
dbg("Sample pos %d", (int)sample_pos);
for (uint32_t i = 0; i < 18; i++) {
if (priv->extended_channels_mask & (1 << i)) {
uint16_t val = priv->dma_buffer[sample_pos];
dbg("Trig line level %d", (int)val);
if (priv->enable_averaging) {
priv->averaging_bins[i] =
priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) +
((float) val) * priv->avg_factor_as_float;
} else {
priv->last_sample[i] = val;
}
if (priv->opmode == ADC_OPMODE_ARMED) {
if (i == priv->trigger_source) {
bool trigd = false;
bool rising = false;
if (priv->trig_prev_level < priv->trig_level && val >= priv->trig_level) {
dbg("******** Rising edge");
// Rising edge
trigd = (bool) (priv->trig_edge & 0b01);
rising = true;
}
else if (priv->trig_prev_level > priv->trig_level && val <= priv->trig_level) {
dbg("******** Falling edge");
// Falling edge
trigd = (bool) (priv->trig_edge & 0b10);
}
if (trigd) {
UADC_HandleTrigger(unit, rising, timestamp);
}
priv->trig_prev_level = val;
}
}
}
}
dbg(" EOS ISR end.");
}
void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp)
{
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
if (priv->trig_holdoff != 0 && priv->trig_holdoff_remain > 0) {
dbg("Trig discarded due to holdoff.");
return;
}
if (priv->trig_holdoff > 0) {
priv->trig_holdoff_remain = priv->trig_holdoff;
// Start the tick
unit->tick_interval = 1;
unit->_tick_cnt = 0;
}
dbg("Trigger condition hit, rising=%d", rising);
// TODO Send pre-trigger
priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR;
priv->trig_stream_remain = priv->trig_len;
UADC_SwitchMode(unit, ADC_OPMODE_TRIGD);
}
void UADC_updateTick(Unit *unit)
{
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
if (priv->trig_holdoff_remain > 0) {
priv->trig_holdoff_remain--;
if (priv->trig_holdoff_remain == 0) {
unit->tick_interval = 0;
unit->_tick_cnt = 0;
}
}
}
void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode)
{
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
if (new_mode == priv->opmode) return; // nothing to do
// if un-itied, can go only to IDLE
assert_param((priv->opmode != ADC_OPMODE_UNINIT) || (new_mode == ADC_OPMODE_IDLE));
if (new_mode == ADC_OPMODE_UNINIT) {
dbg("ADC switch -> UNINIT");
// Stop the DMA, timer and disable ADC - this is called before tearing down the unit
LL_TIM_DisableCounter(priv->TIMx);
// Switch off the ADC
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);
}
else if (new_mode == ADC_OPMODE_IDLE) {
dbg("ADC switch -> IDLE");
// IDLE and ARMED are identical with the exception that the trigger condition is not checked
// In IDLE, we don't need the DMA interrupts
LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum);
LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum);
// Use End Of Sequence to recover results for averaging from the DMA buffer and DR
LL_ADC_EnableIT_EOS(priv->ADCx);
if (priv->opmode == 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);
LL_TIM_EnableCounter(priv->TIMx);
}
}
else if (new_mode == ADC_OPMODE_ARMED) {
dbg("ADC switch -> ARMED");
assert_param(priv->opmode == ADC_OPMODE_IDLE);
// there's nothing else to do here
}
else if (new_mode == ADC_OPMODE_TRIGD || new_mode == ADC_OPMODE_STREAM) {
dbg("ADC switch -> TRIG'D or STREAM");
assert_param(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_IDLE);
// during the capture, we disallow direct readout and averaging to reduce overhead
LL_ADC_DisableIT_EOS(priv->ADCx);
// Enable the DMA buffer interrupts
LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum);
LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum);
}
// the actual switch
priv->opmode = new_mode;
}