diff --git a/F1-nolib/chronometer/Readme_rus.txt b/F1-nolib/chronometer/Readme_rus.txt new file mode 100644 index 0000000..7feff2a --- /dev/null +++ b/F1-nolib/chronometer/Readme_rus.txt @@ -0,0 +1,144 @@ +****** Распиновка ****** + +=== Интерфейсы I/O === +- PA11/12 - USB +- PA9(Tx), PA10(Rx) - USART1 - прокси RMC-сообщений GPS. +- PA2(Tx), PA3(Rx) - USART2 - подключение GPS-приемника. +- PB10(Tx), PB11(Rx) - USART3 - подключение лидара. + +=== Остальные порты === +- PA1 - PPS сигнал от GPS; сюда можно подключать любой дополнительный высокоомный вход напрямую. +- PA4 - TRIG2 - подключен к каналу 12В. +- PA13 - TRIG0 - кнопка или створ, замыкающий контакты. +- PA14 - TRIG1 - так же, как и вход TRIG0. +- PA15 - подтяжка USB. +- PB0 - TRIG4 - триггер по АЦП. +- PB8, PB9 - индикаторные светодиоды. +- PC13 - пищалка. + +=== Светодиоды === +- LED0 (зеленый) - при отсутствии сигнала PPS просто горит, если PPS появляется - мигает (затухает на 0.25с на каждый сигнал). +- LED1 (красный) - индикатор GPS: не горит, если приемник не обнаружен, горит, если неуверенный прием времени (буква "V" во второй позиции RMC-сообщения), мигает при уверенном приеме (буква "A" во второй позиции). +Судя по эксперименту, даже через час после пропадания сигнала точность определения события - не хуже 1мс. Сам GPS-приемник выдает +PPS даже при отсутствии спутников - лишь бы он успел "подхватить" точное время и начать генерировать pps. Начинать работу можно сразу, +как только замигает зеленый светодиод после мигающего красного. + + +****** Триггеры ****** +На прототипе распаяно два входа на триггеры: TRIG0 и TRIG2. К TRIG2 нужно подключать 12-вольтный сигнал, ток не меньше 10мА. +Если створ имеет открытый коллектор, то выход створа подключается к минусу TRIG2, а к плюсу подключается 12В с источника питания. +TRIG0 предназначен для подключения кнопки или концевика, просто замыкающего контакты. Никакого внешнего напряжения там быть не должно! + +TRIG4 - аналоговый вход. Если будут ложные срабатывания на девбордах, порт PB0 нужно напрямую или через резистор до 10кОм посадить на землю. + +Иногда бывают ложные срабатывания триггеров TRIG0..TRIG2, связанные с мощными источниками искр (зажигание, искрящиеся обмотки и т.п.). +В случае таких ложных срабатываний рекомендуется заземлить катод источника питания хронометра. + +При подключении внешней кнопки желательно, чтобы она имела нормально замкнутые контакты - это предотвратит ложные срабатывания из-за электромагнитных помех. + + +****** Подключение ****** +Хронометр эмулирует "китайский" преобразователь PL2303. В линуксе нужно, чтобы был скомпилирован соответствующий модуль ядра. +В андроиде работает "из коробки". В мастдайке новые драйвера PL2303 имеют защиту от подделок (те просто не работают с этими дровами), +поэтому для нормальной работы необходимо найти и установить старые драйвера. + +К выходам PA9/PA10 можно подключить преобразователь USART<>USB или накинуть их напрямую на ноги Rx/Tx "малинки" (не забыв соединить +земли хронометра и малинки): PA9(Tx) соединить с Rx, PA10(Rx) - с Tx. Этот USART проксирует RMC-сообщения GPS-приемника (уже после +обработки микроконтроллером, поэтому если МК выключен, а приемник включен, сигнала все равно не будет). + +Для подключения PPS сигнала к "малинке" нужно напрямую соединить соответствующую ногу GPIO "малинки" с портом PA1 девборды. +На прототипе нужно подпаяться к дорожке, выходящей с ноги PPS (отмечено маркером). + +Подтяжка USB есть лишь на прототипе, на девбордах ее нет. Поэтому в случае перезагрузки микроконтроллера девборды для возобновления +соединения необходимо переткнуть шнурок USB. В этом плане прототип надежней: сбросить МК можно независимо от питания GPS. + +На прототипе и девбордах отсутствует подсоединение пищалки. На девбордах при желании можно накинуть на PC13 что-нибудь для индикации +срабатывания створа (активный выход - "1" в течение 0.3с). + +На девбордах не распаяны светодиодные индикаторы. Особого смысла в них нет, но если понадобится подключить, нагрузка должна висеть на +PB8/PB9. Активный выход - низкий. Нога МК настроена в режиме open-drain, но внешняя подтяжка не должна быть выше +3.5В. И потребление +не больше 5мА на ногу. + + +****** Конфигурация ****** +Хронометр конфигурируется через USB. Ввод команд не сопровождается эхом (чтобы удобней было работать из внешних программ), поэтому +для удобства можно тексты команд копировать из окна текстового редактора. +Чтобы увидеть подсказку, достаточно отправить любую строку, начинающуюся с вопросительного знака. Появится справка: + +adcmax - max ADC value treshold for trigger +adcmin - min -//- (triggered when ADval>min & 0, равен единице, если при + перепаде 0->1. + TRIGPAUSE - пауза между срабатываниями триггера: если после срабатывания произойдет следующее событие за интервал, меньший + данного, это событие учитываться не будет. +- time - отображает текущее время так, как оно бы отобразилось при срабатывании триггера, например, + 55725.961 (15:28:45) + ВРЕМЯ ИЗМЕРЯЕТСЯ В UTC!!! Первое число - количество секунд и миллисекунд с начала суток по UTC, в скобках + указывается человекочитаемое время. +- store - сохранить новую конфигурацию во флеш-памяти МК. +- triglevelNS - рабочий уровень триггера. Здесь N - номер триггера (0..2), S - уровень (0/1). Скажем, чтобы триггер 0 срабатывал + при перепаде 1->0, нужно написать команду + triglevel00 + а чтобы триггер 2 срабатывал при перепаде 0->1, + triglevel21 +- trigpauseNP - задать паузу для триггера N, пауза в миллисекундах. Если написать 0, паузы не будет и каждое срабатывание + будет вызывать соответствующее сообщение. Эта пауза нужнад для защиты от "звона" и нескольких срабатываний на "дырках" + в объекте. Меньше 50мс лучше не делать. +- trigtimeN - отображение последнего времени срабатывания триггера N, например, на запрос trigtime0, может быть выведено: + TRIG0=45212.930 (12:33:32) + Если срабатываний с момента включения не было, выведутся нули: + TRIG4=0.000 (00:00:00) + +После изменения конфигурации и ее сохранения необходимо нажатием на reset или отключением/включением питания перезагрузить МК, +т.к. некоторые параметры активируются лишь при старте. + + +****** Девборды и плата-прототип ****** +На платках из девборд два канала опторазвязок подключены к триггерам TRIG0 и TRIG1. +Синий провод - земля, красные - +12В для каждого канала. Т.е. схема рассчитана на срабатывание по появлению плюса на одном из каналов. + +В случае необходимости срабатывания по подтяжке к земле, нужно разорвать землю на входах опторазвязок и, наоборот, объединить плюсы. +На плюсы подать +12В, минусы подключить к сигнальным выходам створов. + +На прототипе распаяны развязки только на два канала: TRIG0 - для подключения чего-то, замыкающего контакты, и TRIG2 - для подключения +чего-то, выдающего 12В. Я оставил такую конфигурацию: к TRIG0 можно подключить нормально замкнутую кнопку (triglevel01), а TRIG2 +сработает при поступлении туда 12В (triglevel21). + diff --git a/F1-nolib/chronometer/adc.c b/F1-nolib/chronometer/adc.c index eadbd4f..80a98e2 100644 --- a/F1-nolib/chronometer/adc.c +++ b/F1-nolib/chronometer/adc.c @@ -71,18 +71,23 @@ uint32_t getVdd(){ return vdd; } -void chkADCtrigger(){ +/** + * @brief chkADCtrigger - check ADC trigger state + * @return value of `triggered` + */ +uint8_t chkADCtrigger(){ static uint8_t triggered = 0; savetrigtime(); - uint16_t val = getADCval(0); + int16_t val = getADCval(0); if(triggered){ // check untriggered action - if(val < the_conf.ADC_min || val > the_conf.ADC_max){ + if(val < (int16_t)the_conf.ADC_min - ADC_THRESHOLD || val > (int16_t)the_conf.ADC_max + ADC_THRESHOLD){ triggered = 0; } }else{ // check if thigger shot - if(val > the_conf.ADC_min && val < the_conf.ADC_max){ + if(val > (int16_t)the_conf.ADC_min + ADC_THRESHOLD && val < (int16_t)the_conf.ADC_max - ADC_THRESHOLD){ triggered = 1; fillshotms(4); } } + return triggered; } diff --git a/F1-nolib/chronometer/adc.h b/F1-nolib/chronometer/adc.h index 08d253b..f6dc45e 100644 --- a/F1-nolib/chronometer/adc.h +++ b/F1-nolib/chronometer/adc.h @@ -24,10 +24,12 @@ // interval of trigger's shot (>min && MAX_TRIG_LEN) +int16_t triglen[TRIGGERS_AMOUNT]; // if trigger[N] shots, the bit N will be 1 uint8_t trigger_shot = 0; -// time when Buzzer was turned ON -uint32_t BuzzerTime = 0; static inline void gpio_setup(){ BUZZER_OFF(); // turn off buzzer @start @@ -78,7 +80,9 @@ static inline void gpio_setup(){ uint16_t pin = trigpin[i]; // fill trigstate array uint8_t trgs = (the_conf.trigstate & (1<ODR |= pin; EXTI->IMR |= pin; @@ -147,17 +151,52 @@ void savetrigtime(){ memcpy(&trgtm.Time, ¤t_time, sizeof(curtime)); } +/** + * @brief fillshotms - save trigger shot time + * @param i - trigger number + */ void fillshotms(int i){ - if(i < 0 || i > TRIGGERS_AMOUNT) return; + if(i < 0 || i >= TRIGGERS_AMOUNT) return; if(Tms - shotms[i] > (uint32_t)the_conf.trigpause[i]){ - shotms[i] = Tms; memcpy(&shottime[i], &trgtm, sizeof(trigtime)); + shotms[i] = Tms; trigger_shot |= 1< MAX_TRIG_LEN){ + triglen[i] = -1; + rdy = 1; + }else triglen[i] = (uint16_t) len; + if(i == LIDAR_TRIGGER){ + if(!parse_lidar_data(NULL)) rdy = 1; + }else if(i == ADC_TRIGGER){ + if(!chkADCtrigger()) rdy = 1; + }else{ + uint8_t pinval = (trigport[i]->IDR & trigpin[i]) ? 1 : 0; + if(pinval != trigstate[i]) rdy = 1; // trigger is OFF + } + if(rdy){ + shotms[i] = Tms; + show_trigger_shot(X); + trigger_shot &= ~X; + } + } + } +} + void exti4_isr(){ // PA4 - trigger[2] savetrigtime(); fillshotms(2); @@ -176,6 +215,7 @@ void exti15_10_isr(){ // PA13 - trigger[0], PA14 - trigger[1] } } +#ifdef EBUG /** * @brief gettrig - get trigger state * @return 1 if trigger active or 0 @@ -186,3 +226,18 @@ uint8_t gettrig(uint8_t N){ if(curval == trigstate[N]) return 1; else return 0; } +#endif + +void chk_buzzer(){ + if(!trigger_shot && BUZZER_GET()){ // should we turn off buzzer? + uint8_t notrg = 1; + for(int i = 0; i < DIGTRIG_AMOUNT; ++i){ + uint8_t curval = (trigport[i]->IDR & trigpin[i]) ? 1 : 0; + if(curval == trigstate[i]){ + notrg = 0; + break; + } + } + if(notrg) BUZZER_OFF(); // turn off buzzer when there's no trigger events + } +} diff --git a/F1-nolib/chronometer/hardware.h b/F1-nolib/chronometer/hardware.h index fd5bad8..a54c01e 100644 --- a/F1-nolib/chronometer/hardware.h +++ b/F1-nolib/chronometer/hardware.h @@ -39,6 +39,7 @@ extern uint8_t buzzer_on; #define BUZZER_pin (1<<13) #define BUZZER_ON() do{if(buzzer_on)pin_set(BUZZER_port, BUZZER_pin);}while(0) #define BUZZER_OFF() pin_clear(BUZZER_port, BUZZER_pin) +#define BUZZER_GET() (pin_read(BUZZER_port, BUZZER_pin)) // PPS pin - PA1 #define PPS_port GPIOA @@ -49,11 +50,18 @@ extern uint8_t buzzer_on; #define TRIGGERS_AMOUNT (5) // number of LIDAR trigger #define LIDAR_TRIGGER (3) +// number of ADC trigger +#define ADC_TRIGGER (4) // amount of digital triggers (on interrupts) #define DIGTRIG_AMOUNT (3) +// max length of trigger event (ms) +#define MAX_TRIG_LEN (1000) +#ifdef EBUG uint8_t gettrig(uint8_t N); +#endif void fillshotms(int i); +void fillunshotms(); void savetrigtime(); #define GET_PPS() ((GPIOA->IDR & (1<<1)) ? 1 : 0) @@ -83,11 +91,12 @@ typedef struct{ extern uint8_t LEDSon; // time of triggers shot extern trigtime shottime[TRIGGERS_AMOUNT]; +// length (in ms) of trigger event (-1 if > MAX_TRIG_LEN +extern int16_t triglen[TRIGGERS_AMOUNT]; // if trigger[N] shots, the bit N will be 1 extern uint8_t trigger_shot; -// time when Buzzer was turned ON -extern uint32_t BuzzerTime; +void chk_buzzer(); void hw_setup(); #endif // __HARDWARE_H__ diff --git a/F1-nolib/chronometer/lidar.c b/F1-nolib/chronometer/lidar.c index 45b3fc0..156890d 100644 --- a/F1-nolib/chronometer/lidar.c +++ b/F1-nolib/chronometer/lidar.c @@ -24,14 +24,20 @@ uint16_t last_lidar_dist = 0; uint16_t last_lidar_stren = 0; uint16_t lidar_triggered_dist = 0; -void parse_lidar_data(char *txt){ +/** + * @brief parse_lidar_data - parsing of string from lidar + * @param txt - the string or NULL (if you want just check trigger state) + * @return trigger state + */ +uint8_t parse_lidar_data(char *txt){ static uint8_t triggered = 0; + if(!txt) return triggered; last_lidar_dist = txt[2] | (txt[3] << 8); last_lidar_stren = txt[4] | (txt[5] << 8); - if(last_lidar_stren < LIDAR_LOWER_STREN) return; // weak signal + if(last_lidar_stren < LIDAR_LOWER_STREN) return 0; // weak signal if(!lidar_triggered_dist){ // first run lidar_triggered_dist = last_lidar_dist; - return; + return 0; } IWDG->KR = IWDG_REFRESH; if(triggered){ // check if body gone @@ -60,4 +66,5 @@ void parse_lidar_data(char *txt){ #endif } } + return triggered; } diff --git a/F1-nolib/chronometer/lidar.h b/F1-nolib/chronometer/lidar.h index ad90268..903feee 100644 --- a/F1-nolib/chronometer/lidar.h +++ b/F1-nolib/chronometer/lidar.h @@ -34,6 +34,6 @@ extern uint16_t last_lidar_dist; extern uint16_t lidar_triggered_dist; extern uint16_t last_lidar_stren; -void parse_lidar_data(char *txt); +uint8_t parse_lidar_data(char *txt); #endif // LIDAR_H__ diff --git a/F1-nolib/chronometer/main.c b/F1-nolib/chronometer/main.c index 6889944..f060690 100644 --- a/F1-nolib/chronometer/main.c +++ b/F1-nolib/chronometer/main.c @@ -158,23 +158,26 @@ char *parse_cmd(char *buf){ } #endif +#define USBBUF 63 // usb getline static char *get_USB(){ - static char tmpbuf[512], *curptr = tmpbuf; - static int rest = 511; + static char tmpbuf[USBBUF+1], *curptr = tmpbuf; + static int rest = USBBUF; int x = USB_receive(curptr, rest); - curptr[x] = 0; if(!x) return NULL; + curptr[x] = 0; + USB_send(curptr); // echo +//if(x == 1 && *curptr < 32){USB_send("\n"); USB_send(u2str(*curptr)); USB_send("\n");} if(curptr[x-1] == '\n'){ curptr = tmpbuf; - rest = 511; + rest = USBBUF; return tmpbuf; } curptr += x; rest -= x; if(rest <= 0){ // buffer overflow - SEND("USB buffer overflow!\n"); + //SEND("USB buffer overflow!\n"); curptr = tmpbuf; - rest = 511; + rest = USBBUF; } return NULL; } @@ -215,7 +218,6 @@ void break_handler(){ // client disconnected #ifdef EBUG extern int32_t ticksdiff, timecntr, timerval, Tms1; -extern uint32_t last_corr_time; #endif int main(void){ @@ -245,10 +247,8 @@ int main(void){ while (1){ IWDG->KR = IWDG_REFRESH; // refresh watchdog if(Timer > 499) LED_on(); // turn ON LED0 over 0.25s after PPS pulse - if(BuzzerTime && Tms - BuzzerTime > 249){ - BUZZER_OFF(); - BuzzerTime = 0; - } + // check if triggers that was recently shot are off now + fillunshotms(); if(lastT > Tms || Tms - lastT > 499){ if(need2startseq) GPS_send_start_seq(); IWDG->KR = IWDG_REFRESH; @@ -301,8 +301,7 @@ int main(void){ } #endif } - IWDG->KR = IWDG_REFRESH; - if(trigger_shot) show_trigger_shot(trigger_shot); + //if(trigger_shot) show_trigger_shot(trigger_shot); IWDG->KR = IWDG_REFRESH; usb_proc(); IWDG->KR = IWDG_REFRESH; @@ -312,7 +311,7 @@ int main(void){ DBG("Received data over USB:"); DBG(txt); if(parse_USBCMD(txt)) - USB_send(txt); // echo back non-commands data + USB_send("Bad command!"); IWDG->KR = IWDG_REFRESH; } #if defined EBUG || defined USART1PROXY @@ -352,7 +351,7 @@ int main(void){ parse_lidar_data(txt); } } - chkADCtrigger(); + chk_buzzer(); // should we turn off buzzer? } return 0; } diff --git a/F1-nolib/chronometer/str.c b/F1-nolib/chronometer/str.c index 8f6929e..462dc86 100644 --- a/F1-nolib/chronometer/str.c +++ b/F1-nolib/chronometer/str.c @@ -111,6 +111,7 @@ int parse_USBCMD(char *cmd){ CMD_DISTMIN " - min distance threshold (cm)\n" CMD_DISTMAX " - max distance threshold (cm)\n" CMD_GPSRESTART " - send Full Cold Restart to GPS\n" + CMD_GPSSTAT " - get GPS status\n" CMD_GPSSTR " - current GPS data string\n" CMD_LEDS "S - turn leds on/off (1/0)\n" CMD_GETMCUTEMP " - MCU temperature\n" @@ -125,6 +126,7 @@ int parse_USBCMD(char *cmd){ ); }else if(CMP(cmd, CMD_PRINTTIME) == 0){ USB_send(get_time(¤t_time, get_millis())); + USB_send("\n"); }else if(CMP(cmd, CMD_DISTMIN) == 0){ // set low limit DBG("CMD_DISTMIN"); GETNUM(CMD_DISTMIN); @@ -258,14 +260,34 @@ int parse_USBCMD(char *cmd){ if(Nt > 1) goto bad_number; USB_send("BUZZER="); if(Nt){ - BuzzerTime = 0; buzzer_on = 1; USB_send("ON\n"); }else{ - BuzzerTime = 0; buzzer_on = 0; USB_send("OFF\n"); } + }else if(CMP(cmd, CMD_GPSSTAT) == 0){ + USB_send("GPS status: "); + const char *str = "unknown"; + switch(GPS_status){ + case GPS_NOTFOUND: + str = "not found"; + break; + case GPS_WAIT: + str = "waiting"; + break; + case GPS_NOT_VALID: + str = "no satellites"; + break; + case GPS_VALID: + str = "valid time"; + break; + } + USB_send(str); + if(Tms - last_corr_time < 1500) + USB_send(", PPS working\n"); + else + USB_send(", no PPS\n"); }else return 1; IWDG->KR = IWDG_REFRESH; if(succeed) USB_send("Success!\n"); @@ -285,7 +307,7 @@ void show_trigger_shot(uint8_t tshot){ IWDG->KR = IWDG_REFRESH; if(tshot & X) tshot &= ~X; else continue; - if(trigger_shot & X) trigger_shot &= ~X; + if(!triglen[i]) continue; // noice if(i == LIDAR_TRIGGER){ USB_send("LIDAR, dist="); sendu(lidar_triggered_dist); @@ -296,6 +318,9 @@ void show_trigger_shot(uint8_t tshot){ } USB_send("="); USB_send(get_time(&shottime[i].Time, shottime[i].millis)); + USB_send(", len="); + if(triglen[i] < 0) USB_send(">1s"); + else sendu((uint32_t) triglen[i]); USB_send("\n"); } } diff --git a/F1-nolib/chronometer/str.h b/F1-nolib/chronometer/str.h index 399c415..c3d7f48 100644 --- a/F1-nolib/chronometer/str.h +++ b/F1-nolib/chronometer/str.h @@ -42,6 +42,7 @@ #define CMD_LEDS "leds" #define CMD_GPSRESTART "gpsrestart" #define CMD_BUZZER "buzzer" +#define CMD_GPSSTAT "gpsstat" extern uint8_t showGPSstr; diff --git a/F1-nolib/chronometer/time.c b/F1-nolib/chronometer/time.c index d36c9b7..7a562f6 100644 --- a/F1-nolib/chronometer/time.c +++ b/F1-nolib/chronometer/time.c @@ -79,6 +79,26 @@ static char *puttwo(uint8_t N, char *buf){ return buf; } +/** + * @brief ms2str - fill buffer str with milliseconds ms + * @param str (io) - pointer to buffer + * @param T - milliseconds + */ +static void ms2str(char **str, uint32_t T){ + char *bptr = *str; + *bptr++ = '.'; + if(T > 99){ + *bptr++ = T/100 + '0'; + T %= 100; + }else *bptr++ = '0'; + if(T > 9){ + *bptr++ = T/10 + '0'; + T %= 10; + }else *bptr++ = '0'; + *bptr++ = T + '0'; + *str = bptr; +} + /** * print time: Tm - time structure, T - milliseconds */ @@ -95,26 +115,18 @@ char *get_time(curtime *Tm, uint32_t T){ S /= 10; } // now bstart is buffer starting index; bptr points to decimal point - *bptr++ = '.'; - if(T > 99){ - *bptr++ = T/100 + '0'; - T %= 100; - }else *bptr++ = '0'; - if(T > 9){ - *bptr++ = T/10 + '0'; - T %= 10; - }else *bptr++ = '0'; - *bptr++ = T + '0'; + ms2str(&bptr, T); // put current time in HH:MM:SS format into buf *bptr++ = ' '; *bptr++ = '('; bptr = puttwo(Tm->H, bptr); *bptr++ = ':'; bptr = puttwo(Tm->M, bptr); *bptr++ = ':'; - bptr = puttwo(Tm->S, bptr); *bptr++ = ')'; + bptr = puttwo(Tm->S, bptr); + ms2str(&bptr, T); + *bptr++ = ')'; if(GPS_status == GPS_NOTFOUND){ strcpy(bptr, " GPS not found"); bptr += 14; } - *bptr++ = '\n'; *bptr = 0; return bstart; } diff --git a/F1-nolib/chronometer/time.h b/F1-nolib/chronometer/time.h index 569524d..c76f081 100644 --- a/F1-nolib/chronometer/time.h +++ b/F1-nolib/chronometer/time.h @@ -46,6 +46,7 @@ typedef struct{ extern volatile uint32_t Tms; extern volatile uint32_t Timer; extern curtime current_time; +extern uint32_t last_corr_time; extern curtime trigger_time[]; extern uint32_t trigger_ms[]; diff --git a/F1-nolib/chronometer/usart.h b/F1-nolib/chronometer/usart.h index 4ddcf80..638e5dd 100644 --- a/F1-nolib/chronometer/usart.h +++ b/F1-nolib/chronometer/usart.h @@ -45,7 +45,7 @@ #define usartrx(n) (linerdy[n]) #define usartovr(n) (bufovr[n]) -extern volatile int linerdy[4], bufovr[4], txrdy[4]; +extern volatile int linerdy[], bufovr[], txrdy[]; void transmit_tbuf(int n); void usarts_setup();