Software Interrupts

paulhun
Posts: 12
Joined: Wed Jun 06, 2018 11:48 am

Software Interrupts

Postby paulhun » Mon Nov 26, 2018 3:48 pm

Can anyone please help me with example code for a software generated interrupt.

I suspect it will require registration of an interrupt handler, and an handler, perhaps something in the order of below.

I have a number of tasks running on both cores and want to be able to 'check and compare' when the time in the process is right to do so


static ExampleInterruptHandler( void ) {

// Do something here
}


static void vPeriodicTask( void *pvParameters )

{

for(;;) {
// do something, then.....
GenerateSimulatedInterrupt( INTERRUPT_NUMBER );


Thanks in advance Paul
}
}


#ifdef __cplusplus
extern "C" {
#endif
void app_main() {
/* Install the handler for the software interrupt.
SetInterruptHandler( ExampleInterruptHandler );
}

ESP_Sprite
Posts: 9772
Joined: Thu Nov 26, 2015 4:08 am

Re: Software Interrupts

Postby ESP_Sprite » Tue Nov 27, 2018 2:05 am

I feel we may have an X-Y problem here. Can you tell us a bit more about the problem you're trying to solve, the problem you think a software interrupt is the solution to?

paulhun
Posts: 12
Joined: Wed Jun 06, 2018 11:48 am

Re: Software Interrupts

Postby paulhun » Tue Nov 27, 2018 9:34 am

I'm writing an application to monitor a stepper motor and it's steps. I read the steps in and check the motor has moved. Cycle times can be as little as 400us depending on speed but in general 600us ( I tend to run the machine slower rather than faster)
From guidance given to-date I can successfully monitor the incoming steps (using RMT) and monitor motor movement through encoder inputs (using PCNT). I can repeat the output stepping. I use the App core for input and the Pro core for repeating the output.

I have a output task running (rmt_rx_task) waiting on an input to be received to the ringbuffer, it's basic function is to repeat the step as an output and check movement has occurred. I wait 100us after an output for the encoder to register on the PCNT count but I find there is often a latency when reading the count.
So, as an attempt to get around the latency issue I can perform a simple calculation of step count into encoder count to a) tell me where I am and b) if movement has occurred therefore my intention of a software interrupt is after a step output generate an interrupt to make a check for movement and I'll check within the tx task for flags etc.

The whole purpose of this application is to check for stepper motor movement and if it does not occur it will be to send an additional pulse and if this does not work then halt the process.

Code: Select all

//CHOOSE TEST MODE OR NORMAL RUNNiNG (1 for TestMode)
#define TestMode   0
#define sVersion   1.05
#define motor_steps_per_revolution 200
#define encoder_pulses_per_revolution 600
#define doBacklash 0        // to turn backlash on change to 1, else 0 to turn off backlash
#define backlashSteps  5
#define stepEncDivide 3
/*
 * Brief:
 * This test code is used to monitor steps received (destined for a stepper motor) and checks an encoder for movement.
 *
 * GPIO status:
 * GPIO16: output
 * GPIO17: output
 * GPIO18: output, dirPinOut
 * GPIO19: output, stepPinOut
 * GPIO4:  input,  RMT,  steps in
 * GPIO5:  input,  PCNT, encoder in
 * GPIO15: input,  PCNT, encoder in
 * GPIO21: input,  DIR,  pin in
 * GPIO22: input,  RMT,  steps in, validation pin (strap to GPIO4)

 * OLED Display:
 * GPIO26 SCL
 * GPIO25 SDA
 * (Vcc3.3 + GND)
 * 
 * Test:
 * GPIO21 is DIR pin in
 * Connect GPIO23 with Vcc (HIGH), this holds off the eStop
 * 
 * Units:
 * Step in - RMT[0]
 * Validation Step in - RMT[1]
 * Encoder in -PCNT[1]
 */

#include "esp_log.h"
#include "fonts.h"
#include "ssd1306.hpp"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cstdio>
#include <cmath>
#include <string>
#include <sstream>
#include <iostream>

/* Timer */
#include "driver/ledc.h"
#include "esp_err.h"
#include "time.h"
#include "soc/cpu.h"
#include "soc/rtc.h"

#include "esp_timer.h"

/* Quad Encoder (PCNT) */
#include "driver/pcnt.h"

/* RMT Receiver  (RMT)*/
#include "driver/rmt.h"

using namespace std;

/* Encoder, quad unit */
#define PCNT_PULSE_GPIO_0     GPIO_NUM_15      
#define PCNT_CONTROL_GPIO_0   GPIO_NUM_5
#define PCNT_H_LIM_VAL        30000 //  16 bit register therefore count is limited to a max of 32,768
#define PCNT_L_LIM_VAL       -30000

typedef struct {
    pcnt_unit_t unit;
    long int totalCount         = 0;
    long int rx_count           = 0;
} x_pcnt_obj_t;
DRAM_ATTR x_pcnt_obj_t* x_p_pcnt_obj[PCNT_UNIT_MAX] = {0};

DRAM_ATTR long int totalCount_0 = 0;
DRAM_ATTR long int encCountVal  = 0;
DRAM_ATTR int backlashErrAdj = 0;


/* A structure to pass events from the PCNT interrupt handler */
typedef struct {
    int unit;        // the PCNT unit that originated an interrupt
    uint32_t status; // information on the event type that caused the interrupt
} pcnt_evt_t;


/* Step Receiver, RMT */
#define RMT_RX_CHANNEL_0      0             // RMT channel for receiver
#define RMT_RX_GPIO_NUM_0     GPIO_NUM_4    // GPIO number for receiver 
#define RMT_RX_CHANNEL_1      1             // RMT channel for receiver
#define RMT_RX_GPIO_NUM_1     GPIO_NUM_22   // GPIO number for receiver 
#define RMT_CLK_DIV           100           // RMT counter clock divider 
#define RMT_BUFFER_SIZE       5000          // 5000 is max else cpu will crash!!

TaskHandle_t x_rmt_rx_task = NULL; 
TaskHandle_t xrmt_rx_backlash_task = NULL;

#define RMT_CHANNEL_ERROR_STR "RMT CHANNEL ERR"
#define RMT_ADDR_ERROR_STR    "RMT ADDRESS ERR"
#define RMT_DRIVER_ERROR_STR  "RMT DRIVER ERR"
static const char* RMT_TAG =  "RMT";
static uint8_t s_rmt_driver_channels; // Bitmask (bits 0-7) of installed drivers' channels
static rmt_isr_handle_t x_s_rmt_driver_intr_handle;

#define RMT_CHECK(a, str, ret_val) \
    if (!(a)) { \
        ESP_LOGE(RMT_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
        return (ret_val); \
    }

// Mutex lock for protecting concurrent register/unregister of RMT channels' ISR
static _lock_t _driver_isr_lock;

typedef struct {
    rmt_channel_t channel;
    RingbufHandle_t rx_buf;
    long int rx_count =0;
} x_rmt_obj_t;
DRAM_ATTR x_rmt_obj_t* x_p_rmt_obj[RMT_CHANNEL_MAX] = {0};

/* GPIO settings */
#define GPIO_eStop            GPIO_NUM_23  // eStop, to be used to halt all output, pull HIGH to suspend                              
#define GPIO_DUMMY_STEP       GPIO_NUM_16  // To be used to deliver dummy Step interrupt - fired by Timer
#define GPIO_DUMMY_ENCODER    GPIO_NUM_17  // To be used to deliver dummy Encoder interrupt - fired by Timer
#define GPIO_DIR              GPIO_NUM_18  // dirPinOut
#define GPIO_STEP             GPIO_NUM_19  // stepPinOut
// GPIO_NUM_4   // stepPinIn
// GPIO_NUM_5   // encoderPinAIn
// GPIO_NUM_21  // dirPinIn, to be used as dummy dir pin
#define BLINK_GPIO            GPIO_NUM_2   // Used to flask led
#define ESP_INTR_FLAG_DEFAULT 0

/* OLED */
OLED oled = OLED(GPIO_NUM_26, GPIO_NUM_25, SSD1306_128x64);

/* Delay functions */
#define NOP() asm volatile ("nop")

/* Backlash / steps */

#define stepCycleTime  600   // us
/* Handles for the tasks  */
TaskHandle_t x_rx_Task = NULL, x_bl_Task  = NULL;

/* Variables */
static long int  isr_handler_rx_count     = 0;
static long int  isr_handler_rx_count_all = 0;

DRAM_ATTR static bool eStop               = false;
DRAM_ATTR static bool criticalError       = false;

typedef struct  {
    long int pri_rx_count;
    long int sec_rx_count;
    long int handler_rx_count;
    long int handler_err_count;
    long int enc_count;
} display_vars;
display_vars dis_var;

portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;

// Timer
esp_timer_handle_t event_timer;
/* ------------------------------------------------------ */

/* ------------------------------------------------------ */


/* ------------------------------------------------------ */
/* ------------Error control----------------------------- */
/* ------------------------------------------------------ */
void doCriticalError() { 
    for (int i=0; i <= 10; i++){
        gpio_set_level(BLINK_GPIO, 1);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        gpio_set_level(BLINK_GPIO, 0);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    // force a restart 
    //ESP.restart();
} // doCriticalError

static void _eStop_() {
    eStop = true;
    /*
    To do.....
    send notificaion signal to Mach3 to stop sending steps!!
    */
} // _eStop)
/*--------------------------------------------------------*/
static void gpio_isr_eStop_handler(void* arg) { 
    eStop = true;
    _eStop_();
} // gpio_isr_eStop_handler
/*--------------------------------------------------------*/


/* ------------------------------------------------------ */
/* ------------Event timer control----------------------- */
/* ------------------------------------------------------ */
static void event_timer_callback(void* arg)
{
    encCountVal  = backlashErrAdj + (int16_t) PCNT.cnt_unit[PCNT_UNIT_0].cnt_val + x_p_pcnt_obj[0]->totalCount; 
    // eStop backup detection in case GPIO interupt is not detected - becasue of task schedualling issues etc.
    if (gpio_get_level(GPIO_eStop)) {
        _eStop_();
    }
} // event_timer_callback
/* ------------------------------------------------------ */


/* ------------------------------------------------------ */
/* ------------Display Task Section---------------------- */
/* ------------------------------------------------------ */
void displayTask(void *pvParameters) {
    /* Running on Core 1 = APP CPU */
	adc1_config_width(ADC_WIDTH_12Bit);
	adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_0db);

	ostringstream os;

    int16_t encCount;
	while (1) {
		oled.clear();
		oled.select_font(1);
		os.str("");
        os << "Steps P: " << x_p_rmt_obj[0]->rx_count <<" .. S: " << x_p_rmt_obj[1]->rx_count;
		oled.draw_string(0, 0, os.str(), WHITE, BLACK);
		oled.draw_hline(0, 11, 128, WHITE);
        os.str("");
		os << "Step Pos:  " << dis_var.handler_rx_count;
 		oled.draw_string(0, 13, os.str(), WHITE, BLACK);
		oled.draw_hline(0, 24, 128, WHITE);       
        os.str("");
		os << "Step Err:   " << dis_var.handler_err_count;  
 		oled.draw_string(0, 26, os.str(), WHITE, BLACK);
		oled.draw_hline(0, 37, 128, WHITE);  

        os.str("");
        char _str[50];  
        sprintf(_str,"%7.0f", round(dis_var.enc_count/stepEncDivide));
        os << "Enc   Cnt:  " << dis_var.enc_count << " " << _str;

 		oled.draw_string(0, 39, os.str(), WHITE, BLACK);
		oled.draw_hline(0, 50, 128, WHITE);  
        os.str("");      
        if (eStop) {
            os << "eStop:    ACTIVE";
        }
        else if (criticalError) {
            os << "Critical ERROR ACTIVE";
        }
        else
        {
            os << "eStop:    OK" << " ... Enc:  "  << encCountVal ;    
        }
 		oled.draw_string(0, 52, os.str(), WHITE, BLACK);
        os.str("");
		oled.refresh(true);

		vTaskDelay(250 / portTICK_PERIOD_MS);
	}

} // displayTask


/* ------------------------------------------------------ */
/* ------------Micro second delay functions-------------- */
/* ------------------------------------------------------ */
unsigned long IRAM_ATTR micros()
{
    return (unsigned long) esp_timer_get_time();
} // micros
/* ------------------------------------------------------ */


void IRAM_ATTR delayMicroseconds(uint32_t us)
{
    uint32_t m = micros();
    if(us){
        uint32_t e = (m + us);
        if(m > e){ //overflow
            while(micros() > e) {
                NOP();
            }
        }
        while(micros() < e) {
            NOP();
        }
    }
} // delayMicroseconds
/* ------------------------------------------------------ */


/* --------------------------------------------------------- */
/* ------------Quad Encoder Initialise  PCNT---------------- */
/* -----Reciver step validation & Encoder counting---------- */
static void IRAM_ATTR pcnt_intr_handler(void *arg)
{
    uint32_t intr_status = PCNT.int_st.val;
    int i;
    pcnt_evt_t evt;

    for (i = 0; i < PCNT_UNIT_MAX; i++) {
        if (intr_status & (BIT(i))) {
            evt.unit = i;
            x_pcnt_obj_t* p_pcnt = x_p_pcnt_obj[i];
            // deal with the PCNT event type that caused the interrupt    
            evt.status = PCNT.status_unit[i].val;

            if (evt.status & PCNT_STATUS_L_LIM_M) {
                p_pcnt->totalCount = p_pcnt->totalCount + PCNT_L_LIM_VAL;
            }
            if (evt.status & PCNT_STATUS_H_LIM_M) {
                p_pcnt->totalCount = p_pcnt->totalCount + PCNT_H_LIM_VAL;
            }

            PCNT.int_clr.val = BIT(i);          
        }
    }
} // pcnt_intr_handler
/*-----------------------------------------------------------*/


esp_err_t x_pcnt_driver_install(pcnt_unit_t unit)
{
    esp_err_t err = ESP_OK;

    if(x_p_pcnt_obj[unit] != NULL) {
        ESP_LOGD(RMT_TAG, "PCNT driver already installed");
        return ESP_ERR_INVALID_STATE;
    }

    x_p_pcnt_obj[unit] = (x_pcnt_obj_t*) malloc(sizeof(x_pcnt_obj_t));

    if(x_p_pcnt_obj[unit] == NULL) {
        ESP_LOGE(RMT_TAG, "PCNT driver malloc error");
        return ESP_ERR_NO_MEM;
    }
    memset(x_p_pcnt_obj[unit], 0, sizeof(x_rmt_obj_t));

    x_p_pcnt_obj[unit]->unit = unit;

    pcnt_set_filter_value(unit, 100);
    pcnt_filter_enable   (unit);
    //pcnt_event_enable(unit, PCNT_EVT_ZERO);
    pcnt_event_enable    (unit, PCNT_EVT_H_LIM);
    pcnt_event_enable    (unit, PCNT_EVT_L_LIM);
    pcnt_counter_pause   (unit);
    pcnt_counter_clear   (unit);

    _lock_acquire_recursive(&_driver_isr_lock);
    
    if(x_p_pcnt_obj[unit]->unit == 0) { // first PCNT unit using handler
        err = pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL);
        pcnt_intr_enable(unit);
    }
    else {
        pcnt_intr_enable (unit);
    }

    _lock_release_recursive(&_driver_isr_lock);

    pcnt_counter_resume  (unit);

    return err;
} // x_pcnt_driver_install
/*--------------------------------------------------------*/


static void quadrature_encoder_counter_init_0() {
    pcnt_config_t pcnt_config = {      
        .pulse_gpio_num = PCNT_PULSE_GPIO_0,     // Encoder Channel -A- 
        .ctrl_gpio_num  = PCNT_CONTROL_GPIO_0,   // Encoder Channel -B-
        .lctrl_mode     = PCNT_MODE_KEEP,        // Rising -A-, HIGH -B- = CW Step
        .hctrl_mode     = PCNT_MODE_REVERSE,     // Rising -A-, LOW -B- = CCW Step             
        .pos_mode       = PCNT_COUNT_INC,        // Count Only On Rising-Edges
        //.neg_mode       = PCNT_COUNT_DIS,        // Discard Falling-Edge      
        .neg_mode       = PCNT_COUNT_DEC,        // Count Falling edge      
        .counter_h_lim  = (int16_t)PCNT_H_LIM_VAL,
        .counter_l_lim  = (int16_t)PCNT_L_LIM_VAL,
        .unit           = PCNT_UNIT_0,
        .channel        = PCNT_CHANNEL_0,
    };
    pcnt_unit_config(&pcnt_config);
    x_pcnt_driver_install(PCNT_UNIT_0);

} // quadrature_encoder_counter_init
/*-----------------------------------------------------------*/


/* --------------------------------------------------------- */
/* ---Remote Receiver, for input Step control           ---- */
/* --------------------------------------------------------- */
esp_err_t x_rmt_get_ringbuf_handle(rmt_channel_t channel, RingbufHandle_t* buf_handle)
{
    RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
    RMT_CHECK(x_p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL);
    RMT_CHECK(buf_handle != NULL, RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
    *buf_handle = x_p_rmt_obj[channel]->rx_buf;
    return ESP_OK;
} // x_rmt_get_ringbuf_handle
/*--------------------------------------------------------*/


static void IRAM_ATTR x_rmt_driver_isr_default(void* arg)
{
    uint32_t intr_st = RMT.int_st.val;
    uint32_t i = 0;
    uint8_t channel;
    portBASE_TYPE HPTaskAwoken = 0;
    for(i = 0; i < 32; i++) {
        if(i < 24) {
            if(intr_st & BIT(i)) {
                channel = i / 3;                   
                x_rmt_obj_t* p_rmt = x_p_rmt_obj[channel];
                switch(i % 3) {
                    //TX END
                    case 0: {                      
                    }
                    //RX_END
                    case 1: {
                        ESP_EARLY_LOGD(RMT_TAG, "RMT INTR : RX END");
                        // Retrieve a bit-mask of the values of the first 32 GPIOs (0-31). Reading direct from reg is the quickest way to get data                       
                        RMT.conf_ch[channel].conf1.rx_en = 0;
                        RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_TX;
                        p_rmt->rx_count++;
                        if(p_rmt->rx_buf) {  // ringbuffer is attached to channel_0 only!!
                            uint32_t regval = REG_READ(GPIO_IN_REG) & BIT(21);        
                            char tx_item[1];
                            sprintf(tx_item,"%u", (regval & 1<< GPIO_NUM_21 ) >> GPIO_NUM_21);
                            BaseType_t res = xRingbufferSendFromISR(p_rmt->rx_buf, (void*) tx_item, sizeof(tx_item), &HPTaskAwoken);                           
                            if(res == pdFALSE) {
                                ESP_EARLY_LOGE(RMT_TAG, "RMT RX BUFFER FULL");
                            } else {
                                //
                            }
                            if(HPTaskAwoken == pdTRUE) {
                                portYIELD_FROM_ISR();
                            }
                        } 
                        RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
                        RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_RX;
                        RMT.conf_ch[channel].conf1.rx_en = 1;
                        break;  
                    }
                    //ERR
                    case 2: {
                        ESP_EARLY_LOGE(RMT_TAG, "RMT[%d] ERR", channel);
                        ESP_EARLY_LOGE(RMT_TAG, "status: 0x%08x", RMT.status_ch[channel]);
                        RMT.int_ena.val &= (~(BIT(i)));
                        break;
                    }
                    default:
                        break;
                }
                RMT.int_clr.val = BIT(i);
            }
        } else {
            if(intr_st & (BIT(i))) {
                channel = i - 24;
                x_rmt_obj_t* p_rmt = x_p_rmt_obj[channel];
                RMT.int_clr.val = BIT(i);
                ESP_EARLY_LOGD(RMT_TAG, "RMT CH[%d]: EVT INTR", channel);
            }
        }
    }
} // x_rmt_driver_isr_default
/*--------------------------------------------------------*/


esp_err_t x_rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr_alloc_flags)
{
    RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
    RMT_CHECK((s_rmt_driver_channels & BIT(channel)) == 0, "RMT driver already installed for channel", ESP_ERR_INVALID_STATE);

    esp_err_t err = ESP_OK;

    if(x_p_rmt_obj[channel] != NULL) {
        ESP_LOGD(RMT_TAG, "RMT driver already installed");
        return ESP_ERR_INVALID_STATE;
    }

    x_p_rmt_obj[channel] = (x_rmt_obj_t*) malloc(sizeof(x_rmt_obj_t));

    if(x_p_rmt_obj[channel] == NULL) {
        ESP_LOGE(RMT_TAG, "RMT driver malloc error");
        return ESP_ERR_NO_MEM;
    }
    memset(x_p_rmt_obj[channel], 0, sizeof(x_rmt_obj_t));

    x_p_rmt_obj[channel]->channel = channel;

    if(x_p_rmt_obj[channel]->rx_buf == NULL && rx_buf_size > 0) {
        x_p_rmt_obj[channel]->rx_buf = xRingbufferCreate(rx_buf_size, RINGBUF_TYPE_BYTEBUF);
        rmt_set_rx_intr_en(channel, 1);
        rmt_set_err_intr_en(channel, 1);
    }

    _lock_acquire_recursive(&_driver_isr_lock);

    if(s_rmt_driver_channels == 0) { // first RMT channel using driver
        err = rmt_isr_register(x_rmt_driver_isr_default, NULL, intr_alloc_flags, &x_s_rmt_driver_intr_handle);
    }
    else {
        if(s_rmt_driver_channels == 1) { // second RMT channel using driver
            rmt_set_rx_intr_en(channel, 1);
        }
    }
    if (err == ESP_OK) {
        s_rmt_driver_channels |= BIT(channel);
        rmt_set_tx_intr_en(channel, 1);
    }

    _lock_release_recursive(&_driver_isr_lock);

    return err;
} // x_rmt_driver_install
/*--------------------------------------------------------*/


static void rmt_rx_init_0() { 
rmt_config_t rmt_rx;
    rmt_rx.channel = (rmt_channel_t)RMT_RX_CHANNEL_0;
    rmt_rx.gpio_num = RMT_RX_GPIO_NUM_0;
    rmt_rx.clk_div = RMT_CLK_DIV;
    rmt_rx.mem_block_num = 1;
    rmt_rx.rmt_mode = RMT_MODE_RX;
    rmt_rx.rx_config.filter_en = true;
    rmt_rx.rx_config.filter_ticks_thresh = 50;  // Pulses shorter than this setting will be filtered out.
    rmt_rx.rx_config.idle_threshold = 100;
    rmt_config(&rmt_rx);
    x_rmt_driver_install(rmt_rx.channel, RMT_BUFFER_SIZE, ESP_INTR_FLAG_IRAM);  // channel, ringbuffer size, interrup allocation flag 
    rmt_rx_start((rmt_channel_t)RMT_RX_CHANNEL_0, 1);
} // RMT_rx_init_0
/* ------------------------------------------------------ */


static void rmt_rx_init_1() { 
rmt_config_t rmt_rx;
    rmt_rx.channel = (rmt_channel_t)RMT_RX_CHANNEL_1;
    rmt_rx.gpio_num = RMT_RX_GPIO_NUM_1;
    rmt_rx.clk_div = RMT_CLK_DIV;
    rmt_rx.mem_block_num = 1;
    rmt_rx.rmt_mode = RMT_MODE_RX;
    rmt_rx.rx_config.filter_en = true;
    rmt_rx.rx_config.filter_ticks_thresh = 50;  // Pulses shorter than this setting will be filtered out.
    rmt_rx.rx_config.idle_threshold = 100;
    rmt_config(&rmt_rx);
    x_rmt_driver_install(rmt_rx.channel, 0,0);  // channel, ringbuffer size, interrup allocation flag 
    rmt_rx_start((rmt_channel_t)RMT_RX_CHANNEL_1, 1);
} // RMT_rx_init_1
/* ------------------------------------------------------ */


/* ------------------------------------------------------ */
/* ------------GPIO Setup-------------------------------- */
/* ------------------------------------------------------ */
void gpio_init() {
    gpio_pad_select_gpio(BLINK_GPIO);
    gpio_pad_select_gpio(GPIO_DIR);
    gpio_pad_select_gpio(GPIO_STEP);
    gpio_pad_select_gpio(GPIO_eStop);
    gpio_pad_select_gpio(GPIO_DUMMY_STEP);
    gpio_pad_select_gpio(GPIO_DUMMY_ENCODER);
    gpio_set_pull_mode(GPIO_STEP, GPIO_PULLDOWN_ONLY);
    gpio_set_pull_mode(GPIO_eStop, GPIO_PULLUP_ONLY);
    gpio_set_pull_mode(GPIO_DUMMY_STEP, GPIO_PULLDOWN_ONLY);
    gpio_set_direction(BLINK_GPIO,GPIO_MODE_OUTPUT);
    gpio_set_direction(GPIO_DIR,GPIO_MODE_OUTPUT);
    gpio_set_direction(GPIO_STEP,GPIO_MODE_OUTPUT);
    gpio_set_direction(GPIO_eStop,GPIO_MODE_INPUT);
    gpio_set_direction(GPIO_DUMMY_STEP,GPIO_MODE_OUTPUT);
    gpio_set_direction(GPIO_DUMMY_ENCODER,GPIO_MODE_OUTPUT);
    gpio_set_intr_type(GPIO_eStop, GPIO_INTR_ANYEDGE);
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    gpio_isr_handler_add(GPIO_eStop, gpio_isr_eStop_handler, (void*) GPIO_eStop); // pin23 // eStop
}// gpio_init
/*-----------------------------------------------------------*/
  

/* --------------------------------------------------------- */
/* --- Receiver Tasks, for output Step control           ---- */
/* --------------------------------------------------------- */
static void rmt_rx_task(void *pvParameters)
{
    double posError[5] = { }; 
    int minError;
    int i;
    uint32_t ulNotificationValue;
    char lastDir;
    lastDir = ' ';

    RingbufHandle_t rb = NULL;
    //get RMT RX ringbuffer handle
    x_rmt_get_ringbuf_handle((rmt_channel_t)RMT_RX_CHANNEL_0, &rb);
    // write output step pin HIGH
    REG_WRITE(GPIO_OUT_W1TS_REG, BIT(19)); // GPIO_STEP
    while(rb) {
        if (eStop) {
            vTaskSuspend(NULL);          
        }
        size_t rx_size = 0;
        char *item = (char *) xRingbufferReceiveUpTo(rb, &rx_size, portMAX_DELAY,1);
        if(item) {
            isr_handler_rx_count_all++;
            
            // when the Ringbuffer is empty check to see if all input steps have been dealt with
            if ((int)xRingbufferGetCurFreeSize(rb) == RMT_BUFFER_SIZE-2) {
                if ((x_p_rmt_obj[0]->rx_count != isr_handler_rx_count_all) || (x_p_rmt_obj[0]->rx_count != x_p_rmt_obj[1]->rx_count) ) {
                    ESP_LOGE("CONTROL", "Major Error - OUTPUT STOPPED"); 
                    criticalError = true;
                    vTaskSuspend(NULL); 
                    doCriticalError();
                }
            }
                       
            //after reading the data, return spaces to ringbuffer.
            vRingbufferReturnItem(rb, (void*) item);
            char dir = item[0];
            if (dir == '1') {
                // write pin HIGH
                REG_WRITE(GPIO_OUT_W1TS_REG, BIT(18)); // GPIO_OUTPUT_IO_2
                isr_handler_rx_count--;
            };
            if (dir == '0') {
                // write pin LOW
                REG_WRITE(GPIO_OUT_W1TC_REG, BIT(18));
                isr_handler_rx_count++;
            }; 
            
            // if change in direction deal with backlash, but not first time through loop
            if (doBacklash && dir != lastDir  && lastDir != ' ') {
                for (i = 0; i < backlashSteps; i++) {
                    // write pin LOW
                    REG_WRITE(GPIO_OUT_W1TC_REG, BIT(19)); // GPIO_STEP // the step output gets converted / flipped
                    delayMicroseconds(10);                 // so sent as low to high
                    // write pin HIGH
                    REG_WRITE(GPIO_OUT_W1TS_REG, BIT(19)); // GPIO_STEP
                    delayMicroseconds(1200);           
                }

                if (dir == '0') {
                    if (backlashErrAdj == 0) {
                        backlashErrAdj = -15;
                    }
                    else {
                        backlashErrAdj = 0;
                    }
                }
                else {
                    if (backlashErrAdj == 0) {
                        backlashErrAdj = 15;
                    }
                    else {
                        backlashErrAdj = 0;
                    }
                }
            }
            lastDir = dir;
                  
            delayMicroseconds(10);                 // give a minimum 5us delay after a DIR pulse    
            // send STEP                           // output needs to be high to low for falling edge
            // write pin LOW                       // write direct to register as faster than using 'gpio_set_level'
            REG_WRITE(GPIO_OUT_W1TC_REG, BIT(19)); // GPIO_STEP // the step output gets converted / flipped
            delayMicroseconds(10);                 // so sent as low to high
            // write pin HIGH
            REG_WRITE(GPIO_OUT_W1TS_REG, BIT(19)); // GPIO_STEP
            
            delayMicroseconds(100);  // gives time for encoder interrups to be received
 

            //get the best encoder error result achived in last 5 steps
            //store results in an array - posError
            for(int i = 0; i < 4; i++)
            {   
                posError[i] = posError[i+1];  // move results left within array, [1] to [0] etc
            }
            //posError[4] = isr_handler_rx_count -((encCountVal)/3); // add current result to end of array
            posError[4]= std::abs(isr_handler_rx_count -((encCountVal)/stepEncDivide));
            int minError = posError[0] ; // set minError to first element of array
            for ( int i=0;  i < sizeof(posError)/sizeof(posError[0]);  ++i ) {  // find min results from within array
                if ( posError[i] < minError )
                minError = posError[i] ;
            }

            // store step / encoder Variables for display
            portENTER_CRITICAL_ISR(&mux);
            dis_var.enc_count = encCountVal;
            dis_var.handler_rx_count = isr_handler_rx_count;
            // validate step / encoder events  - a step event should always be followed by an encoder event
            dis_var.handler_err_count = minError;
            portEXIT_CRITICAL_ISR(&mux);
            //move the last 4 elements of the array forward


        }
        
    }
    vTaskDelete(NULL);
} // rmt_rx_task
/* ------------------------------------------------------ */


/*-----------------------------------------------------------*/
/* --------------Startup Config Section--------------- */
/* --------------------------------------------------------- */

#ifdef __cplusplus
extern "C" {
#endif
void app_main() {
    //gpio setup
    gpio_init();
    //step receive setup
    rmt_rx_init_0();    
    //step comparison counter setup 
    rmt_rx_init_1(); 
    //encoder input setup
    quadrature_encoder_counter_init_0(); 
    //step received handler task
    xTaskCreatePinnedToCore(rmt_rx_task, "rmt_rx_task", 2048, NULL, 10, &x_rx_Task, 1);
    // create event timer
    const esp_timer_create_args_t event_timer_args = {
        .callback = &event_timer_callback,
        .arg = (void*) event_timer,
        .dispatch_method = ESP_TIMER_TASK,
        .name = "event_timer"
    };
    esp_timer_create(&event_timer_args, &event_timer);
    // The timer has been created, so start it running
    esp_timer_start_periodic(event_timer, 25); // fires off every 25us

    //set oled display 
	oled = OLED(GPIO_NUM_26, GPIO_NUM_25, SSD1306_128x64);
    //display opening screen
	if (oled.init()) {
		ESP_LOGI("OLED", "oled initiated");
		oled.draw_rectangle(10, 30, 20, 20, WHITE);
		oled.fill_rectangle(12,32,16,16, WHITE);
		oled.select_font(0);
		oled.draw_string(0, 0, "Hello Mate", WHITE, BLACK);
		oled.select_font(1);
		oled.draw_string(0, 12, "Step Control is here to stay, oh yeah!", WHITE, BLACK);
		oled.draw_string(35, 35, "E S P 3 2  !", WHITE, BLACK);
		oled.refresh(true);
		vTaskDelay(2500 / portTICK_PERIOD_MS);
		xTaskCreatePinnedToCore(&displayTask, "displaytask", 2048, NULL, 2, NULL, 1);
	} else {
		ESP_LOGE("OLED", "oled initiation failed");
	}

    if (TestMode) {
        uint32_t cpu_freq;
        cpu_freq = rtc_clk_cpu_freq_get();
        ESP_LOGI("DEBUG", "CPU Speed %d", cpu_freq ); // 1 = 80mhz, 2 = 160mhz, 3 = 240mhz
        
        while(1) {
        portENTER_CRITICAL_ISR(&mux);
            ESP_LOGI("DEBUG", "CH0 totalCount: %ld", x_p_rmt_obj[0]->rx_count );
            ESP_LOGI("DEBUG", "CH1 totalCount: %ld", x_p_rmt_obj[1]->rx_count ); 
        portEXIT_CRITICAL_ISR(&mux); 
            vTaskDelay(500 / portTICK_PERIOD_MS);
        }
    }
}
#ifdef __cplusplus
}
#endif

Who is online

Users browsing this forum: No registered users and 75 guests