. Идея это обязательно иметь уникальный номер сообщения, у контроллера, я его в блок данных внесу, крутится счетчик в рамках выделенных бит с сбросом в о после переполнения.
Так во многих автомобильных протоколах сделано. Обычно последний байт в поле данных фрейма.
А можно переменные объявленные в #define использовать в case?
И второй вопрос у меня в коде объявлены 3 структуры тип команд, адрес и тип устройства. И 1 в разных структурах имеет разное значение.
Причем enum мне нужен для кодирования сообщения из переменных в байты, а struct для декодирования.
И к тому же я структуры и перечисления вынесу в общий код для всех контроллеров.
Насчет функций. Я из естественно опптимизирую, но без них никуда. Это типовые действия. Причем Decode я естественно в RX запихну (сейчас просто удобнее) , а вот Code и ТХ точно нужно разделять.
Я пока набрасываю структуру программы, общий алгоритм.
Это мы еще в классы и прерывания по таймеру и аппаратные не полезли ;) Все же протокол свой пишем.
ну у меня совсем другой уровень программирования. Я в этих структурах , перечислениях и typedef ах не разбираюсь пока. А #define это не переменные получаются, а как бы указания компилятору что вместо одного подставить другое.
Мой уровень программирования тоже не велик. А что делать.
У меня 21 узел в сети, на структуре видно кол-во датчиков. Мне проще написать вместо адреса Hallway_net_center вместо его номера 6.
Это как DNS в интернете.
Вот что умные люди пишут
define - текстовая замена препроцессором и для компилятора это уже просто циферки - 0,1,2 , а enum - это тип, соответственно получаем лучший контроль со стороны компилятора над ошибками, возможность вывода дополнительных предупреждений(например, если в switch указаны не все константы из enum-типа) и пр.
define - текстовая замена препроцессором и для компилятора это уже просто циферки - 0,1,2 , а enum - это тип, соответственно получаем лучший контроль со стороны компилятора над ошибками, возможность вывода дополнительных предупреждений(например, если в switch указаны не все константы из enum-типа) и пр.
Если я буду использовать define я могу забыть количество переменных и их не обработать в case. В общем объявление переменных можно делать поразному кому как удобнее.
И структура с декодированием не нужна только для отладки, чтобы видеть что получаю в виде текста. Потом останутся только ENUM, структуры и декодирование уберу.
При увеличении кол-ва обрабатываемых параметров у нас получится увеличение строк кода в геометрической прогрессии
дак эти параметры всё равно где-то придётся писать - не в Rx() дак в труктурах твоих. А вместо IF пожалуйства применяй swith какие проблемы. Просто IF лично мне удобнее и вот так вообщето
Я отладочные структуры уберу потом. Остануться только перечисления. Они в памяти едят столько же сколько 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, это просто твоя библиотека уже переделывает к правильному виду. Получается так:
и чёто капец у тебя сложно как-то всё , запутаться можно - из одной функции в другую функцию и т.д. Попроще можно всё и другим понятнее будет.
вот так например, можно упростить функцию TX(). При этом функцию Code_can_id_msg () убираем за ненадобностью
Упрощения само собой, это пока наброски кода. И я делал оба варианта 11-29 просто попробовать как либа себе ведет. На выходе только 29 бит с использованием FR.
короче всё равно не отправляет FR ы, всяко попробовал
нет, не всяко попробовал. Нужно было как всегда внимательнее читать. И нужно брать не 0x40000000 , а 0xC0000000
Делаем ИЛИ с нужным нам ID и Обязательно DLC в ноль. Я когда пробовал видимо по очереди и то и то , а вместе - нет, поэтому не работало. Рабочий код отправки FR
и анализатор шины собери уже, не знаю как ты без него обходишься, делов то на 5 мин. Ссылку я давал в #217
Обязательно Nano или я могу залить в любой свой контроллер?
И второй вопрос. У меня W7 64, основное окно запустилось при попытке настроит виснет наглухо. (Пришлось подсовывать mscomm32.ocx и регистрировать). Есть подводные камни?
Чето не помню уже, вроде норм всё должно быть. Мегу можно конечно. Когда нажимаешь настроить вроде порт должен быть вставлен. Покажи куда тычешь когда виснет
Програ раступилась. Просто долго думала при первом запуске, видимо порты искала.
Но данные в нее не идут.
Я залил твой скетч, изменил пин с 10 на 53 (Мега), подключил МК с CanHacker к сети, запустил Win прогу, выбрал в ней порт меги с CanHacker, нажал коннект и .... ничего (
Так во многих автомобильных протоколах сделано. Обычно последний байт в поле данных фрейма.
и чёто капец у тебя сложно как-то всё , запутаться можно - из одной функции в другую функцию и т.д. Попроще можно всё и другим понятнее будет.
Например это:
01
// Задаем перечисления и структуры для типов сообщений Y
02
typedef
enum
Message_enum
03
{
04
NULL_C,
//0
05
COMMAND_SEND,
//1
06
COMMAND_ANSVER,
//2
07
REQUEST_SEND,
//3
08
REQUEST_ANSVER,
//4
09
STATUS_REQUEST_SEND,
//5
10
STATUS_REQUEST_ANSVER
//6
11
12
} Message_CAN_ENUM;
13
14
typedef
struct
{
15
uint8_t msg_num;
16
String msg_name;
17
}Message_CANvalueData;
18
19
const
Message_CANvalueData Message_CANData[] =
20
{
21
/*Key KeyName */
22
{ 0 ,
"NULL_C"
},
23
{ 1 ,
"COMMAND SEND"
},
24
{ 2 ,
"COMMAND_ANSVER"
},
25
{ 3 ,
"REQUEST_SEND"
},
26
{ 4 ,
"REQUEST_ANSVER"
},
27
{ 5 ,
"STATUS_REQUEST_SEND"
},
28
{ 6 ,
"STATUS_REQUEST_ANSVER"
}
29
30
};
Может так проще сделать? :
1
#define NULL_C 0
2
#define COMMAND_SEND 1
3
#define COMMAND_ANSVER 2
4
#define REQUEST_SEND 3
5
#define REQUEST_ANSVER 4
6
#define STATUS_REQUEST_SEND 5
7
#define STATUS_REQUEST_ANSVER 6
А можно переменные объявленные в #define использовать в case?
И второй вопрос у меня в коде объявлены 3 структуры тип команд, адрес и тип устройства. И 1 в разных структурах имеет разное значение.
Причем enum мне нужен для кодирования сообщения из переменных в байты, а struct для декодирования.
И к тому же я структуры и перечисления вынесу в общий код для всех контроллеров.
Насчет функций. Я из естественно опптимизирую, но без них никуда. Это типовые действия. Причем Decode я естественно в RX запихну (сейчас просто удобнее) , а вот Code и ТХ точно нужно разделять.
Я пока набрасываю структуру программы, общий алгоритм.
Это мы еще в классы и прерывания по таймеру и аппаратные не полезли ;) Все же протокол свой пишем.
ну у меня совсем другой уровень программирования. Я в этих структурах , перечислениях и typedef ах не разбираюсь пока. А #define это не переменные получаются, а как бы указания компилятору что вместо одного подставить другое.
Например
1
#define COMMAND_SEND 1
в коде везде, где напишешь 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-типа) и пр.
ну напиши тогда
1
const
uint8_t COMMAND_SEND = 1;
Если я буду использовать define я могу забыть количество переменных и их не обработать в case. В общем объявление переменных можно делать поразному кому как удобнее.
И структура с декодированием не нужна только для отладки, чтобы видеть что получаю в виде текста. Потом останутся только ENUM, структуры и декодирование уберу.
Будет так
01
switch
(direction) {
02
case
0:
//выполняется, когда RX Reqv т.е у меня что то запрашивают
03
04
break
;
05
case
1:
//выполняется когда RX Ansv т.е мне отвечают на мой запрос
06
07
break
;
08
default
:
09
10
// выполняется, если не выбрана ни одна альтернатива
11
// default необязателен
12
}
01
const
uint8_t NULL_C = 0;
02
const
uint8_t COMMAND_SEND = 1;
03
const
uint8_t COMMAND_ANSVER = 2;
04
const
uint8_t REQUEST_SEND = 3;
05
const
uint8_t REQUEST_ANSVER = 4;
06
const
uint8_t STATUS_REQUEST_SEND = 5;
07
const
uint8_t STATUS_REQUEST_ANSVER = 6;
08
09
const
uint8_t center_node = 11;
10
const
uint8_t kitchen_node = 22;
11
const
uint8_t bathroom_node = 33;
12
const
uint8_t officeroom_node = 44;
13
const
uint8_t bedroom_node = 55;
14
15
16
void
RX()
17
{
18
uint8_t direction, msg_type, dev_addr, rem_addr, dev_type;
19
20
if
(!digitalRead(CAN0_INT))
//
21
{
22
CAN0.readMsgBuf(&rxId_can, &len_can, rxBuf_can);
// Read data: len = data length, buf = data byte(s)
23
24
direction = (rxId_can & 0x10000000)>>28;
// извлекаем 1-битный идентификатор запроса-ответа из ID
25
msg_type = (rxId_can & 0xF000000)>>24;
// извлекаем 4-битный идентификатор сообщения из ID
26
dev_addr = (rxId_can & 0xFF0000)>>16;
// извлекаем 8-битный адрес отправителя из ID
27
rem_addr = (rxId_can & 0xFF00)>>8;
// извлекаем 8-битный адрес получателя из ID
28
dev_type = (rxId_can & 0xFF);
// извлекаем 8-битный тип устройства у получателя из ID
29
30
Serial
.print(F(
" type_msg - "
));
31
if
(msg_type == COMMAND_SEND)
Serial
.print(F(
"COMMAND_SEND"
));
//макрос F записывает строковую константу не в оперативу, а на флеш
32
else
if
(msg_type == COMMAND_ANSVER)
Serial
.print(F(
"COMMAND_ANSVER"
));
33
else
if
(msg_type == REQUEST_SEND)
Serial
.print(F(
"REQUEST_SEND"
));
34
else
if
(msg_type == REQUEST_ANSVER)
Serial
.print(F(
"REQUEST_ANSVER"
));
35
else
if
(msg_type == STATUS_REQUEST_SEND)
Serial
.print(F(
"STATUS_REQUEST_SEND"
));
36
else
if
(msg_type == STATUS_REQUEST_ANSVER)
Serial
.print(F(
"STATUS_REQUEST_ANSVER"
));
37
38
Serial
.print(F(
" node_addr - "
));
39
40
if
(dev_addr == center_node)
Serial
.print(F(
"center_node"
));
41
else
if
(dev_addr == kitchen_node)
Serial
.print(F(
"kitchen_node"
));
42
else
if
(dev_addr == bathroom_node)
Serial
.print(F(
"bathroom_node"
));
43
else
if
(dev_addr == officeroom_node)
Serial
.print(F(
"officeroom_node"
));
44
else
if
(dev_addr == bedroom_node)
Serial
.print(F(
"bedroom_node"
));
45
46
// и так далее в общем. Таким образом ты не кладёшь все String-и в оперативу, и опять же экономишщ память и функций меньше
47
48
}
49
}
При увеличении кол-ва обрабатываемых параметров у нас получится увеличение строк кода в геометрической прогрессии
Вот это я должен вложить во внутрь первого
01
switch
(msg_type){
02
case
COMMAND_SEND:
//выполняется, когда мне прислали команду от удаленного контроллера
03
04
break
;
05
case
COMMAND_ANSVER:
//выполняется когда удаленный контроллер отвечают на посланную команду
06
07
break
;
08
case
REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
09
10
break
;
11
case
REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
12
13
break
;
14
case
STATUS_REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запрашивает состояние
15
16
break
;
17
case
STATUS_REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
18
19
break
;
20
default
:
21
//если параметр за пределами ENUM
22
}
01
if
(msg_type=1)
02
{
03
}
04
else
if
(msg_type=2)
05
{
06
}
07
else
if
(msg_type=3)
08
{
09
}
10
else
if
(msg_type=4)
11
{
12
}
13
else
if
(msg_type=5)
14
{
15
}
16
else
if
(msg_type=6)
17
{
18
}
+ из Case выходишь сразу а IF полный перебор.
дак эти параметры всё равно где-то придётся писать - не в Rx() дак в труктурах твоих. А вместо IF пожалуйства применяй swith какие проблемы. Просто IF лично мне удобнее и вот так вообщето
1
if
(msg_type == COMMAND_SEND) {выполняется, когда мне прислали команду от удаленного контроллера
2
}
3
else
if
(msg_type == COMMAND_ANSVER) {выполняется когда удаленный контроллер отвечают на посланную команду}
4
else
if
(msg_type == REQUEST_SEND) {выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то}
5
else
if
(msg_type == REQUEST_ANSVER) {выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика}
6
else
if
(msg_type == STATUS_REQUEST_SEND) {выполняется когда удаленный контроллер у меня запрашивает состояние}
7
else
if
(msg_type == STATUS_REQUEST_ANSVER) {выполняется когда удаленный контроллер мне отвечают на мой запрос состояния}
8
else
{выполняется когда параметр за пределами}
Так и получается что вместо define я использую enum, вместо if использую case. И еще функции и процедуры.
Вот смотри что получилось. Глубже только функции иначе код перестанет читаться вообще.
01
switch
(direction) {
02
case
0:
//выполняется, когда RX Reqv т.е у меня что то запрашивают
03
switch
(msg_type){
04
case
COMMAND_SEND:
//выполняется, когда мне прислали команду от удаленного контроллера
05
// Функция обработки присланой команды от удаленного контроллера
06
break
;
07
case
REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
08
// Функция обработки присланного запроса параметра с датчика
09
break
;
10
case
STATUS_REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запрашивает состояние
11
// Функция обработки запроса моего состояния
12
break
;
13
default
:
14
//если параметр за пределами ENUM
15
Serial
.print(
" Default msg - Recive Uncnown type message"
);
16
}
17
break
;
18
case
1:
//выполняется когда RX Ansv т.е мне отвечают на мой запрос
19
switch
(msg_type){
20
case
COMMAND_ANSVER:
//выполняется когда удаленный контроллер отвечает на посланную команду
21
// Функция обработки ответа на мою команду
22
break
;
23
case
REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
24
// Функция обработки ответа на мой запрос параметра датчика
25
break
;
26
case
STATUS_REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
27
// Функция обработки ответа на мой запрос состояния УК
28
break
;
29
default
:
30
//если параметр за пределами ENUM
31
Serial
.print(
" Default msg - Recive Uncnown type message"
);
32
}
33
break
;
34
default
:
35
Serial
.print(
" Default direct "
);
36
//если параметр за пределами ENUM
37
// default необязателен
38
}
ну, раз ты меги используешь, я думаю, памяти тебе хватит.
твой малюсенький вродебы скетч node1 даже на Uno не влазит. 150% оперативы О_О это всё стринги)
Я отладочные структуры уберу потом. Остануться только перечисления. Они в памяти едят столько же сколько define. Я как раз введу параметры компилятору что если флаг отладка использовать структуру если рабочая программа то структуры убираю.
Ну и естественно только меги а в центре вообще Due + Orange Pi + Linux сервер
твой малюсенький вродебы скетч node1 даже на Uno не влазит. 150% оперативы О_О это всё стринги)
Так убери структуры. Протсто закоментируй
тогда уж проще было один дебагер собрать, который декодирует. А все узлы, если надо (ставим флаг например), шлют в кан это служебное сообщение (получается тоже которое получили, но, например, message_type делаем особенный =0) и ты отладчиком уже видишь - кто что видит в кане, и преобразует в удобный тебе вид в сериал.
типа эхо чтоли, получается, только в ID message_type другой.
И не надо на всех контроллерах эту шляпу со стрингами. плюсом тебе не надо со всех сериал монитор - всё по кану.
Зачем
Делаю так
01
#define debug
02
03
04
#ifdef debug
05
typedef
struct
{
06
uint8_t msg_num;
07
String msg_name;
08
}Message_CANvalueData;
09
10
const
Message_CANvalueData Message_CANData[] =
11
{
12
/*Key KeyName */
13
{ 0 ,
"NULL_C"
},
14
{ 1 ,
"COMMAND SEND"
},
15
{ 2 ,
"COMMAND_ANSVER"
},
16
{ 3 ,
"REQUEST_SEND"
},
17
{ 4 ,
"REQUEST_ANSVER"
},
18
{ 5 ,
"STATUS_REQUEST_SEND"
},
19
{ 6 ,
"STATUS_REQUEST_ANSVER"
}
20
21
};
22
#endif
23
24
#ifdef debug
25
DeCode_can_id_msg(direction,msg_type,dev_addr,rem_addr,dev_type,rxBuf_can);
//Отладочная функция
26
#endif
если закоментировать #define debug то структуры и функция декодер в программе не участвует
смотри последнюю версию
https://yadi.sk/d/7pyVRdHJ3TNYXr
а если программа будет побольше занимать, то не сможет работать твой декодер или глюки будут. короче ладно. Идёшь правильной дорогой, просто реально многовато конечно, твой отладчик занимает.
Я просто хочу сделать единую прошику для всех контроллеров
Менять только Node_address и некоторые обработчики
Ну и понятно что головные будут отличаться
это понятно, ТС так и писал в начале
Думаю нужно в конце концов библиотеку сделать и на гитхаб положить.
Я просто хочу сделать единую прошику для всех контроллеров
Менять только Node_address и некоторые обработчики
Ну и понятно что головные будут отличаться
#135 в вот тут #142 посмотри скетчи, как сделал ТС, может полезно будет.
Да именно так через #ifdef определить тип прошивки, вытащить общие структуры в отдельные файлы и лучше сделать либу на наш протокол.
и то верно, чтобы простыней поменьше
вот так например, можно упростить функцию TX(). При этом функцию Code_can_id_msg () убираем за ненадобностью
01
#define EXTERNAL 1
02
#define STANDART 0
03
04
void
TX(uint8_t direction, uint8_t msg_type, uint8_t dev_addr, uint8_t rem_addr, int8_t dev_type, uint8_t can_msg_type, uint8_t can_type_id,
byte
data[8])
05
{
06
uint32_t txId_can;
07
08
if
(can_msg_type==4) txId_can=0x40000000;
09
else
txId_can = (direction & 0xFFFFFFFF)<<28 | (msg_type & 0xFFFFFFFF)<<24 | (dev_addr & 0xFFFFFFFF)<<16 | (rem_addr & 0xFFFFFFFF)<<8 | dev_type;
10
11
if
(can_msg_type==8 && can_type_id == STANDART) txId_can+0x80000000;
12
13
byte
sndStat = CAN0.sendMsgBuf(txId_can, can_type_id ,8, data);
14
15
#ifdef debug
16
if
(sndStat == CAN_OK){
Serial
.print(F(
"Message Sent Successfully! TX CAN ID "
));
Serial
.println(txId_can, HEX);}
17
else
Serial
.println(F(
"Error Sending Message..."
));
18
#endif
19
}
20
21
22
// вызов функции TX (1, STATUS_REQUEST_ANSVER, node_address, rem_addr, null_s, 0, EXTERNAL, data)
"Удаленные кадры можно использовать для реализации управления трафиком шины типа «запрос–ответ». На практике, однако, удаленный кадр используется мало. Это не так важно, поскольку стандарт 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, это просто твоя библиотека уже переделывает к правильному виду. Получается так:
1
прилетает 0x9ABCDEF0 а видим 0x1ABCDEF0, но это сообщение с данными
2
прилетает 0x8ABCDEF0 а видим 0x0ABCDEF0, но это сообщение с данными
3
прилетает 0xDABCDEF0 а видим 0x1ABCDEF0, но это сообщение запрос данных
4
прилетает 0xCABCDEF0 а видим 0x0ABCDEF0, но это сообщение запрос данных
5
6
при всём при этом МОЖНО передавать байты в поле данных во всех этих типах сообщений
вот так например, можно упростить функцию TX(). При этом функцию Code_can_id_msg () убираем за ненадобностью
Упрощения само собой, это пока наброски кода. И я делал оба варианта 11-29 просто попробовать как либа себе ведет. На выходе только 29 бит с использованием FR.
Вот почти боевой код
01
void
TX(uint8_t direction, uint8_t msg_type, uint8_t dev_addr, uint8_t rem_addr, int8_t dev_type, uint8_t can_msg_type,
byte
data[8])
02
{
03
uint32_t txId_can;
04
05
if
(can_msg_type==4)
06
txId_can=0x40000000;
07
else
if
(can_msg_type==0)
08
txId_can = (direction & 0xFFFFFFFF)<<28 | (msg_type & 0xFFFFFFFF)<<24 | (dev_addr & 0xFFFFFFFF)<<16 | (rem_addr & 0xFFFFFFFF)<<8 | dev_type;
09
else
10
{
11
#ifdef debug
12
Serial
.println((
"Error can_msg_type"
));
13
#endif
14
break
;
15
}
16
17
byte
sndStat = CAN0.sendMsgBuf(txId_can, 1 ,8, data);
18
19
#ifdef debug
20
if
(sndStat == CAN_OK){
Serial
.print((
"Message Sent Successfully! - "
;
Serial
.println(txId_can, HEX);}
21
else
Serial
.println((
"Error Sending Message..."
));
22
#endif
23
}
24
25
26
// вызов функции TX (1, STATUS_REQUEST_ANSVER, node_address, rem_addr, null_s, 0, data) предпоследний параметр 0 или 4 если 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
01
// CAN Send Example
02
//
03
04
#include <mcp_can.h>
05
#include <SPI.h>
06
07
MCP_CAN CAN0(10);
// Set CS to pin 10
08
09
void
setup
()
10
{
11
Serial
.begin(115200);
12
13
// Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.
14
if
(CAN0.begin(MCP_ANY, CAN_250KBPS, MCP_8MHZ) == CAN_OK)
Serial
.println(
"MCP2515 Initialized Successfully!"
);
15
else
Serial
.println(
"Error Initializing MCP2515..."
);
16
17
CAN0.setMode(MCP_NORMAL);
// Change to normal mode to allow messages to be transmitted
18
}
19
20
byte
data[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
21
22
void
loop
()
23
{
24
// send data: ID = 0x10000000, Standard CAN Frame, Data length = 8 bytes, 'data' = array of data bytes to send
25
uint32_t txId_can = 0x1ABCDEF6;
26
27
byte
sndStat = CAN0.sendMsgBuf((txId_can|0xС0000000), 0, data);
// SEND REMOTE REQUEST FRAME
28
if
(sndStat == CAN_OK){
29
Serial
.println(
"Message Sent Successfully!"
);
30
}
else
{
31
Serial
.println(
"Error Sending Message..."
);
32
}
33
delay(1000);
// send data per 100ms
34
}
35
36
/*********************************************************************************************************
37
END FILE
38
*********************************************************************************************************/
и анализатор шины собери уже, не знаю как ты без него обходишься, делов то на 5 мин. Ссылку я давал в #217
и анализатор шины собери уже, не знаю как ты без него обходишься, делов то на 5 мин. Ссылку я давал в #217
Обязательно Nano или я могу залить в любой свой контроллер?
И второй вопрос. У меня W7 64, основное окно запустилось при попытке настроит виснет наглухо. (Пришлось подсовывать mscomm32.ocx и регистрировать). Есть подводные камни?
Чето не помню уже, вроде норм всё должно быть. Мегу можно конечно. Когда нажимаешь настроить вроде порт должен быть вставлен. Покажи куда тычешь когда виснет
т.к. мы разобрались с этими ID и Remote ами предлагаю исправить функцию RX()
01
void
RX()
02
{
03
if
(!digitalRead(CAN0_INT))
// If CAN0_INT pin is low, read receive buffer
04
{
05
CAN0.readMsgBuf(&rxId_can, &len_can, rxBuf_can);
// Read data: len = data length, buf = data byte(s)
06
07
uint8_t direction, msg_type, dev_addr, rem_addr, dev_type, can_msg_type;
08
uint8_t data_b0,data_b1,data_b2,data_b3,data_b4,data_b5,data_b6,data_b7;
09
10
can_msg_type = (rxId_can & 0xF0000000)>>28;
//извлекаем тип ID
11
if
(can_msg_type == 0) {тут что нибудт делаем}
// получили ID 11 бит
12
if
(can_msg_type == 8 || can_msg_type == 9) {тут что нибудт делаем}
// получили ID 29 бит, сообщение - DataFrame
13
if
(can_msg_type == 0x0C || can_msg_type == 0x0D) {тут что нибудт делаем}
// получили ID 29 бит, сообщение - RemoteFrame
14
15
direction = (rxId_can & 0x10000000)>>28;
// извлекаем 1-битный идентификатор запроса-ответа из ID
16
msg_type = (rxId_can & 0xF000000)>>24;
// извлекаем 4-битный идентификатор сообщения из ID
17
dev_addr = (rxId_can & 0xFF0000)>>16;
// извлекаем 8-битный адрес отправителя из ID
18
rem_addr = (rxId_can & 0xFF00)>>8;
// извлекаем 8-битный адрес получателя из ID
19
dev_type = rxId_can;
// извлекаем 8-битный тип устройства у получателя из ID
20
21
22
#ifdef debug
23
DeCode_can_id_msg(direction,msg_type,dev_addr,rem_addr,dev_type,rxBuf_can);
//Отладочная функция
24
#endif
25
//Начинаем разбор принятого
26
switch
(direction) {
27
case
0:
//выполняется, когда RX Reqv т.е у меня что то запрашивают
28
switch
(msg_type){
29
case
COMMAND_SEND:
//выполняется, когда мне прислали команду от удаленного контроллера
30
// Функция обработки присланой команды от удаленного контроллера
31
// Что за команда запрошена
32
break
;
33
case
REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
34
// Функция обработки присланного запроса параметра с датчика
35
// Что за датчик запрошен
36
break
;
37
case
STATUS_REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запрашивает состояние
38
// Функция обработки запроса моего состояния
39
// Вернуть состояние
40
break
;
41
default
:
42
//если параметр за пределами ENUM
43
#ifdef debug
44
Serial
.println(
"Default msg - Recive Uncnown type message"
);
45
#endif
46
}
47
break
;
48
case
1:
//выполняется когда RX Ansv т.е мне отвечают на мой запрос
49
switch
(msg_type){
50
case
COMMAND_ANSVER:
//выполняется когда удаленный контроллер отвечает на посланную команду
51
// Функция обработки ответа на мою команду
52
break
;
53
case
REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
54
// Функция обработки ответа на мой запрос параметра датчика
55
break
;
56
case
STATUS_REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
57
// Функция обработки ответа на мой запрос состояния УК
58
break
;
59
default
:
60
//если параметр за пределами ENUM
61
#ifdef debug
62
Serial
.println(
"Default msg - Recive Uncnown type message"
);
63
#endif
64
}
65
break
;
66
default
:
67
#ifdef debug
68
Serial
.print(
" Default direct "
);
69
#endif
70
//если параметр за пределами ENUM
71
// default необязателен
72
}
73
74
}
75
}
Немного поменять, сам проверь, если закоментировать debug то за default: нет ничего, компилятор ругнется. Поэтому так:
01
void
RX()
02
{
03
if
(!digitalRead(CAN0_INT))
// If CAN0_INT pin is low, read receive buffer
04
{
05
CAN0.readMsgBuf(&rxId_can, &len_can, rxBuf_can);
// Read data: len = data length, buf = data byte(s)
06
07
uint8_t direction, msg_type, dev_addr, rem_addr, dev_type, can_msg_type;
08
uint8_t data_b0,data_b1,data_b2,data_b3,data_b4,data_b5,data_b6,data_b7;
09
10
can_msg_type = (rxId_can & 0xF0000000)>>28;
//извлекаем тип ID
11
if
(can_msg_type == 0) {тут что нибудт делаем}
// получили ID 11 бит
12
if
(can_msg_type == 8 || can_msg_type == 9) {тут что нибудт делаем}
// получили ID 29 бит, сообщение - DataFrame
13
if
(can_msg_type == 0x0C || can_msg_type == 0x0D) {тут что нибудт делаем}
// получили ID 29 бит, сообщение - RemoteFrame
14
15
direction = (rxId_can & 0x10000000)>>28;
// извлекаем 1-битный идентификатор запроса-ответа из ID
16
msg_type = (rxId_can & 0xF000000)>>24;
// извлекаем 4-битный идентификатор сообщения из ID
17
dev_addr = (rxId_can & 0xFF0000)>>16;
// извлекаем 8-битный адрес отправителя из ID
18
rem_addr = (rxId_can & 0xFF00)>>8;
// извлекаем 8-битный адрес получателя из ID
19
dev_type = rxId_can;
// извлекаем 8-битный тип устройства у получателя из ID
20
21
22
#ifdef debug
23
DeCode_can_id_msg(direction,msg_type,dev_addr,rem_addr,dev_type,rxBuf_can);
//Отладочная функция
24
#endif
25
//Начинаем разбор принятого
26
switch
(direction) {
27
case
0:
//выполняется, когда RX Reqv т.е у меня что то запрашивают
28
switch
(msg_type){
29
case
COMMAND_SEND:
//выполняется, когда мне прислали команду от удаленного контроллера
30
// Функция обработки присланой команды от удаленного контроллера
31
// Что за команда запрошена
32
break
;
33
case
REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
34
// Функция обработки присланного запроса параметра с датчика
35
// Что за датчик запрошен
36
break
;
37
case
STATUS_REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запрашивает состояние
38
// Функция обработки запроса моего состояния
39
// Вернуть состояние
40
break
;
41
default
:
42
{
43
//если параметр за пределами ENUM
44
#ifdef debug
45
Serial
.println(
"Default msg - Recive Uncnown type message"
);
46
#endif
47
}
48
}
49
break
;
50
case
1:
//выполняется когда RX Ansv т.е мне отвечают на мой запрос
51
switch
(msg_type){
52
case
COMMAND_ANSVER:
//выполняется когда удаленный контроллер отвечает на посланную команду
53
// Функция обработки ответа на мою команду
54
break
;
55
case
REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
56
// Функция обработки ответа на мой запрос параметра датчика
57
break
;
58
case
STATUS_REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
59
// Функция обработки ответа на мой запрос состояния УК
60
break
;
61
default
:
62
{
63
//если параметр за пределами ENUM
64
#ifdef debug
65
Serial
.println(
"Default msg - Recive Uncnown type message"
);
66
#endif
67
}
68
}
69
break
;
70
default
:
71
{
72
#ifdef debug
73
Serial
.print(
" Default direct "
);
74
#endif
75
//если параметр за пределами ENUM
76
// default необязателен
77
}
78
}
79
80
}
81
}
Кстати вот мои Mega в компактном формфакторе (пока резинкой с 2515 соединил. Потом что то буду думать.
И кстати 1 голова хорошо, а две во много крат лучше. Так что к лету думаю протокол доведем ;-)
Это точно. Я ещё лучше придумал выуживание типа ID. Нужно бит определённый выделять а не байт) щас покажу
01
uint8_t msg_type, dev_addr, rem_addr, dev_type;
02
bool
direction, can_frame_type; can_ID_type;
03
uint8_t data_b0,data_b1,data_b2,data_b3,data_b4,data_b5,data_b6,data_b7;
04
05
can_ID_type = (rxId_can & 0x80000000)>>31;
//извлекаем тип ID (0 - 11bit, 1 - 29 bit)
06
can_frame_type = (rxId_can & 0x40000000)>>30;
//извлекаем тип СAN FRAME (0 - Data, 1 - Remote)
07
08
direction = (rxId_can & 0x10000000)>>28;
// извлекаем 1-битный идентификатор запроса-ответа из ID
09
msg_type = (rxId_can & 0xF000000)>>24;
// извлекаем 4-битный идентификатор сообщения из ID
10
dev_addr = (rxId_can & 0xFF0000)>>16;
// извлекаем 8-битный адрес отправителя из ID
11
rem_addr = (rxId_can & 0xFF00)>>8;
// извлекаем 8-битный адрес получателя из ID
12
dev_type = rxId_can;
// извлекаем 8-битный тип устройства у получателя из ID
а че CANHAcker то заработал? Если чё, уно же есть у тебя, там точно должно.
Програ раступилась. Просто долго думала при первом запуске, видимо порты искала.
Но данные в нее не идут.
Я залил твой скетч, изменил пин с 10 на 53 (Мега), подключил МК с CanHacker к сети, запустил Win прогу, выбрал в ней порт меги с CanHacker, нажал коннект и .... ничего (
потому что у тебя нужно во всех скетчах сделать 8MHZ а не 16, (смотри на кварц на модулях)
1
if
(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK)
И ЕЩЁ!!! смотри на каком пине у МЕГИ прерывание INT0, на 23 вроде, попробуй это тоже поменять. У Наны на 2 пине.
скорость шины и компорт выбирается во вкладке Settings
PS в канхакере ( в библиотеке которая по ссылке) я там уже исправил.
вот у тебя типы сообщений
1
COMMAND_SEND:
//выполняется, когда мне прислали команду от удаленного контроллера
2
REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
3
STATUS_REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запрашивает состояние
4
COMMAND_ANSVER:
//выполняется когда удаленный контроллер отвечает на посланную команду
5
REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
6
STATUS_REQUEST_ANSVER:
//выполняется когда удаленный контроль
Только я не могу понять, зачем нужен direction?
Понятно, что раз приставка SEND то выполняется, когда у меня что то запрашивают
и если приставка ANSVER то выполняется когда мне отвечают на мой запрос
Может так проще?
01
void
RX()
02
{
03
if
(!digitalRead(CAN0_INT))
// If CAN0_INT pin is low, read receive buffer
04
{
05
CAN0.readMsgBuf(&rxId_can, &len_can, rxBuf_can);
// Read data: len = data length, buf = data byte(s)
06
07
uint8_t msg_type, dev_addr, rem_addr, dev_type;
08
bool
direction, can_frame_type; can_ID_type;
09
uint8_t data_b0,data_b1,data_b2,data_b3,data_b4,data_b5,data_b6,data_b7;
10
11
can_ID_type = (canMsg.can_id & 0x80000000)>>31;
//извлекаем тип ID (0 - 11bit, 0 - 29 bit)
12
can_frame_type = (canMsg.can_id & 0x40000000)>>30;
//извлекаем тип СAN FRAME (0 - Data, 1 - Remote)
13
14
15
direction = (rxId_can & 0x10000000)>>28;
// извлекаем 1-битный идентификатор запроса-ответа из ID
16
msg_type = (rxId_can & 0xF000000)>>24;
// извлекаем 4-битный идентификатор сообщения из ID
17
dev_addr = (rxId_can & 0xFF0000)>>16;
// извлекаем 8-битный адрес отправителя из ID
18
rem_addr = (rxId_can & 0xFF00)>>8;
// извлекаем 8-битный адрес получателя из ID
19
dev_type = rxId_can;
// извлекаем 8-битный тип устройства у получателя из ID
20
21
22
#ifdef debug
23
DeCode_can_id_msg(direction,msg_type,dev_addr,rem_addr,dev_type,rxBuf_can);
//Отладочная функция
24
#endif
25
26
//Начинаем разбор принятого
27
28
29
switch
(msg_type){
30
31
// ниже выполняется, когда у меня что-то запрашивают
32
33
case
COMMAND_SEND:
//выполняется, когда мне прислали команду от удаленного контроллера
34
// Функция обработки присланой команды от удаленного контроллера
35
// Что за команда запрошена
36
break
;
37
case
REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запросил параметр датчика или еще чего то
38
// Функция обработки присланного запроса параметра с датчика
39
// Что за датчик запрошен
40
break
;
41
case
STATUS_REQUEST_SEND:
//выполняется когда удаленный контроллер у меня запрашивает состояние
42
// Функция обработки запроса моего состояния
43
// Вернуть состояние
44
break
;
45
46
// выполняется когда мне отвечают на мой запрос
47
48
case
COMMAND_ANSVER:
//выполняется когда удаленный контроллер отвечает на посланную команду
49
// Функция обработки ответа на мою команду
50
break
;
51
case
REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос параметра датчика
52
// Функция обработки ответа на мой запрос параметра датчика
53
break
;
54
case
STATUS_REQUEST_ANSVER:
//выполняется когда удаленный контроллер мне отвечают на мой запрос состояния
55
// Функция обработки ответа на мой запрос состояния УК
56
break
;
57
58
}
59
default
:
60
{
61
#ifdef debug
62
Serial
.print(F(
"Default msg - Recive Uncnown type message"
));
63
#endif
64
}
65
66
67
}
68
}
и нужно добавить ещё один тип сообщений - СOMMAND_ANSVER_OK - команда выполнена
отпличие от СOMMAND_ANSVER - команда принята к исполнению
и вот подравнял TX(). Теперь FRы должны отправляться
01
#define 29_BIT 1
02
#define 11_BIT 0
03
#define DATA_FRAME 0
04
#define REMOTE_FRAME 1
05
06
void
TX(
bool
direction, uint8_t msg_type, uint8_t dev_addr, uint8_t rem_addr, int8_t dev_type,
bool
can_ID_type,
bool
can_frame_type, int8_t DLC,
byte
data[8])
07
{
08
uint32_t txId_can;
09
10
if
(!can_ID_type) {}
// тут как-то формируем 11 битный ID если нужно
11
12
else
txId_can = (direction & 0xFFFFFFFF)<<28 | (msg_type & 0xFFFFFFFF)<<24 | (dev_addr & 0xFFFFFFFF)<<16 | (rem_addr & 0xFFFFFFFF)<<8 | dev_type;
13
14
//ниже если это REMOTE FRAME подравняем ID, а DLC в ноль
15
if
(can_frame_type) {txId_can = txId_can | 0xC0000000; DLC = 0;}
16
17
byte
sndStat = CAN0.sendMsgBuf(txId_can, DLC, data);
//отсылаем кадр в CAN
18
19
#ifdef debug
20
if
(sndStat == CAN_OK){
Serial
.print(F(
"Message Sent Successfully! - "
));
Serial
.print(txId_can, HEX);
21
Serial
.print (
" "
);
if
(DLC!=0) {
for
(
int
i=0; i<DLC; i++) {
22
if
(data<=0x0F)
Serial
.print (
"0"
);
23
Serial
.print (data[i]);
Serial
.print (
" "
); }}
24
Serial
.println();
25
}
26
else
Serial
.println(F(
"Error Sending Message..."
));
27
#endif
28
}
29
30
// вызов TX (direction, тип сообщения, адрес отправителя, адрес получателя, тип устройства, тип ID, тип FRAME, длина поля данных кадра, data)
31
// тип ID 0 - 11 bit; 1 - 29 bit
32
// тип FRAME 0 - DATA; 1 - REMOTE FRAME
33
34
// вызов TX (1, STATUS_REQUEST_ANSVER, node_address, rem_addr, null_s, 29_BIT, DATA_FRAME, 8, data)
Только я не могу понять, зачем нужен direction?
Понятно, что раз приставка SEND то выполняется, когда у меня что то запрашивают
и если приставка ANSVER то выполняется когда мне отвечают на мой запрос
Может так проще?
Тут скоре плюшкиничество работает, твой not use покоя не дает. Давай думать куда его приспособить ;-)
Ты там выше про биты писал. Просто пока идей не приходит.