#include <Wire.h>
#include <TM1650.h>
#include <iarduino_RTC.h>
#include <avr/eeprom.h>
//#define OUTR PB1 //Выход на реле Attiny
#define OUTR 7 //Выход на реле UNO
iarduino_RTC watch(RTC_DS1307);
TM1650 d;
//TM16xxButtons buttons(&d); // TM16xx button
//TickerScheduler ts(1);
byte display_mode = 0;//Режимы вывода на дисплей
/*0-показ времени;
1-день-месяц;
2-год;
3-включение утром;
4-выключение вечером;
5-установка минут в часах;
6-установка часов в часах;
7-установка числа в часах;
8-установка месяца в часах;
9-установка года в часах;
10-установка часов включения утром;
11-установка минут включения утром;
12-установка часов выключения вечером.
13-установка минут выключения вечером.*/
//int chour, cmin;
char* out_on, out_off;
bool dot_state = false;
int8_t VAR_ON_MIN = 0; // Задаем минуты включения света утром
int8_t VAR_OFF_MIN = 0; // Задаем минуты выключения света вечером
int8_t VAR_ON_HOUR = 7; // Задаем часы включения света утром
int8_t VAR_OFF_HOUR = 22; // Задаем часы выключения света вечером
unsigned long timer1 = 0;
И вот файлик с дополнительными функциями. Пока в формате ino. Потом переделаю его в .h как советовали выше:
void pressUP(int cmin, int chour, int cday, int cmon, int cyear) {
switch (display_mode) {
case 5: watch.settime(-1, (cmin == 59?cmin = 0:cmin++));break;
case 6: watch.settime(-1, -1, (chour == 23?chour = 0:chour++));break;
case 8: watch.settime(-1, -1, -1, -1, (cmon == 12?cmon = 1:cmon++));break;
case 9: watch.settime(-1, -1, -1, -1, -1, (cyear == 99?cyear = 99:cyear++));break;
case 7: int dinmon = getdinmon(cmon, cyear);
watch.settime(-1, -1, -1, (cday == dinmon?cday = 1:cday++));
break;
}
}
void pressDOWN(int cmin, int chour, int cday, int cmon, int cyear) {
switch (display_mode) {
case 5: watch.settime(-1, (cmin == 0?cmin = 59:cmin--));break;
case 6: watch.settime(-1, -1, (chour == 0?chour = 23:chour--));break;
case 8: watch.settime(-1, -1, -1, -1, (cmon == 1?cmon = 12:cmon--));break;
case 9: watch.settime(-1, -1, -1, -1, -1, (cyear == 22?cyear = 22:cyear--));break;
case 7: int dinmon = getdinmon(cmon, cyear);
watch.settime(-1, -1, -1, (cday == 1?cday = dinmon:cday--));break;
}
}
int getdinmon(int cmon, int cyear) {
switch (cmon) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: return 31;
break;
case 4:
case 6:
case 9:
case 11: return 30;
break;
default: if (cyear % 4 == 0) return 29;
else return 28;
break;
}
}
Пока отработку кнопок не сделал, делаю их нажатие через порт.
Вывод на экран имеет несколько режимов. В set.h они описаны. 2 режима (3 и 4) выводят настройки 2 будильников. По первому свет утром включается, по второму вечером выключается. Днем свет горит или нет - зависит от освещенности. Пока это еще не описывал в скетче.
Так вот вывод времени, даты, года в информацитонном режиме и в режиме установки значений уже сделал и там все работает. А вот вывод значений будильников на экран никак не получается. Со вчерашнего дня пытаюсь въехать в эти printf() и snprintf_P() никак не пойму как в этих вункциях собрать из времени 7 часов 0 минут вывод на экран строки '0700'. Для начала.
Итак вопрос в основном файле в режимах 3 и 4. Ну для начала 3. Остальные аналогично будут.
int time = 0;
int var = 55;
char buff[50];
void setup() {
Serial.begin(115200);
}
void loop() {
sprintf(buff, "the value is %d seconds %d", time++, var);
Serial.println(buff);
}
Выводится будет "the value is <тут увеличенная на 1 time> seconds 55". Собственно сам примет с просторов инета, я только var добавил, что бы понять, что можно туда несколько переменных вставлять. Тогда возникает вопрос. Никак не найду где взять правила написания формата. инет вообще еле двигается, через google вааще ничего открываться не хочет. Можно ли там в формате задать, чтобы вместо 7 писалось 07 и вместо 0 соответственно 00.
byte display_mode = 0;//Режимы вывода на дисплей
/*0-показ времени;
1-день-месяц;
2-год;
3-включение утром;
4-выключение вечером;
5-установка минут в часах;
6-установка часов в часах;
7-установка числа в часах;
8-установка месяца в часах;
9-установка года в часах;
10-установка часов включения утром;
11-установка минут включения утром;
12-установка часов выключения вечером.
13-установка минут выключения вечером.*/
заменил бы на такое:
enum display_mode : uint8_t {
TIME, // показ времени
DAY_MONTH, // день-месяц
YAER, // год
...
SET_MINUTES_OFF_EVENING // установка минут выключения вечером
}
Тогда в case все становится человеко-понятным (чтобы к той "простынке" постоянно не листать):
Да я вчера про это тоже читал. Пока руки не дошли заменить. Сейчас все напишу, потом причесывать буду. А то как-то пока странно работает. То отрабатывает нажатие, а то нет. При чем ошибок никаких нет.
А вот еще я читал, что этот sprintf очень тяжелый для памяти. А у меня все это дело на Attiny работать будет. Может получиться, что не влезет?
Все. Индикацию написал. Теперь буду кнопки паять и обрабатывать и срабатывание реле. Ну срабатывание реле это уж мелочи.
Народ, еще вопросик задам. Сейчас все закончил, причесываю.
Вот такой следующий код вызывает периодические зависания. Выявил это путем закомментирования этого куска и раскомментирования. Это собственно обработка нажатия кнопок. Нашел только одну библиотеку, где отрабатываются кнопки, но она какая-то навороченая и для 1650 там примеров нет. Может кто подскажет в чем я накосячил?
//Отлавливаем кнопки
Wire.requestFrom(0x24, 1); //делаем запрос 1 байта из
//микросхемы с адресом 0x24. //Адресные A0,A1,A2 на землю.
btn_num = Wire.read();
//Этот кусок я нашел в инете. Далее путем экспериментов нашел код каждой кновки.
bool ss = false, us = false, ds = false, sp = false, up = false, dp = false;
if (btn_num == 68 && !ss) {b_timer = millis();ss = true;}
if (btn_num == 4 && ss) {b_hold = millis() - b_timer;ss = false;sp = true;}
if (btn_num == 84 && !us) {b_timer = millis();us = true;}
if (btn_num == 20 && us) {b_hold = millis() - b_timer;us = false;up = true;}
if (btn_num == 76 && !ds) {b_timer = millis();ds = true;}
if (btn_num == 12 && ds) {b_hold = millis() - b_timer;ds = false;dp = true;}
if (b_hold <= 500 && btn_num == 4 && sp) {
if (display_mode < 5) display_mode == 4?display_mode = 0:display_mode++;
else {
switch (display_mode){
case 5: display_mode = 6; break;
case 6: display_mode = 5; break;
case 7: display_mode = 8; break;
case 8: display_mode = 7; break;
case 10: display_mode = 11; break;
case 11: display_mode = 10; break;
case 12: display_mode = 13; break;
case 13: display_mode = 12; break;
}
}
sp = false;
}
else if (b_hold > 500 && btn_num == 4 && sp) {
switch (display_mode){
case 0: display_mode = 5; break;
case 5:
case 6: display_mode = 0; break;
case 1: display_mode = 7; break;
case 7:
case 8: display_mode = 1; break;
case 2: display_mode = 9; break;
case 9: display_mode = 2; break;
case 3: display_mode = 10; break;
case 10:
case 11: display_mode = 3; break;
case 4: display_mode = 12; break;
case 12:
case 13: display_mode = 4; break;
}
sp = false;
}
watch.gettime();
if (btn_num == 20 && up) {
//watch.gettime();
pressUP(watch.minutes, watch.Hours, watch.day, watch.month, watch.year);
up = false;
}
if (btn_num == 12 && dp) {
//watch.gettime();
pressDOWN(watch.minutes, watch.Hours, watch.day, watch.month, watch.year);
dp = false;
}
Вроде все просто, но почему-то зависает устройство. Просто останавливает. Перестает менять изображение на экране и порт перестает выдавать данные.
Да, одна кнопка перебирает режимы, а две другие делают плюс-минус времени, будильникам при установке.
Зависает не сразу, минут через 20-30, может через час.
Dimych70, опять вы выкладываете какие-то куски кода без начала и конца? Кто в них будет копаться?
Если хотите, чтобы кто-то посмотрел ваш код - оформите проблемный кусок в виде отдельного скетча с сетап и луп, с декларацией всех переменных... Чтобы отвечающие могли запустить его у себя и протестировать проблему.
Код на отлов нажатия кнопок с 38 по 95 строки. Если их закомментировать, то все работает.
Я вообще почему кусок только выложил. Там идет обращение к wire. Я это обращение нашел раньше в инете. Там описывал товарищ как он сам отлавливал кнопки. Так вот может как-то что-то в этом wire закрыть надо или еще что сделать. Т.к. остальное то все обычные IF и элементарные математические действия.
Может надо не закрыть, а обозвать как-то отдельно это Wire. Я просто пока не очень въехал как работает wire. Там на более низком уровне я так понял работа идет.
ИМХО код очень тяжело читается, сложно что то сказать по этому поводу.
Я посмотрел, при чтении клавиатуры вы обращаетесь к адресу 0x24. в даташите же указан адрес 0x49.
Протокол TM1650 несколько отличается от стандартного I2C, Slave address в I2C - 7 bit +R/W bit
0x24 << 1 == 0x48; 0x48 + 1(read) == 0x49 это конечно понятно, но как то это неправильно, а он же у вас еще и с DS3107 на одной шине сидит?
Посмотрите TM16xx.h там и индикатор и кнопочки есть.
По кнопкам- попробуйте анализировать только следующие значения получаемые от TM1650: 0x44, 0x4С, 0x54 - кнопки 1, 2, 3 соответственно. (это в десятичной 68, 76,84, как у вас), а остальные значения просто игнорировать. То есть не равно 0x44, 0x4С, 0x54 - обнуляем принятое значение. (некоторые значения возникают на переходах от нажатия к отпусканию и при включении питания, думаю они вам не нужны)
Просто если появилось значение 0x44 - нажали "кнопку 1", 0 - отпустили.
Появилось значение 0x4С- нажали "кнопку 2", 0 - отпустили кнопку.
Будет как мне кажется проще и понятнее.
Посмотрел исходник iarduino_RTC.h, что не понравилось: при каждом вызове функции, возвращающей указатель на строку со временем, удаляется ранее выделенная динамическая память, и создается заново под необходимый размер буфера (через malloc()). То есть делается это на каждом вызове функции, и нигде не проверяется успешность выделения памяти. Просто прописываются данные по указателю. А вы потом еще и пробелы по этому указателю прописываете из loop(). И все это не быстро, и затратно, там и String используется, соответственно подтянется. Я бы отказался от этой библиотеки, как то только для демонстрации она и подходит, да и с переполнением millis() там решение с "костылями".
ИМХО код очень тяжело читается, сложно что то сказать по этому поводу.
Чужой код всегда тяжело читается. Это я заметил еще лет 20 назад когда на Delphi программировал. Но я вроде везде комментарии ставил что делается, да и собственно сам код достаточно короткий.
SergeiL пишет:
Я посмотрел, при чтении клавиатуры вы обращаетесь к адресу 0x24. в даташите же указан адрес 0x49.
В самом начале я сканировал i2c. Уже не вспомню, но там было 8 адресов. Вроде с 24 по 27 и с 34 по 37. 49 точно не было. Но попробую сейчас.
SergeiL пишет:
Посмотрите TM16xx.h там и индикатор и кнопочки есть.
Смотрел. Даже пытался в начале ее использовать. Но в примерах к сожалению 1650 нет. Пытался делать по аналогии с другими. Индикацию выдать получилось, но вот при подключении библиотеки с кнопками и попытках обработать их сразу начинался трэш. Все висло сразу намертво.
SergeiL пишет:
По кнопкам- попробуйте анализировать только следующие значения получаемые от TM1650: 0x44, 0x4С, 0x54 - кнопки 1, 2, 3 соответственно. (это в десятичной 68, 76,84, как у вас), а остальные значения просто игнорировать. То есть не равно 0x44, 0x4С, 0x54 - обнуляем принятое значение. (некоторые значения возникают на переходах от нажатия к отпусканию и при включении питания, думаю они вам не нужны)
Просто если появилось значение 0x44 - нажали "кнопку 1", 0 - отпустили.
Появилось значение 0x4С- нажали "кнопку 2", 0 - отпустили кнопку.
Будет как мне кажется проще и понятнее.
Будет проще. Но мне нужно еще отработать длинное нажатие кнопки установки режимов. А тут без отлова кода, который по отпускании кнопки появляется никак.
SergeiL пишет:
Посмотрел исходник iarduino_RTC.h, что не понравилось: при каждом вызове функции, возвращающей указатель на строку со временем, удаляется ранее выделенная динамическая память, и создается заново под необходимый размер буфера (через malloc()). То есть делается это на каждом вызове функции, и нигде не проверяется успешность выделения памяти. Просто прописываются данные по указателю. А вы потом еще и пробелы по этому указателю прописываете из loop(). И все это не быстро, и затратно, там и String используется, соответственно подтянется. Я бы отказался от этой библиотеки, как то только для демонстрации она и подходит, да и с переполнением millis() там решение с "костылями".
С RTC вообще я бился еще год назад. iarduino_RTC.h работала лучше всех. Но все равно не всегда. Я даже как в начале этой темы сам надергал из библиотеки функции и год их использовал успешно. Но с 1650 наверное не захотела работать. Поэтому я и взял iarduino_RTC.h.
А к чему такие мучения? Тинька, тм-ка? На обычной ардуинке часы делаются на коленке за полчаса и работают без запинки. Или цель - побольше страданий? ))
По кнопкам- попробуйте анализировать только следующие значения получаемые от TM1650: 0x44, 0x4С, 0x54 - кнопки 1, 2, 3 соответственно. (это в десятичной 68, 76,84, как у вас), а остальные значения просто игнорировать. То есть не равно 0x44, 0x4С, 0x54 - обнуляем принятое значение. (некоторые значения возникают на переходах от нажатия к отпусканию и при включении питания, думаю они вам не нужны)
Просто если появилось значение 0x44 - нажали "кнопку 1", 0 - отпустили.
Появилось значение 0x4С- нажали "кнопку 2", 0 - отпустили кнопку.
Будет как мне кажется проще и понятнее.
Будет проще. Но мне нужно еще отработать длинное нажатие кнопки установки режимов. А тут без отлова кода, который по отпускании кнопки появляется никак.
Я TM1650 не тестировал, с описанием там полня хрень, видел только оч. короткое и на китайском, перевел гуглом. По информации из интернет понял, что если нажали кнопку и держим, и если много раз считывать данные из ТМ, каждый раз будет считываться код нажатой кнопки, пока ее не отпустят.
Да так. Нажимаешь, код 68, отпускаешь, код (-64) 4, другая - 84-20 и третья 76-12. Можно конечно ловить просто смену скажем 68 на любой другой, согласен. И я пробовал, но так как я сделал мне показалось проще. (просто я думаю, что это сильно не изменит ситуацию).
А на счет адреса. Я конечно попробую. Но если бы я задал неправильный адрес считывания, разве что-нибудь считалось бы?
И еще я не совсем понял, почему когда я включал сканер, выдавалось 8 адресов i2c? У меня 1650, она рассчитана вроде на 4=сегментный индикатор. Откуда там 8 адресов? Там кнопки физически вешаются общей шиной на разряд.
Попробовал 49 адрес. Не реагирует. Все время 255 выдает.
Зато на 25 скажем тоже работает. Те же коды выдает.
А на счет адреса. Я конечно попробую. Но если бы я задал неправильный адрес считывания, разве что-нибудь считалось бы?
И еще я не совсем понял, почему когда я включал сканер, выдавалось 8 адресов i2c? У меня 1650, она рассчитана вроде на 4=сегментный индикатор. Откуда там 8 адресов? Там кнопки физически вешаются общей шиной на разряд.
Наверное и не поможет.
Тут вопрос в другом, TM1650 работает не по настоящему I2C, а по своему протоколу, похожему на I2C.
Поэтому адреса у TM1650 и нет. В принципе нет адреса, по даташиту.
Есть команды. На них он отвечает, а сканер воспринимает их как адреса.
Главное чтобы TM1650 и DS1307 друг другу не мешали на одной шине.
Снизил скорость до стандартных 100000, прописал 24 порт. Все равно виснет. Не сразу, через 20-30 минут. Блин вот никогда раньше с таким не сталкивался. Не компилилось, но если скомпилилось и запустилось, пахало без проблем.
Сейчас пытаюсь еще с sprintf разобраться. my_itoa имеется ввиду не готовая уже стандартная функция, а самому какой-то обработчик написать или найти надо?
Блин, вот наверно я попоясдеревянный! Пытаюсь найти исходники itoa и нихрена! Ведь понимаю, что она в какой-то библиотеке по умолчанию. Не находится. В инете тоже куча примеров, но исходника не нахожу!
По поводу зависания. Как я раньше писал, всю ночь работа, при отключенной обработке кнопок. Вообще весь кусок удален был. Сейчас начал экспериментировать. Вставляем
Блин, вот наверно я попоясдеревянный! Пытаюсь найти исходники itoa и нихрена!
Зачем тебе исходники? Для чего?
Конвертирование целочисленных данных в строку через функции itoa, ltoa, ultoa.
Функции простые, позволяют конвертировать числа целых форматов в текстовую строку.
itoa (int data, char* string, int radix); // преобразование int
ltoa (long data, char* string, int radix); // преобразование long
ultoa (unsigned long data, char* string, int radix); // преобразование unsigned long
data – это конвертируемая переменная;
char* string – указатель на строку (имя массива);
radix – система исчисления результата в строке:
10 для DEC;
8 для OCT;
16 для HEX;
2 для BIN.
Например, конвертирование переменой x типа int в строку myStr1 можно сделать так.
itoa(x, myStr1, 10); // в десятичном виде
itoa(x, myStr1, 8); // в восьмеричном виде
itoa(x, myStr1, 16); // в шестнадцатеричном виде
itoa(x, myStr1, 2); // в двоичном виде
Ну типа передаем 2 числа, третья переменная, это для мигания, вместо какого параметра выводить пробелы. В самой функции все прекрасно работает. Как положено выдает "0700".
Но вот когда запрашивается из тела программы результат функции, в него влезают какие-то символы.
"⸮0700" это выдается в теле программы, там еще в экране порта спереди два квадратика есть, которые через буфер не передаются. Вот здесь:
В самой функции все прекрасно работает. Как положено выдает "0700".Но вот когда запрашивается из тела программы результат функции, в него влезают какие-то символы.
потому что вы пытаетесь передать наружу функции локальную переменную. Вы что-нибудь про "область видимости" почитайте...
Да и вообще какой-нить учебник по си было бы неплохо почитать на ночь недельки две, прежде чем продолжать. А то жалко на вас смотреть - буквально на каждой строке спотыкаетесь.
Вот тут хотелось бы подробнее. Да наверное я и в других вещах не разбираюсь. Но я думал, что если я обращаюсь к функции и пытаюсь получить ее результат, я не должен объявлять еще и в теле программы переменную, которую использую в функции.
Переменную out я задал в функции и через нее возвращаю результат работы функции. В теле программы функция передает свой результат другой переменной, которая у меня объявлена в теле программы. Да и если бы надо было объвлять эту переменную еще и в теле программы, была бы ошибка во время компиляции! Так всегда было. Если это не так, то я что-то совсем запутался!
во-первых, в коде #88 у вас возвращаемое значение функции было типа char*, а в последнем уже char. Надеюсь. вы понимаете что это огромная разница. Если нет - то сначала разберитесь.
Но в любом случае ни char*, ни char не соответвуют типу переменной out[5], поскольку это массив из 5 чаров...
существенно сокращает количество символов в скетче
с вас за символы в скетче берут оплату посимвольно? - а если нет, к чему это? - если что, размер текста скетча к размеру бинарного кода имеет очень относительное отношение
А что, если функция объявлена как char*, то переменную, которую она внутри использует и передает наружу надо объвлять еще и в теле основной программы?
А если функция char (без звездочки), то можно не объявлять?
Собственно я и говорю, что никак в char не въеду. Я знаю, что * это ссылка. Но вот что при этом внутреннюю переменную функции надо и вне функции объявлять - такого я не знал.
существенно сокращает количество символов в скетче
с вас за символы в скетче берут оплату посимвольно? - а если нет, к чему это? - если что, размер текста скетча к размеру бинарного кода имеет очень относительное отношение
По-моему так просто компактнее и читабельнее. Мне самому сначала было неудобно читать, но как привык очень удобно.
Но по сути ведь написано правильно. Давайте пусть так и останется, если это не влияет на работу.
А что, если функция объявлена как char*, то переменную, которую она внутри использует и передает наружу надо объвлять еще и в теле основной программы?
char* - это не сам массив, а только его адрес. Наличие у вас в руках конверта с адресом не гарантирует. что по этому адресу существует дом. И тут так же.
После окончания работы функции она возвращает адрес массива, но самого массива по этому адресу уже нет, так он был локальным и при выходе из функции затерся новыми данными. Именно поэтому внутри функции ваша строчка out печатается, а снаружи уже нет.
Правильная стратегия - создавать массив снаружи и передавать его адрес внутрь функции. Ну или использовать глобальные переменные.
char* - это не сам массив, а только его адрес. Наличие у вас в руках конверта с адресом не гарантирует. что по этому адресу существует дом. И тут так же.
После окончания работы функции она возвращает адрес массива, но самого массива по этому адресу уже нет, так он был локальным и при выходе из функции затерся новыми данными. Именно поэтому внутри функции ваша строчка out печатается, а снаружи уже нет.
Правильная стратегия - создавать массив снаружи и передавать его адрес внутрь функции. Ну или использовать глобальные переменные.
Ага! Вот тут понятнее! Т.е. лучше сделать как в той же itoa, где результат работы внутри параметров, передаваемых в функцию! Ща попробую. Спасибо!
Может уже к snprintf_P перейдете?
Чего-то ни одного нормального сайта не нахожу, где бы она была описана. Только отсылки на нее.
Но по тому, что я нашел, https://simple-devices.ru/attachments/article/12/AVR_11-2010.pdf например здесь, не пойму как в ней слить целые числа в строку.
Ищите printf. Отличие производной snprintf_P только в том, что она учитывает длину выделенного буфера и формат-строка расположена в PROGMEM.
http://arduino.ru/forum/programmirovanie/progmem-tricks#comment-439233
Добавьте к этому скетчу описания переменных и setup c пустым loop'ом.
Убедитесь что не работает и выложите ПОЛНЫЙ (для особо непонятливых: П О Л Н Ы Й) скетч и напишите, что при это реально выводится.
Вот скетч. Он из 3 файлов.
Вот основной файл:
#include "set.h" void setup() { Serial.begin(115200); pinMode(OUTR, OUTPUT); digitalWrite(OUTR, HIGH); Wire.setClock(400000); Serial.begin(115200); watch.begin(); //watch.settime(tm[0],tm[1],tm[2],tm[3],tm[4],tm[5]); d.init(); d.displayOff(); d.setBrightness(TM1650_MIN_BRIGHT); d.setBrightnessGradually(TM1650_MIN_BRIGHT); d.displayOn(); VAR_ON_MIN = eeprom_read_word(0); VAR_OFF_MIN = eeprom_read_word(2); VAR_ON_HOUR = eeprom_read_word(4); VAR_OFF_HOUR = eeprom_read_word(6); } void loop() { char* tm; if (Serial.available() > 0) { // Если буфер серийного порта содержит несчитанные данные byte str = Serial.readString().toInt(); // Считываем строку if (str >=0 && str < 14) {display_mode = str;} if (str == 14) { watch.gettime(); pressUP(watch.minutes, watch.Hours, watch.day, watch.month, watch.year); } if (str == 15) { watch.gettime(); pressDOWN(watch.minutes, watch.Hours, watch.day, watch.month, watch.year); } } if (millis() - timer1 >= 1000){ Serial.println(display_mode); timer1 = millis(); Serial.println(watch.gettime("H:i:s")); // Вывод на дисплей switch (display_mode) { case 0: d.displayString(watch.gettime("Hi")); dot_state = !dot_state; d.setDot(1, dot_state); break; case 1: d.displayString(watch.gettime("dm")); d.setDot(1, true); break; case 2: d.displayString(watch.gettime("Y")); d.setDot(1, false); break; case 3: char out_on[5] = "1234"; char out_on0[2] = ""; char out_on1[2] = ""; char out_on2[2] = ""; char out_on3[2] = ""; itoa(VAR_ON_HOUR/10, out_on0, DEC); itoa(VAR_ON_HOUR%10, out_on1, DEC); itoa(VAR_ON_MIN/10, out_on2, DEC); itoa(VAR_ON_MIN%10, out_on3, DEC); out_on[0] = out_on0; out_on[1] = out_on1; out_on[2] = out_on2; out_on[3] = out_on3; d.displayString(out_on); Serial.println(out_on); d.setDot(1, true); break; case 4: //out_off[0] = VAR_OFF_HOUR/10; //out_off[1] = VAR_OFF_HOUR%10; //out_off[2] = VAR_OFF_MIN/10; //out_off[3] = VAR_OFF_MIN%10; //d.displayString(out_off); //d.setDot(1, true); break; case 5: tm = watch.gettime("Hi"); if (dot_state) d.displayString(watch.gettime("Hi")); else { tm[2] = ' '; tm[3] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, dot_state); break; case 6: tm = watch.gettime("Hi"); if (dot_state) d.displayString(watch.gettime("Hi")); else { tm[0] = ' '; tm[1] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, dot_state); break; case 7: tm = watch.gettime("dm"); if (dot_state) d.displayString(watch.gettime("dm")); else { tm[0] = ' '; tm[1] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, dot_state); break; case 8: tm = watch.gettime("dm"); if (dot_state) d.displayString(watch.gettime("dm")); else { tm[2] = ' '; tm[3] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, dot_state); break; case 9: tm = watch.gettime("Y"); if (dot_state) d.displayString(watch.gettime("Y")); else { tm[2] = ' '; tm[3] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, false); break; } } }Вот set.h:
И вот файлик с дополнительными функциями. Пока в формате ino. Потом переделаю его в .h как советовали выше:
void pressUP(int cmin, int chour, int cday, int cmon, int cyear) { switch (display_mode) { case 5: watch.settime(-1, (cmin == 59?cmin = 0:cmin++));break; case 6: watch.settime(-1, -1, (chour == 23?chour = 0:chour++));break; case 8: watch.settime(-1, -1, -1, -1, (cmon == 12?cmon = 1:cmon++));break; case 9: watch.settime(-1, -1, -1, -1, -1, (cyear == 99?cyear = 99:cyear++));break; case 7: int dinmon = getdinmon(cmon, cyear); watch.settime(-1, -1, -1, (cday == dinmon?cday = 1:cday++)); break; } } void pressDOWN(int cmin, int chour, int cday, int cmon, int cyear) { switch (display_mode) { case 5: watch.settime(-1, (cmin == 0?cmin = 59:cmin--));break; case 6: watch.settime(-1, -1, (chour == 0?chour = 23:chour--));break; case 8: watch.settime(-1, -1, -1, -1, (cmon == 1?cmon = 12:cmon--));break; case 9: watch.settime(-1, -1, -1, -1, -1, (cyear == 22?cyear = 22:cyear--));break; case 7: int dinmon = getdinmon(cmon, cyear); watch.settime(-1, -1, -1, (cday == 1?cday = dinmon:cday--));break; } } int getdinmon(int cmon, int cyear) { switch (cmon) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31; break; case 4: case 6: case 9: case 11: return 30; break; default: if (cyear % 4 == 0) return 29; else return 28; break; } }Пока отработку кнопок не сделал, делаю их нажатие через порт.
Вывод на экран имеет несколько режимов. В set.h они описаны. 2 режима (3 и 4) выводят настройки 2 будильников. По первому свет утром включается, по второму вечером выключается. Днем свет горит или нет - зависит от освещенности. Пока это еще не описывал в скетче.
Так вот вывод времени, даты, года в информацитонном режиме и в режиме установки значений уже сделал и там все работает. А вот вывод значений будильников на экран никак не получается. Со вчерашнего дня пытаюсь въехать в эти printf() и snprintf_P() никак не пойму как в этих вункциях собрать из времени 7 часов 0 минут вывод на экран строки '0700'. Для начала.
Итак вопрос в основном файле в режимах 3 и 4. Ну для начала 3. Остальные аналогично будут.
Кажется я понял как sprintf() работает:
int time = 0; int var = 55; char buff[50]; void setup() { Serial.begin(115200); } void loop() { sprintf(buff, "the value is %d seconds %d", time++, var); Serial.println(buff); }Выводится будет "the value is <тут увеличенная на 1 time> seconds 55". Собственно сам примет с просторов инета, я только var добавил, что бы понять, что можно туда несколько переменных вставлять. Тогда возникает вопрос. Никак не найду где взять правила написания формата. инет вообще еле двигается, через google вааще ничего открываться не хочет. Можно ли там в формате задать, чтобы вместо 7 писалось 07 и вместо 0 соответственно 00.
Все. Научился/разобрался. Возможно и не полностью, но для моего случая понял как делать.
case 3: char out_on[5]; sprintf(out_on, "%02d%02d", VAR_ON_HOUR, VAR_ON_MIN); d.displayString(out_on); Serial.println(out_on); d.setDot(1, true); break;По запросу "спецификаторы printf" вываливает любой поисковик 100500 вариантов.
Вот скетч. Он из 3 файлов.
У Вас был короткий скетч в #50. И Вы говорили, что он не работает. Вот Вы к нем присобачьте десяток строк чтобы он стал полным. Зачем нам три файла?
Это был кусок скетча. Сейчас там получилось #57.
Описание переменных в set.h. Ну в loop там только switch. Это вариант 3.
Я бы вот это:
заменил бы на такое:
enum display_mode : uint8_t { TIME, // показ времени DAY_MONTH, // день-месяц YAER, // год ... SET_MINUTES_OFF_EVENING // установка минут выключения вечером }Тогда в case все становится человеко-понятным (чтобы к той "простынке" постоянно не листать):
ЗЫ: ВЕРХНИЙ регистр не обязателен, это у меня привычка такая ))
Да я вчера про это тоже читал. Пока руки не дошли заменить. Сейчас все напишу, потом причесывать буду. А то как-то пока странно работает. То отрабатывает нажатие, а то нет. При чем ошибок никаких нет.
А вот еще я читал, что этот sprintf очень тяжелый для памяти. А у меня все это дело на Attiny работать будет. Может получиться, что не влезет?
Все. Индикацию написал. Теперь буду кнопки паять и обрабатывать и срабатывание реле. Ну срабатывание реле это уж мелочи.
Attiny какая?
Да и все равно - сначала нужно все сделать, настроить - потом смотреть куда влезает.
Если штучно для себя - то вообще морочиться не нужно, я считаю. Выгоды никакой.
Есть 25, 45 и 85. Но изначально планировал 25. Но у них контакты одинаковые, так что в какую влезет.
Теперь читай за itoa()
Ну про 25-ую с sprintf можно забыть, сам sprintf - это чуть больше 2К.
По этой же причине и 45-ая (с sprintf, wire, RTC и DS1307) - не пройдет.
Поэтому 85-ая - как вариант.
Полностью поддерживаю, даже предложил бы my_itoa(), исходники же itoa() - есть. Переписать под себя - не проблема.
char * my_itoa(int8_t val, char* buf, uint8_t dig) // dig кол-во цифр с выводимыми нулями слева. dig=0 - без нулей слева.
val - это byte, а не int16_t, signed, а может и unsigned - зависит от задачи, без radix, и с количеством выводимых слева '0'.
И все это около сотни байт памяти программ.
UPD: не около сотни, проверил - получилось - 56 байт
Народ, еще вопросик задам. Сейчас все закончил, причесываю.
Вот такой следующий код вызывает периодические зависания. Выявил это путем закомментирования этого куска и раскомментирования. Это собственно обработка нажатия кнопок. Нашел только одну библиотеку, где отрабатываются кнопки, но она какая-то навороченая и для 1650 там примеров нет. Может кто подскажет в чем я накосячил?
//Отлавливаем кнопки Wire.requestFrom(0x24, 1); //делаем запрос 1 байта из //микросхемы с адресом 0x24. //Адресные A0,A1,A2 на землю. btn_num = Wire.read(); //Этот кусок я нашел в инете. Далее путем экспериментов нашел код каждой кновки. bool ss = false, us = false, ds = false, sp = false, up = false, dp = false; if (btn_num == 68 && !ss) {b_timer = millis();ss = true;} if (btn_num == 4 && ss) {b_hold = millis() - b_timer;ss = false;sp = true;} if (btn_num == 84 && !us) {b_timer = millis();us = true;} if (btn_num == 20 && us) {b_hold = millis() - b_timer;us = false;up = true;} if (btn_num == 76 && !ds) {b_timer = millis();ds = true;} if (btn_num == 12 && ds) {b_hold = millis() - b_timer;ds = false;dp = true;} if (b_hold <= 500 && btn_num == 4 && sp) { if (display_mode < 5) display_mode == 4?display_mode = 0:display_mode++; else { switch (display_mode){ case 5: display_mode = 6; break; case 6: display_mode = 5; break; case 7: display_mode = 8; break; case 8: display_mode = 7; break; case 10: display_mode = 11; break; case 11: display_mode = 10; break; case 12: display_mode = 13; break; case 13: display_mode = 12; break; } } sp = false; } else if (b_hold > 500 && btn_num == 4 && sp) { switch (display_mode){ case 0: display_mode = 5; break; case 5: case 6: display_mode = 0; break; case 1: display_mode = 7; break; case 7: case 8: display_mode = 1; break; case 2: display_mode = 9; break; case 9: display_mode = 2; break; case 3: display_mode = 10; break; case 10: case 11: display_mode = 3; break; case 4: display_mode = 12; break; case 12: case 13: display_mode = 4; break; } sp = false; } watch.gettime(); if (btn_num == 20 && up) { //watch.gettime(); pressUP(watch.minutes, watch.Hours, watch.day, watch.month, watch.year); up = false; } if (btn_num == 12 && dp) { //watch.gettime(); pressDOWN(watch.minutes, watch.Hours, watch.day, watch.month, watch.year); dp = false; }Вроде все просто, но почему-то зависает устройство. Просто останавливает. Перестает менять изображение на экране и порт перестает выдавать данные.
Да, одна кнопка перебирает режимы, а две другие делают плюс-минус времени, будильникам при установке.
Зависает не сразу, минут через 20-30, может через час.
Dimych70, опять вы выкладываете какие-то куски кода без начала и конца? Кто в них будет копаться?
Если хотите, чтобы кто-то посмотрел ваш код - оформите проблемный кусок в виде отдельного скетча с сетап и луп, с декларацией всех переменных... Чтобы отвечающие могли запустить его у себя и протестировать проблему.
Ну вырезать и оформлять в отдельный код может не совсем правильно. Вот весь код:
#include "set.h" void setup() { Serial.begin(115200); pinMode(OUTR, OUTPUT); pinMode(OUTL, OUTPUT); pinMode(OSV, INPUT); digitalWrite(OUTR, LOW); digitalWrite(OUTL, HIGH); Wire.setClock(400000); Serial.begin(115200); watch.begin(); //watch.settime(tm[0],tm[1],tm[2],tm[3],tm[4],tm[5]); d.init(); d.displayOff(); d.setBrightness(TM1650_MIN_BRIGHT); d.setBrightnessGradually(TM1650_MIN_BRIGHT); d.displayOn(); } void loop() { const int ur_osv = 200; char* tm; byte btn_num = 0; /*if (Serial.available() > 0) { // Если буфер серийного порта содержит несчитанные данные byte str = Serial.readString().toInt(); // Считываем строку if (str >=0 && str < 14) {display_mode = str;} if (str == 14) { watch.gettime(); pressUP(watch.minutes, watch.Hours, watch.day, watch.month, watch.year); } if (str == 15) { watch.gettime(); pressDOWN(watch.minutes, watch.Hours, watch.day, watch.month, watch.year); } }*/ char * my_itoa(int8_t val, char* buf, uint8_t dig); //Отлавливаем кнопки Wire.requestFrom(0x24, 1); //делаем запрос 1 байта из //микросхемы с адресом 0x24. //Адресные A0,A1,A2 на землю. btn_num = Wire.read(); //Serial.print("btn_num "); //Serial.println(btn_num); if (btn_num == 68 && !ss) {b_timer = millis();ss = true;} if (btn_num == 4 && ss) {b_hold = millis() - b_timer;ss = false;sp = true;} if (btn_num == 84 && !us) {b_timer = millis();us = true;} if (btn_num == 20 && us) {b_hold = millis() - b_timer;us = false;up = true;} if (btn_num == 76 && !ds) {b_timer = millis();ds = true;} if (btn_num == 12 && ds) {b_hold = millis() - b_timer;ds = false;dp = true;} if (b_hold <= 500 && btn_num == 4 && sp) { if (display_mode < 5) display_mode == 4?display_mode = 0:display_mode++; else { switch (display_mode){ case 5: display_mode = 6; break; case 6: display_mode = 5; break; case 7: display_mode = 8; break; case 8: display_mode = 7; break; case 10: display_mode = 11; break; case 11: display_mode = 10; break; case 12: display_mode = 13; break; case 13: display_mode = 12; break; } } sp = false; } else if (b_hold > 500 && btn_num == 4 && sp) { switch (display_mode){ case 0: display_mode = 5; break; case 5: case 6: display_mode = 0; break; case 1: display_mode = 7; break; case 7: case 8: display_mode = 1; break; case 2: display_mode = 9; break; case 9: display_mode = 2; break; case 3: display_mode = 10; break; case 10: case 11: display_mode = 3; break; case 4: display_mode = 12; break; case 12: case 13: display_mode = 4; break; } sp = false; } watch.gettime(); if (btn_num == 20 && up) { //watch.gettime(); pressUP(watch.minutes, watch.Hours, watch.day, watch.month, watch.year); up = false; } if (btn_num == 12 && dp) { //watch.gettime(); pressDOWN(watch.minutes, watch.Hours, watch.day, watch.month, watch.year); dp = false; } if (millis() - timer1 >= 1000){ //Serial.println(display_mode); Serial.println(analogRead(OSV)); Serial.println(light); timer1 = millis(); Serial.println(watch.gettime("H:i:s")); // Вывод на дисплей switch (display_mode) { case 0: d.displayString(watch.gettime("Hi")); dot_state = !dot_state; d.setDot(1, dot_state); break; case 1: d.displayString(watch.gettime("dm")); d.setDot(1, true); break; case 2: d.displayString(watch.gettime("Y")); d.setDot(1, false); break; case 3: char out_on[5]; VAR_ON_MIN = eeprom_read_word(0); VAR_ON_HOUR = eeprom_read_word(4); sprintf(out_on, "%02d%02d", VAR_ON_HOUR, VAR_ON_MIN); d.displayString(out_on); d.setDot(1, true); break; case 4: VAR_OFF_MIN = eeprom_read_word(2); VAR_OFF_HOUR = eeprom_read_word(6); char out_off[5]; sprintf(out_off, "%02d%02d", VAR_OFF_HOUR, VAR_OFF_MIN); d.displayString(out_off); d.setDot(1, true); break; case 5: tm = watch.gettime("Hi"); if (dot_state) d.displayString(watch.gettime("Hi")); else { tm[2] = ' '; tm[3] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, dot_state); break; case 6: tm = watch.gettime("Hi"); if (dot_state) d.displayString(watch.gettime("Hi")); else { tm[0] = ' '; tm[1] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, dot_state); break; case 7: tm = watch.gettime("dm"); if (dot_state) d.displayString(watch.gettime("dm")); else { tm[0] = ' '; tm[1] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, dot_state); break; case 8: tm = watch.gettime("dm"); if (dot_state) d.displayString(watch.gettime("dm")); else { tm[2] = ' '; tm[3] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, dot_state); break; case 9: tm = watch.gettime("Y"); if (dot_state) d.displayString(watch.gettime("Y")); else { tm[2] = ' '; tm[3] = ' '; d.displayString(tm); } dot_state = !dot_state; d.setDot(1, false); break; case 10: char out_on_cm1[5]; char out_on_cm2[5]; VAR_ON_MIN = eeprom_read_word(0); VAR_ON_HOUR = eeprom_read_word(4); sprintf(out_on_cm1, "%02d%02d", VAR_ON_HOUR, VAR_ON_MIN); sprintf(out_on_cm2, "%02d ", VAR_ON_HOUR); dot_state = !dot_state; d.setDot(1, true); if (dot_state) d.displayString(out_on_cm1); else d.displayString(out_on_cm2); break; case 11: char out_on_ch1[5]; char out_on_ch2[5]; VAR_ON_MIN = eeprom_read_word(0); VAR_ON_HOUR = eeprom_read_word(4); sprintf(out_on_ch1, "%02d%02d", VAR_ON_HOUR, VAR_ON_MIN); sprintf(out_on_ch2, " %02d", VAR_ON_MIN); dot_state = !dot_state; d.setDot(1, true); if (dot_state) d.displayString(out_on_ch1); else d.displayString(out_on_ch2); break; case 12: char out_off_cm1[5]; char out_off_cm2[5]; VAR_OFF_MIN = eeprom_read_word(2); VAR_OFF_HOUR = eeprom_read_word(6); sprintf(out_off_cm1, "%02d%02d", VAR_OFF_HOUR, VAR_OFF_MIN); sprintf(out_off_cm2, "%02d ", VAR_OFF_HOUR); dot_state = !dot_state; d.setDot(1, true); if (dot_state) d.displayString(out_off_cm1); else d.displayString(out_off_cm2); break; case 13: char out_off_ch1[5]; char out_off_ch2[5]; VAR_OFF_MIN = eeprom_read_word(2); VAR_OFF_HOUR = eeprom_read_word(6); sprintf(out_off_ch1, "%02d%02d", VAR_OFF_HOUR, VAR_OFF_MIN); sprintf(out_off_ch2, " %02d", VAR_OFF_MIN); dot_state = !dot_state; d.setDot(1, true); if (dot_state) d.displayString(out_off_ch1); else d.displayString(out_off_ch2); break; } //Работа будильников if (watch.Hours == VAR_ON_HOUR && watch.minutes == VAR_ON_MIN && !light){ light = 1; digitalWrite(OUTR, HIGH); digitalWrite(OUTL, LOW); } if (watch.Hours == VAR_OFF_HOUR && watch.minutes == VAR_OFF_MIN && light){ light = 0; digitalWrite(OUTR, LOW); digitalWrite(OUTL, HIGH); } if (watch.Hours > VAR_ON_HOUR && watch.minutes > VAR_ON_MIN && light && analogRead(OSV) > ur_osv){ light = 0; digitalWrite(OUTR, LOW); digitalWrite(OUTL, HIGH); } if (watch.Hours > VAR_ON_HOUR && watch.minutes > VAR_ON_MIN && !light && analogRead(OSV) <= ur_osv){ light = 1; digitalWrite(OUTR, HIGH); digitalWrite(OUTL, LOW); } } }Это основной файл.
void pressUP(int cmin, int chour, int cday, int cmon, int cyear) { switch (display_mode) { case 5: cmin == 59?cmin = 0:cmin++;watch.settime(-1, cmin);break; case 6: chour == 23?chour = 0:chour++;watch.settime(-1, -1, chour);break; case 8: cmon == 12?cmon = 1:cmon++;watch.settime(-1, -1, -1, -1, cmon);break; case 9: cyear == 99?cyear = 99:cyear++;watch.settime(-1, -1, -1, -1, -1, cyear);break; case 10: VAR_ON_MIN == 59?VAR_ON_MIN = 0:VAR_ON_MIN++;eeprom_update_word(0, VAR_ON_MIN);break; case 11: VAR_ON_HOUR == 23?VAR_ON_HOUR = 0:VAR_ON_HOUR++;eeprom_update_word(4, VAR_ON_HOUR);break; case 12: VAR_OFF_MIN == 59?VAR_OFF_MIN = 0:VAR_OFF_MIN++;eeprom_update_word(2, VAR_OFF_MIN);break; case 13: VAR_OFF_HOUR == 23?VAR_OFF_HOUR = 0:VAR_OFF_HOUR++;eeprom_update_word(6, VAR_OFF_HOUR);break; case 7: int dinmon = getdinmon(cmon, cyear); cday == dinmon?cday = 1:cday++;watch.settime(-1, -1, -1, cday); break; } } void pressDOWN(int cmin, int chour, int cday, int cmon, int cyear) { switch (display_mode) { case 5: cmin == 0?cmin = 59:cmin--;watch.settime(-1, cmin);break; case 6: chour == 0?chour = 23:chour--;watch.settime(-1, -1, chour);break; case 8: cmon == 1?cmon = 12:cmon--;watch.settime(-1, -1, -1, -1, cmon);break; case 9: cyear == 22?cyear = 22:cyear--;watch.settime(-1, -1, -1, -1, -1, cyear);break; case 10: VAR_ON_MIN == 0?VAR_ON_MIN = 59:VAR_ON_MIN--;eeprom_update_word(0, VAR_ON_MIN);break; case 11: VAR_ON_HOUR == 0?VAR_ON_HOUR = 23:VAR_ON_HOUR--;eeprom_update_word(4, VAR_ON_HOUR);break; case 12: VAR_OFF_MIN == 0?VAR_OFF_MIN = 59:VAR_OFF_MIN--;eeprom_update_word(2, VAR_OFF_MIN);break; case 13: VAR_OFF_HOUR == 0?VAR_OFF_HOUR = 23:VAR_OFF_HOUR--;eeprom_update_word(6, VAR_OFF_HOUR);break; case 7: int dinmon = getdinmon(cmon, cyear); cday == 1?cday = dinmon:cday--;watch.settime(-1, -1, -1, cday);break; } } int getdinmon(int cmon, int cyear) { switch (cmon) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31; break; case 4: case 6: case 9: case 11: return 30; break; default: if (cyear % 4 == 0) return 29; else return 28; break; } }Здесь функции.
И последний - set.h
Код на отлов нажатия кнопок с 38 по 95 строки. Если их закомментировать, то все работает.
Я вообще почему кусок только выложил. Там идет обращение к wire. Я это обращение нашел раньше в инете. Там описывал товарищ как он сам отлавливал кнопки. Так вот может как-то что-то в этом wire закрыть надо или еще что сделать. Т.к. остальное то все обычные IF и элементарные математические действия.
Может надо не закрыть, а обозвать как-то отдельно это Wire. Я просто пока не очень въехал как работает wire. Там на более низком уровне я так понял работа идет.
А зачем для кнопок Wire? К чему они подключены?
А зачем для кнопок Wire? К чему они подключены?
Они подключены к TM1650. Не к Ардуино.
ИМХО код очень тяжело читается, сложно что то сказать по этому поводу.
Я посмотрел, при чтении клавиатуры вы обращаетесь к адресу 0x24. в даташите же указан адрес 0x49.
Протокол TM1650 несколько отличается от стандартного I2C, Slave address в I2C - 7 bit +R/W bit
0x24 << 1 == 0x48; 0x48 + 1(read) == 0x49 это конечно понятно, но как то это неправильно, а он же у вас еще и с DS3107 на одной шине сидит?
Посмотрите TM16xx.h там и индикатор и кнопочки есть.
По кнопкам- попробуйте анализировать только следующие значения получаемые от TM1650: 0x44, 0x4С, 0x54 - кнопки 1, 2, 3 соответственно. (это в десятичной 68, 76,84, как у вас), а остальные значения просто игнорировать. То есть не равно 0x44, 0x4С, 0x54 - обнуляем принятое значение. (некоторые значения возникают на переходах от нажатия к отпусканию и при включении питания, думаю они вам не нужны)
Просто если появилось значение 0x44 - нажали "кнопку 1", 0 - отпустили.
Появилось значение 0x4С- нажали "кнопку 2", 0 - отпустили кнопку.
Будет как мне кажется проще и понятнее.
Посмотрел исходник iarduino_RTC.h, что не понравилось: при каждом вызове функции, возвращающей указатель на строку со временем, удаляется ранее выделенная динамическая память, и создается заново под необходимый размер буфера (через malloc()). То есть делается это на каждом вызове функции, и нигде не проверяется успешность выделения памяти. Просто прописываются данные по указателю. А вы потом еще и пробелы по этому указателю прописываете из loop(). И все это не быстро, и затратно, там и String используется, соответственно подтянется. Я бы отказался от этой библиотеки, как то только для демонстрации она и подходит, да и с переполнением millis() там решение с "костылями".
ИМХО код очень тяжело читается, сложно что то сказать по этому поводу.
Чужой код всегда тяжело читается. Это я заметил еще лет 20 назад когда на Delphi программировал. Но я вроде везде комментарии ставил что делается, да и собственно сам код достаточно короткий.
Я посмотрел, при чтении клавиатуры вы обращаетесь к адресу 0x24. в даташите же указан адрес 0x49.
В самом начале я сканировал i2c. Уже не вспомню, но там было 8 адресов. Вроде с 24 по 27 и с 34 по 37. 49 точно не было. Но попробую сейчас.
Посмотрите TM16xx.h там и индикатор и кнопочки есть.
Смотрел. Даже пытался в начале ее использовать. Но в примерах к сожалению 1650 нет. Пытался делать по аналогии с другими. Индикацию выдать получилось, но вот при подключении библиотеки с кнопками и попытках обработать их сразу начинался трэш. Все висло сразу намертво.
По кнопкам- попробуйте анализировать только следующие значения получаемые от TM1650: 0x44, 0x4С, 0x54 - кнопки 1, 2, 3 соответственно. (это в десятичной 68, 76,84, как у вас), а остальные значения просто игнорировать. То есть не равно 0x44, 0x4С, 0x54 - обнуляем принятое значение. (некоторые значения возникают на переходах от нажатия к отпусканию и при включении питания, думаю они вам не нужны)
Просто если появилось значение 0x44 - нажали "кнопку 1", 0 - отпустили.
Появилось значение 0x4С- нажали "кнопку 2", 0 - отпустили кнопку.
Будет как мне кажется проще и понятнее.
Будет проще. Но мне нужно еще отработать длинное нажатие кнопки установки режимов. А тут без отлова кода, который по отпускании кнопки появляется никак.
Посмотрел исходник iarduino_RTC.h, что не понравилось: при каждом вызове функции, возвращающей указатель на строку со временем, удаляется ранее выделенная динамическая память, и создается заново под необходимый размер буфера (через malloc()). То есть делается это на каждом вызове функции, и нигде не проверяется успешность выделения памяти. Просто прописываются данные по указателю. А вы потом еще и пробелы по этому указателю прописываете из loop(). И все это не быстро, и затратно, там и String используется, соответственно подтянется. Я бы отказался от этой библиотеки, как то только для демонстрации она и подходит, да и с переполнением millis() там решение с "костылями".
С RTC вообще я бился еще год назад. iarduino_RTC.h работала лучше всех. Но все равно не всегда. Я даже как в начале этой темы сам надергал из библиотеки функции и год их использовал успешно. Но с 1650 наверное не захотела работать. Поэтому я и взял iarduino_RTC.h.
А к чему такие мучения? Тинька, тм-ка? На обычной ардуинке часы делаются на коленке за полчаса и работают без запинки. Или цель - побольше страданий? ))
По кнопкам- попробуйте анализировать только следующие значения получаемые от TM1650: 0x44, 0x4С, 0x54 - кнопки 1, 2, 3 соответственно. (это в десятичной 68, 76,84, как у вас), а остальные значения просто игнорировать. То есть не равно 0x44, 0x4С, 0x54 - обнуляем принятое значение. (некоторые значения возникают на переходах от нажатия к отпусканию и при включении питания, думаю они вам не нужны)
Просто если появилось значение 0x44 - нажали "кнопку 1", 0 - отпустили.
Появилось значение 0x4С- нажали "кнопку 2", 0 - отпустили кнопку.
Будет как мне кажется проще и понятнее.
Будет проще. Но мне нужно еще отработать длинное нажатие кнопки установки режимов. А тут без отлова кода, который по отпускании кнопки появляется никак.
Я TM1650 не тестировал, с описанием там полня хрень, видел только оч. короткое и на китайском, перевел гуглом. По информации из интернет понял, что если нажали кнопку и держим, и если много раз считывать данные из ТМ, каждый раз будет считываться код нажатой кнопки, пока ее не отпустят.
Не так?
Да так. Нажимаешь, код 68, отпускаешь, код (-64) 4, другая - 84-20 и третья 76-12. Можно конечно ловить просто смену скажем 68 на любой другой, согласен. И я пробовал, но так как я сделал мне показалось проще. (просто я думаю, что это сильно не изменит ситуацию).
А на счет адреса. Я конечно попробую. Но если бы я задал неправильный адрес считывания, разве что-нибудь считалось бы?
И еще я не совсем понял, почему когда я включал сканер, выдавалось 8 адресов i2c? У меня 1650, она рассчитана вроде на 4=сегментный индикатор. Откуда там 8 адресов? Там кнопки физически вешаются общей шиной на разряд.
Попробовал 49 адрес. Не реагирует. Все время 255 выдает.
Зато на 25 скажем тоже работает. Те же коды выдает.
А на счет адреса. Я конечно попробую. Но если бы я задал неправильный адрес считывания, разве что-нибудь считалось бы?
И еще я не совсем понял, почему когда я включал сканер, выдавалось 8 адресов i2c? У меня 1650, она рассчитана вроде на 4=сегментный индикатор. Откуда там 8 адресов? Там кнопки физически вешаются общей шиной на разряд.
Наверное и не поможет.
Тут вопрос в другом, TM1650 работает не по настоящему I2C, а по своему протоколу, похожему на I2C.
Поэтому адреса у TM1650 и нет. В принципе нет адреса, по даташиту.
Есть команды. На них он отвечает, а сканер воспринимает их как адреса.
Главное чтобы TM1650 и DS1307 друг другу не мешали на одной шине.
Главное чтобы TM1650 и DS1307 друг другу не мешали на одной шине.
Это понятно. У DS1307 вроде как 0х68 адрес. А сканер i2c находил для 1650 другие адреса.
Ну и без вот этих команд в скетче
Все работает и не виснет
Это понятно. У DS1307 вроде как 0х68 адрес. А сканер i2c находил для 1650 другие адреса.
Ну и без вот этих команд в скетче
Все работает и не виснет
Для wire c TM1650 адрес должн быть 0x24, не 0x25, если я не ошибаюсь:
Я бы попробовал для начала снизить скорость на I2C до стандартных 100 000 Hz
Для wire c TM1650 адрес должн быть 0x24, не 0x25, если я не ошибаюсь:
Я бы попробовал для начала снизить скорость на I2C до стандартных 100 000 Hz
Я 25 поставил для теста, что бы посмотреть зависнет или нет. Пока не виснет вот уже пару часов. Но нажатие кнопок отрабатывается.
Скорость попробую снизить до стандартных 100000.
Для wire c TM1650 адрес должн быть 0x24, не 0x25, если я не ошибаюсь:
Я бы попробовал для начала снизить скорость на I2C до стандартных 100 000 Hz
Я 25 поставил для теста, что бы посмотреть зависнет или нет. Пока не виснет вот уже пару часов. Но нажатие кнопок отрабатывается.
Скорость попробую снизить до стандартных 100000.
Посмотрел, по даташиту там два последних бита помечены как "Х" - то есть не имеют значения.
Поэтому, что 0x24, что 0x25, что 0x26, что 0x27 - будет работать одинаково.
Красным выделена команда целиком (входят: 0x24, 0x25, 0x26, 0x27 - выделено зеленым, последние два бита - не принципиальны)
Так, с использованием wire передаются байты адреса ((B6,B5,B4,B3,B2,B1,B0)<<1) и 1 бит (write:0/read:1 - он должен быть 1, - чтение).
То есть зеленым, выделено то, что должно быть адресом на I2C (wire), синим - чтение.
Снизил скорость до стандартных 100000, прописал 24 порт. Все равно виснет. Не сразу, через 20-30 минут. Блин вот никогда раньше с таким не сталкивался. Не компилилось, но если скомпилилось и запустилось, пахало без проблем.
Сейчас пытаюсь еще с sprintf разобраться. my_itoa имеется ввиду не готовая уже стандартная функция, а самому какой-то обработчик написать или найти надо?
Блин, вот наверно я попоясдеревянный! Пытаюсь найти исходники itoa и нихрена! Ведь понимаю, что она в какой-то библиотеке по умолчанию. Не находится. В инете тоже куча примеров, но исходника не нахожу!
По поводу зависания. Как я раньше писал, всю ночь работа, при отключенной обработке кнопок. Вообще весь кусок удален был. Сейчас начал экспериментировать. Вставляем
и сразу виснет через стандартные 10-20-30 минут.
Блин, вот наверно я попоясдеревянный! Пытаюсь найти исходники itoa и нихрена!
Зачем тебе исходники? Для чего?
Конвертирование целочисленных данных в строку через функции itoa, ltoa, ultoa. Функции простые, позволяют конвертировать числа целых форматов в текстовую строку. itoa (int data, char* string, int radix); // преобразование int ltoa (long data, char* string, int radix); // преобразование long ultoa (unsigned long data, char* string, int radix); // преобразование unsigned long data – это конвертируемая переменная; char* string – указатель на строку (имя массива); radix – система исчисления результата в строке: 10 для DEC; 8 для OCT; 16 для HEX; 2 для BIN. Например, конвертирование переменой x типа int в строку myStr1 можно сделать так. itoa(x, myStr1, 10); // в десятичном виде itoa(x, myStr1, 8); // в восьмеричном виде itoa(x, myStr1, 16); // в шестнадцатеричном виде itoa(x, myStr1, 2); // в двоичном видеЧто еще ты там увидеть хочешь?
http://mypractic.ru/urok-30-tekstovye-stroki-v-arduino-konvertirovanie-d...
SergeiL предложил взять исходник и переделать его под себя, чтобы не использовать sprintf.
В принципе я уже накидал. Громоздко получается конечно, но без тяжелой sprintf.
Ну и этот out на экран дисплея выводить.
Если нужно конвертировать только число 0..59 в два символа, то можно и без itoa и strcat обойтись, на нескольких ифах все реализовав.
Я все-таки не разбираюсь в char переменных.
Вот написал функцию
char* convert_time_to_char(byte val1, byte val2, byte prop){ char x1[2], x2[2],y1[2],y2[2], out[5]; itoa(val1/10, x1, 10); itoa(val1%10, x2, 10); itoa(val2/10, y1, 10); itoa(val2%10, y2, 10); switch (prop){ case 0: strcat(out, x1); strcat(out, x2); strcat(out, y1); strcat(out, y2); break; case 1: strcat(out, x1); strcat(out, x2); strcat(out, y1); strcat(out, y2); out[0] = ' '; out[1] = ' '; break; case 2: strcat(out, x1); strcat(out, x2); strcat(out, y1); strcat(out, y2); out[2] = ' '; out[3] = ' '; break; } Serial.println(out); return out; }Ну типа передаем 2 числа, третья переменная, это для мигания, вместо какого параметра выводить пробелы. В самой функции все прекрасно работает. Как положено выдает "0700".
Но вот когда запрашивается из тела программы результат функции, в него влезают какие-то символы.
"⸮0700" это выдается в теле программы, там еще в экране порта спереди два квадратика есть, которые через буфер не передаются. Вот здесь:
case 3: char* out_on; VAR_ON_MIN = eeprom_read_word(0); VAR_ON_HOUR = eeprom_read_word(4); //sprintf(out_on, "%02d%02d", VAR_ON_HOUR, VAR_ON_MIN); out_on = convert_time_to_char(VAR_ON_HOUR, VAR_ON_MIN, 0); Serial.println(out_on); d.displayString(out_on); //d.displayString(convert_time_to_char(VAR_ON_HOUR, VAR_ON_MIN, 0)); d.setDot(1, true); break;И в итоге на табло вообще ничего не передается!
Что у Вас написано в строке №15 в #68?
if (display_mode < 5) display_mode == 4?display_mode = 0:display_mode++; else { ...Можете пояснить?
Что у Вас написано в строке №15 в #68?
if (display_mode < 5) display_mode == 4?display_mode = 0:display_mode++; else { ...Можете пояснить?
Это у него аналог такого
if (++display_mode >= 5) display_mode =0;
))
Я все-таки не разбираюсь в char переменных.
только в чар-переменных? - вы себе льстите...
потому что вы пытаетесь передать наружу функции локальную переменную. Вы что-нибудь про "область видимости" почитайте...
Да и вообще какой-нить учебник по си было бы неплохо почитать на ночь недельки две, прежде чем продолжать. А то жалко на вас смотреть - буквально на каждой строке спотыкаетесь.
Вот тут хотелось бы подробнее. Да наверное я и в других вещах не разбираюсь. Но я думал, что если я обращаюсь к функции и пытаюсь получить ее результат, я не должен объявлять еще и в теле программы переменную, которую использую в функции.
char convert_time_to_char(byte val1, byte val2, byte prop){ char x1[2], x2[2],y1[2],y2[2], out[5]; itoa(val1/10, x1, 10); itoa(val1%10, x2, 10); itoa(val2/10, y1, 10); itoa(val2%10, y2, 10); strcat(out, x1); strcat(out, x2); strcat(out, y1); strcat(out, y2); switch (prop){ case 1: out[0] = ' '; out[1] = ' '; break; case 2: out[2] = ' '; out[3] = ' '; break; } Serial.println(out); return out; }Переменную out я задал в функции и через нее возвращаю результат работы функции. В теле программы функция передает свой результат другой переменной, которая у меня объявлена в теле программы. Да и если бы надо было объвлять эту переменную еще и в теле программы, была бы ошибка во время компиляции! Так всегда было. Если это не так, то я что-то совсем запутался!
Что у Вас написано в строке №15 в #68?
if (display_mode < 5) display_mode == 4?display_mode = 0:display_mode++; else { ...Можете пояснить?
Уже давно такую конструкцию нашел. Работает, и существенно сокращает количество символов в скетче.
По хорошему надо было бы писать:
if (display_mode < 5) { if (display_mode == 4) display_mode = 0; else display_mode++; } else {..........А тут все значительно короче.
if (display_mode < 5) display_mode == 4?display_mode = 0:display_mode++; else {во-первых, в коде #88 у вас возвращаемое значение функции было типа char*, а в последнем уже char. Надеюсь. вы понимаете что это огромная разница. Если нет - то сначала разберитесь.
Но в любом случае ни char*, ни char не соответвуют типу переменной out[5], поскольку это массив из 5 чаров...
существенно сокращает количество символов в скетче
с вас за символы в скетче берут оплату посимвольно? - а если нет, к чему это? - если что, размер текста скетча к размеру бинарного кода имеет очень относительное отношение
А что, если функция объявлена как char*, то переменную, которую она внутри использует и передает наружу надо объвлять еще и в теле основной программы?
А если функция char (без звездочки), то можно не объявлять?
Собственно я и говорю, что никак в char не въеду. Я знаю, что * это ссылка. Но вот что при этом внутреннюю переменную функции надо и вне функции объявлять - такого я не знал.
существенно сокращает количество символов в скетче
с вас за символы в скетче берут оплату посимвольно? - а если нет, к чему это? - если что, размер текста скетча к размеру бинарного кода имеет очень относительное отношение
По-моему так просто компактнее и читабельнее. Мне самому сначала было неудобно читать, но как привык очень удобно.
Но по сути ведь написано правильно. Давайте пусть так и останется, если это не влияет на работу.
А что, если функция объявлена как char*, то переменную, которую она внутри использует и передает наружу надо объвлять еще и в теле основной программы?
char* - это не сам массив, а только его адрес. Наличие у вас в руках конверта с адресом не гарантирует. что по этому адресу существует дом. И тут так же.
После окончания работы функции она возвращает адрес массива, но самого массива по этому адресу уже нет, так он был локальным и при выходе из функции затерся новыми данными. Именно поэтому внутри функции ваша строчка out печатается, а снаружи уже нет.
Правильная стратегия - создавать массив снаружи и передавать его адрес внутрь функции. Ну или использовать глобальные переменные.
Что у Вас написано в строке №15 в #68?
if (display_mode < 5) display_mode == 4?display_mode = 0:display_mode++; else { ...Можете пояснить?
Уже давно такую конструкцию нашел. Работает, и существенно сокращает количество символов в скетче.
По хорошему надо было бы писать:
if (display_mode < 5) { if (display_mode == 4) display_mode = 0; else display_mode++; } else {..........А тут все значительно короче.
if (display_mode < 5) display_mode == 4?display_mode = 0:display_mode++; else {По хорошему я привел в #90
char* - это не сам массив, а только его адрес. Наличие у вас в руках конверта с адресом не гарантирует. что по этому адресу существует дом. И тут так же.
После окончания работы функции она возвращает адрес массива, но самого массива по этому адресу уже нет, так он был локальным и при выходе из функции затерся новыми данными. Именно поэтому внутри функции ваша строчка out печатается, а снаружи уже нет.
Правильная стратегия - создавать массив снаружи и передавать его адрес внутрь функции. Ну или использовать глобальные переменные.
Ага! Вот тут понятнее! Т.е. лучше сделать как в той же itoa, где результат работы внутри параметров, передаваемых в функцию! Ща попробую. Спасибо!