. Идея это обязательно иметь уникальный номер сообщения, у контроллера, я его в блок данных внесу, крутится счетчик в рамках выделенных бит с сбросом в о после переполнения.
Так во многих автомобильных протоколах сделано. Обычно последний байт в поле данных фрейма.
А можно переменные объявленные в #define использовать в case?
И второй вопрос у меня в коде объявлены 3 структуры тип команд, адрес и тип устройства. И 1 в разных структурах имеет разное значение.
Причем enum мне нужен для кодирования сообщения из переменных в байты, а struct для декодирования.
И к тому же я структуры и перечисления вынесу в общий код для всех контроллеров.
Насчет функций. Я из естественно опптимизирую, но без них никуда. Это типовые действия. Причем Decode я естественно в RX запихну (сейчас просто удобнее) , а вот Code и ТХ точно нужно разделять.
Я пока набрасываю структуру программы, общий алгоритм.
Это мы еще в классы и прерывания по таймеру и аппаратные не полезли ;) Все же протокол свой пишем.
ну у меня совсем другой уровень программирования. Я в этих структурах , перечислениях и typedef ах не разбираюсь пока. А #define это не переменные получаются, а как бы указания компилятору что вместо одного подставить другое.
Например
#define COMMAND_SEND 1
в коде везде, где напишешь COMMAND_SEND компилятор подставит 1. Поэтому в case работать будет.
Заметь, точка с запятой не ставится при этом. И ещё, директива #define не занимает оперативную память.
Мой уровень программирования тоже не велик. А что делать.
У меня 21 узел в сети, на структуре видно кол-во датчиков. Мне проще написать вместо адреса Hallway_net_center вместо его номера 6.
Это как DNS в интернете.
Вот что умные люди пишут
define - текстовая замена препроцессором и для компилятора это уже просто циферки - 0,1,2 , а enum - это тип, соответственно получаем лучший контроль со стороны компилятора над ошибками, возможность вывода дополнительных предупреждений(например, если в switch указаны не все константы из enum-типа) и пр.
define - текстовая замена препроцессором и для компилятора это уже просто циферки - 0,1,2 , а enum - это тип, соответственно получаем лучший контроль со стороны компилятора над ошибками, возможность вывода дополнительных предупреждений(например, если в switch указаны не все константы из enum-типа) и пр.
Если я буду использовать define я могу забыть количество переменных и их не обработать в case. В общем объявление переменных можно делать поразному кому как удобнее.
И структура с декодированием не нужна только для отладки, чтобы видеть что получаю в виде текста. Потом останутся только ENUM, структуры и декодирование уберу.
Будет так
switch (direction) {
case 0: //выполняется, когда RX Reqv т.е у меня что то запрашивают
break;
case 1: //выполняется когда RX Ansv т.е мне отвечают на мой запрос
break;
default:
// выполняется, если не выбрана ни одна альтернатива
// default необязателен
}
const uint8_t NULL_C = 0;
const uint8_t COMMAND_SEND = 1;
const uint8_t COMMAND_ANSVER = 2;
const uint8_t REQUEST_SEND = 3;
const uint8_t REQUEST_ANSVER = 4;
const uint8_t STATUS_REQUEST_SEND = 5;
const uint8_t STATUS_REQUEST_ANSVER = 6;
const uint8_t center_node = 11;
const uint8_t kitchen_node = 22;
const uint8_t bathroom_node = 33;
const uint8_t officeroom_node = 44;
const uint8_t bedroom_node = 55;
void RX()
{
uint8_t direction, msg_type, dev_addr, rem_addr, dev_type;
if(!digitalRead(CAN0_INT)) //
{
CAN0.readMsgBuf(&rxId_can, &len_can, rxBuf_can); // Read data: len = data length, buf = data byte(s)
direction = (rxId_can & 0x10000000)>>28; // извлекаем 1-битный идентификатор запроса-ответа из ID
msg_type = (rxId_can & 0xF000000)>>24; // извлекаем 4-битный идентификатор сообщения из ID
dev_addr = (rxId_can & 0xFF0000)>>16; // извлекаем 8-битный адрес отправителя из ID
rem_addr = (rxId_can & 0xFF00)>>8; // извлекаем 8-битный адрес получателя из ID
dev_type = (rxId_can & 0xFF); // извлекаем 8-битный тип устройства у получателя из ID
Serial.print(F(" type_msg - "));
if (msg_type == COMMAND_SEND) Serial.print(F("COMMAND_SEND")); //макрос F записывает строковую константу не в оперативу, а на флеш
else if (msg_type == COMMAND_ANSVER) Serial.print(F("COMMAND_ANSVER"));
else if (msg_type == REQUEST_SEND) Serial.print(F("REQUEST_SEND"));
else if (msg_type == REQUEST_ANSVER) Serial.print(F("REQUEST_ANSVER"));
else if (msg_type == STATUS_REQUEST_SEND) Serial.print(F("STATUS_REQUEST_SEND"));
else if (msg_type == STATUS_REQUEST_ANSVER) Serial.print(F("STATUS_REQUEST_ANSVER"));
Serial.print(F(" node_addr - "));
if (dev_addr == center_node) Serial.print(F("center_node"));
else if (dev_addr == kitchen_node) Serial.print(F("kitchen_node"));
else if (dev_addr == bathroom_node) Serial.print(F("bathroom_node"));
else if (dev_addr == officeroom_node) Serial.print(F("officeroom_node"));
else if (dev_addr == bedroom_node) Serial.print(F("bedroom_node"));
// и так далее в общем. Таким образом ты не кладёшь все String-и в оперативу, и опять же экономишщ память и функций меньше
}
}
При увеличении кол-ва обрабатываемых параметров у нас получится увеличение строк кода в геометрической прогрессии
Вот это я должен вложить во внутрь первого
switch (msg_type){
case COMMAND_SEND: //выполняется, когда мне прислали команду от удаленного контроллера
break;
case COMMAND_ANSVER: //выполняется когда удаленный контроллер отвечают на посланную команду
break;
case REQUEST_SEND: //выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
break;
case REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
break;
case STATUS_REQUEST_SEND: //выполняется когда удаленный контроллер у меня запрашивает состояние
break;
case STATUS_REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
break;
default:
//если параметр за пределами ENUM
}
а дальше в него вложить адреса
а потом типы датчиков
CASE это практически и есть IF ELSE
Только посмотри где наглядней код, вот тоже что и у меня только на IF
if (msg_type=1)
{
}
else if (msg_type=2)
{
}
else if (msg_type=3)
{
}
else if (msg_type=4)
{
}
else if (msg_type=5)
{
}
else if (msg_type=6)
{
}
При увеличении кол-ва обрабатываемых параметров у нас получится увеличение строк кода в геометрической прогрессии
дак эти параметры всё равно где-то придётся писать - не в Rx() дак в труктурах твоих. А вместо IF пожалуйства применяй swith какие проблемы. Просто IF лично мне удобнее и вот так вообщето
if (msg_type == COMMAND_SEND) {выполняется, когда мне прислали команду от удаленного контроллера
}
else if (msg_type == COMMAND_ANSVER) {выполняется когда удаленный контроллер отвечают на посланную команду}
else if (msg_type == REQUEST_SEND) {выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то}
else if (msg_type == REQUEST_ANSVER) {выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика}
else if (msg_type == STATUS_REQUEST_SEND) {выполняется когда удаленный контроллер у меня запрашивает состояние}
else if (msg_type == STATUS_REQUEST_ANSVER) {выполняется когда удаленный контроллер мне отвечают на мой запрос состояния}
else {выполняется когда параметр за пределами}
Так и получается что вместо define я использую enum, вместо if использую case. И еще функции и процедуры.
Вот смотри что получилось. Глубже только функции иначе код перестанет читаться вообще.
switch (direction) {
case 0: //выполняется, когда RX Reqv т.е у меня что то запрашивают
switch (msg_type){
case COMMAND_SEND: //выполняется, когда мне прислали команду от удаленного контроллера
// Функция обработки присланой команды от удаленного контроллера
break;
case REQUEST_SEND: //выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
// Функция обработки присланного запроса параметра с датчика
break;
case STATUS_REQUEST_SEND: //выполняется когда удаленный контроллер у меня запрашивает состояние
// Функция обработки запроса моего состояния
break;
default:
//если параметр за пределами ENUM
Serial.print(" Default msg - Recive Uncnown type message");
}
break;
case 1: //выполняется когда RX Ansv т.е мне отвечают на мой запрос
switch (msg_type){
case COMMAND_ANSVER: //выполняется когда удаленный контроллер отвечает на посланную команду
// Функция обработки ответа на мою команду
break;
case REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
// Функция обработки ответа на мой запрос параметра датчика
break;
case STATUS_REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
// Функция обработки ответа на мой запрос состояния УК
break;
default:
//если параметр за пределами ENUM
Serial.print(" Default msg - Recive Uncnown type message");
}
break;
default:
Serial.print(" Default direct ");
//если параметр за пределами ENUM
// default необязателен
}
Я отладочные структуры уберу потом. Остануться только перечисления. Они в памяти едят столько же сколько define. Я как раз введу параметры компилятору что если флаг отладка использовать структуру если рабочая программа то структуры убираю.
Ну и естественно только меги а в центре вообще Due + Orange Pi + Linux сервер
тогда уж проще было один дебагер собрать, который декодирует. А все узлы, если надо (ставим флаг например), шлют в кан это служебное сообщение (получается тоже которое получили, но, например, message_type делаем особенный =0) и ты отладчиком уже видишь - кто что видит в кане, и преобразует в удобный тебе вид в сериал.
типа эхо чтоли, получается, только в ID message_type другой.
И не надо на всех контроллерах эту шляпу со стрингами. плюсом тебе не надо со всех сериал монитор - всё по кану.
а если программа будет побольше занимать, то не сможет работать твой декодер или глюки будут. короче ладно. Идёшь правильной дорогой, просто реально многовато конечно, твой отладчик занимает.
Кстати начал тут глубже копать CAN так вот эти 3 бита забирает т.н. удаленный кадр, в котором нет поля данных но он является запросчиком. (Вернее даже не удаленный кадр а типизация видов сообщений)
"Удаленные кадры можно использовать для реализации управления трафиком шины типа «запрос–ответ». На практике, однако, удаленный кадр используется мало. Это не так важно, поскольку стандарт CAN не предписывает действовать именно так, как здесь обозначено. Большинство контроллеров CAN можно запрограммировать так, что они будут автоматически отвечать на удаленный кадр, или же вместо этого извещать локальный процессор."
На самом деле всё не совсем так. Я вроде бы разобрался с этим. В стандартных 11 битных ID , служебный бит (недостающий до 12) - один, это бит RTR. Он и определяет сообщение с данными это ли запрос данных. НО! даже,если это запрос параметров, то мы можем передавать байты в поле данных, хотя в стандарте CAN не должно быть поля данных и ответ должен быть с таким же ID.
В кадрах данных RTR бит должен быть передан нулевым уровнем. Внутри кадра удаленного запроса данных RTR бит должен быть единичным.
В расширенных ID служебных бита уже три:
1. При передаче просто раширенного ID бит RTR также определяет сообщение с данными это или запрос данных.
2. В расширенном варианте могут также передаваться и обычные стандартные 11 битные ID. Дак вот в таком случае определение сообщение с данными это или запрос данных делает уже не бит RTR, а бит SRR (это только в случае, когда при схеме с раширенными ID передаётся стандартный ID).
3. Ну и посдний бит, собственно, и определяет расширенный данный ID или стандариный - бит IDE
Я научился управлять этим служебным битом RTR, поэтому мы имеем в распоряжении ещё один бит в ID, можно назначение его и оставить таким же как в стандарте CAN - определять либо это данные, либо запрос данных.
поэтому в моей библе так и было :
MaksVV пишет:
Единственное, для того, чтобы значение первого разряда в моей библиотеке (<mcp2515.h>) было равно 1, нужно отправлять 9,
и соответственно чтобы было равно 0 - нужно отправлять 8 . Такова особенность библиотеки видимо.
Пример. Для отправки ID 0х0aabbccd нам нужно отправить 0х8aabbccd
Для отправки ID 0х1aabbccd нам нужно отправить 0х9aabbccd
т.е. если прилетает ID 0x1ABCDEF0, то в реале, на самом деле, прилетает 0x9ABCDEF0, это просто твоя библиотека уже переделывает к правильному виду. Получается так:
прилетает 0x9ABCDEF0 а видим 0x1ABCDEF0, но это сообщение с данными
прилетает 0x8ABCDEF0 а видим 0x0ABCDEF0, но это сообщение с данными
прилетает 0xDABCDEF0 а видим 0x1ABCDEF0, но это сообщение запрос данных
прилетает 0xCABCDEF0 а видим 0x0ABCDEF0, но это сообщение запрос данных
при всём при этом МОЖНО передавать байты в поле данных во всех этих типах сообщений
и чёто капец у тебя сложно как-то всё , запутаться можно - из одной функции в другую функцию и т.д. Попроще можно всё и другим понятнее будет.
вот так например, можно упростить функцию TX(). При этом функцию Code_can_id_msg () убираем за ненадобностью
Упрощения само собой, это пока наброски кода. И я делал оба варианта 11-29 просто попробовать как либа себе ведет. На выходе только 29 бит с использованием FR.
короче всё равно не отправляет FR ы, всяко попробовал
нет, не всяко попробовал. Нужно было как всегда внимательнее читать. И нужно брать не 0x40000000 , а 0xC0000000
Делаем ИЛИ с нужным нам ID и Обязательно DLC в ноль. Я когда пробовал видимо по очереди и то и то , а вместе - нет, поэтому не работало. Рабочий код отправки FR
// CAN Send Example
//
#include <mcp_can.h>
#include <SPI.h>
MCP_CAN CAN0(10); // Set CS to pin 10
void setup()
{
Serial.begin(115200);
// Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.
if(CAN0.begin(MCP_ANY, CAN_250KBPS, MCP_8MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!");
else Serial.println("Error Initializing MCP2515...");
CAN0.setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted
}
byte data[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
void loop()
{
// send data: ID = 0x10000000, Standard CAN Frame, Data length = 8 bytes, 'data' = array of data bytes to send
uint32_t txId_can = 0x1ABCDEF6;
byte sndStat = CAN0.sendMsgBuf((txId_can|0xС0000000), 0, data); // SEND REMOTE REQUEST FRAME
if(sndStat == CAN_OK){
Serial.println("Message Sent Successfully!");
} else {
Serial.println("Error Sending Message...");
}
delay(1000); // send data per 100ms
}
/*********************************************************************************************************
END FILE
*********************************************************************************************************/
и анализатор шины собери уже, не знаю как ты без него обходишься, делов то на 5 мин. Ссылку я давал в #217
Обязательно Nano или я могу залить в любой свой контроллер?
И второй вопрос. У меня W7 64, основное окно запустилось при попытке настроит виснет наглухо. (Пришлось подсовывать mscomm32.ocx и регистрировать). Есть подводные камни?
Чето не помню уже, вроде норм всё должно быть. Мегу можно конечно. Когда нажимаешь настроить вроде порт должен быть вставлен. Покажи куда тычешь когда виснет
т.к. мы разобрались с этими ID и Remote ами предлагаю исправить функцию RX()
void RX()
{
if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer
{
CAN0.readMsgBuf(&rxId_can, &len_can, rxBuf_can); // Read data: len = data length, buf = data byte(s)
uint8_t direction, msg_type, dev_addr, rem_addr, dev_type, can_msg_type;
uint8_t data_b0,data_b1,data_b2,data_b3,data_b4,data_b5,data_b6,data_b7;
can_msg_type = (rxId_can & 0xF0000000)>>28; //извлекаем тип ID
if (can_msg_type == 0) {тут что нибудт делаем} // получили ID 11 бит
if (can_msg_type == 8 || can_msg_type == 9) {тут что нибудт делаем} // получили ID 29 бит, сообщение - DataFrame
if (can_msg_type == 0x0C || can_msg_type == 0x0D) {тут что нибудт делаем} // получили ID 29 бит, сообщение - RemoteFrame
direction = (rxId_can & 0x10000000)>>28; // извлекаем 1-битный идентификатор запроса-ответа из ID
msg_type = (rxId_can & 0xF000000)>>24; // извлекаем 4-битный идентификатор сообщения из ID
dev_addr = (rxId_can & 0xFF0000)>>16; // извлекаем 8-битный адрес отправителя из ID
rem_addr = (rxId_can & 0xFF00)>>8; // извлекаем 8-битный адрес получателя из ID
dev_type = rxId_can; // извлекаем 8-битный тип устройства у получателя из ID
#ifdef debug
DeCode_can_id_msg(direction,msg_type,dev_addr,rem_addr,dev_type,rxBuf_can); //Отладочная функция
#endif
//Начинаем разбор принятого
switch (direction) {
case 0: //выполняется, когда RX Reqv т.е у меня что то запрашивают
switch (msg_type){
case COMMAND_SEND: //выполняется, когда мне прислали команду от удаленного контроллера
// Функция обработки присланой команды от удаленного контроллера
// Что за команда запрошена
break;
case REQUEST_SEND: //выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
// Функция обработки присланного запроса параметра с датчика
// Что за датчик запрошен
break;
case STATUS_REQUEST_SEND: //выполняется когда удаленный контроллер у меня запрашивает состояние
// Функция обработки запроса моего состояния
// Вернуть состояние
break;
default:
//если параметр за пределами ENUM
#ifdef debug
Serial.println("Default msg - Recive Uncnown type message");
#endif
}
break;
case 1: //выполняется когда RX Ansv т.е мне отвечают на мой запрос
switch (msg_type){
case COMMAND_ANSVER: //выполняется когда удаленный контроллер отвечает на посланную команду
// Функция обработки ответа на мою команду
break;
case REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
// Функция обработки ответа на мой запрос параметра датчика
break;
case STATUS_REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
// Функция обработки ответа на мой запрос состояния УК
break;
default:
//если параметр за пределами ENUM
#ifdef debug
Serial.println("Default msg - Recive Uncnown type message");
#endif
}
break;
default:
#ifdef debug
Serial.print(" Default direct ");
#endif
//если параметр за пределами ENUM
// default необязателен
}
}
}
Немного поменять, сам проверь, если закоментировать debug то за default: нет ничего, компилятор ругнется. Поэтому так:
void RX()
{
if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer
{
CAN0.readMsgBuf(&rxId_can, &len_can, rxBuf_can); // Read data: len = data length, buf = data byte(s)
uint8_t direction, msg_type, dev_addr, rem_addr, dev_type, can_msg_type;
uint8_t data_b0,data_b1,data_b2,data_b3,data_b4,data_b5,data_b6,data_b7;
can_msg_type = (rxId_can & 0xF0000000)>>28; //извлекаем тип ID
if (can_msg_type == 0) {тут что нибудт делаем} // получили ID 11 бит
if (can_msg_type == 8 || can_msg_type == 9) {тут что нибудт делаем} // получили ID 29 бит, сообщение - DataFrame
if (can_msg_type == 0x0C || can_msg_type == 0x0D) {тут что нибудт делаем} // получили ID 29 бит, сообщение - RemoteFrame
direction = (rxId_can & 0x10000000)>>28; // извлекаем 1-битный идентификатор запроса-ответа из ID
msg_type = (rxId_can & 0xF000000)>>24; // извлекаем 4-битный идентификатор сообщения из ID
dev_addr = (rxId_can & 0xFF0000)>>16; // извлекаем 8-битный адрес отправителя из ID
rem_addr = (rxId_can & 0xFF00)>>8; // извлекаем 8-битный адрес получателя из ID
dev_type = rxId_can; // извлекаем 8-битный тип устройства у получателя из ID
#ifdef debug
DeCode_can_id_msg(direction,msg_type,dev_addr,rem_addr,dev_type,rxBuf_can); //Отладочная функция
#endif
//Начинаем разбор принятого
switch (direction) {
case 0: //выполняется, когда RX Reqv т.е у меня что то запрашивают
switch (msg_type){
case COMMAND_SEND: //выполняется, когда мне прислали команду от удаленного контроллера
// Функция обработки присланой команды от удаленного контроллера
// Что за команда запрошена
break;
case REQUEST_SEND: //выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
// Функция обработки присланного запроса параметра с датчика
// Что за датчик запрошен
break;
case STATUS_REQUEST_SEND: //выполняется когда удаленный контроллер у меня запрашивает состояние
// Функция обработки запроса моего состояния
// Вернуть состояние
break;
default:
{
//если параметр за пределами ENUM
#ifdef debug
Serial.println("Default msg - Recive Uncnown type message");
#endif
}
}
break;
case 1: //выполняется когда RX Ansv т.е мне отвечают на мой запрос
switch (msg_type){
case COMMAND_ANSVER: //выполняется когда удаленный контроллер отвечает на посланную команду
// Функция обработки ответа на мою команду
break;
case REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
// Функция обработки ответа на мой запрос параметра датчика
break;
case STATUS_REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
// Функция обработки ответа на мой запрос состояния УК
break;
default:
{
//если параметр за пределами ENUM
#ifdef debug
Serial.println("Default msg - Recive Uncnown type message");
#endif
}
}
break;
default:
{
#ifdef debug
Serial.print(" Default direct ");
#endif
//если параметр за пределами ENUM
// default необязателен
}
}
}
}
Програ раступилась. Просто долго думала при первом запуске, видимо порты искала.
Но данные в нее не идут.
Я залил твой скетч, изменил пин с 10 на 53 (Мега), подключил МК с CanHacker к сети, запустил Win прогу, выбрал в ней порт меги с CanHacker, нажал коннект и .... ничего (
COMMAND_SEND: //выполняется, когда мне прислали команду от удаленного контроллера
REQUEST_SEND: //выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
STATUS_REQUEST_SEND: //выполняется когда удаленный контроллер у меня запрашивает состояние
COMMAND_ANSVER: //выполняется когда удаленный контроллер отвечает на посланную команду
REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
STATUS_REQUEST_ANSVER: //выполняется когда удаленный контроль
Только я не могу понять, зачем нужен direction?
Понятно, что раз приставка SEND то выполняется, когда у меня что то запрашивают
и если приставка ANSVER товыполняется когда мне отвечают на мой запрос
Может так проще?
void RX()
{
if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer
{
CAN0.readMsgBuf(&rxId_can, &len_can, rxBuf_can); // Read data: len = data length, buf = data byte(s)
uint8_t msg_type, dev_addr, rem_addr, dev_type;
bool direction, can_frame_type; can_ID_type;
uint8_t data_b0,data_b1,data_b2,data_b3,data_b4,data_b5,data_b6,data_b7;
can_ID_type = (canMsg.can_id & 0x80000000)>>31; //извлекаем тип ID (0 - 11bit, 0 - 29 bit)
can_frame_type = (canMsg.can_id & 0x40000000)>>30; //извлекаем тип СAN FRAME (0 - Data, 1 - Remote)
direction = (rxId_can & 0x10000000)>>28; // извлекаем 1-битный идентификатор запроса-ответа из ID
msg_type = (rxId_can & 0xF000000)>>24; // извлекаем 4-битный идентификатор сообщения из ID
dev_addr = (rxId_can & 0xFF0000)>>16; // извлекаем 8-битный адрес отправителя из ID
rem_addr = (rxId_can & 0xFF00)>>8; // извлекаем 8-битный адрес получателя из ID
dev_type = rxId_can; // извлекаем 8-битный тип устройства у получателя из ID
#ifdef debug
DeCode_can_id_msg(direction,msg_type,dev_addr,rem_addr,dev_type,rxBuf_can); //Отладочная функция
#endif
//Начинаем разбор принятого
switch (msg_type){
// ниже выполняется, когда у меня что-то запрашивают
case COMMAND_SEND: //выполняется, когда мне прислали команду от удаленного контроллера
// Функция обработки присланой команды от удаленного контроллера
// Что за команда запрошена
break;
case REQUEST_SEND: //выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
// Функция обработки присланного запроса параметра с датчика
// Что за датчик запрошен
break;
case STATUS_REQUEST_SEND: //выполняется когда удаленный контроллер у меня запрашивает состояние
// Функция обработки запроса моего состояния
// Вернуть состояние
break;
// выполняется когда мне отвечают на мой запрос
case COMMAND_ANSVER: //выполняется когда удаленный контроллер отвечает на посланную команду
// Функция обработки ответа на мою команду
break;
case REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
// Функция обработки ответа на мой запрос параметра датчика
break;
case STATUS_REQUEST_ANSVER: //выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
// Функция обработки ответа на мой запрос состояния УК
break;
}
default:
{
#ifdef debug
Serial.print(F("Default msg - Recive Uncnown type message"));
#endif
}
}
}
Так во многих автомобильных протоколах сделано. Обычно последний байт в поле данных фрейма.
и чёто капец у тебя сложно как-то всё , запутаться можно - из одной функции в другую функцию и т.д. Попроще можно всё и другим понятнее будет.
Например это:
Может так проще сделать? :
А можно переменные объявленные в #define использовать в case?
И второй вопрос у меня в коде объявлены 3 структуры тип команд, адрес и тип устройства. И 1 в разных структурах имеет разное значение.
Причем enum мне нужен для кодирования сообщения из переменных в байты, а struct для декодирования.
И к тому же я структуры и перечисления вынесу в общий код для всех контроллеров.
Насчет функций. Я из естественно опптимизирую, но без них никуда. Это типовые действия. Причем Decode я естественно в RX запихну (сейчас просто удобнее) , а вот Code и ТХ точно нужно разделять.
Я пока набрасываю структуру программы, общий алгоритм.
Это мы еще в классы и прерывания по таймеру и аппаратные не полезли ;) Все же протокол свой пишем.
ну у меня совсем другой уровень программирования. Я в этих структурах , перечислениях и typedef ах не разбираюсь пока. А #define это не переменные получаются, а как бы указания компилятору что вместо одного подставить другое.
Например
в коде везде, где напишешь COMMAND_SEND компилятор подставит 1. Поэтому в case работать будет.
Заметь, точка с запятой не ставится при этом. И ещё, директива #define не занимает оперативную память.
а что байты это не переменные ? какого ещё кодирования и декодирования, я не пойму если чесно.
При получении мессаги из CAN прилетели байты, приравниваешь нужные тебе переменные соответствующим байтам.
Наоборот, при отправке приравниваешь байты библиотеки CAN своим соответсвтующим переменным.
Мой уровень программирования тоже не велик. А что делать.
У меня 21 узел в сети, на структуре видно кол-во датчиков. Мне проще написать вместо адреса Hallway_net_center вместо его номера 6.
Это как DNS в интернете.
Вот что умные люди пишут
define - текстовая замена препроцессором и для компилятора это уже просто циферки - 0,1,2 , а enum - это тип, соответственно получаем лучший контроль со стороны компилятора над ошибками, возможность вывода дополнительных предупреждений(например, если в switch указаны не все константы из enum-типа) и пр.
Вот сейчас изучаю http://robotosha.ru/arduino/multi-tasking-arduino.html
Нужно решить задачу с многозадачностью для этого отказываемя от delay используя вместо него millis (), так же нужны прерывания по таймеру
https://habrahabr.ru/post/337430/
http://mypractic.ru/urok-10-preryvanie-po-tajmeru-v-arduino-biblioteka-mstimer2-parallelnye-processy.html
и по событиям
http://arduino.ru/Reference/AttachInterrupt
http://robocraft.ru/blog/arduino/45.html
Без них сеть не построить. Мы же CAN ради почти реального времени используем.
Вот что умные люди пишут
define - текстовая замена препроцессором и для компилятора это уже просто циферки - 0,1,2 , а enum - это тип, соответственно получаем лучший контроль со стороны компилятора над ошибками, возможность вывода дополнительных предупреждений(например, если в switch указаны не все константы из enum-типа) и пр.
ну напиши тогда
Если я буду использовать define я могу забыть количество переменных и их не обработать в case. В общем объявление переменных можно делать поразному кому как удобнее.
И структура с декодированием не нужна только для отладки, чтобы видеть что получаю в виде текста. Потом останутся только ENUM, структуры и декодирование уберу.
Будет так
При увеличении кол-ва обрабатываемых параметров у нас получится увеличение строк кода в геометрической прогрессии
Вот это я должен вложить во внутрь первого
+ из Case выходишь сразу а IF полный перебор.
дак эти параметры всё равно где-то придётся писать - не в Rx() дак в труктурах твоих. А вместо IF пожалуйства применяй swith какие проблемы. Просто IF лично мне удобнее и вот так вообщето
Так и получается что вместо define я использую enum, вместо if использую case. И еще функции и процедуры.
Вот смотри что получилось. Глубже только функции иначе код перестанет читаться вообще.
ну, раз ты меги используешь, я думаю, памяти тебе хватит.
твой малюсенький вродебы скетч node1 даже на Uno не влазит. 150% оперативы О_О это всё стринги)
Я отладочные структуры уберу потом. Остануться только перечисления. Они в памяти едят столько же сколько define. Я как раз введу параметры компилятору что если флаг отладка использовать структуру если рабочая программа то структуры убираю.
Ну и естественно только меги а в центре вообще Due + Orange Pi + Linux сервер
твой малюсенький вродебы скетч node1 даже на Uno не влазит. 150% оперативы О_О это всё стринги)
Так убери структуры. Протсто закоментируй
тогда уж проще было один дебагер собрать, который декодирует. А все узлы, если надо (ставим флаг например), шлют в кан это служебное сообщение (получается тоже которое получили, но, например, message_type делаем особенный =0) и ты отладчиком уже видишь - кто что видит в кане, и преобразует в удобный тебе вид в сериал.
типа эхо чтоли, получается, только в ID message_type другой.
И не надо на всех контроллерах эту шляпу со стрингами. плюсом тебе не надо со всех сериал монитор - всё по кану.
Зачем
Делаю так
если закоментировать #define debug то структуры и функция декодер в программе не участвует
смотри последнюю версию
https://yadi.sk/d/7pyVRdHJ3TNYXr
а если программа будет побольше занимать, то не сможет работать твой декодер или глюки будут. короче ладно. Идёшь правильной дорогой, просто реально многовато конечно, твой отладчик занимает.
Я просто хочу сделать единую прошику для всех контроллеров
Менять только Node_address и некоторые обработчики
Ну и понятно что головные будут отличаться
это понятно, ТС так и писал в начале
Думаю нужно в конце концов библиотеку сделать и на гитхаб положить.
Я просто хочу сделать единую прошику для всех контроллеров
Менять только Node_address и некоторые обработчики
Ну и понятно что головные будут отличаться
#135 в вот тут #142 посмотри скетчи, как сделал ТС, может полезно будет.
Да именно так через #ifdef определить тип прошивки, вытащить общие структуры в отдельные файлы и лучше сделать либу на наш протокол.
и то верно, чтобы простыней поменьше
вот так например, можно упростить функцию TX(). При этом функцию Code_can_id_msg () убираем за ненадобностью
"Удаленные кадры можно использовать для реализации управления трафиком шины типа «запрос–ответ». На практике, однако, удаленный кадр используется мало. Это не так важно, поскольку стандарт CAN не предписывает действовать именно так, как здесь обозначено. Большинство контроллеров CAN можно запрограммировать так, что они будут автоматически отвечать на удаленный кадр, или же вместо этого извещать локальный процессор."
На самом деле всё не совсем так. Я вроде бы разобрался с этим. В стандартных 11 битных ID , служебный бит (недостающий до 12) - один, это бит RTR. Он и определяет сообщение с данными это ли запрос данных. НО! даже, если это запрос параметров, то мы можем передавать байты в поле данных, хотя в стандарте CAN не должно быть поля данных и ответ должен быть с таким же ID.
В кадрах данных RTR бит должен быть передан нулевым уровнем. Внутри кадра удаленного запроса данных RTR бит должен быть единичным.
В расширенных ID служебных бита уже три:
1. При передаче просто раширенного ID бит RTR также определяет сообщение с данными это или запрос данных.
2. В расширенном варианте могут также передаваться и обычные стандартные 11 битные ID. Дак вот в таком случае определение сообщение с данными это или запрос данных делает уже не бит RTR, а бит SRR (это только в случае, когда при схеме с раширенными ID передаётся стандартный ID).
3. Ну и посдний бит, собственно, и определяет расширенный данный ID или стандариный - бит IDE
Я научился управлять этим служебным битом RTR, поэтому мы имеем в распоряжении ещё один бит в ID, можно назначение его и оставить таким же как в стандарте CAN - определять либо это данные, либо запрос данных.
поэтому в моей библе так и было :
и соответственно чтобы было равно 0 - нужно отправлять 8 . Такова особенность библиотеки видимо.
Пример. Для отправки ID 0х0aabbccd нам нужно отправить 0х8aabbccd
Для отправки ID 0х1aabbccd нам нужно отправить 0х9aabbccd
т.е. если прилетает ID 0x1ABCDEF0, то в реале, на самом деле, прилетает 0x9ABCDEF0, это просто твоя библиотека уже переделывает к правильному виду. Получается так:
вот так например, можно упростить функцию TX(). При этом функцию Code_can_id_msg () убираем за ненадобностью
Упрощения само собой, это пока наброски кода. И я делал оба варианта 11-29 просто попробовать как либа себе ведет. На выходе только 29 бит с использованием FR.
Вот почти боевой код
попробовал, чето твоя либа не хочет Request Frame отправлять, на моей получилось. Сужу по анализатору шины. Он так и пишет: RTR.
я думаю нафиг эта поддержка стандартых ID и кадров Remote не нужна короче.
И зря убираешь макрос F из печати Serial.print
Тут все хитрее, смотри поисание либы
есть ф-я
sendMsgBuf(txId_can, 8, data); тут 3 параметра
здесь можно передать RF если txId_can=0x4000000
а есть
sendMsgBuf(txId_can, 0, 8, data); тут 4 праметра
здесь нельза передать RF и даже если txId_can=0x4000000 либа просто отфильтрует и в сеть уйдет txId_can=0x0000000
я думаю нафиг эта поддержка стандартых ID и кадров Remote не нужна короче.
И зря убираешь макрос F из печати Serial.print
Ху из F?
этот маркос всю писанину (строковые константы в кавычках при сериалпринте) кладет не ОЗУ а на флеш
здесь можно передать RF если txId_can=0x4000000
интересно, а нафига тогда цифра 8 и дата, если поля данных нет
короче всё равно не отправляет FR ы, всяко попробовал
нет, не всяко попробовал. Нужно было как всегда внимательнее читать. И нужно брать не 0x40000000 , а 0xC0000000
Делаем ИЛИ с нужным нам ID и Обязательно DLC в ноль. Я когда пробовал видимо по очереди и то и то , а вместе - нет, поэтому не работало. Рабочий код отправки FR
и анализатор шины собери уже, не знаю как ты без него обходишься, делов то на 5 мин. Ссылку я давал в #217
и анализатор шины собери уже, не знаю как ты без него обходишься, делов то на 5 мин. Ссылку я давал в #217
Обязательно Nano или я могу залить в любой свой контроллер?
И второй вопрос. У меня W7 64, основное окно запустилось при попытке настроит виснет наглухо. (Пришлось подсовывать mscomm32.ocx и регистрировать). Есть подводные камни?
Чето не помню уже, вроде норм всё должно быть. Мегу можно конечно. Когда нажимаешь настроить вроде порт должен быть вставлен. Покажи куда тычешь когда виснет
т.к. мы разобрались с этими ID и Remote ами предлагаю исправить функцию RX()
Немного поменять, сам проверь, если закоментировать debug то за default: нет ничего, компилятор ругнется. Поэтому так:
Кстати вот мои Mega в компактном формфакторе (пока резинкой с 2515 соединил. Потом что то буду думать.
И кстати 1 голова хорошо, а две во много крат лучше. Так что к лету думаю протокол доведем ;-)
Это точно. Я ещё лучше придумал выуживание типа ID. Нужно бит определённый выделять а не байт) щас покажу
а че CANHAcker то заработал? Если чё, уно же есть у тебя, там точно должно.
Програ раступилась. Просто долго думала при первом запуске, видимо порты искала.
Но данные в нее не идут.
Я залил твой скетч, изменил пин с 10 на 53 (Мега), подключил МК с CanHacker к сети, запустил Win прогу, выбрал в ней порт меги с CanHacker, нажал коннект и .... ничего (
потому что у тебя нужно во всех скетчах сделать 8MHZ а не 16, (смотри на кварц на модулях)
И ЕЩЁ!!! смотри на каком пине у МЕГИ прерывание INT0, на 23 вроде, попробуй это тоже поменять. У Наны на 2 пине.
скорость шины и компорт выбирается во вкладке Settings
PS в канхакере ( в библиотеке которая по ссылке) я там уже исправил.
вот у тебя типы сообщений
Только я не могу понять, зачем нужен direction?
Понятно, что раз приставка SEND то выполняется, когда у меня что то запрашивают
и если приставка ANSVER то выполняется когда мне отвечают на мой запрос
Может так проще?
и нужно добавить ещё один тип сообщений - СOMMAND_ANSVER_OK - команда выполнена
отпличие от СOMMAND_ANSVER - команда принята к исполнению
и вот подравнял TX(). Теперь FRы должны отправляться
Только я не могу понять, зачем нужен direction?
Понятно, что раз приставка SEND то выполняется, когда у меня что то запрашивают
и если приставка ANSVER то выполняется когда мне отвечают на мой запрос
Может так проще?
Тут скоре плюшкиничество работает, твой not use покоя не дает. Давай думать куда его приспособить ;-)
Ты там выше про биты писал. Просто пока идей не приходит.