![]()
Здравствуйте дорогие друзья самодельщики!
Cегодня мы соберем а также рассмотрим хронограф на IPS 0.96" 80x160.Устройство на оптическом ИК диапазоне 940нм.
Что такое и для чего нужен хронограф?
Хронограф это устройство предназначенное для измерения параметров пролетающего объекта (Пули, мяча и тд.).Принцип роботы заключается измерении разницы пролета объекта между двумя датчиками, а уже потом информация обрабатывается на расстояние между датчиками. Делятся они на: оптические и индукционные.
Плюс оптических это измерения любых объектов, но с минуса это другие источники ИК спектра могут мешать роботе, а так же простая калибровка и сборка устройства.
Единственными плюсами индукционных есть это нет реакции на ИК спектр и меньше энергопотребление, а вот измерять можно только ферромагнетики, Чувствительность к магнитным полям, сложная калибровка и сборка устройства.
В нашем случае будет более широкой вариант с оптическими датчиками.
Параметры
-Индустриальный IPS дисплей с 160 пикселями по x и 80 пикселей по y.
-Удобная робота, имеет удобное управления, а так же присутствует стиль.
-Калибровка без прошивка(В меню).
-Возможность установки на ствол через переходник.
![]()
Детали
Как всегда первым делом мы рассмотрим детали которые понадобятся для сборки их всего 12 или 13, максимум 14, вот они:
-ИК светодиод 5мм 2шт
-ИК Фототранзистор с Али 5мм 2шт
-Повышающий преобразователь на 5В BS01
-Ардуино NANO 328Р
-Дисплей 0.96" IPS
-Тактовые кнопки 6мм высота 3шт
-Конденсатор на 100нФ и на 100мкФ
-Резистор на 33кОм
-Резистор на 330кОм
-Резисторы на 220Ом 2шт
-Под. резисторы на 20кОм 2шт
-Корпус пластиковый NM42 110x92xH52мм, из двух неравных частей высотой 34мм и 18мм
-Отсек ААА 3шт
-Переключатель KCD-1-11
-Кусок трубы пропиленовая (Размеры на чертеже)
Схемы
![]()
Рис1.1
Эта схема Рис1.1 на основе литий-ионного аккумулятора, можно взять на 400мА но желательно не меньше 800мА. Обязательно на плате заряда ТР4056 поменять резистор на такой же ток какая самая емкость аккумулятора(1с)!
ОБЕЗАТЕЛЬНО МЕЖДУ 2 И + ПИТАНИЯ ДОЛЖЕН ПОД, РЕЗИСТОР НА 20кОм.АНАЛОГИЯ С 3 И +ПИТАНИЯ!!!
Рис1.2
Эта схема Рис1.2 основана на элементах питания ААА. Желательно брать алкалиновые!
ОБЕЗАТЕЛЬНО МЕЖДУ 2 И + ПИТАНИЯ ДОЛЖЕН ПОД, РЕЗИСТОР НА 20кОм.АНАЛОГИЯ С 3 И +ПИТАНИЯ!!!
Прошивка
/*
Created 2022
by Rad_TV
RadSan Inc.
Ukraine
*/
/*Коды:0xffff-
* 0xfff0-
* 0xff00-Обрыв ИК датчиков(Реализовано)
* 0xf000-Обрыв вольтметра(Реализовано)
* 0x0000-Нет ошибок
*/
//Библиотеки
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <EEPROM.h>
#include <AnalogKey.h>
#include <GyverButton.h>
#define EE_DATA_ADDR 1 // Адрес хранения настроек
#define EE_KEY_ADDR 0 // Адрес Хранения ключа ЕЕПРОМ
#define EE_KEY 0xBE // Ключ
#define TFT_CS 8
#define TFT_RST 10 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC 9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
//Переменые
char masschar[5]; //массив символов для перевода
String massstring; //строка, в которую задаётся масса в режиме выбора массы
int set,setmass,rapidtime;
boolean initial,flagmass, flagmassset, rapidflag, men, disp; //флажки
int n=0;//номер выстрела, начиная с 0
float input_volt = 0.0;
float temp=0.0;
float r1=330000.0; //сопротивление резистора r1
float r2=33300.0; // сопротивление резистора r2
float velocity; //переменная для хранения скорости
float energy; //переменная для хранения енергии
volatile unsigned long gap1, gap2; //отметки времени прохождения пулей датчиков
unsigned long lastshot;
unsigned long Last_time;
boolean butt_flag = 0;
int8_t arrowPos = 0; // позиция стрелки
struct {
float mass; //масса снаряда в килограммах
float dist;
int iDist, iMass;
byte bright;
} deta;
//Кнопки
GButton left(4,HIGH_PULL);
GButton ok(6,HIGH_PULL);
GButton right(7,HIGH_PULL);
#define ITEMS 4 // Общее кол во пунктов
uint8_t data[ITEMS]; // Массив для параметров
bool flag = true; // Флаг выбора
void setup() {
if (EEPROM.read(EE_KEY_ADDR) != EE_KEY) { // Если ключ в еепром не совпадает
EEPROM.put(EE_DATA_ADDR, data); // Это - первый запуск, записываем дефолт
EEPROM.write(EE_KEY_ADDR, EE_KEY); // Пишем ключ
}
EEPROM.get(EE_DATA_ADDR, deta);
Serial.begin(9600); //открываем COM порт
Serial.println("Коды:0xffff-");
Serial.println(" 0xfff0-");
Serial.println(" 0xff00-Обрыв ИК датчиков(Реализовано)");
Serial.println(" 0xf000-Обрыв вольтметра(Реализовано)");
Serial.println(" 0x0000-Нет ошибок");
analogReference(INTERNAL);
attachInterrupt(1,start,RISING); //аппаратное прерывание при прохождении первого датчика
attachInterrupt(0,finish,RISING); //аппаратное прерывание при прохождении второго датчика
tft.initR(INITR_MINI160x80);
}
void start()
{
if (gap1==0) { //если измерение еще не проводилось
gap1=micros(); //получаем время работы ардуино с момента включения до момента пролетания первой пули
}
}
void finish()
{
if (gap2==0) { //если измерение еще не проводилось
gap2=micros(); //получаем время работы ардуино с момента включения до момента пролетания второй пули
}
}
void loop() {
//Яркость
analogWrite(5, deta.bright);
//Приведствие
if (initial==0) { //флажок первого запуска
tft.setRotation(3);
tft.fillScreen(ST7735_BLACK);
tft.fillScreen(0x0066);
tft.setCursor(40, 20);
tft.setTextColor(0x27e7);
tft.setTextSize(2);
tft.print("RADSAN");
tft.setCursor(40, 43);
tft.setTextSize(2);
tft.println("ISC");
tft.setCursor(40, 63);
tft.setTextSize(1);
tft.println("ver1.2beta");
delay(2400);
initial=1; //первый запуск, больше не показываем сообщения
}
//Расчет без float()
deta.dist = deta.iDist / 10000.0;
deta.mass = deta.iMass / 100000.0;
/* Кнопки */
left.tick(); // Опрос кнопок
right.tick();
ok.tick();
if (ok.isHold() && butt_flag == 0) {
disp = 1;
butt_flag = 1;
}
if(ok.isDouble() && butt_flag == 1){
disp = 0;
butt_flag = 0;
}
if (disp==0){
//Графика
tft.fillScreen(0x0066);//фон:0x0000-белый 0xffff-черный голубой-0x0066
tft.fillRoundRect (80, 26, 75, 35, 5, 0xf0e0);
//Вольтметр
int analogvalue = analogRead(A0);
temp = (analogvalue * 1.1) / 1024.0;
input_volt = temp / (r2/(r1+r2));
tft.drawRoundRect (138, 5, 19, 13, 3, 0x54fe);
tft.drawLine (137, 8, 137, 14, 0x74fe);
if (input_volt > 3.5) {
tft.fillRoundRect(151, 5, 5, 13, 2, 0x74fe);
if (input_volt > 3.8) {
tft.fillRoundRect(147, 5, 5, 13, 2, 0x74fe);
if (input_volt > 4.0) {
tft.fillRoundRect(143, 5, 5, 13, 2, 0x74fe);
if (input_volt > 4.3) {
tft.fillRoundRect(139, 5, 5, 13, 2, 0x74fe);
}
}
}
}
//Ошибки
tft.setCursor(120, 70);
tft.setTextColor(0xffff);
tft.setTextSize(1);
if(analogRead(1) <= 400 && analogRead(4) <= 400){
tft.print("0xff00");
}else{
// tft.print("0x0000");
}
if(temp >= 3.0){
tft.print("0xf000");
}else{
tft.print("0x0000");
}
//Serial.println(analogRead(1)); //показать значение на первом датчике
//Serial.println(analogRead(4)); //показать значение на втором датчике
//Serial.println(input_volt);
//Serial.println(deta.dist, 4);
//Serial.println(deta.mass, 5);
if (gap1!=0 && gap2!=0 && gap2>gap1) { //если пуля прошла оба датчика в 0 режиме
velocity=(1000000*(deta.dist)/(gap2-gap1)); //вычисление скорости как расстояние/время
energy=velocity*velocity*deta.mass/2; //вычисление энергии
gap1=0; //сброс значений
gap2=0;
n++; //номер выстрела +1
}
if (gap1!=0) { //если пролетели через первый датчик
rapidtime=60/((float)(gap1-lastshot)/1000000); //расчет скорострельности выстр/мин
lastshot=gap1;
gap1=0;
}
tft.setCursor(3, 23);
tft.setTextColor(0x23e7);
tft.setTextSize(2);
tft.print("N");
tft.setCursor(15, 23);
tft.setTextSize(1);
tft.print("0");
tft.setCursor(15, 28);
tft.print("-");
tft.setTextSize(2);
tft.setCursor(23, 23);
tft.print(n);
tft.setCursor(83, 30);
tft.setTextColor(0xff00);
tft.setTextSize(4);
if (velocity>9.9) {
tft.print(velocity,0);
}else{
tft.print(velocity,1);
}
tft.setTextSize(2);
tft.setTextColor(0x2fe0);
tft.setCursor(3, 42);
tft.print("E: ");
tft.setCursor(30, 42);
tft.println(energy);
tft.setCursor(3, 5);
tft.setTextColor(0xf0ff);
tft.setTextSize(2);
tft.print("s/min: "); //вывод
tft.setCursor(75, 5);
tft.print(rapidtime);
delay(700); //задержка для стабильности
}
if (disp==1){
menu();
}
}
void menu(){
static int8_t pointer = 5; // Переменная указатель
if (right.isClick() or right.isHold() && disp==1) {
if (flag) {
arrowPos++;
if (arrowPos >= 3) arrowPos = 2; // ограничиваем позицию курсора
}else{
switch (arrowPos) {
case 0: deta.iMass++;
break;
case 1: deta.iDist++;
break;
case 2: deta.bright++;
break;
}
}
}
if (left.isClick() or left.isHold() && disp==1) {
if (flag) {
arrowPos--;
if (arrowPos < 0) arrowPos = 0; // ограничиваем позицию курсора
}else{
switch (arrowPos) {
case 0: deta.iMass--;
break;
case 1: deta.iDist--;
break;
case 2: deta.bright--;
break;
}
}
}
if (ok.isClick()) { // Нажатие на ОК - переключение цели параметр/значение параметра
flag = !flag; // Инвертируем флаг
EEPROM.put(EE_DATA_ADDR, deta);
}
if (ok.isHold()) { // Нажатие на ОК - переключение цели параметр/значение параметра
disp = 1;
}
if(millis() - Last_time > 700){
Last_time = millis();
tft.fillScreen(0x0066);//фон:0x0000-белый 0xffff-черный голубой-0x0066
tft.fillRoundRect(93, 3, 40, 50, 3, 0xf0e0);
tft.fillRoundRect(20, 3, 65, 50, 3, 0x39e0);
tft.setCursor(0,5);
tft.setTextColor(0xef30);
tft.setTextSize(2);
tft.print // Вывод всех пунктов
(F(
" Massa:\n" // Не забываем про '\n' - символ переноса строки
" Dista:\n"
" Brigh:\n"
));
tft.setCursor(95, 5);
tft.print(deta.iMass);
tft.setCursor(95, 20);
tft.print(deta.iDist);
tft.setCursor(95, 37);
tft.print(deta.bright);
// выводим стрелку
switch (arrowPos) {
case 0: pointer = 5;
break;
case 1: pointer = 20;
break;
case 2: pointer = 35;
break;
}
printPointer(pointer); // Вывод указателя
}
}
void printPointer(uint8_t pointer) {
if (flag) { // Если флаг установлен
tft.setCursor(3, pointer); // Указываем на параметр
tft.print(">");
} else { // Иначе
tft.setCursor(140, pointer); // Указываем на значение параметра
tft.print("<");
}
}
Ссылки:
1.0demo-Не рекомендуется;
1.1omeg-Добавлено индикатор заряда/Обработка ошибок и увеличена стабильность;
1.2bet-Добавленно меню/EEPROM/Регулировка яркости, расстояние между датчика (Можно калибровать без кода) и масса пуль/Убрали баги.
О МЕНЮ
![]()
![]()
Описано меню в документе Инструкция в архиве!
Архив