Read Sensor at 1344Hz

Teynaker
Posts: 5
Joined: Thu May 11, 2023 8:52 pm

Read Sensor at 1344Hz

Postby Teynaker » Thu May 11, 2023 9:16 pm

Dear community,

I want to acquire data from accelerometer LIS3DH at 1344Hz sample rate.

- Approach using ISR rising edge for data ready pin, that means LIS3DH generate interrupt at approximately 1344Hz. In the ISR will enable semaphore which is evaluate in ReadSensorTask, the problem with this approach is that system doesn't work. It run slowly or sometime stuck, I try to collect 1024 samples and print msg when it succeed however the time to get 1024 samples is irregular or sometimes stuck. Maybe because tick rate is 1ms and the interrupt happen fastest than it.

- Another approach, will try to use a DelayTask with 1/1344ms, i don't know if tick rate can be reconfigure without generate possible error in RTOS. That is possible?

Another approach or some idea to test.

MicroController
Posts: 1709
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Read Sensor at 1344Hz

Postby MicroController » Fri May 12, 2023 8:50 am

Can you show some code? Specifically the ISR and the task that's reacting to it?

Do you use the I2C or the SPI interface?

Teynaker
Posts: 5
Joined: Thu May 11, 2023 8:52 pm

Re: Read Sensor at 1344Hz

Postby Teynaker » Fri May 12, 2023 1:45 pm

MicroController wrote:
Fri May 12, 2023 8:50 am
Can you show some code? Specifically the ISR and the task that's reacting to it?

Do you use the I2C or the SPI interface?
I use I2C at 400kHz, here the code:

Code: Select all

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
#include "hal/gpio_types.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_log.h"
#include "driver/gpio.h"

#include "lis3dh.h"
#include "vibration.h"

/* Vibration Analysis Parameters */
#define samples             1024     
#define samplingFrequency   1344
#define ampFactor           3.7067873
#define GPIO_INPUT_PIN      15

SemaphoreHandle_t readSemaphore; // Semaphore handle
static const char *TAG = "esp-basic-vibration";

void VibrationTask(void *pvParameter)
{
    printf("============== Starting Vibration Analysis Task ==============\n");

    // Variables definition
    AxesAccel_t ac;
    float aRMS = 0;
    float vRMS = 0;
    float ax[samples];
    float ay[samples];
    float az[samples];
    float ve[samples];

    float Img[samples];

    // Temp debug
    uint8_t value = 0x02;
    bool sts = 0;
    unsigned int counter = 0;

    // Peripherals Init
    ESP_ERROR_CHECK(I2C_MASTER_INIT());
    ESP_LOGI(TAG, "I2C initialized successfully");

    // Accelerometer Config
    LIS3DH_SetInt1Pin(LIS3DH_I1_DRDY1_ON_INT1_ENABLE);
    LIS3DH_SetODR(LIS3DH_ODR_1344Hz_NP_5367HZ_LP);
    LIS3DH_SetMode(LIS3DH_NORMAL);
    LIS3DH_SetFullScale(LIS3DH_FULLSCALE_8);
    LIS3DH_SetAxis(LIS3DH_X_ENABLE | LIS3DH_Y_ENABLE | LIS3DH_Z_ENABLE);

    // Config Internal High Pass Filter
    // LIS3DH_SetMode(LIS3DH_HPM_NORMAL_MODE);
    // LIS3DH_SetHPFCutOFF(LIS3DH_HPFCF_3);
    // LIS3DH_SetFilterDataSel(0x00);

    sts = LIS3DH_GetWHO_AM_I(&value);
    printf("Read Status: %d\n", sts);
    printf("Who I am : %d\n", value);

    vTaskDelay(2000 / portTICK_PERIOD_MS);
    LIS3DH_GetAccAxes(&ac); 

    while (true) 
    {   

        // Get acceleration data in mg and save each axis
        LIS3DH_GetAccAxes(&ac); 
        az[counter] = ac.axis_z;
        ay[counter] = ac.axis_y;
        ax[counter] = ac.axis_x;

        // Espera a que la interrupción indique que se puede leer el sensor
        xSemaphoreTake(readSemaphore, 100 / portTICK_PERIOD_MS);

        if (counter >= samples)
        {   
            printf("============== Running Vibration Analysis Task ==============\n");
            printf("= Counter value : %d\n", counter);

            // Counter reset
            counter = 0;

            // Remove DC Component
            RemoveOffset(ax, samples);
            RemoveOffset(ay, samples);
            RemoveOffset(az, samples);

            // Get Acceleration resultant 3 axis
            // Resultant(ax, ay, az, samples);

            // Get velocity applying numerical integration
            Velocity(ax, ve, samples, samplingFrequency);

            // Get Vibration RMS Values
            RMS_Value(ax, samples, &aRMS);
            RMS_Value(ve, samples, &vRMS);

            // Save vector data on vReal and vImg for FFT
            memset(Img, 0, samples * sizeof(float));

            // FFT
            FFT_Init(ve, Img, samples, samplingFrequency);
            FFT_Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD); // Recommended use for high frequencies. Relative to Sampling Frequency. In this case: 70Hz
            FFT_Compute(FFT_FORWARD);
            FFT_ComplexToMagnitude();

            // Find Peaks
            float amp[5], freq[5];
            FFT_MajorPeaks(&amp[0], &freq[0], 1, 3);

            // printf("============== Ended Vibration Analysis ==============\n");

            // =========== DEBUG LOG ================
            // printf("[$] Ac RMS : %.2f mg.\n", aRMS);
            // printf("[$] Ve RMS : %.2f mm/s.\n", vRMS);
            // printf("1st Peak -> Freq: %.2f Hz - Amp: %.2f mm/s \n", freq[0], amp[0]);
            // printf("2nd Peak -> Freq: %.2f Hz - Amp: %.2f mm/s \n", freq[0], amp[0]);
            // printf("3rd Peak -> Freq: %.2f Hz - Amp: %.2f mm/s \n", freq[0], amp[0]);
            
            // for (uint i = 128; i < 256; i++) {
            //     printf("[#] Accel [%d] --> X: %.2f  -  Y: %.2f  -  Z: %.2f\n", i, ax[i], ay[i], az[i]);
            // }

            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }
        else 
            counter++;

    }
}

void IRAM_ATTR isr_handler(void *arg)
{   
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(readSemaphore, &xHigherPriorityTaskWoken); // Se despierta la tarea que espera por el semáforo
    if (xHigherPriorityTaskWoken) {
        portYIELD_FROM_ISR(); // Se cambia a una tarea de mayor prioridad
    }
}

void configure_interrupt(void)
{

    gpio_config_t gpio_cfg = {
        .intr_type = GPIO_INTR_POSEDGE, // Configurar el modo de interrupción como flanco de subida
        .mode = GPIO_MODE_INPUT, // Configurar el modo de pin como entrada
        .pin_bit_mask = (1ULL << GPIO_INPUT_PIN) // Configurar el pin que se utilizará como entrada
    };
    gpio_config(&gpio_cfg);

    // Configurar la rutina de interrupción
    gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3); // Nivel de prioridad de interrupción
    gpio_isr_handler_add(GPIO_INPUT_PIN, isr_handler, NULL); // Configurar el manejador de interrupción

}

void app_main(void)
{

    configure_interrupt();
    readSemaphore = xSemaphoreCreateBinary(); // Se crea el semáforo
    xTaskCreate(&VibrationTask, "xVibrationTask", 24576, NULL, 5, NULL);

}

MicroController
Posts: 1709
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Read Sensor at 1344Hz

Postby MicroController » Fri May 12, 2023 8:42 pm

Code looks ok, though I cannot find the code of the library you use to control the sensor.

One thing however is that you don't check the result of

Code: Select all

xSemaphoreTake(readSemaphore, 100 / portTICK_PERIOD_MS);
So you don't know if the interrupt actually got triggered or if the SemaphoreTake timed out without the interrupt working.

Teynaker
Posts: 5
Joined: Thu May 11, 2023 8:52 pm

Re: Read Sensor at 1344Hz

Postby Teynaker » Mon May 15, 2023 1:58 pm

MicroController wrote:
Fri May 12, 2023 8:42 pm
Code looks ok, though I cannot find the code of the library you use to control the sensor.

One thing however is that you don't check the result of

Code: Select all

xSemaphoreTake(readSemaphore, 100 / portTICK_PERIOD_MS);
So you don't know if the interrupt actually got triggered or if the SemaphoreTake timed out without the interrupt working.
I decided define timeout to prevent process stuck, if i replace with max delay the program sometimes stuck and If the read happen at interrupt frequency, it shouldn't´t active timeout. My question is about the ISR (1344Hz), it enable a semaphore for a task. but scheduler switching frequency are limited for tickrate (1000Hz) If it is the problem how i can solve it? Also i tried to execute I2C read function into ISR but stack isn't enough and the system reboot.

MicroController
Posts: 1709
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Read Sensor at 1344Hz

Postby MicroController » Mon May 15, 2023 7:08 pm

I decided define timeout to prevent process stuck, if i replace with max delay the program sometimes stuck
I know. And checking the return value of xSemaphoreTake would have let you know directly that there was no interrupt detected within 100ms.
Since you're using a binary semaphore, the signalling of the ISR may also get lost when the ISR gives the semaphore more than once before the task has a chance to take it.

That said, an important question is: How long does LIS3DH_GetAccAxes(...) take to run?

scheduler switching frequency are limited for tickrate
No, it's not. The tick rate is the lower limit for the rate at which tasks get switched, i.e. at 100 Hz a task will be preempted after 10ms by FreeRTOS itself, but it can be switched in and out much faster by other means, e.g. by your ISR.
i tried to execute I2C read function into ISR
Don't bother, this is never going to work.

Teynaker
Posts: 5
Joined: Thu May 11, 2023 8:52 pm

Re: Read Sensor at 1344Hz

Postby Teynaker » Mon May 15, 2023 9:23 pm

MicroController wrote:
Mon May 15, 2023 7:08 pm
Since you're using a binary semaphore, the signalling of the ISR may also get lost when the ISR gives the semaphore more than once before the task has a chance to take it.
I think this happens or the problem is related to this, but I don't know why it happens.
MicroController wrote:
Mon May 15, 2023 7:08 pm
No, it's not. The tick rate is the lower limit for the rate at which tasks get switched, i.e. at 100 Hz a task will be preempted after 10ms by FreeRTOS itself, but it can be switched in and out much faster by other means, e.g. by your ISR.
Then, it should be worked with ISR, if task execute around 1 - 2 microseconds and ISR its called each 744us. Now i dont know how and where find the error, please some suggest?
MicroController wrote:
Mon May 15, 2023 7:08 pm
That said, an important question is: How long does LIS3DH_GetAccAxes(...) take to run?
Using esp_timer_get_time() to get times, it execute in less than 1 micro second.

**Important note is that sensor not generate a new interrupt if u don't read it previous.
**When I set sensor at 400Hz using exactly same code it works.

MicroController
Posts: 1709
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Read Sensor at 1344Hz

Postby MicroController » Tue May 16, 2023 7:18 am

Using esp_timer_get_time() to get times, it execute in less than 1 micro second.
Think about that for a moment. At 400kHz clock, can the I2C bus transfer dozens of bits of data in "less than 1 micro second"?

Teynaker
Posts: 5
Joined: Thu May 11, 2023 8:52 pm

Re: Read Sensor at 1344Hz

Postby Teynaker » Wed May 17, 2023 4:07 pm

MicroController wrote:
Tue May 16, 2023 7:18 am
Think about that for a moment. At 400kHz clock, can the I2C bus transfer dozens of bits of data in "less than 1 micro second"?
Thats true, I made a mistake. Now I corrected it and get 1095us read time, its the problem but I just execute simple reads I guess I2C at 400kHz not support 1344Hz data rate at 16bits resolution. I configured 8 bits resolution with 1620Hz(618us) data rate and get a read time 550us but sometime stuck. Thanks u so much! I find the problem, maybe then I use SPI to test.

MicroController
Posts: 1709
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Read Sensor at 1344Hz

Postby MicroController » Thu May 18, 2023 11:30 am

I guess I2C at 400kHz not support 1344Hz data rate at 16bits
Well, it could work. The whole transaction should be able to be done in about 210us of bus time:
START, Addr+W, (0x28 | 0x80), START, Addr+R, Read 6 bytes, STOP
I guess the library you're using is making sub-optimal use of the LIS3DH's I2C interface, i.e. using 6 transactions of one byte each instead of one transaction to read 6 consecutive bytes. If you want, you can create and run an "optimized" I2C transaction yourself just to retrieve the data, while still using the library for all the non-time critical stuff.

Who is online

Users browsing this forum: No registered users and 93 guests