diff options
author | Alexey 'Cluster' Avdyukhin <ClusterM@users.noreply.github.com> | 2016-07-08 16:04:47 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-07-08 16:04:47 +0300 |
commit | 50e130cc0440c92106c667cc674c5b8d82844485 (patch) | |
tree | a01de3345e8ed31bf2d20a6cc4ceb1eaa9797c36 | |
parent | 7f78b560a2f77ac91942aba71318cebf78b6e9f5 (diff) | |
parent | 5817eb14f4aec2e9fa7eb0c36fe8657e185c58bd (diff) |
Merge pull request #6 from dudanov/master
Оптимизация
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | clunet.c | 173 | ||||
-rw-r--r-- | clunet.h | 14 | ||||
-rw-r--r-- | demo_project/clunet_config.h | 5 |
4 files changed, 69 insertions, 125 deletions
@@ -26,7 +26,7 @@ You need a pin with an external interrupt to read data and any other pin to send ### Software part -You will need one free 8-bit timer with `output compare match` and `overflow` interrupts. +You will need one free 8-bit timer with `output compare match` interrupt. #### Configuration#### @@ -27,20 +27,17 @@ volatile uint8_t clunetReadingCurrentBit; volatile uint8_t clunetCurrentPrio; volatile uint8_t clunetTimerStart = 0; -volatile uint8_t clunetTimerPeriods = 0; volatile char dataToSend[CLUNET_SEND_BUFFER_SIZE]; volatile char dataToRead[CLUNET_READ_BUFFER_SIZE]; -static void +static inline void clunet_start_send() { - CLUNET_SEND_0; - if (clunetSendingState != CLUNET_SENDING_STATE_PREINIT) // Если не нужна пауза... - clunetSendingState = CLUNET_SENDING_STATE_INIT; // Инициализация передачи - clunetSendingCurrentByte = clunetSendingCurrentBit = 0; // Обнуляем счётчик - CLUNET_TIMER_REG_OCR = CLUNET_TIMER_REG + CLUNET_T; // Планируем таймер, обычно почему-то прерывание срабатывает сразу - CLUNET_ENABLE_TIMER_COMP; // Включаем прерывание сравнения таймера + clunetSendingState = CLUNET_SENDING_STATE_INIT; + // подождем 1.5Т, чтобы нас гарантированно могли остановить при передаче на линии со стороны другого устройства в процедуре внешнего прерывания + CLUNET_TIMER_REG_OCR = CLUNET_TIMER_REG + (CLUNET_T + CLUNET_T / 2); + CLUNET_ENABLE_TIMER_COMP; // Включаем прерывание сравнения таймера (передачу) } static inline char @@ -73,7 +70,7 @@ clunet_data_received(const uint8_t src_address, const uint8_t dst_address, const if ((dst_address != CLUNET_DEVICE_ID) && (dst_address != CLUNET_BROADCAST_ADDRESS)) return; // Игнорируем сообщения не для нас - // Команда перезагрузки. Перезагрузка по сторожевому таймеру. + // Команда перезагрузки. Перезагрузим по сторожевому таймеру if (command == CLUNET_COMMAND_REBOOT) { cli(); @@ -83,9 +80,9 @@ clunet_data_received(const uint8_t src_address, const uint8_t dst_address, const if ((clunetSendingState == CLUNET_SENDING_STATE_IDLE) || (clunetCurrentPrio <= CLUNET_PRIORITY_MESSAGE)) { - if (command == CLUNET_COMMAND_DISCOVERY) // Ответ на поиск устройств + /* Ответ на поиск устройств */ + if (command == CLUNET_COMMAND_DISCOVERY) { - clunetSendingState = CLUNET_SENDING_STATE_PREINIT; #ifdef CLUNET_DEVICE_NAME char buf[] = CLUNET_DEVICE_NAME; uint8_t len = 0; while(buf[len]) len++; @@ -94,22 +91,14 @@ clunet_data_received(const uint8_t src_address, const uint8_t dst_address, const clunet_send(src_address, CLUNET_PRIORITY_MESSAGE, CLUNET_COMMAND_DISCOVERY_RESPONSE, 0, 0); #endif } - else if (command == CLUNET_COMMAND_PING) // Ответ на пинг - { - clunetSendingState = CLUNET_SENDING_STATE_PREINIT; + /* Ответ на пинг */ + else if (command == CLUNET_COMMAND_PING) clunet_send(src_address, CLUNET_PRIORITY_COMMAND, CLUNET_COMMAND_PING_REPLY, data, size); - } } - + if (on_data_received) (*on_data_received)(src_address, dst_address, command, data, size); - /* Если есть неотосланные данные, шлём, линия освободилась */ - if ((clunetSendingState == CLUNET_SENDING_STATE_WAITING_LINE) && !CLUNET_READING) - { - clunetSendingState = CLUNET_SENDING_STATE_PREINIT; - clunet_start_send(); - } } /* Процедура прерывания сравнения таймера */ @@ -117,54 +106,53 @@ ISR(CLUNET_TIMER_COMP_VECTOR) { uint8_t now = CLUNET_TIMER_REG; // Запоминаем текущее значение таймера - switch (clunetSendingState) + /* Если достигли фазы завершения передачи, то завершим ее и освободим передатчик */ + if (clunetSendingState == CLUNET_SENDING_STATE_DONE) { - - /* Завершение передачи */ - case CLUNET_SENDING_STATE_STOP: - - CLUNET_SEND_0; // Отпускаем линию - - /* Нужно подождать перед отправкой */ - case CLUNET_SENDING_STATE_PREINIT: - - CLUNET_TIMER_REG_OCR = now + CLUNET_T; - clunetSendingState++; // Начинаем следующую фазу - break; - - /* Освобождение передатчика */ - case CLUNET_SENDING_STATE_DONE: - CLUNET_DISABLE_TIMER_COMP; // Выключаем прерывание сравнения таймера clunetSendingState = CLUNET_SENDING_STATE_IDLE; // Указываем, что передатчик свободен - break; - - /* Этот блок кода может прижать линию к земле */ - default: - - // Проверка на конфликт. Если мы линию не держим, но она уже занята - if (!CLUNET_SENDING && CLUNET_READING) - { - CLUNET_DISABLE_TIMER_COMP; // Выключаем прерывание сравнения таймера (передачу) - clunetSendingState = CLUNET_SENDING_STATE_WAITING_LINE; // Переходим в режим ожидания линии - break; - } - + CLUNET_SEND_0; // Отпускаем линию + } + /* Иначе если передачу необходимо продолжить, то сначала проверим на конфликт */ + else if (!CLUNET_SENDING && CLUNET_READING) + { + CLUNET_DISABLE_TIMER_COMP; // Выключаем прерывание сравнения таймера (передачу) + clunetSendingState = CLUNET_SENDING_STATE_WAITING_LINE; // Переходим в режим ожидания линии + } + /* Все в порядке, можем продолжать */ + else + { CLUNET_SEND_INVERT; // Инвертируем значение сигнала /* Если отпустили линию */ if (!CLUNET_SENDING) - CLUNET_TIMER_REG_OCR = now + CLUNET_T; // то запланируем время паузы перед следующей передачей длительностью 1Т - + /* Если прижали линию к земле, то запланируем время передачи сигнала в зависимости от текущей фазы передачи */ else switch (clunetSendingState) { + /* Фаза передачи данных */ + case CLUNET_SENDING_STATE_DATA: + + // Планируем следующее прерывание чтобы отпустить линию в зависимости от значения бита + CLUNET_TIMER_REG_OCR = now + ((dataToSend[clunetSendingCurrentByte] & (1 << clunetSendingCurrentBit)) ? CLUNET_1_T : CLUNET_0_T); + /* Если передан байт данных */ + if (++clunetSendingCurrentBit & 8) + { + /* и не все данные отосланы */ + if (++clunetSendingCurrentByte < clunetSendingDataLength) + clunetSendingCurrentBit = 0; // то начинаем передачу следующего байта с бита 0 + /* и передача всех данных закончена */ + else + clunetSendingState++; // то переходим к следующей фазе завершения передачи пакета + } + break; + /* Фаза инициализации передачи пакета (время 10Т) */ case CLUNET_SENDING_STATE_INIT: - + CLUNET_TIMER_REG_OCR = now + CLUNET_INIT_T; // Планируем следующее прерывание clunetSendingState++; // К следующей фазе передачи старшего бита приоритета break; @@ -180,26 +168,8 @@ ISR(CLUNET_TIMER_COMP_VECTOR) case CLUNET_SENDING_STATE_PRIO2: CLUNET_TIMER_REG_OCR = now + ((clunetCurrentPrio & 1) ? CLUNET_0_T : CLUNET_1_T); + clunetSendingCurrentByte = clunetSendingCurrentBit = 0; // Обнуляем счётчик clunetSendingState++; // К следующей фазе передачи данных - break; - - /* Фаза передачи данных */ - case CLUNET_SENDING_STATE_DATA: - - // Планируем следующее прерывание чтобы отпустить линию в зависимости от значения бита - CLUNET_TIMER_REG_OCR = now + ((dataToSend[clunetSendingCurrentByte] & (1 << clunetSendingCurrentBit)) ? CLUNET_1_T : CLUNET_0_T); - - /* Если передан байт данных */ - if (++clunetSendingCurrentBit & 8) - { - /* и не все данные отосланы */ - if (++clunetSendingCurrentByte < clunetSendingDataLength) - clunetSendingCurrentBit = 0; // то начинаем передачу следующего байта с бита 0 - /* и передача всех данных закончена */ - else - clunetSendingState++; // то переходим к следующей фазе завершения передачи пакета - } - } } } @@ -211,13 +181,14 @@ clunet_send(const uint8_t address, const uint8_t prio, const uint8_t command, co if (size < (CLUNET_SEND_BUFFER_SIZE - CLUNET_OFFSET_DATA)) { /* Прерываем текущую передачу, если есть такая */ - CLUNET_DISABLE_TIMER_COMP; - CLUNET_SEND_0; + if (clunetSendingState) + { + CLUNET_DISABLE_TIMER_COMP; + CLUNET_SEND_0; + } /* Заполняем переменные */ - if (clunetSendingState != CLUNET_SENDING_STATE_PREINIT) - clunetSendingState = CLUNET_SENDING_STATE_IDLE; - clunetCurrentPrio = (!prio) ? 1 : (prio > 4) ? 4 : prio; // Ограничим приоритет диапазоном (1 ; 4) + clunetCurrentPrio = (prio > 4) ? 4 : prio ? : 1; // Ограничим приоритет диапазоном (1 ; 4) dataToSend[CLUNET_OFFSET_SRC_ADDRESS] = CLUNET_DEVICE_ID; dataToSend[CLUNET_OFFSET_DST_ADDRESS] = address; dataToSend[CLUNET_OFFSET_COMMAND] = command; @@ -233,37 +204,8 @@ clunet_send(const uint8_t address, const uint8_t prio, const uint8_t command, co clunetSendingDataLength = size + (CLUNET_OFFSET_DATA + 1); - if (clunetReadingState == CLUNET_READING_STATE_IDLE) // Если мы ничего не получаем в данный момент, то посылаем сразу - clunet_start_send(); // Запускаем передачу сразу - else - clunetSendingState = CLUNET_SENDING_STATE_WAITING_LINE; // Иначе ждём линию - - } -} + clunet_start_send(); // Запускаем отправку данных и полагаемся на нашу отработку конфликтов при чтении и отправки -/* Процедура прерывания переполнения таймера */ -ISR(CLUNET_TIMER_OVF_VECTOR) -{ - /* Считаем периоды переполнения таймера */ - if (clunetTimerPeriods < 3) - clunetTimerPeriods++; - /* Слишком долго нет сигнала */ - else - { - CLUNET_SEND_0; // А вдруг мы забыли линию отжать? Хотя по идее не должно... - clunetReadingState = CLUNET_READING_STATE_IDLE; - /* Если линия свободна */ - if (!CLUNET_READING) - switch (clunetSendingState) - { - /* и ничего не передаем, то отключим прерывания переполнения */ - case CLUNET_SENDING_STATE_IDLE: - CLUNET_DISABLE_TIMER_OVF; - break; - /* есть неотосланные данные - шлём, линия свободна */ - case CLUNET_SENDING_STATE_WAITING_LINE: - clunet_start_send(); - } } } @@ -273,18 +215,25 @@ ISR(CLUNET_INT_VECTOR) uint8_t now = CLUNET_TIMER_REG; // Текущие значение таймера - CLUNET_ENABLE_TIMER_OVF; // Активируем прерывания переполнения таймера при любой активности линии - /* Если линию прижало к нулю */ if (CLUNET_READING) { + /* Если мы в режиме передачи и прижали не мы, то замолкаем и ожидаем, тем более, что наши передаваемые данные уже битые */ + /* Обеспечивается быстрая отработка ошибки на линии во избежание конфликтов */ + if (clunetSendingState && !CLUNET_SENDING) + { + CLUNET_DISABLE_TIMER_COMP; + clunetSendingState = CLUNET_SENDING_STATE_WAITING_LINE; + } clunetTimerStart = now; // Запомним время начала сигнала - clunetTimerPeriods = 0; // Сбросим счетчик периодов таймера } /* Иначе если линию отпустило */ else { + /* Надеемся на то, что линию освободило совсем и пробуем запланировать отправку, если нет, то конфликт будет 100% отработан */ + if (clunetSendingState == CLUNET_SENDING_STATE_WAITING_LINE) + clunet_start_send(); uint8_t ticks = now - clunetTimerStart; // Вычислим время сигнала в тиках таймера @@ -13,14 +13,12 @@ #include "clunet_config.h" #define CLUNET_SENDING_STATE_IDLE 0 -#define CLUNET_SENDING_STATE_PREINIT 1 -#define CLUNET_SENDING_STATE_INIT 2 -#define CLUNET_SENDING_STATE_PRIO1 3 -#define CLUNET_SENDING_STATE_PRIO2 4 -#define CLUNET_SENDING_STATE_DATA 5 -#define CLUNET_SENDING_STATE_STOP 6 -#define CLUNET_SENDING_STATE_DONE 7 -#define CLUNET_SENDING_STATE_WAITING_LINE 8 +#define CLUNET_SENDING_STATE_INIT 1 +#define CLUNET_SENDING_STATE_PRIO1 2 +#define CLUNET_SENDING_STATE_PRIO2 3 +#define CLUNET_SENDING_STATE_DATA 4 +#define CLUNET_SENDING_STATE_DONE 5 +#define CLUNET_SENDING_STATE_WAITING_LINE 6 #define CLUNET_READING_STATE_IDLE 0 #define CLUNET_READING_STATE_INIT 1 diff --git a/demo_project/clunet_config.h b/demo_project/clunet_config.h index f366b06..ed5ba86 100644 --- a/demo_project/clunet_config.h +++ b/demo_project/clunet_config.h @@ -47,17 +47,14 @@ #define CLUNET_TIMER_REG_OCR OCR2 /* How to enable and disable timer interrupts */ -#define CLUNET_ENABLE_TIMER_COMP set_bit(TIMSK, OCIE2) +#define CLUNET_ENABLE_TIMER_COMP {set_bit(TIFR, OCF2);set_bit(TIMSK, OCIE2);} #define CLUNET_DISABLE_TIMER_COMP unset_bit(TIMSK, OCIE2) -#define CLUNET_ENABLE_TIMER_OVF set_bit(TIMSK, TOIE2) -#define CLUNET_DISABLE_TIMER_OVF unset_bit(TIMSK, TOIE2) /* How to init and enable external interrupt (read pin) */ #define CLUNET_INIT_INT {set_bit(MCUCR,ISC00);unset_bit(MCUCR,ISC01); set_bit(GICR, INT0);} /* Interrupt vectors */ #define CLUNET_TIMER_COMP_VECTOR TIMER2_COMP_vect -#define CLUNET_TIMER_OVF_VECTOR TIMER2_OVF_vect #define CLUNET_INT_VECTOR INT0_vect #endif |