Если в скетче, то низачем (при условии, что Ваша библиотека не пользуется экземпляром). А вот если в библиотеке, то для того, чтобы скетч знал, что где-то экземпляр определен.
extern вообще делается для того, чтобы знать, что такая вещь где-то есть.
Есть еще одна задача, которую пытаюсь решить но пока с трудом....
При создании экземпляра класса, мне нужно передать имя функции, которую должел вызвать экземпляр класса Bus4.
В функцию нужно передать ссылку на заполненную структуру
Пример
MyClass Bus( 33, func<с параметрами>)
33 - это просто uint, а func - это имя функции, которая описана в скетче как
void func(ссылка на структуру экземпляра Bus) {
f1 = <Элемент структуры экземпляра Bus>
}
Ну, передавать классу ссылку на функцию - само по себе идея плохая. Классы и ссылки на функции - это разные технологии программирования.
Но если сильно надо, так передавайте. Если нужна помощь по синтаксису, то напишите нормально прототип функции (конкретный) , а то в Ваших обощениях я ничего не понимаю.
// *********************************************************************
// Bus4.h - library for Arduino - Version 0.2
//
// Original library (0.2) by Igor Kuznetsov
//
// Протокол обмена
// sendBuf[7], recvBuf[7]
// <Адрес><Команда><Регистр><D3><D2><D1><D0><CRC>
// Адрес - Адрес устройства кому предназначен пакет. Если FF - всем
// Команда-0xff - Передача ID устройства (ID в поле данных)
// 0x01 - Чтение uint8_t D0
// 0x02 - Чтение uint16_t D1, D0
// 0x03 - Чтение uint32_t D3, D2, D1, D0
// 0x11 - Запись байта D0
// 0x12 - Запись uint8_t D0
// 0x13 - Запись uint16_t D1, D0
// 0x14 - Запись uint32_t D3, D2, D1, D0
// 0xFA - Чтение переменных из EEPROM
// 0xFB - Запись переменных в EEPROM
// Регистр - 00..FF - Адрес регистра
// D0..D3 - данные
// CRC - контрольная сумма
//
// =====================================================================
#ifndef Bus4_h
#define Bus4_h
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
// *******************************************************************
//Command code
#define CMD_READL 1
#define CMD_READ8 2
#define CMD_READ16 3
#define CMD_READ32 4
#define CMD_WRITEL 5
#define CMD_WRITE8 6
#define CMD_WRITE16 7
#define CMD_WRITE32 8
// Структура буфера приема
struct xBuf_t { // Структура буфера
uint8_t adr;
uint8_t cmd;
uint8_t reg;
uint8_t b3;
uint8_t b2;
uint8_t b1;
uint8_t b0;
};
class ProtocolBus4 {
public:
// Кноструктор
ProtocolBus4(uint32_t SIGN, void (*f)() );
void Recv(void); // Процедура приема.
// передача данных
void sendLine(uint8_t adr, uint8_t reg, uint8_t value); //
void sendLine(uint8_t adr, uint8_t reg, uint16_t value); //
void sendLine(uint8_t adr, uint8_t reg, uint32_t value); //
void (*func)();
private:
uint32_t _SIGN; // = 0x33333333; // Сигнатура начала пакета
uint8_t _count; // Счетчик байт данных (принимаем 4 байта данных)
uint8_t _inChr; // принятый байт
uint32_t _sign; // регистр сигнатуры с линии. Данные полученные с линии.
uint8_t _crc; // Подсчет CRC (XOR) с линии. Сигнатура не считается.
uint8_t _mode; // Режим.
// 0 - ожидание сигнатуры, 1 - ожидание адреса,
// 2 - ожидание команды, 3 - ожидание регистра,
// 4 - ожидание данных, 5 - проверка crc Если crc верно - буфер готов
xBuf_t xBuf; // Буфер данных с линии
uint8_t *p_b0; // Указатель на xBuf.b0
uint32_t _tStart; // = 0;
uint32_t _millisec; // = 0;
uint32_t _timeOut; // = 10000; // В реальной системе тайм аут следует снизить. Например 50 мс.
// Следующие переменные для замера времени выполнения основного цикла, мкс
//uint32_t t1;
//uint32_t t2;
uint8_t sendBuf[8]; // Буфер для отправки
void sendPacket(void);
};
#endif
Bus4.cpp
#include <Bus4.h>
//
// Протокол обмена
// sendBuf[7], recvBuf[7]
// <Адрес><Команда><Регистр><D3><D2><D1><D0><CRC>
// Адрес - Адрес устройства кому предназначен пакет. Если FF - всем
// Команда-0xff - Передача ID устройства (ID в поле данных)
// 0x01 - Чтение uint8_t D0
// 0x02 - Чтение uint16_t D1, D0
// 0x03 - Чтение uint32_t D3, D2, D1, D0
// 0x11 - Запись байта D0
// 0x12 - Запись uint8_t D0
// 0x13 - Запись uint16_t D1, D0
// 0x14 - Запись uint32_t D3, D2, D1, D0
// 0xFA - Чтение переменных из EEPROM
// 0xFB - Запись переменных в EEPROM
// Регистр - 00..FF - Адрес регистра
// D0..D3 - данные
// CRC - контрольная сумма
//
//********************************************************************
void (*func)();
// Конструктор
ProtocolBus4::ProtocolBus4(uint32_t SIGN, void (*f)() ) {
_SIGN = SIGN;
func = f;
}
void ProtocolBus4::Recv() {
// Обработка тайм-аута. Если на линии ничего нет более чем TIME_OUT - сбрасываем режим
_millisec = millis();
if ( (_millisec - _tStart) > _timeOut) {
_mode = 0; // Сброс режима
_sign = 0; // Сброс регистра сигнатуры
}
uint8_t n = Serial.available();
// Если буфер приема не пустой
if ( n > 0) {
_tStart = _millisec; // Время отсчета тайм-аута
//t1 = micros(); // Для отладки. Вычисление времени работы цикла
_inChr = Serial.read();
//Serial.print(" inChr = ");
//Serial.println(inChr, HEX);
//Serial.print(" n = ");
//Serial.println(n);
//Serial.print(" mode = ");
//Serial.println(mode);
switch ( _mode ) {
// Режим 0. Проверка на сигнатуру
case 0:
_sign |= _inChr; // Принятый с линии байт помещаем в младший байт 4-х байтовой переменной
//Serial.print(" sign = ");
//Serial.println(sign, HEX);
if (_sign == _SIGN) { // Если сигнатура с линии совпала с заданной
_sign = 0; // Обнуляем сигнатуру для следующего пакета
//Serial.println("SIGN - OK");
_mode = 1; // Переходим на ввод адреса
_crc = 0; // Очистили подсчет crc
_count = 0; // Обнулили счетчик байт данных. (4 байта)
}
else {
_sign = _sign << 8;
//Serial.println("SIGN - Filed");
}
break;
// Ввод адреса устройства
case 1:
_crc ^= _inChr; // Считаем контрольную сумму XOR
xBuf.adr = _inChr; // записали адрес в буфер
_mode = 2; // Переходим на ввод команды
//Serial.print(" xBuf.adr = ");
//Serial.println(xBuf.adr, HEX);
break;
// Ввод команды
case 2:
_crc ^= _inChr; // Считаем контрольную сумму XOR
xBuf.cmd = _inChr; // Записали команду в буфер
_mode = 3; // Переходим на ввод регистра
//Serial.print(" xBuf.cmd = ");
//Serial.println(xBuf.cmd, HEX);
break;
// Ввод регистра
case 3:
_crc ^= _inChr; // Считаем контрольную сумму XOR
xBuf.reg = _inChr; // Записали регистр в буфер
_mode = 4; // Переходим на ввод команды
p_b0 = &xBuf.b0; // Получаем адрес xBuf.b0
//Serial.print(" xBuf.reg = ");
//Serial.println(xBuf.reg, HEX);
break;
// Ввод данных 4 байта
case 4:
_crc ^= _inChr; // Считаем контрольную сумму XOR
*(p_b0 - _count) = _inChr; // Записываем байт по адресу xBuf.b0 скорректированному на номер байта
_count++; // Переходим к следующему байту данных
if (_count == 4) { // Если 4 байта приняты (от 0 до 3)
_mode = 5; // Переходим на ввод crc
//Serial.print("b3 = ");
//Serial.println(xBuf.b3, HEX);
//Serial.print("b2 = ");
//Serial.println(xBuf.b2, HEX);
//Serial.print("b1 = ");
//Serial.println(xBuf.b1, HEX);
//Serial.print("b0 = ");
//Serial.println(xBuf.b0, HEX);
}
//Serial.print("count = ");
//Serial.println(count, HEX);
break;
// Ввод crc
case 5:
if (_crc == _inChr) { // Сравниваем подсчитанную crc с указанной в inChr
_mode = 0; // Сбрасываем режим
//Serial.println(" crc - OK!");
//Serial.println("OK"); // Успешное чтение пакета
// Тут может быть вызов процедуры установки значений
// или просто взведение флага готовности
// или копирование буфера в выходные переменные
// (*func)();
}
else { // Контрольная сумма не совпала
_mode = 0; // Сброс режима
//Serial.println(" crc - FALSE!");
//Serial.println("Err");
// (*func)();
};
break;
} // switch ( mode )
//Serial.print("crc = ");
//Serial.println(crc, HEX);
// Для отладки. Подсчте времени выполнения цикла.
// Без отладочных вызовов Serial.print колеблется от 12 до 40 мкс
// С отладочными Serial.print - время увеличивается до 800 мкс
//t2 = micros();
//Serial.print(" T = ");
//Serial.println(t2-t1);
} // if ( n > 0)
} // Recv()
// **************************************************
// ПЕРЕДАЧА
//
// Передача байта
void ProtocolBus4::sendLine(uint8_t adr, uint8_t reg, uint8_t value) {
sendBuf[0] = adr;
sendBuf[1] = CMD_WRITE8;
sendBuf[2] = reg;
sendBuf[3] = 0;
sendBuf[4] = 0;
sendBuf[5] = 0;
sendBuf[6] = value;
sendBuf[7] = adr ^ CMD_WRITE8 ^ reg ^ sendBuf[3] ^ sendBuf[4] ^ sendBuf[5] ^ sendBuf[6];
sendPacket();
}
// Передача uint16_t
void ProtocolBus4::sendLine(uint8_t adr, uint8_t reg, uint16_t value) {
sendBuf[0] = adr;
sendBuf[1] = CMD_WRITE8;
sendBuf[2] = reg;
sendBuf[3] = 0;
sendBuf[4] = 0;
sendBuf[5] = (value & 0xff00) >> 8;
sendBuf[6] = value & 0x00ff;
sendBuf[7] = adr ^ CMD_WRITE8 ^ reg ^ sendBuf[3] ^ sendBuf[4] ^ sendBuf[5] ^ sendBuf[6];
sendPacket();
}
// Передача uint32_t
void ProtocolBus4::sendLine(uint8_t adr, uint8_t reg, uint32_t value) {
sendBuf[0] = adr;
sendBuf[1] = CMD_WRITE32;
sendBuf[2] = reg;
sendBuf[3] = (value & 0xff000000) >> 24;
sendBuf[4] = (value & 0x00ff0000) >> 16;
sendBuf[5] = (value & 0x0000ff00) >> 8;
sendBuf[6] = value & 0x000000ff;
sendBuf[7] = adr ^ CMD_WRITE8 ^ reg ^ sendBuf[3] ^ sendBuf[4] ^ sendBuf[5] ^ sendBuf[6];
sendPacket();
}
void ProtocolBus4::sendPacket() {
Serial.write((uint8_t*)(&_SIGN),4);
for (int i=0; i < 8; i++) {
Serial.write(sendBuf[i]);
}
}
Ошибки компиляции
Arduino: 1.8.5 (Windows 7), Плата:"Arduino Leonardo"
sign:10: error: 'exec' was not declared in this scope
ProtocolBus4 Bus4(0x33333333, exec);
^
exit status 1
'exec' was not declared in this scope
Этот отчёт будет иметь больше информации с
включенной опцией Файл -> Настройки ->
"Показать подробный вывод во время компиляции"
Ну, передавать классу ссылку на функцию - само по себе идея плохая. Классы и ссылки на функции - это разные технологии программирования.
Скорее все здесь немного непонятый ТС принцип. К примеру у нас есть канал. Но нам не нужен конкретный канал, а организация механизма, когда придет определеный пакет, то выполнится определеная функция. И да что бы можно было добавлять в систему новые устройства, а в канал новые функции под новые варианты пакетов.
ПС: так что скорее сейчас идет просто отработка кода.
...И да что бы можно было добавлять в систему новые устройства, а в канал новые функции под новые варианты пакетов.
ПС: так что скорее сейчас идет просто отработка кода.
Предполагаю вызывать указанную функцию при приеме любого валидного пакета. А уже в самой функции определять, что за команда пришла и что с ней делать. Для этого у нее будет адрес, команда, регистр и данные.
Идея понятна. Можно сразу ссылку на структуру (или массив) передать, где номер команды - он же номер строки массива - с элементом адреса функции, которую необходимо выполнить
В таком случае отпадает необходимость в явном описании конструктора. Я думал все начальные параметры в нем устанавливать. А раз имеются ограничения, то конструктор можно опустить (не указывать явно), а всю инициализацию провести в процедуре init.
Теперь осталось разобраться как передавать параметры в
Arduino: 1.8.5 (Windows 7), Плата:"Arduino Leonardo"
In file included from D:\Project\Arduino\sign\sign.ino:1:0:
C:\Program Files (x86)\Arduino\libraries\Bus4/Bus4.h:72:25: error: function definition does not declare parameters
Channel( (* f)() ) : func(f) {} ;
^
sign:3: error: 'exec' was not declared in this scope
Channel Bus4(exec);
^
exit status 1
'exec' was not declared in this scope
/**/
class Cl_AAA {
int val;
public:
Cl_AAA(int val_): val(val_) {}
};
class Cl_BBB {
int val;
Cl_AAA AAA;
public:
Cl_BBB(int val_, int val2_): val(val_), AAA(val2_) {} //AAA(val2_) <---обратите на это
};
//------------------------
Cl_BBB Cl_BBB(1, 2);
//----main----------------------
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
/* использует 502 байт (1%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 13 байт (0%) динамической памяти, оставляя 2035 байт для локальных переменных. Максимум: 2048 байт.
*/
У Вас проблема не в конструкторе, а в том, что Вы не везде неверно записываете тип "указатель на функцию".
До тех пор, пока Вы не привыкли, определяёте этот тип с помощью typedef и не таскайте за собой этот громоздкий синтаксис, а просто пишите одним словом.
Вот смотрите, вот вся Ваша задача
// Определение типа - указатель на функцияю void f(void)
// название типа - SuperFunction
typedef void (* SuperFunction)(void);
// Класс, конструтор которого принимает SuperFunction
class Kaka {
SuperFunction m_fPtr; // Сввойство для хранения SuperFunction
int m_k;
public:
Kaka(int n, SuperFunction f) {
m_k = n;
m_fPtr = f; // Присваивание свойству
}
void doSomething(void) {
m_fPtr(); // Вызов по сохранённому указателю
}
};
void exec(void) {}
Kaka instance(3, exec); // Сохздание экземпляра
И всего делов-то!
Создаётся экземпляр. указатель передаётся конструктору - всё как у людей.
------------------
Но ещё раз провторюсь, если Вам пришлось классу передавать указательна функцию, значит класс плохо спроектирован.
#include <Bus4.h>
// Конструктор
Channel::Channel(void (* f)() ) { }
/*
void Channel::init(uint32_t SIGN, void (* f)() ) {
_SIGN = SIGN;
_crc = 0;
_mode = 0;
_tStart = 0;
_millisec = 0;
_timeOut = 10000;
func = f;
}
*/
void (* func)();
void Channel::run() {
// Обработка тайм-аута. Если на линии ничего нет более чем TIME_OUT - сбрасываем режим
_millisec = millis();
if ( (_millisec - _tStart) > _timeOut) {
_mode = 0; // Сброс режима
_sign = 0; // Сброс регистра сигнатуры
}
uint8_t n = Serial.available();
// Если буфер приема не пустой
if ( n > 0) {
_tStart = _millisec; // Время отсчета тайм-аута
//t1 = micros(); // Для отладки. Вычисление времени работы цикла
_inChr = Serial.read();
//Serial.print(" inChr = ");
//Serial.println(inChr, HEX);
//Serial.print(" n = ");
//Serial.println(n);
//Serial.print(" mode = ");
//Serial.println(mode);
switch ( _mode ) {
// Режим 0. Проверка на сигнатуру
case 0:
_sign |= _inChr; // Принятый с линии байт помещаем в младший байт 4-х байтовой переменной
//Serial.print(" sign = ");
//Serial.println(sign, HEX);
if (_sign == _SIGN) { // Если сигнатура с линии совпала с заданной
_sign = 0; // Обнуляем сигнатуру для следующего пакета
//Serial.println("SIGN - OK");
_mode = 1; // Переходим на ввод адреса
_crc = 0; // Очистили подсчет crc
_count = 0; // Обнулили счетчик байт данных. (4 байта)
}
else {
_sign = _sign << 8;
//Serial.println("SIGN - Filed");
}
break;
// Ввод адреса устройства
case 1:
_crc ^= _inChr; // Считаем контрольную сумму XOR
packetIn.adr = _inChr; // записали адрес в буфер
_mode = 2; // Переходим на ввод команды
//Serial.print(" xBuf.adr = ");
//Serial.println(xBuf.adr, HEX);
break;
// Ввод команды
case 2:
_crc ^= _inChr; // Считаем контрольную сумму XOR
packetIn.cmd = _inChr; // Записали команду в буфер
_mode = 3; // Переходим на ввод регистра
//Serial.print(" xBuf.cmd = ");
//Serial.println(xBuf.cmd, HEX);
break;
// Ввод регистра
case 3:
_crc ^= _inChr; // Считаем контрольную сумму XOR
packetIn.reg = _inChr; // Записали регистр в буфер
_mode = 4; // Переходим на ввод команды
ptrX = &packetIn.b0; // Получаем адрес xBuf.b0
//Serial.print(" xBuf.reg = ");
//Serial.println(xBuf.reg, HEX);
break;
// Ввод данных 4 байта
case 4:
_crc ^= _inChr; // Считаем контрольную сумму XOR
*(ptrX + _count) = _inChr; // Записываем байт по адресу xBuf.b0 скорректированному на номер байта
_count++; // Переходим к следующему байту данных
if (_count == 4) { // Если 4 байта приняты (от 0 до 3)
_mode = 5; // Переходим на ввод crc
//Serial.print("b3 = ");
//Serial.println(xBuf.b3, HEX);
//Serial.print("b2 = ");
//Serial.println(xBuf.b2, HEX);
//Serial.print("b1 = ");
//Serial.println(xBuf.b1, HEX);
//Serial.print("b0 = ");
//Serial.println(xBuf.b0, HEX);
}
//Serial.print("count = ");
//Serial.println(count, HEX);
break;
// Ввод crc
case 5:
if (_crc == _inChr) { // Сравниваем подсчитанную crc с указанной в inChr
_mode = 0; // Сбрасываем режим
//Serial.println(" crc - OK!");
//Serial.println("OK"); // Успешное чтение пакета
// Тут может быть вызов процедуры установки значений
// или просто взведение флага готовности
// или копирование буфера в выходные переменные
(*func)();
}
else { // Контрольная сумма не совпала
_mode = 0; // Сброс режима
//Serial.println(" crc - FALSE!");
//Serial.println("Err");
(*func)();
};
break;
} // switch ( mode )
//Serial.print("crc = ");
//Serial.println(crc, HEX);
// Для отладки. Подсчте времени выполнения цикла.
// Без отладочных вызовов Serial.print колеблется от 12 до 40 мкс
// С отладочными Serial.print - время увеличивается до 800 мкс
//t2 = micros();
//Serial.print(" T = ");
//Serial.println(t2-t1);
} // if ( n > 0)
} // Recv()
// **************************************************
// ПЕРЕДАЧА
//
// Передача байта
void Channel::sendLine(uint8_t adr, uint8_t reg, uint8_t value) {
packetOut.adr = adr;
packetOut.cmd = CMD_WRITE8;
packetOut.reg = reg;
packetOut.b0 = value;
packetOut.b1 = 0;
packetOut.b2 = 0;
packetOut.b3 = 0;
packetOut.crc = adr ^ CMD_WRITE8 ^ reg ^ packetOut.b0 ^ packetOut.b1 ^ packetOut.b2 ^ packetOut.b3;
sendPacket();
}
// Передача uint16_t
void Channel::sendLine(uint8_t adr, uint8_t reg, uint16_t value) {
packetOut.adr = adr;
packetOut.cmd = CMD_WRITE8;
packetOut.reg = reg;
packetOut.b0 = value & 0x00ff;
packetOut.b1 = (value & 0xff00) >> 8;
packetOut.b2 = 0;
packetOut.b3 = 0;
packetOut.crc = adr ^ CMD_WRITE8 ^ reg ^ packetOut.b0 ^ packetOut.b1 ^ packetOut.b2 ^ packetOut.b3;
sendPacket();
}
// Передача uint32_t
void Channel::sendLine(uint8_t adr, uint8_t reg, uint32_t value) {
packetOut.adr = adr;
packetOut.cmd = CMD_WRITE8;
packetOut.reg = reg;
packetOut.b0 = value & 0x000000ff;
packetOut.b1 = (value & 0x0000ff00) >> 8;
packetOut.b2 = (value & 0x00ff0000) >> 16;
packetOut.b3 = (value & 0xff000000) >> 24;
packetOut.crc = adr ^ CMD_WRITE8 ^ reg ^ packetOut.b0 ^ packetOut.b1 ^ packetOut.b2 ^ packetOut.b3;
sendPacket();
}
void Channel::sendLine() {
sendPacket();
}
void Channel::sendPacket() {
Serial.write((uint8_t*)(&_SIGN),4);
Serial.write((uint8_t *)(&packetOut), 8);
}
Ошибка компиляции
Arduino: 1.8.5 (Windows 7), Плата:"Arduino Leonardo"
sign:3: error: 'exec' was not declared in this scope
Channel Bus4(exec);
^
exit status 1
'exec' was not declared in this scope
У Вас проблема не в конструкторе, а в том, что Вы не везде неверно записываете тип "указатель на функцию".
До тех пор, пока Вы не привыкли, определяёте этот тип с помощью typedef и не таскайте за собой этот громоздкий синтаксис, а просто пишите одним словом.
Вот смотрите, вот вся Ваша задача
// Определение типа - указатель на функцияю void f(void)
// название типа - SuperFunction
typedef void (* SuperFunction)(void);
// Класс, конструтор которого принимает SuperFunction
class Kaka {
SuperFunction m_fPtr; // Сввойство для хранения SuperFunction
int m_k;
public:
Kaka(int n, SuperFunction f) {
m_k = n;
m_fPtr = f; // Присваивание свойству
}
void doSomething(void) {
m_fPtr(); // Вызов по сохранённому указателю
}
};
void exec(void) {}
Kaka instance(3, exec); // Сохздание экземпляра
И всего делов-то!
Создаётся экземпляр. указатель передаётся конструктору - всё как у людей.
------------------
Но ещё раз провторюсь, если Вам пришлось классу передавать указательна функцию, значит класс плохо спроектирован.
Вообще, старайтесь поменьше таскать за собой все эти страшные скобки и звёздочкм. Там, блин, когда функция, например, с шаблоном, там бывает и профи-то гороху накушается пока выпишет тип указателя на неё.
В простых случаях можно вообще никак не париться с указателями на функции, а использовать объявление "по образцу" и пусть у компилятора голова болит что там за тип.
Например:
void cat(void) {
Serial.begin(115200);
Serial.println("Op-pa! I am working!");
}
void setup(void) {
// Объявление указателя "по образцу".
// Компилятор сам разберётся что там за cat и какой у неё тип
auto f = cat;
//
f(); // Выоз функции по указателю
}
void loop(void) {}
Вообще, старайтесь поменьше таскать за собой все эти страшные скобки и звёздочкм. Там, блин, когда функция, например, с шаблоном, там бывает и профи-то гороху накушается пока выпишет тип указателя на неё.
В простых случаях можно вообще никак не париться с указателями на функции, а использовать объявление "по образцу" и пусть у компилятора голова болит что там за тип.
Например:
void cat(void) {
Serial.begin(115200);
Serial.println("Op-pa! I am working!");
}
void setup(void) {
// Объявление указателя "по образцу".
// Компилятор сам разберётся что там за cat и какой у неё тип
auto f = cat;
//
f(); // Выоз функции по указателю
}
void loop(void) {}
Можете запустить, всё работает.
Компилируется без ошибок. Но в терминале пусто. Я обратил внимание, что Serial.print выводит информацию только в loop()
Он написал, что конструктор Channel::Channel(SuperFunction f) определён дважды.
Зачем Вы сначала определили его в h файле (строки 52-54), а птом ещё и в .cpp файле?
Определяйте в одном месте. Хотите - целиком в .h, но тогда в cpp он вообще не нужен. Хотите в cpp - пожалуйста, тода в h вместо строк 52-52 напишите только заголовок
Channel(SuperFunction f);
а в .сpp файле первой строкой конструктора вставьте
Согласуйте скорость, указанную в строке 2 и ту, что у Вас указана в правом нижнем углу окна монитора порта. Должна быть одинаковая. Тогда и заработает, тут нечему не работать.
Smith2007 пишет:
Я обратил внимание, что Serial.print выводит информацию только в loop()
Сейчас Вы сказали бред. Больше так не говорите - будут смеяться и троллить. Он выводит независимо от того где его вызывают. Как говорилось с старом анекдоте: "У нас тут всё по-простому, где поймают, там и прут".
void cat(void) {
Serial.println("Op-pa! I am working!");
}
void setup(void) {
Serial.begin(9600);
delay(2000);
// Объявление указателя "по образцу".
// Компилятор сам разберётся что там за cat и какой у неё тип
auto f = cat;
//
f(); // Выоз функции по указателю
}
void loop(void) {}
Он написал, что конструктор Channel::Channel(SuperFunction f) определён дважды.
Зачем Вы сначала определили его в h файле (строки 52-54), а птом ещё и в .cpp файле?
Определяйте в одном месте. Хотите - целиком в .h, но тогда в cpp он вообще не нужен. Хотите в cpp - пожалуйста, тода в h вместо строк 52-52 напишите только заголовок
Channel(SuperFunction f);
а в .сpp файле первой строкой конструктора вставьте
Да, именно это хотел. Все оказывается гораздо проще чем я предполагал. Получить ссылку на адрес как на uint16 и потом обратиться к этой области памяти. Да, многому ещё нужно учиться. Спасибо за подсказку.
Спасибо. Значит я не верно истолковал строчку в h файле
Но если определять экземпляр класса в основном скетче - то зачем эта строка в хидере?
Если в скетче, то низачем (при условии, что Ваша библиотека не пользуется экземпляром). А вот если в библиотеке, то для того, чтобы скетч знал, что где-то экземпляр определен.
extern вообще делается для того, чтобы знать, что такая вещь где-то есть.
Спасибо, Евгений.
Скомпилилось все врено.
Есть еще одна задача, которую пытаюсь решить но пока с трудом....
При создании экземпляра класса, мне нужно передать имя функции, которую должел вызвать экземпляр класса Bus4.
В функцию нужно передать ссылку на заполненную структуру
Пример
Ну, передавать классу ссылку на функцию - само по себе идея плохая. Классы и ссылки на функции - это разные технологии программирования.
Но если сильно надо, так передавайте. Если нужна помощь по синтаксису, то напишите нормально прототип функции (конкретный) , а то в Ваших обощениях я ничего не понимаю.
Передача имени функции (без параметров)
Bus4.h
Bus4.cpp
Ошибки компиляции
прототип нужен
void
exec(void);
ProtocolBus4 Bus4(0x33333333, exec);
Для начала перенесите строки 13-15 выше строки 3. А там посмотрим
Для начала перенесите строки 13-15 выше строки 3. А там посмотрим
Ошибка
Добавил в Bus4.cpp
void ProtocolBus4::(*func)();
а это
void
ProtocolBus4::(*func)();
что должно означать?
ПС: так что скорее сейчас идет просто отработка кода.
...И да что бы можно было добавлять в систему новые устройства, а в канал новые функции под новые варианты пакетов.
ПС: так что скорее сейчас идет просто отработка кода.
Предполагаю вызывать указанную функцию при приеме любого валидного пакета. А уже в самой функции определять, что за команда пришла и что с ней делать. Для этого у нее будет адрес, команда, регистр и данные.
Упрощеная схема такая.
Упрощеная схема такая.
Идея понятна. Можно сразу ссылку на структуру (или массив) передать, где номер команды - он же номер строки массива - с элементом адреса функции, которую необходимо выполнить
Похоже, что в конструкторе нельзя в качестве параметра указывать ссылку на функцию.
Убрал этот параметр из конструткора и завел отдельную процедуру.
Все скомпилилось без ошибок и сразу заработало.
Похоже, что в конструкторе нельзя в качестве параметра указывать ссылку на функцию.
В таком случае отпадает необходимость в явном описании конструктора. Я думал все начальные параметры в нем устанавливать. А раз имеются ограничения, то конструктор можно опустить (не указывать явно), а всю инициализацию провести в процедуре init.
Теперь осталось разобраться как передавать параметры в
exec(параметры);
Похоже, что в конструкторе нельзя в качестве параметра указывать ссылку на функцию.
Хм..... Почему же появляется ошибка при компиляции?
Покажите фрагмент .cpp
Не могу понять вот эту строку. Что такое ":" ?
В конструктор передаете 2 значения byte _pin и ссылку на функцию (* _Do)()
Прочитайте про конструкторы https://msdn.microsoft.com/ru-ru/library/s16xw1a8.aspx#default_constructors
А если надо писать ответ мастеру по посылке код станет таким
Остатки мозга вскипели :)
Соответственно в h файле так же поправки вношу. Особенность объявления конструктора с передачей имени функции не смог понять :(
Может быть проблема в разнице компилятора или версии arduino IDE?
void ProtocolBus4::(*func)();
Раньше такого боеда не было. Зачем Вы это добавили
Я это спрашивал в #109. Нет ответа
Может быть проблема в разнице компилятора или версии arduino IDE?
Разница в том, что Вы постоянно придумываете что-то новое и грузите людей новыми ошибками, не успев исправить старые.
Хотите нормально нормально работать - прекратите это
Перебрал все варианты :(
Ошибки при компиляции
Оставил только один параметр - передача функции
header
cpp
Подскажите пожалуйста, где я туплю?
а это
void
ProtocolBus4::(*func)();
что должно означать?
Это была опечатка.
Разница в том, что Вы постоянно придумываете что-то новое и грузите людей новыми ошибками, не успев исправить старые.
Хотите нормально нормально работать - прекратите это
Смотрю примеры, пробую. Но не доходит как правильно записать конструктор.
Это вообще в ступор ввело :)
_crc - это же не функция, а переменная и другие...
Сравните вашу строку Channel( (* f)() ) : func(f) {} ;
и правильную Channel(void (* f)() ) : func(f) {} ;
ПС:
Пройдитесь по мой теме. Там вроде понятно , но лучше в личную своими пальцами эти скетчи пройти. Мускулы C++ подкачать.
А это
Причем весь мой код это полный примитивизм в Си++
У Вас проблема не в конструкторе, а в том, что Вы не везде неверно записываете тип "указатель на функцию".
До тех пор, пока Вы не привыкли, определяёте этот тип с помощью typedef и не таскайте за собой этот громоздкий синтаксис, а просто пишите одним словом.
Вот смотрите, вот вся Ваша задача
И всего делов-то!
Создаётся экземпляр. указатель передаётся конструктору - всё как у людей.
------------------
Но ещё раз провторюсь, если Вам пришлось классу передавать указательна функцию, значит класс плохо спроектирован.
Невнимательность
Скетч
cpp
Ошибка компиляции
У Вас проблема не в конструкторе, а в том, что Вы не везде неверно записываете тип "указатель на функцию".
До тех пор, пока Вы не привыкли, определяёте этот тип с помощью typedef и не таскайте за собой этот громоздкий синтаксис, а просто пишите одним словом.
Вот смотрите, вот вся Ваша задача
И всего делов-то!
Создаётся экземпляр. указатель передаётся конструктору - всё как у людей.
------------------
Но ещё раз провторюсь, если Вам пришлось классу передавать указательна функцию, значит класс плохо спроектирован.
Вот теперь мне стал понятен код. Спасибо.
Буду пробовать
Разбирайтесь
Разбирайтесь
Я описал почти в точности Ваш пример
Но ошибка компиляции не исчезла.
Я описал это .h файле. Это верно?
В cpp оставил
Скетч
Ошибка
Мой опыт в программировании на Си чуть больше недели :(
Вообще, старайтесь поменьше таскать за собой все эти страшные скобки и звёздочкм. Там, блин, когда функция, например, с шаблоном, там бывает и профи-то гороху накушается пока выпишет тип указателя на неё.
В простых случаях можно вообще никак не париться с указателями на функции, а использовать объявление "по образцу" и пусть у компилятора голова болит что там за тип.
Например:
Можете запустить, всё работает.
Вообще, старайтесь поменьше таскать за собой все эти страшные скобки и звёздочкм. Там, блин, когда функция, например, с шаблоном, там бывает и профи-то гороху накушается пока выпишет тип указателя на неё.
В простых случаях можно вообще никак не париться с указателями на функции, а использовать объявление "по образцу" и пусть у компилятора голова болит что там за тип.
Например:
Можете запустить, всё работает.
Компилируется без ошибок. Но в терминале пусто. Я обратил внимание, что Serial.print выводит информацию только в loop()
Вы прочитали что Вам написал компилятор?
Он написал, что конструктор Channel::Channel(SuperFunction f) определён дважды.
Зачем Вы сначала определили его в h файле (строки 52-54), а птом ещё и в .cpp файле?
Определяйте в одном месте. Хотите - целиком в .h, но тогда в cpp он вообще не нужен. Хотите в cpp - пожалуйста, тода в h вместо строк 52-52 напишите только заголовок
Channel(SuperFunction f);
а в .сpp файле первой строкой конструктора вставьте
m_fPtr = f;
а дальше - что Вам надо. Т.е. там в cpp будет
Channel::Channel(SuperFunction f) {
m_fPtr = f;
// ..... la-la-la
}
Компилируется без ошибок. Но в терминале пусто.
Согласуйте скорость, указанную в строке 2 и ту, что у Вас указана в правом нижнем углу окна монитора порта. Должна быть одинаковая. Тогда и заработает, тут нечему не работать.
Я обратил внимание, что Serial.print выводит информацию только в loop()
Сейчас Вы сказали бред. Больше так не говорите - будут смеяться и троллить. Он выводит независимо от того где его вызывают. Как говорилось с старом анекдоте: "У нас тут всё по-простому, где поймают, там и прут".
Вот так работает.
Видимо встроенный терминал не успевает включиться
Вы прочитали что Вам написал компилятор?
Он написал, что конструктор Channel::Channel(SuperFunction f) определён дважды.
Зачем Вы сначала определили его в h файле (строки 52-54), а птом ещё и в .cpp файле?
Определяйте в одном месте. Хотите - целиком в .h, но тогда в cpp он вообще не нужен. Хотите в cpp - пожалуйста, тода в h вместо строк 52-52 напишите только заголовок
Channel(SuperFunction f);
а в .сpp файле первой строкой конструктора вставьте
m_fPtr = f;
а дальше - что Вам надо. Т.е. там в cpp будет
Channel::Channel(SuperFunction f) {
m_fPtr = f;
// ..... la-la-la
}
Скомпилилось без ошибок.
Теперь там где мне необходимо вызвать exec (указатель на нее ссылается) я просто пишу m_fPtr(); ?
Если внутри класса, то да. Если через экземпляр, то instancename.m_fPtr()
С этим вроде более понятно стало. Нужно еще несколько примеров сделать для закрепления материала. Можно ли в такую функцию m_fPtr() (она же exec)
передавать параметры? Например ссылку на структуру или массив? И как это будет указываться в конструкторе?
Ну, в такую - нет, Вы же при описании титпа её без параметров описали. Опишите с параметрами - будет можно.
(в примере с auto - можно всё)
Заменил в хидере тип указателя
В классе сделал вызов
Все получилось! Значение передается.
Вопрос (не совсем по классам)
Есть переменная
uint16_t var16;
Как она располагается в памяти? Т.е. где расположен младший байт, а гле старший?
В струтуре сейчас имею
Если мне нужно записать значение в переменную var16, то я хотел бы обратиться к элементам данных струтуры как uint16_t :
Но компилятор выдает предупреждение
При этом код загружается и выполняется. Но явно не то число получаю.
Есть такой код
Вот сообщение об ошибке
И вот в строке 5 есть значек ^ .вот это и есть стрелочка указывающая на ошибку.
sketch_dec03g:1 - цифра в :1 это строка где есть ошибка
Может вы хотели так var16 = (uint16_t)(Bus4.packetIn.b0);
Да, именно это хотел. Все оказывается гораздо проще чем я предполагал. Получить ссылку на адрес как на uint16 и потом обратиться к этой области памяти. Да, многому ещё нужно учиться. Спасибо за подсказку.