pierremolinaro / acan2517

Arduino CAN driver for MCP2517FD CAN Controller (in CAN 2.0B mode)
MIT License
22 stars 10 forks source link

Use without global ACAN2517 objects #4

Open driftregion opened 5 years ago

driftregion commented 5 years ago

Hi Pierre, thanks for this great library! It is working out of the box on my ESP32 dev board with two MCP2517FD devices on a single SPI bus.

I'm trying to organize ACAN2517 objects as struct members of a parent object instead of as globally scoped instances. I'm failing to understand how to properly call ACAN2517.begin(...) in this scenario.

void Parent::init(uint32_t baud_rate)
{
    ACAN2517Settings settings(ACAN2517Settings::OSC_40MHz, baudrate);
    settings.mRequestedMode = ACAN2517Settings::Normal20B;

    const uint32_t errorCode = driver->begin(settings, [] {driver->isr();});
    if (errorCode)
        Serial.printf("Configuration error 0x%x\n", errorCode);
    else
        Serial.println("ACAN2517 Configuration success");
}

This fails to compile with the error message: the enclosing-function 'this' cannot be referenced in a lambda body unless it is in the capture list

As best I understand: driver->begin() requires a callback function of type void (*)(void).

If I change the lambda function definition to include this:

const uint32_t errorCode = driver->begin(settings, [this] {driver->isr();});

Then the lambda function signature becomes void(*)(Parent*) which is incompatible with driver.begin().

How might one call begin() on ACAN2517 objects that are private to a class?

Thank you, Nick

pierremolinaro commented 5 years ago

Hi Nick,

Thank you for your message!

I am not a C++ lambda specialist. ESP32 current build system uses gcc with -std=gnu++11 option, and it seems C++11 with the lambda expression partially supports closure.

So I did find a good solution for your problem.

I only find a workaround: using the global Parent instance. If you have declared : Parent parent ;

You can write: const uint32_t errorCode = driver.begin (settings, [] { parent.driver.isr () ; }) ;

I expect this can help.

Best regards,

Pierre

Le 25 mai 2019 à 10:19, Nick Kirkby notifications@github.com a écrit :

Hi Pierre, thanks for this great library! It is working out of the box on my ESP32 dev board with two MCP2517FD devices on a single SPI bus https://github.com/nkirkby/acan2517/blob/two_devices/examples/LoopBackDemoESP32/LoopBackDemoESP32.ino#L46.

I'm trying to organize ACAN2517 objects as struct members of a parent object instead of as globally scoped instances. I'm failing to understand how to properly call ACAN2517.begin(...) in this scenario.

void Parent::init(uint32_t baud_rate) { ACAN2517Settings settings(ACAN2517Settings::OSC_40MHz, baudrate); settings.mRequestedMode = ACAN2517Settings::Normal20B;

const uint32_t errorCode = driver->begin(settings, [] {driver->isr();});
if (errorCode)
    Serial.printf("Configuration error 0x%x\n", errorCode);
else
    Serial.println("ACAN2517 Configuration success");

} This fails to compile with the error message: the enclosing-function 'this' cannot be referenced in a lambda body unless it is in the capture list

As best I understand: driver->begin() requires a callback function of type void (*)(void).

If I change the lambda function definition to include this:

const uint32_t errorCode = driver->begin(settings, [this] {driver->isr();}); Then the lambda function signature becomes void()(Parent) which is incompatible with driver.begin().

How might one call begin() on ACAN2517 objects that are private to a class?

Thank you, Nick

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/pierremolinaro/acan2517/issues/4?email_source=notifications&email_token=AEWKZVDIQTK54MBA22U5ICTPXDY7PA5CNFSM4HPUEH7KYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4GV2TXFA, or mute the thread https://github.com/notifications/unsubscribe-auth/AEWKZVERVKQ6EENQ4GDWYFTPXDY7PANCNFSM4HPUEH7A.

driftregion commented 5 years ago

Pierre, Thank you for your response! This is a good solution for the time being.

I think I've found a relevant Q&A on StackOverflow: https://stackoverflow.com/questions/27045304/callback-to-a-member-function

FreeRTOS's xTaskCreate handles this scenario by accepting a callback function of signature void (*)(void *). This allows the optional use of an object within a static callback function. For example:

static void led_callback(void *pvParameter)
{
    StatusLED *led = (StatusLED*)pvParameter;
    while (1)
    {
        switch (led->state)
        {
            case FAST_BLINK:
                vTaskDelay(50 / portTICK_PERIOD_MS);
                led->set_brightness(0);
                vTaskDelay(50 / portTICK_PERIOD_MS);
                led->set_brightness(1);
                break;
            case OFF:
            default:
                led->set_brightness(0);
                vTaskDelay(500 / portTICK_PERIOD_MS);
                break;
        }
    }
}

void StatusLED::setup()
{
    xTaskCreate(led_callback, "LED blinker", 2048, this, 1, NULL);
}

In the event that no such object is needed, a NULL pointer may be passed instead of this.

I looked at adding this to ACAN2517, but found that the Arduino attachInterrupt function only accepts callbacks of type void (*)(void).

pierremolinaro commented 5 years ago

Hello,

You are right, ESP32 task creation has a parameter, I use this feature for the myESP32Task task. But the problem is on the interrupt service routine, that does not accept any argument.

Pierre

Le 26 mai 2019 à 02:51, Nick Kirkby notifications@github.com a écrit :

Pierre, Thank you for your response! This is a good solution for the time being.

I think I've found a relevant Q&A on StackOverflow: https://stackoverflow.com/questions/27045304/callback-to-a-member-function https://stackoverflow.com/questions/27045304/callback-to-a-member-function FreeRTOS's xTaskCreate handles this scenario by accepting a callback function of signature void ()(void ). This allows the optional use of an object within a static callback function. For example:

static void led_callback(void pvParameter) { StatusLED led = (StatusLED*)pvParameter; while (1) { switch (led->state) { case FAST_BLINK: vTaskDelay(50 / portTICK_PERIOD_MS); led->set_brightness(0); vTaskDelay(50 / portTICK_PERIOD_MS); led->set_brightness(1); break; case OFF: default: led->set_brightness(0); vTaskDelay(500 / portTICK_PERIOD_MS); break; } } }

void StatusLED::setup() { xTaskCreate(led_callback, "LED blinker", 2048, this, 1, NULL); } In the event that no such object is needed, a NULL pointer may be passed instead of this.

I looked at adding this to ACAN2517, but found that the Arduino attachInterrupt function only accepts callbacks of type void (*)(void).

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pierremolinaro/acan2517/issues/4?email_source=notifications&email_token=AEWKZVCRBWI32V5PKRZIOHLPXHNJ5A5CNFSM4HPUEH7KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWH3VPA#issuecomment-495958716, or mute the thread https://github.com/notifications/unsubscribe-auth/AEWKZVBVT6OTW6WMHWNJWVLPXHNJ5ANCNFSM4HPUEH7A.

driftregion commented 5 years ago

After reading StackOverflow: Passing capturing lambda as a function pointer, I made another attempt at this and failed. I learned a couple things along the way:

pierremolinaro commented 5 years ago

Thank you for theses points.

Best regards,

Pierre

Le 27 mai 2019 à 22:32, Nick Kirkby notifications@github.com a écrit :

After reading StackOverflow: Passing capturing lambda as a function pointer https://stackoverflow.com/questions/28746744/passing-capturing-lambda-as-function-pointer, I made another attempt at this and failed. I learned a couple things along the way:

it's possible to change the compiler flags used by Arduino to compile with xtensa-esp32-elf-gcc in ~/ Arduino/hardware/espressif/esp32/platform.txt ESP32 for Arduino currently uses std=gnu++11 (source https://github.com/espressif/esp-idf/issues/2449) — You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pierremolinaro/acan2517/issues/4?email_source=notifications&email_token=AEWKZVFFK7IOX4QZ2XGQEMTPXRANDA5CNFSM4HPUEH7KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWKPEBY#issuecomment-496300551, or mute the thread https://github.com/notifications/unsubscribe-auth/AEWKZVEDWZ37EBTLFOXOT4TPXRANDANCNFSM4HPUEH7A.

driftregion commented 5 years ago

It looks like the necessary function exists inside arduino-esp32 FunctionalInterrupt.cpp, but it's not a public function. I've posted a question on StackOverflow hoping to draw on the expertise there.