This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

AD7794 long delay

Hi,

I'm trying to write a quick driver for the AD7794 on nrf51822. This works on a special SPI: the MISO line is alse the "data ready" line.
So when I want to read a value, I need first to ask to read the right channel, then to wait for a value, and at last to read value itself. This is not so easy with the spi_master library, but I dealed with it... out of the softdevice section.

this it my function:

int read_data(int address)
{
    uint8_t resp[5]={1,2,3,4,5};
    uint8_t config_data[2]={0b01001010,0x90};
    uint8_t mode_data[2]={0x30,0x11};
    uint32_t value;
    config_data[1]+=address;
    

    //configure the reading
    printf("read\r\n");
    spi_write(BSP_SPI_CS,0b10,config_data,2);

    //use manual slave select, and set it low
    nrf_gpio_pin_clear(BSP_SPI_CS);
    nrf_gpio_cfg_output(BSP_SPI_CS);
    nrf_gpio_pin_clear(BSP_SPI_CS);

    //configure the channel, and ast for single read
    spi_write(-1,0b1,mode_data,2);


    //set the MOSI line up
    nrf_gpio_pin_set(BSP_SPI_SDI);
    nrf_gpio_cfg_output(BSP_SPI_SDI);
    nrf_gpio_pin_set(BSP_SPI_SDI);

    //set the MISO line as input
    nrf_gpio_cfg_input(BSP_SPI_SD0, NRF_GPIO_PIN_NOPULL  );
    //wait fot the line to be down (! ! ! )
    while(nrf_gpio_pin_read(BSP_SPI_SD0)!=0);
    //MISO and MOSI back to norma
    nrf_gpio_cfg_default (BSP_SPI_SD0);
    nrf_gpio_cfg_default (BSP_SPI_SDI);

    //read the value
    spi_read(-1,0b11,resp,3);
    value=(uint32_t)(((uint32_t)resp[1]<<16)+((uint32_t)resp[2]<<8)+(uint32_t)resp[3]);
    //set the CS back to high
    nrf_gpio_pin_set(BSP_SPI_CS);
    return value;
}

this is what it looks like:

The problem is when I'm using the softdevice, I'm locked into the "while GPIO not 0" loop. The MISO line still works fine and go down (but so not read correctly by the app). Afer few mode ms, the MISO line get crazy, and afer a long while (1 or 2 sec), the chip reboot.

I tried many things such as add delay in the loop, add app_sched_execute() in it as I thought it might get bored waiting(stupid, I know), etc... but I don't find a solution, even a bad one.

Does someone has any idea? thanks a lot !


 

  • Hi,
    The function is called by a app_timer (from app_timer_create), depending of the state of the bluetooth state machine(if connected, timer is on fast, if off, it's on slow). So the main tread, on the main loop just check the scheduler and low power the module.

        for (;;)
        {
            app_sched_execute();
            power_manage();
        }

    For now, I use the timeout of the timer as interrupt, but the reading rate is just ok (5ms/measure so 33Hz).
    On every scheduler I ever met, there was a function "delay" and "wait_for_event", and even on linux, there is mutexs, and and non-crashing "sleep" function for threads. That's all what I want :
    -Or a way to create and wait for an event
    -Or a way to wait for some specific time, but without limit but max value of an integer.
    -Or both
    And if they don't exist:
    -whats the limit of time, for a function called on a timer, without any hulk risk, and why, and how to change it, if it's possible?

  • Hi,

    I see atleast one deadlock scenario here. At the below line in your code

        //set the MISO line as input
        nrf_gpio_cfg_input(BSP_SPI_SD0, NRF_GPIO_PIN_NOPULL  );
        //wait fot the line to be down (! ! ! )
        while(nrf_gpio_pin_read(BSP_SPI_SD0)!=0);

    You are assuming that your application will be active and able to catch the pin toggle to low in that while loop. But on a multi context system (when using softdevice or scheduler or any other RTOS) it is possible that some other context is running other than your app. I think this is what is happening in your application 

    1) you set MISO as input

    2) wait for pin to go low.

    3) something interrupts your app (softdevice or interrupt) 

    4) When something else is running other than your app, then the MISO pin toggles to 0 and toggles back to 1

    5) your application gets control back from (softdevice or interrupt) and it has missed the toggle it waits for and sees that the pin is still at 1 and continues the while loop.

    5) Since your app is stuck in this loop it is not sending read commands to the sensor and hence the MISO pin will not toggle anymore.

    This is clearly a deadlock in your app.

    You need to make sure that when your app is in the background and something else is running, then the pin toggle is captured somehow. It is wise to configure the pin to generate interrupt at this time when the pin goes low and then set a flag in the interrupt. You can wait for this flag in the while loop instead of waiting for pin to toggle. If you set the flag the the interrupt then you make sure that this toggle is not lost and captured as a flag value change in ISR.

  • Hin thanks, and congratulation, you found the bug.
    Actually, as you see in the 1st screenshot, asking and waiting for a measurement takes 4ms. 
    When the while loop explodes, the 4ms are not done yet, wich means that if I was waiting for an interrupt, the interrupt woudn't have been called yet.
    Maybe you are right, and this is cleaner, but I'm quite sure it won't work, would it?

    In an other hand, I fully agree that the while loop is really ugly and is a great way to block the CPU and everything including the softdevice. I'm looking for a softer function, such as:
    void no_breaking_delay_ms( int delay_time);
    or
    void wait_quietly_for_an_event_enjoying_a_mojito_on_the_beach_watching_all_the_other_thread_are_still_running(event_t event_name);
    If both functions exist, it would be even better.

    As Stian, and I said, there is an other way: cut the function into 3 function, the beginning, the interrup, the end. And of course, cut any function that need to read the adc in two too. Let'ts try to avoid this, please. Lingchi is a programming technic which leads the code to death.

    In linux, there is "sleep" and "mutex"
    In chibios there is "chThdSleep (systime_t time)" and chEvtWaitOne (eventmask_t mask)
    In freeRTOS there is "vTaskDealy" and eventGroups
    etc...

    Maybe, there is just no solution, is it?

  • In an other hand, I fully agree that the while loop is really ugly and is a great way to block the CPU and everything including the softdevice

    The while loop does not block softdevice from interrupting your app. That is what we believe is the source of this problem. 

    There is a solution as I wrote earlier, instead of waiting for the pin state change, your app needs to wait/block/suspend on a flag/mutex/semaphore. Make sure that the flag/mutex/semaphore change their state in the gpio ISR which is making sure that you do not loose this info. I do not see any other way to do this a failproof way.

  • Great, it tooks me, 1mninute on google to deal with it on 3 different scheduler, I still don't know how to do it with the nordic softdevice scheduler. Can you name a function to "wait/block/suspend on a flag/mutex/semaphore"? can you link me an exemple? or the best can you ask me to read the manual, but with a link to the right page of the manual? this would be lovely, and thank you in advance :)

Related