Для начала неплохо бы выяснить как работает функция micros(). На самом деле считает время таймер/счетчик 0 микроконтроллера. Он запущен с предделителем 64. При тактовой частоте микроконтроллера в 16 МГц значение счетного регистра таймера/счетчика 0 изменится на 1 за:
64.0 * (1.0 / 16000000.0) * 1000000.0 = 4.0 мкс
Функция micros() всего лишь читает счетный регистр таймера/счетчика 0 и прибавив полученное число к системному времени хранящемуся в глобальной переменной возвращает результат. Это означает, что большей точности от этой функции не добиться.
Поэтому я предлагаю непосредственную работу с таймером/счетчиком микроконтроллера. Без предделителя и при тактовой частоте микроконтроллера в 16 МГц значение счетного регистра таймера/счетчика изменится на 1 за:
(1.0 / 16000000.0) * 1000000.0 = 0.0625 мкс
Разница весьма существенна. Но сделать этот код подходящим для всех платок непросто. Да и не смогу я протестировать эти программы. Нет у меня всех Arduino. И мне немного лень так серьезно заморачиваться. Я напишу программу для самых распространенных в мире Arduino микроконтроллеров - ATmega168 и ATmega328.
Только прежде чем написать скетч упомяну еще кое какие моменты влияющие на точность. Ненужные в данной программе прерывания способны вызвать задержки в вызове функций обрабатывающих сигналы с фототранзисторов сенсора. Прежде всего это прерывания таймеров. Поэтому их стоит отключить. В связи с этим не будут работать некоторые стандартные функции. Но так как их использование в этой программе не предполагается, то ничего страшного в этом нет.
Новый скетч для ATmega168 и ATmega328:
//////////////////////// // // Arduino Uno // //////////////////////// // // Sketch: Chronograph // #include <util/delay.h> double data; volatile unsigned char s1 = 0; volatile unsigned char s2 = 0; // Timer/Counter 0 initialization void Timer0_Init( void ) { TCNT0 = 0; // Bits: COM0A1 COM0A0 COM0B1 COM0B0 - - WGM01 WGM00 TCCR0A = 0; // Bits: FOC0A FOC0B - - WGM02 CS02 CS01 CS00 TCCR0B = 0; // Bits: - - - - - OCIE0B OCIE0A TOIE0 TIMSK0 = 0; // Bits: - - - - - OCF0B OCF0A TOV0 TIFR0 = 0; } // Timer/Counter 1 initialization void Timer1_Init( void ) { TCNT1 = 0; // Bits: COM1A1 COM1A0 COM1B1 COM1B0 - - WGM11 WGM10 TCCR1A = 0; // Bits: ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10 TCCR1B = 0; // Bits: FOC1A FOC1B - - - - - - TCCR1C = 0; // Bits: - - ICIE1 - - OCIE1B OCIE1A TOIE1 TIMSK1 = 0; // Bits: – – ICF1 – - OCF1B OCF1A TOV1 TIFR1 = 0; } void setup() { // Global disable interrupts cli(); // Timer/Counter 0 initialization Timer0_Init(); // Timer/Counter 1 initialization Timer1_Init(); // Global enable interrupts sei(); Serial.begin(9600); attachInterrupt(0, sensor_1, FALLING); attachInterrupt(1, sensor_2, FALLING); } void loop() { while ( s1 == 0 && s2 == 0 ) ; _delay_ms(800); // wait 800 ms if ( s1 != 0 && s2 != 0 ) { data = 0.06 / (TCNT1 * (1.0 / 16000000.0)); // v = s / t } else { data = 0; } Serial.println(data); TCCR1B = 0; TCNT1 = 0; s1 = 0; s2 = 0; } void sensor_1() { if ( s1 == 0 ) { TCCR1B = (1<<CS10); // Timer/Counter 1 running (no prescaling) s1 = 1; } } void sensor_2() { if ( s2 == 0 ) { TCCR1B = 0; // Timer/Counter 1 stopped (no clock source) s2 = 1; } } // // End // ////////////////////////
Для подсчета времени я решил использовать шестнадцатиразрядный таймер/счетчик 1. При работе микроконтроллера на частоте в 16 MHz таймер/счетчик 1 работая без предделителя переполнится за:
(1.0 / 16000000.0) * 65536.0 = 0.004096 с
При расстоянии между фототранзисторами равном 6 сантиметрам получим скорость:
0.06 / 0.004096 = 14.6484 м/с
Это минимальная скорость которая может быть измерена в данном случае.
При скорости пули в 100 м/с и при расстоянии между фототранзисторами равном 6 сантиметрам таймер/счетчик 1 успеет сосчитать до:
(0.06 / 100.0) / (1.0 / 16000000.0) = 9600.0
Точность хронографа с этой программой очень высока. Ошибка вносимая в измерения составляет всего лишь несколько тактов МК. Причина в работе компилятора. Можно дизассемблировать прошивку и посмотреть код обработки внешних прерываний INT0 и INT1 (они обрабатывают сигналы с фототранзисторов). Есть способы позволяющие компенсировать эту ошибку. Но я пойду другим путем. Каким именно, напишу в следующей статье.
Добрый день! Подскажите эта программа справедлива для Ардуино нано?
ОтветитьУдалитьДа. Программа будет корректно работать и в ардуино нано.
УдалитьСкажите, а какой дисплей применен ? Что-то совсем не видно в программе какая библиотека, нет инициализации....
ОтветитьУдалитьВ моем хроне данные передаются в ПК через USB. Результат измерений можно наблюдать в стандартном ардуиновском мониторе порта.
Удалить