Как вызвать функции C из скетча ардуино?

Я хотел бы знать, есть ли способ вызвать функции, содержащиеся в файлах C, с помощью скетча Arduino?

Мой файл C объявляет и определяет функцию. Чтобы не помещать грязное определение функции в мой скетч Arduino sketch, я бы хотел вызвать эту функцию прямо из скетча.

Есть ли стандартный способ сделать это с помощью Arduino и C? Вот скетч:

#include "crc16.h";

void setup(){

}

void loop(){

  CalculateCRC16("<09M", 4);

}

а это урезанный файл C:

#include <stdio.h>
#include <stdint.h>

uint16_t crctable[256] =
{
    0x0000, 0x1189,.....



uint16_t // Returns Calculated CRC value
CalculateCRC16( // Call example CalculateCRC16("<09M", 4);
    const void *c_ptr, // Pointer to byte array to perform CRC on
    size_t len)        // Number of bytes to CRC
{

    uint16_t crc = 0xFFFF // Seed for CRC calculation
    const uint8_t *c = c_ptr;

    while (len--)
        crc = (crc << 8) ^ crctable[((crc >> 8) ^ *c++)];

    return crc;
}

, 👍11

Обсуждение

Есть ли причина, по которой ваш файл должен использовать C вместо C++?, @Peter Bloomfield

Вообще-то да. Когда я пытаюсь скомпилировать файл с помощью C++, есть ошибки, но в C. Ошибка вызвана строками: const void *c_ptr и`const uint8_t *c = c_ptr;'. В сообщении об ошибке упоминается недопустимое преобразование между типами., @user_name

Не могли бы вы опубликовать 2 файла кода (или упрощенную минимальную версию), которые производят ошибку, и скопировать и вставить сообщение об ошибке в полном объеме?, @drodri

Сообщения об ошибках не так хороши: В функции uint16_t CalculateCRC16(uint16_t, const void*, size_t)': 46 недопустимое преобразование из const void*' в `const uint8_t*' В функции int main()': 57 system' undeclared (сначала используйте эту функцию) (Каждый незаявленный идентификатор сообщается только один раз для каждой функции, в которой он появляется.), @user_name

Когда функция была вызвана в файле c, как я замечаю это в оригинальном файле Arduino (ino)? Могу ли я создать любую функцию обратного вызова из файла c в файл ino?, @tk97tk

Файлы INO - это C++. Если вы хотите вызвать функцию C++ из C, вы должны создать ее с помощью связи C. extern "C" void foo() { Serial.println("Foo"); }, @Majenko


3 ответа


Лучший ответ:

11

Вы можете добавить "C" #, например, следующее:

extern "C"{
#include "crc16.h"
};

void setup(){
}

void loop(){
  CalculateCRC16("<09M", 4);
}

И файл crc16.h может быть (некоторые незначительные исправления, #pragma один раз, приведение):

#pragma once

#include <stdio.h>
#include <stdint.h>

uint16_t crctable[2] ={ 0x0000, 0x1189};

uint16_t CalculateCRC16( // Вызов примера CalculateCRC16("<09M", 4);
    const void *c_ptr, // Указатель на массив байтов для выполнения CRC на
    size_t len)        // Количество байтов для CRC
{
    uint16_t crc = 0xFFFF; // Начальное значение для вычисления CRC
    const uint8_t *c = (const uint8_t *)c_ptr;

    while (len--)
        crc = (crc << 8) ^ crctable[((crc >> 8) ^ *c++)];

    return crc;
}
,

Спасибо, сейчас это работает просто отлично. Не могли бы вы, пожалуйста, объяснить необходимость прагмы?, @user_name

Конечно, это хорошая практика, хотя в вашем примере она не нужна. Это позволяет избежать повторного включения одного и того же файла заголовка в файл компиляции. Представьте себе a.cpp->(b.h и c.h) и b.h->>c.h., которые будут дублировать содержимое c.h при компиляции a.cpp. #pragma однажды избежит этого. Также для этого используются директивы защиты #ifndef _MY_FILE_H_INCLUDED #define _MY_FILE_H_INCLUDED. Однако обратите внимание, что, как указывает Питер Р. Блумфилд, возможно, было бы лучше поместить реализацию CalculateCRC16 в файл cpp и оставить только объявление в файле заголовка., @drodri

Хорошо, я вижу, что это становится проблемой, когда код становится все более и более сложным. Спасибо за совет., @user_name


1

Да, просто скопируйте его строку объявления в свой скетч:

extern "C" {
    void myfunction(int arg);
}
,

4

Вашу функцию CRC можно легко преобразовать в C++, чтобы она могла перейти в файл *.cpp. Все, что вам нужно сделать, это использовать явное приведение при инициализации указателя c. Вот "правильный" способ C++ сделать это:

const uint8_t *c = static_cast<const uint8_t*>(c_ptr);

Тем не менее, старый актерский состав в стиле C также будет работать:

const uint8_t *c = (const uint8_t*)c_ptr;

Проблема в основном в том, что C может быть немного более снисходительным, позволяя вам неявно преобразовывать указатели между типами. Чтобы сделать это на C++, вам нужно явно сообщить компилятору, что преобразование является преднамеренным.

,