Покритикуйте написанный код)) И несколько вопросов...
- Войдите на сайт для отправки комментариев
Всем доброго времени суток! Написал скетч вывода на 4-х разрядный семисегментный индикатор времени с часов DS3231 и температуры с датчика BMP180. Применил сдвиговый регистр 74HC595 для экономии выходов ардуины. Работает всё хорошо, за исключением небольшого мерцания в момент чтения показаний температуры. Хоть скетч и работает, но всё же хочу попросить знатоков прокомментировать написанное. Что можно поправить, что убрать совсем или добавить, ну и так далее…
Ну и куда же без вопросов))
Первый из них – каким образом можно избавится от мерцания разрядов индикатора в момент чтения показания. Как бы это не критично, мерцание не очень заметное, но всё же…
Второй – на этот скетч худо-бедно у меня мозгов хватило, но всё-таки хочется добавить возможность настройки часов и будильника. Подсадите, как правильно это реализовать???
#include <Wire.h> #include <BMP085.h> #include <DS3231.h> // Библиотека для работы с модулем DS3231 #define latchPin 3 // 12 вывод(защёлка) #define clockPin 2 // 11 вывод (вход для тактовых импульсов) #define dataPin 4 // 14 вывод (вход для последовательных данных) DS3231 clock; RTCDateTime DateTime; BMP085 dps = BMP085(); unsigned long gen1P = 0UL; bool dot = false; // включение/выключение двоеточия bool statusTime = false ; // разрешение отображения времени bool statusTemp = false ; // разрешение отображения температуры int Thousandth=0, Hundredth=0, Tenth=0, Ones=0; byte tenthTemp=0, onesTemp=0; long Temperature = 0; // хранение температуры byte Pin_Digit[4] = {5, 6, 7, 8}; byte Numbers_array[12] = { 0x3F, 0x6, 0x5B, // 0, 1, 2 0x4F, 0x66, 0x6D,// 3, 4, 5 0x7D, 0x7, 0x7F, // 6, 7, 8 0x6F, 0x39,0x63, // 9,C,* }; void setup() { //Serial.begin(9600); DDRD = B11111100; // 2, 3, 4, 5, 6, 7 - выход DDRB = B00000011; // 8,9 - выход PORTD |= B11100000; // выключаем 1, 2, 3 разряды PORTB |= B00000001; // выключаем 4 разряд clock.begin(); Wire.begin(); delay(1000); dps.init(); gen1P = millis(); } void loop() { DateTime = clock.getDateTime(); // получаем текущее время String Time = clock.dateFormat("Hi",DateTime); // записываем время в Time String Second = clock.dateFormat("s",DateTime); // записываем секунды в Second byte switchTime = Second.toInt(); // конвертируем string в long и записываем в switchTime //Serial.println(switchTime); blinkDot(1000); // мигаем двоеточием раз в секунду displayTime(Time); displayTemp(switchTime); if (switchTime>=1 && switchTime<=6 ) // с 1 по 6 секунду отображаем температуру { statusTime = false; statusTemp = true; } else // затем время { statusTime = true; statusTemp = false; } } //_____ФУНКЦИЯ ОТОБРАЖЕНИЯ ДАННЫХ НА ИНДИКАТОРЕ_____// void showNumber(byte reqDigit, byte reqNumber) { digitalWrite(latchPin, LOW); // защёлка LOW shiftOut(dataPin, clockPin, MSBFIRST, Numbers_array[reqNumber]); // передаём последовательность в регистр digitalWrite(latchPin, HIGH); // защёлкиваем регистр for(int i; i<4; i++) digitalWrite(Pin_Digit[i], HIGH); // выключаем все сегменты // если разрешено отображение времени И запрещено отображение температуры ИЛИ запрещено отображение времени И разрешено отображение температуры // выводим данные на индикатор if (statusTime==true && statusTemp!=true || statusTime!=true && statusTemp==true ) digitalWrite(Pin_Digit[reqDigit], LOW); delay(5); } //_____ТАЙМЕР ОТЧЁТА КОЛИЧЕСТВА МИЛЛИСЕКУНД_____// bool isTimer(unsigned long startTime, unsigned long period ) { unsigned long currentTime; currentTime = millis(); if (currentTime >= startTime) return (currentTime >= (startTime + period)); else return (currentTime >= (4294967295 - startTime + period)); } //_____ФУНКЦИЯ ВКЛЮЧЕНИЯ/ВЫКЛЮЧЕНИЯ ДВОЕТОЧИЯ_____// void blinkDot(int frequencyBlink) { if (isTimer (gen1P, frequencyBlink)) { gen1P = millis(); dot = !dot; } if (statusTemp==true) PORTB &= (0 << 1); // если включено отображение температуры,выключаем отображение двоеточия else digitalWrite(9,dot); } //_____ФУНКЦИЯ РАЗДЕЛЕНИЯ ПЕРЕМЕННОЙ ВРЕМЕНИ НА ТЫСЯЧИ,СОТНИ,ДЕСЯТКИ И ЕДИНИЦЫ И ОТПРАВКИ ИХ В ФУНКЦИЮ SHOWNUMBER()_____// void displayTime(String curTime) { int x = curTime.toInt(); // конвертируем string в long и записываем в х // Далее разбиваем число х на тысячи,сотни,десятки и единицы... Thousandth = x/1000; // тысячи Hundredth = x/100%10; // сотни Tenth = x%100/10; // десятки Ones = x%10; // единицы if (statusTemp != true ) // если включено отображение температуры, запрещаем отправку значений времени в функцию showNumber() { // Выводим последовательно на индикатор showNumber(0, Thousandth); showNumber(1, Hundredth); showNumber(2, Tenth); showNumber(3, Ones); } } //_____ФУНКЦИЯ РАЗДЕЛЕНИЯ ПЕРЕМЕННОЙ ТЕМПЕРАТУРЫ НА ДЕСЯТКИ И ЕДИНИЦЫ И ОТПРАВКИ ИХ В ФУНКЦИЮ SHOWNUMBER()_____// void displayTemp(byte sec) { if(sec==1) dps.getTemperature(&Temperature); // каждую минуту считываем показания с датчика byte i = Temperature/10; // т.к с датчика получаем 3-х значное число без запятой (255==25,5), данным действием избавляемся от единиц // Далее разбиваем число i десятки и единицы... tenthTemp = i/10; // десятки onesTemp = i%10; // единицы if (statusTime != true) // если включено отображение времени, запрещаем отправку значений температуры в функцию showNumber() { showNumber(0, tenthTemp); showNumber(1, onesTemp); showNumber(2, 10); // отображаем С showNumber(3, 11); // отображаем знак * } }
странный код
//_____ФУНКЦИЯ РАЗДЕЛЕНИЯ ПЕРЕМЕННОЙ ВРЕМЕНИ НА ТЫСЯЧИ,СОТНИ,ДЕСЯТКИ И ЕДИНИЦЫ И ОТПРАВКИ ИХ В ФУНКЦИЮ SHOWNUMBER()_____//
как ты в десятичной системе времени ориентируешься?
странный код
//_____ФУНКЦИЯ РАЗДЕЛЕНИЯ ПЕРЕМЕННОЙ ВРЕМЕНИ НА ТЫСЯЧИ,СОТНИ,ДЕСЯТКИ И ЕДИНИЦЫ И ОТПРАВКИ ИХ В ФУНКЦИЮ SHOWNUMBER()_____//
как ты в десятичной системе времени ориентируешься?
это сначала неудобно. потом привыкаешь
Вы уверены, что хотите услышать критику?
Ничего сложного нет,простая математика. Т.к индикация динамическая в любом случаи надо было каким то образом раздилить 4-х значное число на отдельные составляющие, этот метот один из самых простых))
Вы уверены, что хотите услышать критику?
Так за этим я и создал тему. Я только учусь этому делу, так, что готов выслушать всё, что посчитайте нужным сказать!
Ничего сложного нет,простая математика. Т.к индикация динамическая в любом случаи надо было каким то образом раздилить 4-х значное число на отдельные составляющие, этот метот один из самых простых))
зачем делить 4-х значное число на отдельные составляющие - у тебя же экран 4-х значный и не вместит тысячные(6-ти значные)?
Ничего сложного нет,простая математика. Т.к индикация динамическая в любом случаи надо было каким то образом раздилить 4-х значное число на отдельные составляющие, этот метот один из самых простых))
зачем делить 4-х значное число на отдельные составляющие - у тебя же экран 4-х значный и не вместит тысячные(6-ти значные)?
Объясняю ещё раз... Индикация у меня динамическая, при динамической индикации в каждый момент времени выводится только одна цифра, но происходит это очень быстро (каждые 5 мс.) и кажется,что цифры горят постоянно Можно было и без неё обойтись, но тогда бы потребовалось бы 7х4=28 + 2 точки = 30 ног микроконтроллера))
Вы уверены, что хотите услышать критику?
Так за этим я и создал тему. Я только учусь этому делу, так, что готов выслушать всё, что посчитайте нужным сказать!
Да, просто практика показывает, что пока критика выглядит как "одобрямс, молодец, классно", всё хорошо. Но как только отнесёшся к делу серьёзно, потратишь пару часов и выложишь реальные замечания - становишься мудаком, обидевшим ранимую душу автора. Получаешь полный букет эпитетов и ... становится так жалко потраченных пары часов.
Вы уверены, что хотите услышать критику?
Так за этим я и создал тему. Я только учусь этому делу, так, что готов выслушать всё, что посчитайте нужным сказать!
Да, просто практика показывает, что пока критика выглядит как "одобрямс, молодец, классно", всё хорошо. Но как только отнесёшся к делу серьёзно, потратишь пару часов и выложишь реальные замечания - становишься мудаком, обидевшим ранимую душу автора. Получаешь полный букет эпитетов и ... становится так жалко потраченных пары часов.
Так давайте с чего начнем уже.
Вот к примеру
047
String Time = clock.dateFormat(
"Hi"
,DateTime);
// записываем время в Time
Зачем String? Вы не знаете какой максимальной длины вернется строка? Знаете. Потому масив char здесь явно лучше. А далее вы эту строку передаете в функцию чтоб там сразу конвертнуть в целое. Логичней уже в функцию передавать сразу int. И посмотреть внимательней на clock, может вобще без строк тут обойтись?
А вот типичная локальная Thousandth зачемто обявлена глобальной. И др. ей подобные.
С пинами в сетапе Вы работаете оч. красиво, напрямую с регистрами. Хороше. Но не обязательно, сетап, он раз отработает, быстро или медленно не так важно. Но вот вывод на экран из лопа через digitalWrite. А именно здесь бы напрямую, по быстрому. Так нет жеж.
Аларм с ахтунгом! Империо в опасности!
077
delay(5);
Вобще странное архитектурное решение - каждый луп получать данные формировать его а затем проверять условия и выводить чего. Ну Вы представте как это будет выглядеть если будет больше типов экрана. Включаем здравый смысл, как бы чел по жизни все делал ручками? На каждом проходе цикла он определял нужно ли чтото менять. А менять нужно будет одно из 2-х: сигналы динамической индикации для отображения того что сейчас надо отображать или сами отображаемые данные. Очевидно что сигналы динамической индикации прийдется формировать для заданых ранее отображаемых данных. Значить их хранить надо, и назовем это хранилище экранным буфером. Получается для смены отображаемых данных надо просто сформировать новый экранный буфер в тот момент когда такая смена потребуется. Т.е. если изменилось время, пора мигнуть двоеточием или отобразить температуру - мы изменим содержимое экранного буфера на требуемое. Таков обычный подход.
Так давайте с чего начнем уже.
Вот к примеру
047
String Time = clock.dateFormat(
"Hi"
,DateTime);
// записываем время в Time
Зачем String? Вы не знаете какой максимальной длины вернется строка? Знаете. Потому масив char здесь явно лучше. А далее вы эту строку передаете в функцию чтоб там сразу конвертнуть в целое. Логичней уже в функцию передавать сразу int. И посмотреть внимательней на clock, может вобще без строк тут обойтись?
Попробую пояснить. Да, возможно я не прав, но мне показалось проще конвертировать string в int функцией toInt(), чем зделать этоже с char массивом. Тем более, сначала я отправил Strig в сериал, там было 4-х значное значения минут и часов одной строкой, как раз то, что мне и нужно. Сегодня на свежую голову подумал, почему не попробовать записывать значения времени напрямую в int, примерно вот так: int x = DateTime.minute; Не знаю будет ли это работать,вечером проверю!
А вот типичная локальная Thousandth зачемто обявлена глобальной. И др. ей подобные.
Тут согласен, изначально пробовал всё по отдельности в loop, затем уже создавал отдельные функции под каждую операцию. Перенос глобальных переменных в нужные места у меня запланирован))
С пинами в сетапе Вы работаете оч. красиво, напрямую с регистрами. Хороше. Но не обязательно, сетап, он раз отработает, быстро или медленно не так важно. Но вот вывод на экран из лопа через digitalWrite. А именно здесь бы напрямую, по быстрому. Так нет жеж.
Решил сделать такой setup, просто из-за того, что не захотелось расписывать каждый порт по отельности, по моему это даже правельнее, и как вы сказали выглядит красиво! Насчёт digitalWrite, если вы имейте ввиду функцию void showNumber(); Тут я просто не в курсе как, например оформить в виде битовой маски подубную запись
Или цикл
Или когда портом управляет переменная, как в записи ниже
Аларм с ахтунгом! Империо в опасности!
077
delay(5);
Вобще странное архитектурное решение - каждый луп получать данные формировать его а затем проверять условия и выводить чего. Ну Вы представте как это будет выглядеть если будет больше типов экрана. Включаем здравый смысл, как бы чел по жизни все делал ручками? На каждом проходе цикла он определял нужно ли чтото менять. А менять нужно будет одно из 2-х: сигналы динамической индикации для отображения того что сейчас надо отображать или сами отображаемые данные. Очевидно что сигналы динамической индикации прийдется формировать для заданых ранее отображаемых данных. Значить их хранить надо, и назовем это хранилище экранным буфером. Получается для смены отображаемых данных надо просто сформировать новый экранный буфер в тот момент когда такая смена потребуется. Т.е. если изменилось время, пора мигнуть двоеточием или отобразить температуру - мы изменим содержимое экранного буфера на требуемое. Таков обычный подход.
Тут я изначально понимал, что это не очень хорошо выводить данные каждые 5 мс. Так же понимал, что можно просто, как вы говорите, хранить данных где-то в "буфере обмена" и обновлять их когда это необходимо. Но, к сожелению мозгов у меня на это пока не хватает((( Буду признателен, если объясните как это реализовать, или дадите ссылочку на ресурс, где это можно посмотреть.
Вот еще нюанс. Как строют из домино башню. Ложат одну доминошку, поверх еще одну, и так далее, пока башня не станет шаткой. Разумеется ложат по 1 штучке. Можно и сразу по две и больше. Но конечная конструкция будет ниже. Так и просходит с программированием. У некоторых глючность программы происходит на 10 строке, у некоторой на 100, 200. Но конечность не глючности программы всегда есть. И это конечность индивидуальна для каждого программиста. Как же средний программист строит большие программы, если "индивидуальная граничность не глючности программы у него уже пройдена". Разумеется он должен переходить и колличественных методов к качественным. Это технологии из С++, видуал Си , и визуал проектирования.
Это так информация для размышления. Для возможности настройки часов и будильника надо организовать систему меню. А это новая программа , которая не будет конфликтовать с вашей уже рабочей . А ваша рабочая уже написана конфликтна в добавочным компонентам. И на
Аларм с ахтунгом! Империо в опасности!
077
delay(5);
Вобще странное архитектурное решение - каждый луп получать данные формировать его а затем проверять условия и выводить чего. Ну Вы представте как это будет выглядеть если будет больше типов экрана. Включаем здравый смысл, как бы чел по жизни все делал ручками? На каждом проходе цикла он определял нужно ли чтото менять. А менять нужно будет одно из 2-х: сигналы динамической индикации для отображения того что сейчас надо отображать или сами отображаемые данные. Очевидно что сигналы динамической индикации прийдется формировать для заданых ранее отображаемых данных. Значить их хранить надо, и назовем это хранилище экранным буфером. Получается для смены отображаемых данных надо просто сформировать новый экранный буфер в тот момент когда такая смена потребуется. Т.е. если изменилось время, пора мигнуть двоеточием или отобразить температуру - мы изменим содержимое экранного буфера на требуемое. Таков обычный подход.
Тут я изначально понимал, что это не очень хорошо выводить данные каждые 5 мс. Так же понимал, что можно просто, как вы говорите, хранить данных где-то в "буфере обмена" и обновлять их когда это необходимо. Но, к сожелению мозгов у меня на это пока не хватает((( Буду признателен, если объясните как это реализовать, или дадите ссылочку на ресурс, где это можно посмотреть.
))))
Понимаете в чем ирония ситуации. Ваш код намного сложней в придумывании и отладке чем то, о чем пишу я. И раз вам на него мозгов хватило то на более простой тем более хватит. Банальная ситуация, можна уметь бегать быстро и долго, но побежав не по той дороге к цели не попадеш. В псевдокоде будет дето так.
Z=0 W=0 //две глобальные переменные
Луп
(
если истекло время с прошлого обновления динамической индикации то
(
выводим очередной символ из экранного буфера(меняем строб, выводим сигналы...)
)
если выводим часы т.е. Z==0 и прошло пол секунды с прошлого вывода времени
(
W=W+1 //некая глобальная переменная
если W==2(заменяем в экранном буфере ":" на " " W=0 )
определяем не пора ли вывести температуру и если пора Z=1
)
если пора выводить температуру т.е. Z==1
(
считываем с датчика температуру и формируем экранный буфер
Z=2
)
если выводим температуру т.е. Z==2 и истекло время её вывода
(
Z=0
)
)
Гдето так. Обратите внимание что проверки "если пора ", "если истекло время " и пр. связаное с формированием временных интервалов делаем как в "блинк без делея". При динамической индикации так же потребуется переменная похожая на использованая в коде Z и W.
... строют ...Ложат ... ложат ...
(с) Доживем до понедельника.
просто из-за того, что не захотелось расписывать каждый порт по отельности
Когдато я себе такое написал для 328-го контролера
И соответственно работа с пинами так
вместо digitalWrite(pin, LOW) будет bitClear(ARDUINO_PORT(pin), ARDUINO_PIN_NUM(pin));
вместо digitalWrite(pin, HIGH) будет bitSet(ARDUINO_PORT(pin), ARDUINO_PIN_NUM(pin));
вместо pinMode(pin, OUTPUT) будет bitSet(ARDUINO_DDR((pin), ARDUINO_PIN_NUM(pin));
вместо digitalWrite(pin, val) будет val? bitSet(ARDUINO_PORT(pin), ARDUINO_PIN_NUM(pin)); :bitClear(ARDUINO_PORT(pin), ARDUINO_PIN_NUM(pin));
Это почти как в сетапе у Вас, но немного удобней в применении и не надо помнить какой пин в какой регистр попадает.
... строют ...Ложат ... ложат ...
(с) Доживем до понедельника.
Так уже на это все поклали ;)
Это почти как в сетапе у Вас, но немного удобней в применении и не надо помнить какой пин в какой регистр попадает.
Тут как говорят, на вкус и цвет и товарищей нет)) Так как у меня УНА и портов там не много, то подобная запись PORTD |= B11100000; лично для меня более понятна...
И всё таки, можно ли оформить подобные записи в виде битовых масок??
Луп
(
если истекло время с прошлого обновления динамической индикации то
(
выводим очередной символ из экранного буфера(меняем строб, выводим сигналы...)
)
если выводим часы т.е. Z==0 и прошло пол секунды с прошлого вывода времени
(
W=W+1 //некая глобальная переменная
если W==2(заменяем в экранном буфере ":" на " " W=0 )
определяем не пора ли вывести температуру и если пора Z=1
)
если пора выводить температуру т.е. Z==1
(
считываем с датчика температуру и формируем экранный буфер
Z=2
)
если выводим температуру т.е. Z==2 и истекло время её вывода
(
Z=0
)
)
Гдето так. Обратите внимание что проверки "если пора ", "если истекло время " и пр. связаное с формированием временных интервалов делаем как в "блинк без делея". При динамической индикации так же потребуется переменная похожая на использованая в коде Z и W.
Често, не очень понятно как это реализовать на практике( Может всему свое время....
«Всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть» (Л. Кэрролл)
Так что ваш единственный путь развития, улучшайте и развивайте ваш "вкус и цвет".
Так этим я и пытаюсь заниматься)))
Так этим я и пытаюсь заниматься)))
Често, не очень понятно как это реализовать на практике( Может всему свое время....
Время важный фактор, когда его тратят с пользой. Начните с простого. Прочитайте про блин без делея, попробуйте. Потом в него же впишите динамический вывод из экранного буфера, а в буфер что угодно закиньте, 1234 например, как увидите это на экране - знач процесс пошел:)
Често, не очень понятно как это реализовать на практике( Может всему свое время....
Время важный фактор, когда его тратят с пользой. Начните с простого. Прочитайте про блин без делея, попробуйте. Потом в него же впишите динамический вывод из экранного буфера, а в буфер что угодно закиньте, 1234 например, как увидите это на экране - знач процесс пошел:)
Как без делея работать я в курсе, у меня это с двоеточием реализовано. Я пробовал выводит с момощью таймера, но получилась ху...я! Вот и решил на delay остановится, тем более попробовал подключить кнопку и во время работы программы управлять этой кнопкой процессом, добавляя дополнительные условия, типа: если кнопка нажата отображаем температуру, пробовал светодиодом управлять. Ну и в итоге, что удивительно, проблем не заметил, всё срабатывает без нареканий. С выводом с помощью таймера буду пробовать... может чего и получится))
Вот еще Почему вы выводите с помощью 1 74HC595. Лучше выводить с помощью двух. Один у вас должен выбирать сегменты, а другой уже информацию на сегменте. массив из 4 byte для буфера и переключать с частотой 50 гц*4 =200 => 5милисек.
Вот еще Почему вы выводите с помощью 1 74HC595. Лучше выводить с помощью двух. Один у вас должен выбирать сегменты, а другой уже информацию на сегменте. массив из 4 byte для буфера и переключать с частотой 50 гц*4 =200 => 5милисек.
Т.к сегмента всего 4, посчитал, что их можно и напрямую подключить и не заморачиваться c shiftOut(). Есть же массив byte Pin_Digit[4] = {5, 6, 7, 8}; он и занимается включением сегментов каждые 5 мс.
Ещё момент, в чём разница между uint32_t и unsigned long или, например int32_t и long, если вместимость у них одинаковая?
uint32_t это unsigned long только короче написано . unsigned long и long без знака и с знаком.