Для начала неплохо бы выяснить как работает функция 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. Результат измерений можно наблюдать в стандартном ардуиновском мониторе порта.
Удалить