Если в скетче, то низачем (при условии, что Ваша библиотека не пользуется экземпляром). А вот если в библиотеке, то для того, чтобы скетч знал, что где-то экземпляр определен.
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.
В функцию нужно передать ссылку на заполненную структуру
Пример
MyClass Bus( 33, func<с параметрами>) 33 - это просто uint, а func - это имя функции, которая описана в скетче как void func(ссылка на структуру экземпляра Bus) { f1 = <Элемент структуры экземпляра Bus> }Ну, передавать классу ссылку на функцию - само по себе идея плохая. Классы и ссылки на функции - это разные технологии программирования.
Но если сильно надо, так передавайте. Если нужна помощь по синтаксису, то напишите нормально прототип функции (конкретный) , а то в Ваших обощениях я ничего не понимаю.
Передача имени функции (без параметров)
#include <Bus4.h> ProtocolBus4 Bus4(0x33333333, exec); void setup() { Serial.begin(115200); } void loop() { Bus4.Recv(); } void exec() { Serial.println("Ok"); }Bus4.h
// ********************************************************************* // 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); }; #endifBus4.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 Этот отчёт будет иметь больше информации с включенной опцией Файл -> Настройки -> "Показать подробный вывод во время компиляции"прототип нужен
voidexec(void);ProtocolBus4 Bus4(0x33333333, exec);
Для начала перенесите строки 13-15 выше строки 3. А там посмотрим
Для начала перенесите строки 13-15 выше строки 3. А там посмотрим
#include <Bus4.h> void exec() { Serial.println("Ok"); } ProtocolBus4 Bus4(0x33333333, exec); void setup() { Serial.begin(115200); } void loop() { Bus4.Recv(); }Ошибка
Arduino: 1.8.5 (Windows 7), Плата:"Arduino Leonardo" C:\Program Files (x86)\Arduino\libraries\Bus4\Bus4.cpp:41:20: error: expected unqualified-id before '(' token void ProtocolBus4::(*func)(); ^ exit status 1 Ошибка компиляции для платы Arduino Leonardo.Добавил в Bus4.cpp
void ProtocolBus4::(*func)();
#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 - контрольная сумма // //******************************************************************** // Конструктор ProtocolBus4::ProtocolBus4(uint32_t SIGN, void (*f)() ) { _SIGN = SIGN; _crc = 0; _mode = 0; _tStart = 0; _millisec = 0; _timeOut = 10000; func = f; } void ProtocolBus4::(*func)(); 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]); } }а это
voidProtocolBus4::(*func)();что должно означать?ПС: так что скорее сейчас идет просто отработка кода.
...И да что бы можно было добавлять в систему новые устройства, а в канал новые функции под новые варианты пакетов.
ПС: так что скорее сейчас идет просто отработка кода.
Предполагаю вызывать указанную функцию при приеме любого валидного пакета. А уже в самой функции определять, что за команда пришла и что с ней делать. Для этого у нее будет адрес, команда, регистр и данные.
Упрощеная схема такая.
const char *packet1 = "Packet1"; void DoPacket1() { }; const char *packet2 = "Packet2"; void DoPacket2() { }; Cl_channel channel; void setup() { channel.init(); channel.Add(/*пришел пакет такой*/packet1,/*выполнить это*/&DoPacket1); channel.Add(/*пришел пакет такой*/packet2,/*выполнить это*/&DoPacket2); } void loop() { channel.run(); }Упрощеная схема такая.
Идея понятна. Можно сразу ссылку на структуру (или массив) передать, где номер команды - он же номер строки массива - с элементом адреса функции, которую необходимо выполнить
Похоже, что в конструкторе нельзя в качестве параметра указывать ссылку на функцию.
Убрал этот параметр из конструткора и завел отдельную процедуру.
Все скомпилилось без ошибок и сразу заработало.
#include <Bus4.h> ProtocolBus4 Bus4(0x33333333); void setup() { Serial.begin(115200); Bus4.init(exec); } void loop() { Bus4.Recv(); } void exec() { Serial.println("Ok"); }// ********************************************************************* // Протокол обмена // 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 init(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#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 - контрольная сумма // //******************************************************************** // Конструктор ProtocolBus4::ProtocolBus4(uint32_t SIGN) { _SIGN = SIGN; _crc = 0; _mode = 0; _tStart = 0; _millisec = 0; _timeOut = 10000; // func = f; } void ProtocolBus4::init(void (*f)() ) { func = f; } void (*func)(); 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]); } }/**/ unsigned long mill; //--------------------- // класс канал struct packet_t { byte adr; byte commamd; int param1; } //-----------компoновка-------------------------- Cl_Led Led(/*пин*/13);// <-устройство с адр 5 packet_t packet1 = {/*адрес*/5,/*команда*/1,/*параметр*/0};//<- параметр param1 не важен void DoPacket1() { Led.ON(); }; packet_t packet2 = {/*адрес*/5,/*команда*/2,/*параметр*/0};//<- параметр param1 не важен void DoPacket2() { Led.OFF(); }; packet_t packet2 = {/*адрес*/5,/*команда*/3,/*параметр*/0};//<- параметр param1 передает частоту мигания void DoPacket3(int var) { Led.blink(var); }; Cl_channel channel(/*параметры установки канала*/); void setup() { Led.init(); channel.init(); channel.Add(/*пришел пакет такой*/packet1,/*выполнить это*/&DoPacket1); channel.Add(/*пришел пакет такой*/packet2,/*выполнить это*/&DoPacket2); channel.Add(/*пришел пакет такой*/packet3,/*выполнить это*/&DoPacket3); } void loop() { mill = millis(); Led.run(); channel.run(); } /**/Похоже, что в конструкторе нельзя в качестве параметра указывать ссылку на функцию.
В таком случае отпадает необходимость в явном описании конструктора. Я думал все начальные параметры в нем устанавливать. А раз имеются ограничения, то конструктор можно опустить (не указывать явно), а всю инициализацию провести в процедуре init.
Теперь осталось разобраться как передавать параметры в
exec(параметры);
Похоже, что в конструкторе нельзя в качестве параметра указывать ссылку на функцию.
Хм..... Почему же появляется ошибка при компиляции?
Покажите фрагмент .cpp
/* Пример 2.0 Кнопка пин 1 <-> Ардуино пин 2 пин 2 <-> Ардуино GND принцип кода : нажатие на кнопку отсылает в Serial что кнопку нажали */ //---------------классы--------- //класс кнопка подключ на землю с программной подтяжкой class Cl_Btn { byte pin; // номер ноги на кнопке void (* Do)();// указатель на обработчик bool btn, btn_old; bool bounce = 0; // антидребезговый флаг uint32_t past = 0 ; public: // конструктор класса Cl_Btn( byte _pin, void (* _Do)()): pin(_pin), Do(_Do) {} // метод setup() void setup() { pinMode(pin, INPUT_PULLUP);// подключить кнопку 1 с подтяжкой btn = digitalRead(pin); // прочитать реальное значение на выводе}; } // метод loop() void loop() { if (! bounce && btn != digitalRead(pin)) { // если прошел фронт изм на выводн bounce = 1; // выставить флаг past = millis(); // сделать временую засветку } else if ( bounce && millis() - past >= 5 ) { // если прошло антидребезговое время bounce = 0; // то снять флаг btn_old = btn ; btn = digitalRead(pin) ; // прочитать реальное значение на выводе if (btn_old && ! btn) Do(); } } }; //--------------компоновка------------ void Do_Btn1() { Serial.println("Btn1 press"); } Cl_Btn Btn1(/*пин*/2,/*обработчик*/&Do_Btn1); //------------main()-------------- void setup() { Serial.begin(9600); Btn1.setup(); } void loop() { Btn1.loop(); }Не могу понять вот эту строку. Что такое ":" ?
Cl_Btn( byte _pin, void (* _Do)()): pin(_pin), Do(_Do) {}В конструктор передаете 2 значения byte _pin и ссылку на функцию (* _Do)()
Прочитайте про конструкторы https://msdn.microsoft.com/ru-ru/library/s16xw1a8.aspx#default_constructors
А если надо писать ответ мастеру по посылке код станет таким
/**/ unsigned long mill; //--------------------- // класс канал // структура пакета-команды struct packet_t { unsigned int ind;// уникальный индификатор пакета byte adr; // адресс устройства получателя byte commamd; // команда устройству получателю int param1; // параметр команды } // структура пакета-ответа struct packetOut_t { unsigned int ind;// уникальный индификатор пакета byte adr; // адресс устройства получателя byte stat; // 0 - OК / 1 - ошибка } //-----------компoновка-------------------------- Cl_channel channel(/*параметры установки канала*/); Cl_Led Led(/*пин*/13);// <-устройство с адр 5 packet_t packet1 = {/*индетуфикатор*/0,/*адрес*/5,/*команда*/1,/*параметр*/0};//<- параметр param1 не важен void DoPacket1(/**/ind) { Led.ON(); packetOut_t packet(/*индетуфикатор*/ind,/*адрес*/0);//<- отправить ответ OK мастеру channel.tansferOut(packet); }; packet_t packet2 = {/*индетификатор*/0,/*адрес*/5,/*команда*/2,/*параметр*/0};//<- параметр param1 не важен void DoPacket2(/*индетификатор*/ind) { Led.OFF(); packetOut_t packet(/*индетификатор*/ind,/*адрес*/0);//<- отправить ответ OK мастеру channel.tansferOut(packet); }; packet_t packet2 = {/*индетефикатор*/0,/*адрес*/5,/*команда*/3,/*параметр*/0};//<- параметр param1 передает частоту мигания void DoPacket3(int var,/*индетификатор*/ind) { Led.blink(var); packetOut_t packet(/*индетификатор*/ind,/*адрес*/0);//<- отправить ответ OK мастеру channel.tansferOut(packet); }; void setup() { Led.init(); channel.init(); channel.Add(/*пришел пакет такой*/packet1,/*выполнить это*/&DoPacket1); channel.Add(/*пришел пакет такой*/packet2,/*выполнить это*/&DoPacket2); channel.Add(/*пришел пакет такой*/packet3,/*выполнить это*/&DoPacket3); } void loop() { mill = millis(); Led.run(); channel.run(); } /**/Остатки мозга вскипели :)
/* Этот конструктор выдает ошибку компиляции ProtocolBus4::ProtocolBus4(uint32_t SIGN, void (* f)() ) { _SIGN = SIGN; _crc = 0; _mode = 0; _tStart = 0; _millisec = 0; _timeOut = 10000; func = f; } */ удаляю описание конструктора и завожу функцию init - работает void ProtocolBus4::init(uint32_t SIGN, void (* f)() ) { _SIGN = SIGN; _crc = 0; _mode = 0; _tStart = 0; _millisec = 0; _timeOut = 10000; func = f; }Соответственно в h файле так же поправки вношу. Особенность объявления конструктора с передачей имени функции не смог понять :(
Может быть проблема в разнице компилятора или версии arduino IDE?
ProtocolBus4::ProtocolBus4(uint32_t SIGN, void (* f)() ) : _SIGN(SIGN),_crc(0),_mode(0),_tStart(0),_millisec(0),_timeOut(10000),func(f){}void ProtocolBus4::(*func)();
Раньше такого боеда не было. Зачем Вы это добавили
Я это спрашивал в #109. Нет ответа
Может быть проблема в разнице компилятора или версии arduino IDE?
Разница в том, что Вы постоянно придумываете что-то новое и грузите людей новыми ошибками, не успев исправить старые.
Хотите нормально нормально работать - прекратите это
ProtocolBus4::ProtocolBus4(uint32_t SIGN, void (* f)() ) : _SIGN(SIGN),_crc(0),_mode(0),_tStart(0),_millisec(0),_timeOut(10000),func(f){}Перебрал все варианты :(
Ошибки при компиляции
Оставил только один параметр - передача функции
header
class Channel { public: // Кноструктор Channel( (* f)() ) : func(f) {} ; // void init(uint32_t SIGN, void (* f)() ); void (* func)(); void run(void); // Процедура приема.cpp
Channel::Channel(void (* f)() ) { // _SIGN = SIGN; // _crc = 0; // _mode = 0; // _tStart = 0; // _millisec = 0; // _timeOut = 10000; // func = f; }Подскажите пожалуйста, где я туплю?
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а это
voidProtocolBus4::(*func)();что должно означать?Это была опечатка.
Разница в том, что Вы постоянно придумываете что-то новое и грузите людей новыми ошибками, не успев исправить старые.
Хотите нормально нормально работать - прекратите это
Смотрю примеры, пробую. Но не доходит как правильно записать конструктор.
Это вообще в ступор ввело :)
ProtocolBus4::ProtocolBus4(uint32_t SIGN, void (* f)() ) : _SIGN(SIGN),_crc(0),_mode(0),_tStart(0),_millisec(0),_timeOut(10000),func(f){}_crc - это же не функция, а переменная и другие...
Сравните вашу строку Channel( (* f)() ) : func(f) {} ;
и правильную Channel(void (* f)() ) : func(f) {} ;
ПС:
/**/ unsigned long mill; // переменная под millis() //-------------------------------- class Cl_Channel { protected: void (* func)(); public: /*констуктор*/ Cl_Channel(void (* f)() ) : func(f) {} ; /*инициализация */ void init() {}; /*работа*/ void run() {}; // Процедура приема. }; //---Компоновка----------------------------- void DoChannel() {}; Cl_Channel Channel(/**/&DoChannel); //---main----------------------------- void setup() { Channel.init(); } void loop() { mill = millis(); Channel.run(); } /*Скетч использует 534 байт (1%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 15 байт (0%) динамической памяти, оставляя 2033 байт для локальных переменных. Максимум: 2048 байт. */Пройдитесь по мой теме. Там вроде понятно , но лучше в личную своими пальцами эти скетчи пройти. Мускулы C++ подкачать.
А это
/**/ 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); // Сохздание экземпляраИ всего делов-то!
Создаётся экземпляр. указатель передаётся конструктору - всё как у людей.
------------------
Но ещё раз провторюсь, если Вам пришлось классу передавать указательна функцию, значит класс плохо спроектирован.
Невнимательность
Channel(void (* f)() ) : func(f) {} ;Скетч
#include <Bus4.h> Channel Bus4(exec); void setup() { Serial.begin(115200); //Bus4.init(0x33333333, exec); } void loop() { Bus4.run(); } void exec() { Serial.print("Cmd = OK"); }#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 packetIn_t { // Структура принятого пакета uint8_t adr; uint8_t cmd; uint8_t reg; uint8_t b0; uint8_t b1; uint8_t b2; uint8_t b3; }; struct packetOut_t { // Структура передаваемого пакета uint8_t adr; uint8_t cmd; uint8_t reg; uint8_t b0; uint8_t b1; uint8_t b2; uint8_t b3; uint8_t crc; }; class Channel { public: // Кноструктор Channel(void (* f)() ) : func(f) {} ; // void init(uint32_t SIGN, void (* f)() ); void (* func)(); void run(void); // Процедура приема. packetIn_t packetIn; // Принятый пакет с линии packetOut_t packetOut; // Пакет для отправки // передача данных 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 sendLine(void); 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 верно - буфер готов uint8_t *ptrX; // Указатель uint32_t _tStart; // = 0; uint32_t _millisec; // = 0; uint32_t _timeOut; // = 10000; // В реальной системе тайм аут следует снизить. Например 50 мс. // Следующие переменные для замера времени выполнения основного цикла, мкс //uint32_t t1; //uint32_t t2; void sendPacket(void); }; #endifcpp
#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); // Сохздание экземпляраИ всего делов-то!
Создаётся экземпляр. указатель передаётся конструктору - всё как у людей.
------------------
Но ещё раз провторюсь, если Вам пришлось классу передавать указательна функцию, значит класс плохо спроектирован.
Вот теперь мне стал понятен код. Спасибо.
Буду пробовать
Разбирайтесь
Разбирайтесь
Я описал почти в точности Ваш пример
Но ошибка компиляции не исчезла.
Я описал это .h файле. Это верно?
#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 packetIn_t { // Структура принятого пакета uint8_t adr; uint8_t cmd; uint8_t reg; uint8_t b0; uint8_t b1; uint8_t b2; uint8_t b3; }; struct packetOut_t { // Структура передаваемого пакета uint8_t adr; uint8_t cmd; uint8_t reg; uint8_t b0; uint8_t b1; uint8_t b2; uint8_t b3; uint8_t crc; }; // Определение типа - указатель на функцияю void f(void) // название типа - SuperFunction typedef void (* SuperFunction)(void); // Класс, конструтор которого принимает SuperFunction class Channel { SuperFunction m_fPtr; // Сввойство для хранения SuperFunction public: // Кноструктор Channel(SuperFunction f) { m_fPtr = f; } void doSomething(void) { m_fPtr(); // Вызов по сохранённому указателю } // void init(uint32_t SIGN, void (* f)() ); // void (* func)(); void run(void); // Процедура приема. packetIn_t packetIn; // Принятый пакет с линии packetOut_t packetOut; // Пакет для отправки // передача данных 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 sendLine(void); 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 верно - буфер готов uint8_t *ptrX; // Указатель uint32_t _tStart; // = 0; uint32_t _millisec; // = 0; uint32_t _timeOut; // = 10000; // В реальной системе тайм аут следует снизить. Например 50 мс. // Следующие переменные для замера времени выполнения основного цикла, мкс //uint32_t t1; //uint32_t t2; void sendPacket(void); }; // Channel class #endifВ cpp оставил
// Конструктор Channel::Channel(SuperFunction f) { }Скетч
#include <Bus4.h> void exec() { Serial.print("Cmd = OK"); } Channel Bus4(exec); void setup() { Serial.begin(115200); //Bus4.init(0x33333333, exec); } void loop() { Bus4.run(); }Ошибка
Arduino: 1.8.5 (Windows 7), Плата:"Arduino Leonardo" C:\Program Files (x86)\Arduino\libraries\Bus4\Bus4.cpp:32:1: error: redefinition of 'Channel::Channel(SuperFunction)' Channel::Channel(SuperFunction f) { } ^ In file included from C:\Program Files (x86)\Arduino\libraries\Bus4\Bus4.cpp:1:0: C:\Program Files (x86)\Arduino\libraries\Bus4/Bus4.h:78:8: note: 'Channel::Channel(SuperFunction)' previously defined here Channel(SuperFunction f) { ^ exit status 1 Ошибка компиляции для платы Arduino Leonardo.Мой опыт в программировании на Си чуть больше недели :(
Вообще, старайтесь поменьше таскать за собой все эти страшные скобки и звёздочкм. Там, блин, когда функция, например, с шаблоном, там бывает и профи-то гороху накушается пока выпишет тип указателя на неё.
В простых случаях можно вообще никак не париться с указателями на функции, а использовать объявление "по образцу" и пусть у компилятора голова болит что там за тип.
Например:
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 файле первой строкой конструктора вставьте
m_fPtr = f;
а дальше - что Вам надо. Т.е. там в cpp будет
Channel::Channel(SuperFunction f) {
m_fPtr = f;
// ..... la-la-la
}
Компилируется без ошибок. Но в терминале пусто.
Согласуйте скорость, указанную в строке 2 и ту, что у Вас указана в правом нижнем углу окна монитора порта. Должна быть одинаковая. Тогда и заработает, тут нечему не работать.
Я обратил внимание, что 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 файле первой строкой конструктора вставьте
m_fPtr = f;
а дальше - что Вам надо. Т.е. там в cpp будет
Channel::Channel(SuperFunction f) {
m_fPtr = f;
// ..... la-la-la
}
Скомпилилось без ошибок.
Теперь там где мне необходимо вызвать exec (указатель на нее ссылается) я просто пишу m_fPtr(); ?
Если внутри класса, то да. Если через экземпляр, то instancename.m_fPtr()
С этим вроде более понятно стало. Нужно еще несколько примеров сделать для закрепления материала. Можно ли в такую функцию m_fPtr() (она же exec)
передавать параметры? Например ссылку на структуру или массив? И как это будет указываться в конструкторе?
Ну, в такую - нет, Вы же при описании титпа её без параметров описали. Опишите с параметрами - будет можно.
(в примере с auto - можно всё)
#include <Bus4.h> #define SIGN 0x33333333 #define TIME_OUT 5000 void exec(uint8_t i1); Channel Bus4(SIGN, TIME_OUT, exec); void exec(uint8_t i1) { Serial.print("exec cmd = "); Serial.println(Bus4.packetIn.cmd, HEX); Serial.print("i1 = "); Serial.println(i1, HEX); } void setup() { Serial.begin(115200); } void loop() { Bus4.run(); }Заменил в хидере тип указателя
В классе сделал вызов
Все получилось! Значение передается.
Вопрос (не совсем по классам)
Есть переменная
uint16_t var16;
Как она располагается в памяти? Т.е. где расположен младший байт, а гле старший?
В струтуре сейчас имею
struct packetIn_t { // Структура принятого пакета uint8_t adr; uint8_t cmd; uint8_t reg; uint8_t b0; uint8_t b1; uint8_t b2; uint8_t b3; };Если мне нужно записать значение в переменную var16, то я хотел бы обратиться к элементам данных струтуры как uint16_t :
Но компилятор выдает предупреждение
D:\Project\Arduino\sign\sign.ino: In function 'void parsing()': D:\Project\Arduino\sign\sign.ino:32:15: warning: invalid conversion from 'uint16_t* {aka unsigned int*}' to 'uint16_t {aka unsigned int}' [-fpermissive] var16 = (uint16_t *)(&Bus4.packetIn.b0);При этом код загружается и выполняется. Но явно не то число получаю.
Есть такой код
hernya void setup() { // put your setup code here, to run once: } void loop() { // put your main code here, to run repeatedly: }Вот сообщение об ошибке
И вот в строке 5 есть значек ^ .вот это и есть стрелочка указывающая на ошибку.
sketch_dec03g:1 - цифра в :1 это строка где есть ошибка
Может вы хотели так var16 = (uint16_t)(Bus4.packetIn.b0);
Да, именно это хотел. Все оказывается гораздо проще чем я предполагал. Получить ссылку на адрес как на uint16 и потом обратиться к этой области памяти. Да, многому ещё нужно учиться. Спасибо за подсказку.