Using the ADC window compare function

I'm currently using the ADC in a pre/post trigger way similar to an oscilloscope. For triggering I currently use hardware based triggers that will generate an external interrupt.

Now I want to use the window compare function to skip the hardware triggers. It's supposed to generate an interrupt when one of the channels leaves the window.

I'm using the ADC0 channels 3,4,5 and 6 on S5D5 and SSP 1.7.8.

Here's my current non-working code:

void ADCDevice::enable_window_trigger()
{
    //Calculate the window around the average baseline of all channels
    uint16_t margin = 25;
    float fbaseline = 0.0f;
    for(auto& channel : adc_device.channels)
        fbaseline += channel.baseline;
    fbaseline /= adc_device.channels.size();
    uint16_t baseline = static_cast<uint16_t>(std::round(fbaseline));

    auto status = g_gpt0.p_api->stop(g_gpt0.p_ctrl);
    ASSERT_STATUS(status);

    status = g_adc0.p_api->scanStop(g_adc0.p_ctrl);
    ASSERT_STATUS(status);

    //--- Set the Window_A ------
    //Select a channel for Window_A Comparison
    do {
        R_S12ADC0->ADCMPANSR0_b.CMPCHA03 = 0x1;
    } while (!(R_S12ADC0->ADCMPANSR0_b.CMPCHA03));
    do {
        R_S12ADC0->ADCMPANSR0_b.CMPCHA04 = 0x1;
    } while (!(R_S12ADC0->ADCMPANSR0_b.CMPCHA04));
    do {
        R_S12ADC0->ADCMPANSR0_b.CMPCHA05 = 0x1;
    } while (!(R_S12ADC0->ADCMPANSR0_b.CMPCHA05));
    do {
        R_S12ADC0->ADCMPANSR0_b.CMPCHA06 = 0x1;
    } while (!(R_S12ADC0->ADCMPANSR0_b.CMPCHA06));

    //Set the Window_A Lower boundary
    do {
        R_S12ADC0->ADCMPDR0_b.ADCMPDR0 = static_cast<uint16_t>(max(0, baseline - margin));
    } while (R_S12ADC0->ADCMPDR0_b.ADCMPDR0 & ~static_cast<uint16_t>(max(0, baseline - margin)));

    //Set the Window_A Upper boundary
    do {
        R_S12ADC0->ADCMPDR1_b.ADCMPDR1 = static_cast<uint16_t>(min(baseline + margin, 4095));
    } while (R_S12ADC0->ADCMPDR1_b.ADCMPDR1 & ~static_cast<uint16_t>(min(baseline + margin, 4095)));

    //Set the Window_A comparison condition to trigger when outside the window boundary
    do {
        R_S12ADC0->ADCMPLR0_b.CMPLCHA03 = 0x0;
    } while (R_S12ADC0->ADCMPLR0_b.CMPLCHA03);
    do {
        R_S12ADC0->ADCMPLR0_b.CMPLCHA04 = 0x0;
    } while (R_S12ADC0->ADCMPLR0_b.CMPLCHA04);
    do {
        R_S12ADC0->ADCMPLR0_b.CMPLCHA05 = 0x0;
    } while (R_S12ADC0->ADCMPLR0_b.CMPLCHA05);
    do {
        R_S12ADC0->ADCMPLR0_b.CMPLCHA06 = 0x0;
    } while (R_S12ADC0->ADCMPLR0_b.CMPLCHA06);

    //Enable Window_A operation
    do {
        R_S12ADC0->ADCMPCR_b.CMPAE = 0x1;
    } while (!(R_S12ADC0->ADCMPCR_b.CMPAE));

    //Enable Window_A interrupt for meeting the condition, ADC120_CMPAI
    do {
        R_S12ADC0->ADCMPCR_b.CMPAIE = 0x1;
    } while (!(R_S12ADC0->ADCMPCR_b.CMPAIE));
    //Enable Window_A/B Comparison Function
    do {
        R_S12ADC0->ADCMPCR_b.WCMPE = 0x1;
    } while (!(R_S12ADC0->ADCMPCR_b.WCMPE));


    /* These variables are used to set the ISR in the ICU & NVIC */
    fmi_feature_info_t info = {0U};
    ssp_feature_t ssp_feature = {{(ssp_ip_t) 0}};
    fmi_event_info_t event_info = {(IRQn_Type) 0U};
    ssp_vector_info_t * p_vector_info;

    /* Enable the compare match interrupt for ADC0*/
    ssp_feature.channel = 0;
    ssp_feature.unit = 0U;
    ssp_feature.id = SSP_IP_ADC;

    g_fmi_on_fmi.productFeatureGet(&ssp_feature, &info);
    g_fmi_on_fmi.eventInfoGet(&ssp_feature, SSP_SIGNAL_ADC_COMPARE_MATCH, &event_info);
    R_SSP_VectorInfoGet(event_info.irq, &p_vector_info);
    NVIC_SetPriority(event_info.irq, 3);

    R_BSP_IrqStatusClear(event_info.irq);
    NVIC_ClearPendingIRQ(event_info.irq);
    NVIC_EnableIRQ(event_info.irq);

    status = g_adc0.p_api->scanStart(g_adc0.p_ctrl);
    ASSERT_STATUS(status);

    status = g_gpt0.p_api->start(g_gpt0.p_ctrl);
    ASSERT_STATUS(status);
}

SSP_VECTOR_DEFINE_CHAN(out_of_window_isr, ADC, COMPARE_MATCH, 0);
extern "C" void out_of_window_isr(void);
extern "C" void out_of_window_isr(void)
{
    /** Save context if RTOS is used */
    SF_CONTEXT_SAVE;

    /** Clear the BSP IRQ Flag     */
    R_BSP_IrqStatusClear (R_SSP_CurrentIrqGet());

    /** Application logic */

    /** Restore context if RTOS is used */
    SF_CONTEXT_RESTORE;
}

Using this code, the program stays in the loop where R_S12ADC0->ADCMPANSR0_b.CMPCHA03 = 0x1; is set. ADCDevice::enable_window_trigger() is called after the ADC is initialized and already running (scanStart has been called before). In the debugger I can see that CMPCHA03 is 0.

Any idea why this happens? Do I have an error somewhere?

Apart from that, the debugger doesn't show memory addresses for the ISR, indicating that the function is not included in the binary. Do I need to force this somehow?


I also have the requirement of being able to enable and disable this trigger and be able to disable the interrupt from occuring again during a measurement.

For disabling the window function, is this ok? Would it suffice to simply set R_S12ADC0->ADCMPCR_b.CMPAIE = 0x0; ?

void ADCDevice::disable_window_trigger()
{

    auto status = g_gpt0.p_api->stop(g_gpt0.p_ctrl);
    ASSERT_STATUS(status);

    status = g_adc0.p_api->scanStop(g_adc0.p_ctrl);
    ASSERT_STATUS(status);

    //Disable Window_A interrupt for meeting the condition, ADC120_CMPAI
    do {
        R_S12ADC0->ADCMPCR_b.CMPAIE = 0x0;
    } while (!(R_S12ADC0->ADCMPCR_b.CMPAIE));

    //Disable Window_A operation
    do {
        R_S12ADC0->ADCMPCR_b.CMPAE = 0x0;
    } while (R_S12ADC0->ADCMPCR_b.CMPAE);

    status = g_adc0.p_api->scanStart(g_adc0.p_ctrl);
    ASSERT_STATUS(status);

    status = g_gpt0.p_api->start(g_gpt0.p_ctrl);
    ASSERT_STATUS(status);
}

  • I just found out that when I'm doing

    R_S12ADC0->ADCMPANSR0 = 1 << 3 | 1 << 4 | 1 << 5 | 1 << 6;

    instead of

    R_S12ADC0->ADCMPANSR0_b.CMPCHA03 = 0x1;
    R_S12ADC0->ADCMPANSR0_b.CMPCHA04 = 0x1;
    R_S12ADC0->ADCMPANSR0_b.CMPCHA05 = 0x1;
    R_S12ADC0->ADCMPANSR0_b.CMPCHA06 = 0x1;

    the register appears to have the correct value set. Any ideas about this?

  • ChrisS

    Apart from that, the debugger doesn't show memory addresses for the ISR, indicating that the function is not included in the binary. Do I need to force this somehow?

    Part of this may be because of wrong linkage in the example above, as the SSP_VECTOR_DEFINE_CHAN macro also declares the function, but in this case without C linkage. I have tried putting all in an extern "C" environment and also tried removing extern "C" from the function definitions but the function is still not linked into the binary.

  • In reply to ChrisS:

    Hi ChrisS-
    Can you tell us what values you are getting after each write? Have you checked the macros generate the expected addresses?
  • In reply to WarrenM:

    Hi Warren,
    with R_S12ADC0->ADCMPANSR0_b.CMPCHA03 = 0x1; the register remained at 0 value, when setting R_S12ADC0->ADCMPANSR0 directly it worked as expected.

    According to e²Studio the macro expands to:
    void out_of_window_isr (void); \
    static void * gp_ctrl_ADC_0_COMPARE_MATCH; \
    const ssp_vector_t g_vector_ADC_0_COMPARE_MATCH \
    __attribute__ ((section(".vector." "ADC""_""0""_""COMPARE_MATCH"))) __attribute__ ((__used__))=out_of_window_isr; \
    const ssp_vector_info_t g_vector_info_ADC_0_COMPARE_MATCH \
    __attribute__ ((section(".vector_info.""ADC""_""0""_""COMPARE_MATCH"))) __attribute__ ((__used__))= \
    {.event_number=ELC_EVENT_ADC0_COMPARE_MATCH, \
    .ip_id = SSP_IP_ADC, .ip_channel=(0), .ip_unit=0U, \
    .ip_signal=SSP_SIGNAL_ADC_COMPARE_MATCH, .pp_ctrl = &gp_ctrl_ADC_0_COMPARE_MATCH};

    However, in the .o file where SSP_VECTOR_DEFINE_CHAN is used I cannot find the isr or the symbols generated by the macro (using readelf -a file.o).

    Anything else you want me to check?
  • In reply to ChrisS:

    Hello,
    I came across this issue when using the window function of the ADC.
    There is an issue in the bit access definition for this register.
    If you look in the file S7G2.h, you will see that the register access is defined as:

    union {
    __IO uint16_t ADCMPANSR0; /*!< A/D Compare Function Window A Channel Select Register 0 */

    struct {
    __IO uint16_t CMPCHA00 : 1; /*!< AN000 Select */
    __IO uint16_t CMPCHA01 : 1; /*!< AN001 Select */
    __IO uint16_t CMPCHA02 : 1; /*!< AN002 Select */
    __IO uint16_t CMPCHA03 : 1; /*!< AN003 Select */
    __IO uint16_t CMPCHA04 : 1; /*!< AN004 Select */
    __IO uint16_t CMPCHA05 : 1; /*!< AN005 Select */
    __IO uint16_t CMPCHA06 : 1; /*!< AN006 Select */
    } ADCMPANSR0_b; /*!< BitSize */
    };

    I'm not too sure how or why GCC mishandles this, but it results in the issue you are seeing.
    If you were to set the register access definition as
    union {
    __IO uint16_t ADCMPANSR0; /*!< A/D Compare Function Window A Channel Select Register 0 */

    struct {
    __IO uint16_t CMPCHA00 : 1; /*!< AN000 Select */
    __IO uint16_t CMPCHA01 : 1; /*!< AN001 Select */
    __IO uint16_t CMPCHA02 : 1; /*!< AN002 Select */
    __IO uint16_t CMPCHA03 : 1; /*!< AN003 Select */
    __IO uint16_t CMPCHA04 : 1; /*!< AN004 Select */
    __IO uint16_t CMPCHA05 : 1; /*!< AN005 Select */
    __IO uint16_t CMPCHA06 : 1; /*!< AN006 Select */
    __IO uint16_t : 9;
    } ADCMPANSR0_b; /*!< BitSize */
    };
    the addition of the :9 padding results in correct alignment and then the bit access will work.
    This alignment modification would have to be made to ADCMPANSR0, ADCMPANSR1, ADCMPLR0, ADCMPLR1, ADCMPSR0, ADCMPSR1. There may be others, but I have not checked.
    Note, that if you make changes to S7G2.h file then when you make the changes, save the file and set it as read only. Otherwise, the header file will be extracted from the pack file and the changes will be overwritten. Or, make the changes in the pack file itself.

    OR, simply use the Word access as you have been doing

    Regards,
    Richard
  • In reply to Richard:

    I think that bit width specifiers are not very well defined by the C standard and might be an implementation detail in some aspects.

    Any idea why the ISR is not getting linked?
    In the meantime I have also tried to place the SSP_VECTOR_DEFINE_CHAN macro in a C file and use extern "C" linkage for the ISR itself. Unfortunately this didn't succeed either.
  • In reply to ChrisS:

    I think I may have had an error somewhere. I tried a few things again and got some compiler errors when trying to use the macro in C++, because it uses non-trivial initializers for structs (with .member = value syntax) which are not supported in C++. I placed it in a C file again and now the ISR is getting linked.

    I haven't yet been able to get it to trigger, I will try again on Monday.
  • In reply to ChrisS:

    I got the ISR to work, I needed to use SSP_SIGNAL_ADC_WINDOW_A instead of SSP_SIGNAL_ADC_COMPARE_MATCH.
  • In reply to ChrisS:

    Hi ChrisS-
    Thanx for letting us know!

    Warren
  • In reply to WarrenM:

    Sure, no problem.
    May I suggest adapting the SSP_VECTOR_DEFINE_CHAN macro to support C++?
  • In reply to ChrisS:

    Hi ChrisS-
    I will pass this issue along to the development team.

    Thanx!