SAML21E17B Not able to read two ADC channels

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Hi all.

 

I have a board with two batteries to read by the ADC module. One is connected to PA02-AIN0 and the other to PA07-AIN7.

 

In Atmel Start I make this configure:

 

 And in the code I am testing is the following:

static void adc_callback_general_battery(const struct adc_async_descriptor *const descr, const uint8_t channel)

{

    uint8_t bytes_read;

    uint8_t buf_adc[28]; //un buffer para poder leer 14 canales

    memset(buf_adc, 0x00, sizeof(buf_adc));

    bytes_read = adc_async_read_channel(&ADC_BATERIA, 0, (uint8_t *)&buf_adc, 28);

    

        

    adc_value_battery = (buf_adc[1] << 8) + buf_adc[0];

    adc_value_general_battery = (buf_adc[3] << 8) + buf_adc[2]; 

    

 

    general_battery_voltage_mv = adc_value_general_battery * VREF_V_2V5 * GENERAL_BAT_DIV_V / RESOLUTION_12_BIT;  

    battery_voltage_mv = adc_value_battery * VREF_V_2V5 * BAT_DIV_V / RESOLUTION_12_BIT;

    

   

    adc_gb_conv_ready = true;

}

 

void adc_init()

{

    SUPC->VREF.bit.SEL = 0x07;  //for 2.5V

    adc_async_register_callback(&ADC_BATERIA, 0, ADC_ASYNC_CONVERT_CB, adc_callback_general_battery);

    adc_async_enable_channel(&ADC_BATERIA, 0);    

}

 

void adc_read_general_battery_value()

{

    adc_gb_conv_ready = false;

    adc_async_start_conversion(&ADC_BATERIA);

}

int main(void){

    static uint32_t last_battery_time=0;

    uint32_t current_time;

    /* Initializes MCU, drivers and middleware */

    atmel_start_init();

    usart_init();

    systick_init();

    adc_init();

    

    while (1){

        current_time    = systick_get();

        if((current_time - last_battery_time) > 5000)

        {

            adc_read_general_battery_value();

            last_battery_time = current_time;

        }

    }

}

 

 

But in buf_adc[] I only have values for channel 0, i.e, I only obtain values in buf_adc[0] and buf_adc[1].

 

What I'm doing wrong?? I really need help. I have read lot of datasheets, technical notes, forums, ... and I don't find the answer.

 

Thanks for all.

This topic has a solution.
Last Edited: Thu. Sep 19, 2019 - 10:57 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


I have been checking more information. I tried to use the function that changes the values + and - del MUX_INPUT. And although I think this is de correct way I continue without results.

 

This is the new code:

 

static void adc_callback_general_battery(const struct adc_async_descriptor *const descr, const uint8_t channel)
{
    uint8_t bytes_read;
    uint8_t buf_adc[28]; //a buffer to be able to read 14 channels with 12bit resolution

//     memset(buf_adc, 0x00, sizeof(buf_adc));
//     bytes_read = adc_async_read_channel(&ADC_BATERIA, 0, (uint8_t *)&buf_adc, 28);
//     adc_value_battery = (buf_adc[1] << 8) + buf_adc[0];
//     adc_value_general_battery = (buf_adc[3] << 8) + buf_adc[2];
//     general_battery_voltage_mv = adc_value_general_battery * VREF_V_2V5 * GENERAL_BAT_DIV_V / RESOLUTION_12_BIT;  
//     battery_voltage_mv = adc_value_battery * VREF_V_2V5 * BAT_DIV_V / RESOLUTION_12_BIT;
    
    bytes_read = adc_async_read_channel(&ADC_BATERIA, 0, (uint8_t *)&adc_value_general_battery, 2);

    adc_gb_conv_ready = true;
}

void adc_read_general_battery_value(char p_n_channel)
{    
    adc_gb_conv_ready = false;

    if( p_n_channel == 0){
        //SUPC->VREF.bit.SEL = 0x02;    //for 1.2V
        adc_async_set_inputs(&ADC_BATERIA,0x00,0x18,0); //+ = AIN0, - = GROUND
    }else if( p_n_channel == 1){
        //SUPC->VREF.bit.SEL = 0x07;    //for 2.5V
        adc_async_set_inputs(&ADC_BATERIA,0x07,0x18,0); //+ = AIN7, - = GROUND
    }
    adc_async_start_conversion(&ADC_BATERIA);
    
    while(adc_gb_conv_ready == false);
    
    if( p_n_channel == 0){
        //battery_voltage_mv = adc_value_general_battery * VREF_V_1V2 * BAT_DIV_V / RESOLUTION_12_BIT;
        battery_voltage_mv = adc_value_general_battery * VREF_V_2V5 * BAT_DIV_V / RESOLUTION_12_BIT;
    }else if( p_n_channel == 1){
        general_battery_voltage_mv = adc_value_general_battery * VREF_V_2V5 * GENERAL_BAT_DIV_V / RESOLUTION_12_BIT;
    }    
}

void adc_init()
{
    SUPC->VREF.bit.SEL = 0x07;  //for 2.5V
    adc_async_register_callback(&ADC_BATERIA, 0, ADC_ASYNC_CONVERT_CB, adc_callback_general_battery);
    adc_async_enable_channel(&ADC_BATERIA, 0);
}

//**************************************************
int main(void){
    static uint32_t last_battery_time=0;
    uint32_t current_time;

    /* Initializes MCU, drivers and middleware */
    atmel_start_init();
    usart_init();
    systick_init();
    adc_init();    

    while (1){
        current_time    = systick_get();
        if((current_time - last_battery_time) > 5000)
        {
            adc_read_general_battery_value(0);
            adc_read_general_battery_value(1);

            last_battery_time = current_time;
        }
    }
}

 

 

If in the main loop I only use adc_read_general_battery_value(0); or adc_read_general_battery_value(1); I obtain the correct results. But debugging if I use both of them the code jump to Disassembly and void Dummy_Handler(void) function just in the  adc_read_general_battery_value(1); line

 

May it be that I have to put other order? Or may it be problem of timers?

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Referring to you initial code you have missed the advanced configuration "Bitmask for positive input sequence". This should be 0x81 (129) for your example. Note you will still get a callback for every completed conversion so something like this can be added in the callback

                if (ADC->SEQSTATUS.bit.SEQBUSY) {
                    return;
                }

Then you read out all results only when the sequence is done (2 values 4 bytes not 14 values in your case).

It's important that the ADC is converting slow enough (or the CPU runs fast enough) that there is time for the start code to read and put every result in a buffer. If you put a break point in the callback and notice the ADC INTFLAG has OVERRUN set then results are lost. One (useful) way of slowing down the ADC conversion time (other than just slowing down the ADC clock) is averaging and oversampling which is also available in the advanced configuration in start.

/Lars

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Hi Lajon!! million of thanks.

I knew that the  size of the buffer should be 2* N-channels, but just in case it was a fix buffer of 2*N-MAX-channels I declare 14 bytes to see channel 7. 

 

About your post: I had some problems at the beggining. When I started to read the 4 bytes (when I put in Atmel Start the bitmap mask and 16 sample) at first it seemed that first bytes refered to channel 7 and the other 2 to channel 0. And we started to reduce the frequency and we never achieved the correct values. And if we changed the value of one Analog input the read value of the other channel was changed too!!

 

At the end we selected a frequency of 500KHz and evaluated the first 2 bytes for channel 0 and the next 2 for channel 7 and with only 1 sample and it runs correctly. THANK YOU VERY MUCH!!laugh

 

static void adc_callback_general_battery(const struct adc_async_descriptor *const descr, const uint8_t channel)
{
    uint8_t bytes_read;
    uint8_t buf_adc[4]; //a buffer to be able to read 2 channels with 12bit resolution

// 	if(ADC->INTFLAG.bit.OVERRUN){
// 		uart_write_debug_string("overrun\r\n");
// 	}

	if (ADC->SEQSTATUS.bit.SEQBUSY) {
		return;
	}

    memset(buf_adc, 0x00, sizeof(buf_adc));
	bytes_read = adc_async_read_channel(&ADC_BATERIA, 0, (uint8_t *)&buf_adc, 4);

    adc_value_channel_0 = (buf_adc[1] << 8) + buf_adc[0];
	adc_value_channel_7 = (buf_adc[3] << 8) + buf_adc[2];

    battery_voltage_mv = adc_value_channel_0 * VREF_V_2V5 * BAT_DIV_V / RESOLUTION_12_BIT;
	general_battery_voltage_mv = adc_value_channel_7 * VREF_V_2V5 * GENERAL_BAT_DIV_V / RESOLUTION_12_BIT;	

    adc_gb_conv_ready = true;
}

void adc_read_general_battery_value(char p_n_channel)
{
    adc_gb_conv_ready = false;
    adc_async_start_conversion(&ADC_BATERIA);
    while(adc_gb_conv_ready == false);
}

void adc_init()
{
    SUPC->VREF.bit.SEL = 0x07;  //for 2.5V
    adc_async_register_callback(&ADC_BATERIA, 0, ADC_ASYNC_CONVERT_CB, adc_callback_general_battery);
    adc_async_enable_channel(&ADC_BATERIA, 0);
}

//**************************************************
int main(void){
    static uint32_t last_battery_time=0;
    uint32_t current_time;

    /* Initializes MCU, drivers and middleware */
    atmel_start_init();
    usart_init();
    systick_init();
    adc_init();    

    while (1){
        current_time    = systick_get();
        if((current_time - last_battery_time) > 5000)
        {
            adc_read_general_battery_value();
            last_battery_time = current_time;
        }
    }
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Only one more doubt. I understand that I have to read in the worst case of time and discriminate de values I don't want. And I understand that I have to select the same VREF for all, am I wrong?

 

I tried to change that VREF in the callback, analyzing the times I had entered to know in which channel I was and it broke indecision. Couldn't I use different VREF??

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Different VREF with sequence will not work, there is no time to change VREF (the ADC starts converting the next channel while you are still handling the interrupt of the previous).

/Lars