Author Topic: K64 ADC Results  (Read 5744 times)

Offline Phil

  • Jr. Member
  • **
  • Posts: 54
    • View Profile
K64 ADC Results
« on: May 03, 2021, 04:52:19 AM »
Mark,

I have been studying the uTasker ADC documentation, the forum, and ADC_Timers.c for quite some time. I think I understand the process fairly well. However, I am still unable to confidently get resultant data back from the ADC module as I would like.

I am trying to read the voltage on a variable potentiometer. 0-3.3V. Connected to SE14. I know the voltages are changing at the port. I am able to generate the interrupt depending upon the voltage. However, I am unable to see the actual line voltage as read from the ADC module.

Can you explain why I am unable to obtain a valid read from SE14 using the ADC module?

Step 1 - Initialization of ADC configuration:

    ADC_SETUP adc_setup;                        // ADC Setup Structure for Headphone Rotary Dial (POT)
    ADC_RESULTS adc_results;                     // ADC Raw Voltage Level of Headphone Rotary Dial (POT)
    #define AD_DA_BUFFER_LENGTH    (8 * 1024)                        // DMA buffer for 1s at 8k bytes/s
    static signed short sADC_buffer[AD_DA_BUFFER_LENGTH] = {0};      // 16 bit samples

    // ADC Setup for Headphone Rotary Dial (POT) Calibration
    adc_setup.int_type = ADC_INTERRUPT;                                  // identifier when configuring
    adc_setup.int_handler = 0;                                   // adc INT handler routine
    adc_setup.dma_int_priority = 3;                                      // priority of DMA interrupt the user wants to set
    adc_setup.dma_int_handler = 0;                                       // no interrupt so that free-running circular buffer is used (when ADC_FULL_BUFFER_DMA_AUTO_REPEAT is not defined)
    adc_setup.ucDmaChannel = 6;
    adc_setup.ptrADC_Buffer = sADC_buffer;                               // ADC sample buffer to be used
    adc_setup.ulADC_buffer_length = sizeof(sADC_buffer);                 // physical length of the buffer
    adc_setup.usDmaTriggerSource = 0;                                    // default trigger is the ADC conversion completion of the channel in question
    adc_setup.pga_gain = PGA_GAIN_OFF;                                   // {13} PGA gain can be specified for certain inputs
    adc_setup.int_priority = PRIORITY_ADC;                               // ADC interrupt priority
    adc_setup.int_adc_controller = 1;                                    // ADC controller 1
    adc_setup.int_adc_int_type = (ADC_SINGLE_SHOT_TRIGGER_INT);          // interrupt type
    adc_setup.int_adc_offset = 0;                                        // no offset
    adc_setup.int_adc_bit = ADC_SE14_SINGLE;                             // SE14 Pin
    adc_setup.int_adc_mode = (ADC_CALIBRATE | ADC_SELECT_INPUTS_A | ADC_CLOCK_BUS_DIV_2 | ADC_CLOCK_DIVIDE_8 | ADC_SAMPLE_ACTIVATE_LONG | ADC_CONFIGURE_ADC | ADC_REFERENCE_VREF | ADC_CONFIGURE_CHANNEL | ADC_SINGLE_ENDED_INPUT | ADC_SINGLE_SHOT_MODE | ADC_12_BIT_MODE | ADC_SW_TRIGGERED);
    adc_setup.int_adc_sample = (ADC_SAMPLE_LONG_PLUS_12 | ADC_SAMPLE_AVERAGING_32); // additional sampling clocks
    adc_setup.int_adc_result = 0;                                        // no result is requested at this stage
    fnConfigureInterrupt((void *)&adc_setup);   // Perform Configure and Calibration of ADC Channel

Step 2 - Setup the interrupt for the ADC:  (just triggering on any voltage below 1.0V, which seems to trigger properly)

    adc_setup.int_adc_controller = 1;                                    // ADC controller 1
    adc_setup.int_adc_bit = ADC_SE14_SINGLE;                             
    adc_setup.int_adc_int_type = (ADC_LOW_LIMIT_INT);                    // interrupt type (trigger only when lower than the defined level)
    adc_setup.int_low_level_trigger = (unsigned short)(ADC_VOLT * 1.0);  // the low level trigger threshold represented as input voltage (note: setting low threshold higher than the high threshold causes a trigger inside the range rather than outside of it)
    adc_setup.int_handler = GetRotaryPos;                                   // handling function
    adc_setup.int_adc_result = &adc_results;                               // I want to know the current value
    adc_setup.int_adc_mode = (ADC_SELECT_INPUTS_A | ADC_CLOCK_BUS_DIV_2 | ADC_CLOCK_DIVIDE_8 | ADC_SAMPLE_ACTIVATE_LONG | ADC_CONFIGURE_ADC | ADC_REFERENCE_VREF | ADC_CONFIGURE_CHANNEL | ADC_SINGLE_ENDED_INPUT | ADC_SINGLE_SHOT_MODE | ADC_12_BIT_MODE | ADC_SW_TRIGGERED); // note that the first configuration should calibrate the ADC - single shot with interrupt on completion {12}
    fnConfigureInterrupt((void *)&adc_setup);                            // start operation now

Step 3 - Evaluate the results of the analog line and setup the ADC for subsequent interrupts:

void GetRotaryPos(void)            // Service Routine for ADC Rotary Dial Read
{
    signed short rotary_raw;                     // value of SE14 pin voltage

    // ucADC_status[0] and [1] are always not valid (0)
    if (adc_results.ucADC_status[0] == ADC_RESULT_VALID)           // if the conversion is ready
        rotary_raw = adc_results.sADC_value[0];
    else
       rotary_raw = 0;

    // Relaunch ADC Interrupt for Headphone Rotary Dial (POT) - Get Value of Rotary Dial Position (voltage level)
    // ...

    return;
}

Result:  rotary_raw is always 0.  Goal: rotary_raw should be between 0.0 and 3.3.  In this interrupt example above, rotary_raw should be between 0.0 and 1.0.

Any suggestions?

Regards,

Phil

 

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3238
    • View Profile
    • uTasker
Re: K64 ADC Results
« Reply #1 on: May 03, 2021, 09:10:10 AM »
Hi Phil

I don't see that you are calling fnConfigureInterrupt() with adc_setup.int_adc_mode = (ADC_READ_ONLY); [or similar] to load the result.


Eg.
Code: [Select]
        ADC_RESULTS adc_results;
        ADC_SETUP adc_setup;                                             // interrupt configuration parameters
        adc_setup.int_type = ADC_INTERRUPT;                              // identifier
        adc_setup.int_adc_mode = (ADC_READ_ONLY | ADC_CHECK_CONVERSION); // check whether the conversion has completed and don't wait for it to become ready
        adc_setup.int_adc_controller = 0;
        adc_setup.int_adc_result = &adc_results;
        fnConfigureInterrupt((void *)&adc_setup);
        if (adc_results.ucADC_status[0] == ADC_RESULT_VALID) {           // if the conversion is ready


or


Code: [Select]
                    adc_setup.int_adc_mode = (ADC_START_OPERATION | ADC_GET_RESULT);// start operation now and return the present result
                    fnConfigureInterrupt((void *)&adc_setup);            // enter interrupt for low level trigger
                    fnDebugHex(results.sADC_value[adc_setup.int_adc_bit], (WITH_SPACE | WITH_LEADIN | WITH_CR_LF | 2)); // print out the result


If the limit interrupts are working the ADC will be in free-running mode and always have a result read to read but you need to use the call to collect the result in the adc_resuts struct.


Regards

Mark




Offline Phil

  • Jr. Member
  • **
  • Posts: 54
    • View Profile
Re: K64 ADC Results
« Reply #2 on: May 03, 2021, 04:06:00 PM »
Mark,

In the last two statements of both examples below, the fnConfigureInterrupt sets an interrupt which will be handled by my handling routine (e.g. GetRotaryPos in my example). So, how is the second statement below the fnConfigureInterrupt going to be valid if they interrupt has not been tripped?  Are these fnConfigureInterrupt only for internal processes and not generating a change in the application/program pointer?  And, in both cases, shouldn't the checking of ucADC_status be in a loop waiting for the data to become valid?

Regards,

Phil

Offline Phil

  • Jr. Member
  • **
  • Posts: 54
    • View Profile
Re: K64 ADC Results
« Reply #3 on: May 04, 2021, 01:02:32 AM »
Mark,

No joy.

ADC_CHECK_CONVERSION and ADC_START_OPERATION are not defined in my build. Maybe I have an older version of uTasker.

The following code is inside the handler.  When run (after the interrupt), this routine gets into an infinite loop waiting for the conversion.

Code: [Select]
    adc_setup.int_adc_mode = (ADC_GET_RESULT);
    fnConfigureInterrupt((void *)&adc_setup);                            // start operation now
    fnDebugMsg("\n\r\n\rResults: ");
    fnDebugHex(adc_results.sADC_value[adc_setup.int_adc_bit], (WITH_SPACE | WITH_LEADIN | WITH_CR_LF | 2)); // print out the result

and when I modify it for READ_ONLY as shown below, the routine always returns zeros for ADC return values.  Doesn't lock up but returns no values.

Code: [Select]
    adc_setup.int_adc_mode = (ADC_READ_ONLY);
    fnConfigureInterrupt((void *)&adc_setup);                            // start operation now
    fnDebugMsg("\n\r\n\rResults: ");
    fnDebugHex(adc_results.sADC_value[adc_setup.int_adc_bit], (WITH_SPACE | WITH_LEADIN | WITH_CR_LF | 2)); // print out the result

Like I said, I am able to generate interrupts but there are no results, likely because the conversion is not happening for some reason.

Regards,

Phil

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3238
    • View Profile
    • uTasker
Re: K64 ADC Results
« Reply #4 on: May 04, 2021, 03:57:31 PM »
Hi Phil

If you can generate interrupts the ADC will be running and also have results available. You can also directly access these without going through the cover function:

result = ADC0_RA; // assuming ADC0

The option to poll the ADC (rather than use it in interrupt driven mode) was added in 2018 so your version must be earlier than that. I have attached the latest file as reference.

If the ADC is free-running there is no need to wait for conversion since it will always have a valid value in its output register so I don't know that it makes sense to use the new polling option anyway.
Try just reading the HW register (see above) directly.

Good luck

Regards

Mark