This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Clear HW Interrupt for ADC measurement

Other Parts Discussed in Thread: SYSBIOS, AM3359

Hello,

I am trying to use the ADC of the AM3359 on the ICE Board. Using HW interrupt (with sysbios) to get the ADC Value.

Getting the  ADC Interrupt End of sequence is working well. I think I also delete it (the Register IRQStatus & IRQStatus_Raw are == 0).

But the problem is, that even if this register are zero. SysBios continue to reenter in the HWI-Task (the with the Interrupt Register set to 0).

Do I need to clear something else? Why does it reenter in this HWI even if the register is ==0 

Best regards

  • Andreas,

    I haven't done any ADC programming on the AM335x yet but I had similar issues with other interrupts. As the ARM INTC interrupts are level triggered you really need to clear the interrupt in the source block too. Otherwise the HWI will be called many times (using the RTA tools does show that nicely).

    Regards.

  • ok, thank's for this fast answer.

    Can you tell me how i can do that ore give me a link to a document who explain me how to clear the interrupt in the source block?

    Regards 

  • Andreas,

    the key doc for this device is the Technical Reference Manual (SPRUH73). The ADCs are described in the section 12 (Touchscreen Controller!).

    Now there are a couple of regs related to IRQs and as I said I don't know details. But you may ask specific questions here again if it doesn't work for you.

    Alternatively I recommend to look at examples in Starterware. Although not developed for Sys/Bios this code usually shows basics of the operation.

    Regards.

  • In the Linux ADC driver, the ISR clears the corresponding bit in IRQSTATUS register, and set IRQEOI register (offset=20h) to 0 as while.

  • Frank,

    I used the Starterware example "adcVoltMeasure" as base for my code and used the SPRU73 Chap. 12 for more informations.

    As I can see, they use a function to "clear the IRQ" for that they set the Register 0x44E0D000 + 20h. But in the documentation in SPRUH73F this adress doesn't exist? See Page 1157.

    By the way I clear also the register  and 0x44E0D000 + 28h

  • Here is what I did to stop ADC interrupt, I cleared  STEPENABLE(offset 0x54) register, so there is no enabled steps anymore.

  • Andreas Weier said:

    As I can see, they use a function to "clear the IRQ" for that they set the Register 0x44E0D000 + 20h. But in the documentation in SPRUH73F this adress doesn't exist? See Page 1157.

    The info of this register is missing in TRM. It will be added in next version.

  • Bin,

    any chance we can give the info for that single reg here so that customers can continue?

    Thanks.

  • Andreas,

    Which revision of the SPRUH73 document are you using? The IRQ_EOI register at offset 0x20 is 'named' in revision C but with no info about it's usage. The register name I suppose could mean EndOfInterrupt. The description of its only meaningful bit (0 named LINE_NUMBER) is: "Write 0: EOI for interrupt Read is always 0". But what should it really mean???

    The really 'funny' thing is that in the latest revision (F) of the document, that register is never named!!! I know: it is easier to remove something than to add meaningful and useful details about it  and its usage. The point is that if you do not set it things won't work as expected! So the final question is: who is the genius?

    If it could help you, the kernel driver interrupt service routine after clearing the served interrupts in register 0x28, it also writes 0x0 into IRQ_EOI register: try to see if it make things working also in your case.

    I hope TI will clarify TRM changes.

    Regards, Max

  • At the end of my ADC interrupt handler, I did not write 0x0 into IRQ_EOI, still the interrupt handler works. Given the fact they did not even mention the register in the manual, I tends to believe it will not make any difference, at least on am335x.

    Or maybe I am wrong, right now I am pretty confused.

    Think about it, I can write my own interrupt handler, but I do not have correct/thorough info, heaven bless me!

  • Guoqiang, your guessing is correct. The response from the TRM owner is that IRQ_EOI is NOT required for AM335x as the INT controller is level sensitive. Hence the register was removed in rev F.

  • Qmax,

    I use SPRUH73 version f. Why this register desapeared is now anser. (But my problem isn't)

     regards, Andreas

  • Bin,

    Thanks for the Information about the IRQ_EOI.

    Even thought,I couldnt resolve my problem. I also tried to clear the 0x54 Reg without success to my problem.

    Here is my Test Code Maybe you can find a simple explication to the reason why it always generate a HW interrupt:

    /*
    * ======== main.c ========
    */

    #include <xdc/std.h>

    #include <xdc/runtime/Error.h>
    #include <xdc/runtime/System.h>
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>

    #include "adc.h" // owne .h file
    #include "tsc_adc.h"
    #include "hw/soc_AM335x.h"
    #include "evmAM335x.h"
    #include "startup.h"

    /******************************************************************************
    ** INTERNAL FUNCTION PROTOTYPES
    ******************************************************************************/
    static void ADCConfigure(void);
    static void StepConfigure(unsigned int, unsigned int, unsigned int);
    /******************************************************************************
    ** GLOBAL VARIABLE DEFINITIONS
    ******************************************************************************/
    unsigned int sample1;
    unsigned int sample2;
    unsigned int readADC=0;

    /****************************************************************************/
    /* LOCAL FUNCTION DEFINITIONS */
    /****************************************************************************/

    /******************************************************************************
    ** INTERNAL MACRO DEFINITIONS
    ******************************************************************************/
    #define RESOL_X_MILLION (439u)

    /*
    * ======== taskFxn ========
    */
    Void taskFxn(UArg a0, UArg a1){

    while(1){
    if(readADC){
    sample1 = TSCADCFIFOADCDataRead(SOC_ADC_TSC_0_REGS, TSCADC_FIFO_0);// Read data from fifo 0
    sample2 = TSCADCFIFOADCDataRead(SOC_ADC_TSC_0_REGS, TSCADC_FIFO_1);// Read data from fifo 1
    readADC=0;
    }
    }
    }


    /* ADC is configured */
    static void ADCConfigure(void)
    {

    TSCADCModuleClkConfig(); // Enable the clock for touch screen
    TSCADCPinMuxSetUp(); // Multiplex AN0 - AN7 input pins
    TSCADCConfigureAFEClock(SOC_ADC_TSC_0_REGS, 24000000, 3000000); // Configure Input ADC Clock to 3MHz
    TSCADCTSTransistorConfig(SOC_ADC_TSC_0_REGS, TSCADC_TRANSISTOR_ENABLE); // Enable Transistor bias
    TSCADCStepIDTagConfig(SOC_ADC_TSC_0_REGS, 1);
    TSCADCStepConfigProtectionDisable(SOC_ADC_TSC_0_REGS); // Disable Write Protection of Step Configuration regs

    StepConfigure(0, TSCADC_FIFO_0, TSCADC_POSITIVE_INP_CHANNEL1); // Configure step 1 for channel 1(AN0)
    StepConfigure(1, TSCADC_FIFO_1, TSCADC_POSITIVE_INP_CHANNEL2); // Configure step 2 for channel 2(AN1)

    TSCADCTSModeConfig(SOC_ADC_TSC_0_REGS, TSCADC_GENERAL_PURPOSE_MODE);// General purpose inputs (= Not TSC)

    TSCADCConfigureStepEnable(SOC_ADC_TSC_0_REGS, 1, 1); // Enable step 1
    TSCADCConfigureStepEnable(SOC_ADC_TSC_0_REGS, 2, 1); // Enable step 2

    TSCADCIntStatusClear(SOC_ADC_TSC_0_REGS, 0x7FF); // Clear the status of all interrupts
    TSCADCEventInterruptEnable(SOC_ADC_TSC_0_REGS, TSCADC_END_OF_SEQUENCE_INT); // End of sequence interrupt is enable */

    TSCADCModuleStateSet(SOC_ADC_TSC_0_REGS, TSCADC_MODULE_ENABLE); // Enable the TSC_ADC_SS module
    }

    /* Configures the step */
    void StepConfigure(unsigned int stepSel, unsigned int fifo,
    unsigned int positiveInpChannel)
    {
    /* Configure ADC to Single ended operation mode */
    TSCADCTSStepOperationModeControl(SOC_ADC_TSC_0_REGS,
    TSCADC_SINGLE_ENDED_OPER_MODE, stepSel);

    /* Configure step to select Channel, refernce voltages */
    TSCADCTSStepConfig(SOC_ADC_TSC_0_REGS, stepSel, TSCADC_NEGATIVE_REF_VSSA,
    positiveInpChannel, TSCADC_NEGATIVE_INP_CHANNEL1, TSCADC_POSITIVE_REF_VDDA);

    TSCADCTSStepFIFOSelConfig(SOC_ADC_TSC_0_REGS, stepSel, fifo); /* select fifo 0 or 1*/
    TSCADCTSStepModeConfig(SOC_ADC_TSC_0_REGS, stepSel, TSCADC_ONE_SHOT_SOFTWARE_ENABLED); /* Configure ADC to one short mode */
    }


    /* ADC Interrupt Handling */
    extern void ADCIsr()
    {
    unsigned int status;

    status = TSCADCIntStatus(SOC_ADC_TSC_0_REGS); // Get Interrupt
    TSCADCIntStatusClear(SOC_ADC_TSC_0_REGS, status); // Clear Interrupt
    if(status & TSCADC_END_OF_SEQUENCE_INT)
    readADC = 1;
    (*((volatile unsigned int *)(SOC_ADC_TSC_0_REGS + (0x54))))= 0; // Clear step Enable ... Dont Work!??

    }


    /*
    * ======== main ========
    */
    Void main()
    {
    Task_Handle task;
    Error_Block eb;

    mmuInit();
    ADCConfigure(); /* Configure ADC*/
    Error_init(&eb);

    task = Task_create(taskFxn, NULL, &eb);
    if (task == NULL)
    BIOS_exit(0);
    BIOS_start(); /* enable interrupts and start SYS/BIOS */

    }

    Regards Andreas

  • Andreas,

     After read your code segment I realized that you are doing one shot ADC, so there is no need to disable steps(0x54 register), I was doing continuous sampling. As long as I can tell, write status back to 0x28 register should clear the interrupt.

      Are you sure you are doing one shot sampling?  Is TSCADC_ONE_SHOT_SOFTWARE_ENABLED  defined as 0x0?

      Although I did not know why you keep getting interrupt. One thing I'd like to suggest is clear TSCADC_REG_IRQENABLE(register 0x2c) so there is no enabled interrupts.

      I have to say  this is puzzling.

    Guoqiang

     

  • Guoqiang,

    Yes TSCADC_ONE_SHOT_SOFTWARE_ENABLED is defined as 0x0.

    Writing 0x0 to the reg 0x2c doesn't solve the problem.

    Is it possible that there is a "proper procedure for configuring interrupts" such as for ECAP (example SPRUH73F/ Page 1769)?

    As I dont know how to disable global interrupts like descripted there. Can you tell me how to do that in order that I can test this option?

    Regards Andreas

  • Andreas,

       I am not sure what is wrong in your setup, but this is my setup:

       hardware:               beaglebone

       kernel:                     linux-ti33x-psp-3.2.16

       adc driver:              ti_tscadc.c

      The adc interrupt handler in ti_tscadc.c is:

    static irqreturn_t tsc_adc_interrupt(int irq, void *dev)
    {
        struct tscadc        *ts_dev = (struct tscadc *)dev;
        struct input_dev    *input_dev = ts_dev->input;
        unsigned int        status, irqclr = 0;
        int            i;
        int            fsm = 0, fifo0count = 0, fifo1count = 0;
        unsigned int        read_sample = 0, ready1 = 0;
        unsigned int        prev_val_x = ~0, prev_val_y = ~0;
        unsigned int        prev_diff_x = ~0, prev_diff_y = ~0;
        unsigned int        cur_diff_x = 0, cur_diff_y = 0;
        unsigned int        val_x = 0, val_y = 0, diffx = 0, diffy = 0;

        status = tscadc_readl(ts_dev, TSCADC_REG_IRQSTATUS);

        // printk("interrupt! status=%x\n", status);
        // if (status & TSCADC_IRQENB_EOS) {
        //     irqclr |= TSCADC_IRQENB_EOS;
        // }

        if (status & TSCADC_IRQENB_FIFO0THRES) {
            fifo1count = tscadc_readl(ts_dev, TSCADC_REG_FIFO0CNT);
            // printk("fifo 0 count = %d\n", fifo1count);
        
            for (i = 0; i < fifo1count; i++) {
                read_sample = tscadc_readl(ts_dev, TSCADC_REG_FIFO0);
                printk("sample: %d: %x\n", i, read_sample);
            }
            irqclr |= TSCADC_IRQENB_FIFO0THRES;
        }


        if (status & TSCADC_IRQENB_FIFO1THRES) {
            fifo1count = tscadc_readl(ts_dev, TSCADC_REG_FIFO1CNT);

            for (i = 0; i < fifo1count; i++) {
                read_sample = tscadc_readl(ts_dev, TSCADC_REG_FIFO1);
                // read_sample = read_sample & 0xfff;
                printk("sample: %d: %d\n", i, read_sample);
                panic("sample read from fifo1!");
            }
            irqclr |= TSCADC_IRQENB_FIFO1THRES;
        }

        // mdelay(500);

        tscadc_writel(ts_dev, TSCADC_REG_IRQSTATUS, irqclr);

        /* check pending interrupts */
       // tscadc_writel(ts_dev, TSCADC_REG_IRQEOI, 0x0);

        /* Turn on Step 1 again */
        // tscadc_writel(ts_dev, TSCADC_REG_SE, TSCADC_STPENB_STEPENB_GENERAL);
        return IRQ_HANDLED;
    }

    As you can see this handler returns IRQ_HANDLED to kernel, but in your code the interrupt handler does not return anything. So my guess is there must be some differences in the design of the interrupt service routines(ISR), which actually calls the interrupt handlers. Could it be something wrong there?

      Sorry I can not help here, I am rather unexperienced in linux kernel.

      Please let us know when you figured something out.

    Best,

    Guoqiang

  • Guoqiang,

    Thanks for all your inputs. Finally I could find out the mistake. In the SysBios Hwi setup I put the wrong Interrupt number (don't ask me why!). 

    Now everything is working right (continious and one-shot mode). 

    Best,

    Andreas

  • Hi Bin,

    Thanks for the clarification about IRQ_EOI register and TRM changes. Hope TCS kernel driver will be updated too, not to me misleading as it is now.

    Please, if you know, could you give further clarifications about some ADC related questions I've done some time ago here: http://e2e.ti.com/support/dsp/sitara_arm174_microprocessors/f/791/t/178778.aspx? Mainly the question about FIFO depth, which should be 64, and the result of FIFO0COUNT read, which I've verified can be up to 127.

    Thanks in advance for your support.

    Regards, Max