cancel
Showing results for 
Search instead for 
Did you mean: 

STM8 ADC does not use the whole conversion result buffer, despite the manual saying it does

TSadr
Associate

I want to use the single buffering mode of the ADC on a STM8S003.

As the reference manual describes, in this mode then 8 or 10 consecutive conversions (depending on the model) are carried out on a single channel. The results are stored in ADC buffer registers DB0R..DB9R then the End Of Conversion flag (EOC) is set, which can generate an interrupt. So it's somehow an efficient way to convert 10 values without stalling the CPU or firing many interrupts.


_legacyfs_online_stmicro_images_0693W00000FABQv.pngHowever, it seems that a conversion on a specific channel only fills the buffer registers with numbers greater than the chosen channel. For example, if we try to convert from channel 4, buffer registers 4..9 (6 buffers) get filled and the EOC fires. Buffers 0..3 will have a value of 0. Instead, if we choose channel 2, buffers 2..9 get filled.

I've connected the selected channel to VCC. I'm pretty sure that the conversion takes place correctly and all the buffers have the same value. Consecutive conversions don't also fill the remaining registers.

Converting channel 4, after firing EOC interrupt:


_legacyfs_online_stmicro_images_0693W00000FABSf.pngConverting channel 2, after firing EOC interrupt:


_legacyfs_online_stmicro_images_0693W00000FABT4.pngAnd here's the part of ADC code that manages a conversion:

void tadc_start_buffered(uint8_t tadc_channel, uint8_t batch_conversion_count)
{
    ADC1->CR1       &=~(ADC1_CR1_ADON);
    ADC1->CSR       = tadc_channel;
    if(batch_conversion_count)  tadc_conv_count = batch_conversion_count;
    else return;
    //disable the schmitt trigger
    if(tadc_channel < 😎    
    {
        ADC1->TDRH = 0;
        ADC1->TDRL = (1TDRH = (1TDRL = 0;
    }
    //enable buffering, reset OVF
    ADC1->CR3       = ADC1_CR3_DBUF;
    //clear the EOC
    ADC1->CSR       &=~(ADC1_CSR_EOC);
    //enable EOC interrupt
    ADC1->CSR       |= (ADC1_CSR_EOCIE);
    //enable continuous mode, turn the whole **** on
    ADC1->CR1       |= (ADC1_CR1_CONT | ADC1_CR1_ADON);
    nop();
    //reset status flags and conversion index
    tadc_status             = TADC_STAT_CONV_ONGOING;
    //reset accumulator
    tadc_accumulator    = 0;
    //start conversion and let it go
    ADC1->CR1       |= (ADC1_CR1_ADON);
}

Please notice that the selected mode is continuous on a single channel. It is not in scan mode, which would convert each channel and store it in the corresponding buffer reg.

Where could be the problem?

Thanx for the time and knowledge.

1 REPLY 1
TSadr
Associate

Well I found the problem. These are the erroneous steps I took earlier:

  1. Setup the ADC in single continuous mode
  2. Enable interrupt, clear EOC (end of conversion) flag
  3. Start Conversion and wait for EOC interrupt
  4. [in EOC ISR] disable the continuous mode bit so we have time to collect data before buffers gettting overwritten
  5. Re-enable the continuous mode bit and wait for the next interrupt

I edited the code and added a dummy batch conversion, see step 2:

  1. Setup the ADC in single continuous mode
  2. Start a dummy conversion, with the interrupt request being disabled, and wait for the EOC flag, without disabling continuous mode bit. it's just a dummy conversion.
  3. Enable interrupt, clear flag
  4. Start Conversion and wait for EOC interrupt
  5. [in EOC ISR] disable the continuous mode bit so we have time to collect data before buffers gettting overwritten
  6. Re-enable the continuous mode bit and wait for the next interrupt

Probable explanation:

It seems that in the beginning, the ADC result register pointer points to a buffer with the same channel number. that's why the channel 4 starts at buffer 4 and channel 2 starts at buffer 2. it is the default pointer value used for ADC scan mode, which each channel value is stored on the corresponding buffer with the same number.

It appears however, that this pointer gets updated after an EOC event, while the continuous mode bit being set. my first approach disables continuous mode bit as soon as EOC happens. so it never updates the pointer, even in the next iterations. in the second approach, a dummy conversion fires and clears EOC while the cont bit is set, so the pointer updates and points to the first buffer register and fills the whole buffer in the next conversion. it stays this way for next iterations on the same channel even with cont bit getting disabled temporarily.

This pointer-updating procedure should be applied once after switching ADC channel.