|
Редактирование строки на ЖКИ., Ввод времени в RTC |
|
|
|
Dec 14 2007, 16:53
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Простая, казалось бы на первый взгляд задача... Но вот уже вторые сутки ломаю голову. Не могу придумать как это лучше сделать. Хочу, как в мобильном телефоне настраивать время. Вот мой кусок кода: Код void EditTime_Func(void) { unsigned char temp; rtc_get_time(0x99); //значение времени в yr, mn, dt, dy, hr, min, sec ClearScreen(); // display clear GotoXY(0,0); sprintf_P(lcd_buf,"%d/%d/%d %d %02d:%02d:%02d", yr, mn, dt, dy, hr, min, sec); PutStr(lcd_buf); w=CharWidth(*plcd_buf)-2; start_Timer(TIMER_0, 500, blink, TIMER_CYCLE);
for(;;) { temp=key_get(); switch(temp) //key_code.scan { case LEFT: //k_esc {......} break; case RIGHT: //k_enter {......} break; case DOWN: //k_left {......} break; case UP: //k_right {......} break; default: // 0...9 {......} break; } if(key_code.scan == LEFT) //выход из функции { stop_Timer(TIMER_0); break; } } }
//------------------------------------------------------------------------------ void blink(void) { InvertRect(x, y, w, 9); } Идея такая. Вывожу на экран значение времени в виде строки: "2007/12/24 1 12:32:00" Ф-ция start_Timer() запускает каждые 0,5 сек ф-цию blink(), которая в свою очередь инвертирует заданный прямоугольник. Т.е. должно мигать текущее знакоместо. Кнопками UP, DOWN выбираю следующее, предыдущее знакоместо. Ввод числа осуществляется с клавиатуры 4х4. Это все у меня работает. Проблема еще в том, что символы имеют переменную ширину. Поэтому после каждого ввода символа необходимо ,наверное, перерисовывать всю строку. После нажатия ENTER новые значения переменных необходимо записать в RTC. Как сделать ввод чисел только в заданные места, т.е. пропускать '/' , ' ' , ':' ? Меня не покидает чувство, что изобретаю велосипед.
|
|
|
|
|
Dec 14 2007, 17:27
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Я реализовывал такую задачу. Правда на ассемблере и на х51 процессоре. Я поступал следующим образом (правда у меня было много меню с различными зонами, возможно несколькими, на экране) И я строил меню, как структуру, где указывал тип зоны ввода, левый край и размер, а также максимальное и минимальное возможное значение. В самом меню использую псевдо-символы управления курсором по принципу терминала. Это даёт возможность грамотного управления и редактирования. Приведу кусок хидера своей библиотеки. Код // Управляющие символы дисплея LCD
#define CR_BS 8 // BackSpace /b #define CR_TAB 9 // TAB /t #define CR_LF 0xa // LF /a #define CR_NEW 0xc // CLR /f #define CR_ENTER 0xd // ENTER /r #define CR_LEFT 0x11 // LEFT /x11 #define CR_RIGHT 0x12 // RIGHT /x12 #define CR_UP 0x13 // UP /x13 #define CR_DOWN 0x14 // DOWN /x14 #define CR_HOME 0x15 // HOME /x15 #define CR_BEEP 0x17 // BEEP /x17 #define CR_CURHIDE 0x18 // HIDE /x18 #define CR_CURSHOW 0x19 // SHOW /x19 #define CR_CURDIRECT 0x1b // Direct Cursor /x1b
// Прототипы функций
void LCDInit(void); // Инициализация LCD дисплея. Вызывается из модуля инициализации. void GotoXY(uint8_t x,uint8_t y); // Установит курсор в позицию X,Y. int8_t cr_left(void); // Курсор влево. int8_t cr_right(void); // Курсор вправо. uint8_t cr_up(void); // Курсор вверх uint8_t cr_down(void); // Курсор вниз. void cr_home(void); // в начало строки int16_t putchar(int16_t c_in); // Вывести символ в кодировке Win. с управляющими символами void spliter_init(void); // Инициализировать сплитер. void spliter(uint8_t pos, uint8_t maxpos); // Отобразить сплитер
|
|
|
|
|
Dec 14 2007, 20:32
|

Просто Che
    
Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881

|
Цитата(rezident @ Dec 14 2007, 20:16)  Разделите логически в программе ввод, вывод и редактирование, а потом совместите их. Т.е. с помощью клавиатуры вы будете редактировать какую-то переменную время/дата. И эту же переменную выводить на экран после каждой модификации полностью. Не нужно будет ломать голову как совместить видимую позицию курсора и редактируемый символ. rezident дело говорит. "В одну корзину класть не можно на яйца сверху серп и молот" Всегда делаю различные процедуры для индикации и обработки клавиатуры. Логически эти процедуры связаны только через переменную MenuNumber, которая указывает на номер текущего меню (на тип информации, выводимой на дисплей). Обработчик клавиатуры верхнего уровня ServiceKeys в зависимости от значения MenuNumber и нажатых кнопок производит нужные действия, напр.: -выбор параметра для редактирования - перемещает мигающее поле на дисплее; -вход в режим изменения параметра - копирует выделенную переменную в буфер редактора; -изменение параметра - меняет переменную в буфере редактора; -выход из режима изменения параметра - сохраняет переменную; Процедура вывода на дисплей RedrawLCD вызывается раз в 1 сек и после обработки клавиатуры. Эта процедура по значению MenuNumber выводит на дисплей нужные параметры, беря их или напрямую, или из буфера редактора. Эта процедура и занимается преобразованием бинарных переменных в ASCII коды.
|
|
|
|
|
Dec 15 2007, 16:15
|

Просто Che
    
Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881

|
Цитата(alux @ Dec 15 2007, 11:57)  Можете привести для наглядности свой код? Какой то конкретный код привести вряд ли смогу, это все ненаглядно и громоздко. Еще некоторые соображения по поводу вашего примера кода. Если вы не применяете RTOS и не запускаете эту процедуру как отдельную задачу, то у вас во время редактирования часов прибор ничего больше не делает (только прерывания). Я применяю несколько другой метод организации обработки клавиатуры и дисплея, который без RTOS обеспечивает выполнение алгоритмов кусками, отдельными состояниями конечного автомата (по сути это работа по флагам). Общая структура следующая: - Один таймер выделяется под системный, который обеспечивает регулярное прерывание, в котором выставляется флаг f10ms_Past. - Этот флаг обрабатывается в основном цикле main, где формируются места, которые выполняются через равные промежутки: 10 мс, 1 сек, другие необходимые. Код //--------------------------------------------------------------// // 10 milliseconds, One and Ten seconds Procedures // //--------------------------------------------------------------// if (f10ms_Past) // 10 ms past { f10ms_Past = 0; ReadKeys(); RunADC(); Task1_Service(); Task2_Service(); ................... if (++TenMilliSecCnt >= 100) // 1 second past { TenMilliSecCnt = 0; poGreenLED ^= 1; // Toggle green LED RedrawLCD(); // Redraw LCD module .................... if (++SecCnt >= 10) // 10 seconds past { SecCnt = 0; ................. } } } //--------------------------------------------------------------// // Keys service Conditions Determination // //--------------------------------------------------------------// if (fShortKey || fLongKey) { ServiceKey(); RedrawLCD(); } ..................... - Каждые 10 мс выполняется процедура ReadKeys(); которая является драйвером клавиатуры нижнего уровня и занимается сканированием кнопок, обработкой дребезга, распознает длительность нажатия, автоповтор и т.д. Результат ее работы - значения регистров статуса и флаги нажатия. - Обработчик клавиатуры верхнего уровня ServiceKeys(); вызывается по наличию флагов. Его работу я уже описал. - Про функцию вывода на дисплей я уже тоже писал. Важное замечание: вся эта структура будет хорошо работать только в том случае, если обеспечивается, что время работы любой функции не превышает 10 мс и хотя бы раз за 10 мс выполняется главный цикл. Но я обычно пишу так, что это условие всегда выполняется.
|
|
|
|
|
Dec 15 2007, 20:28
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Baser @ Dec 15 2007, 21:15)  - Один таймер выделяется под системный, который обеспечивает регулярное прерывание, в котором выставляется флаг f10ms_Past. А я делаю более логично. В "тиковом" прерывании инкрементирую переменную на величину периода "тика". Получается, что ее значение является временной меткой, выраженной в миллисекундах. 16-и битной переменной хватает для отсчета интервала времени больше минуты, а 32-х битная переменная переполняется только через почти полтора месяца. Цитата(Baser @ Dec 15 2007, 21:15)  Важное замечание: вся эта структура будет хорошо работать только в том случае, если обеспечивается, что время работы любой функции не превышает 10 мс и хотя бы раз за 10 мс выполняется главный цикл. Но я обычно пишу так, что это условие всегда выполняется. Если имеется упомянутая мной выше переменная "миллисекундных тиков", то нет необходимости так строго выдерживать период "суперцикла". Ограничивать период выполнения суперцикла приходится лишь в том, случае в его теле вычисляется, например, управляющее воздействие, которое должно быть обязательно выдано за определенный промежуток времени. Если же вернуться к теме топика, то при наличии ресурсов в ОЗУ формируется буфер экрана и все изменения, определяемые функциями меню, делаются в нем. Затем весь буфер одной функцией выводится на экран. При необходимости для ускорения вывода можно пропускать элементы изображения, которые не изменились за текущий цикл, а выводить на экран только изменения. Клавиатура у меня обычно тоже имеет свой буфер в который помещаются заранее определенные коды клавиш. Обрабатываются кнопки, устраняется дребезг, формируются коды клавиш и заполняется буфер в том же самом "тиковом"прерывании.
|
|
|
|
|
Dec 15 2007, 21:49
|

Просто Che
    
Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881

|
Цитата(rezident @ Dec 15 2007, 22:28)  А я делаю более логично. В "тиковом" прерывании инкрементирую переменную на величину периода "тика". Получается, что ее значение является временной меткой, выраженной в миллисекундах. 16-и битной переменной хватает для отсчета интервала времени больше минуты, а 32-х битная переменная переполняется только через почти полтора месяца. При таком решении мне немного не понятно, как можно без дополнительных флагов обеспечивать выполнение периодических процедур главного цикла ОДИН раз за их номинальный период. МК в моменты простоя может все время крутиться в main и пролетать через них сотни раз за время одного "тика" прерывания. Цитата(rezident @ Dec 15 2007, 22:28)  Если имеется упомянутая мной выше переменная "миллисекундных тиков", то нет необходимости так строго выдерживать период "суперцикла". Ограничивать период выполнения суперцикла приходится лишь в том, случае в его теле вычисляется, например, управляющее воздействие, которое должно быть обязательно выдано за определенный промежуток времени. Ну, как показывает практика, даже пропадание 1-2-х 10 мс процедур никак не сказывается на нормальной обработке клавиатуры и дисплея. Дребезг то на уровне 50-70 мс, сами понимаете. Цитата(rezident @ Dec 15 2007, 22:28)  Клавиатура у меня обычно тоже имеет свой буфер в который помещаются заранее определенные коды клавиш. Обрабатываются кнопки, устраняется дребезг, формируются коды клавиш и заполняется буфер в том же самом "тиковом" прерывании. В первых своих работах я тоже делал обработку клавиатуры в прерывании, а потом перестал, потому что нету смысла: обработка неспешная и нечего ей занимать драгоценное время прерываний.
|
|
|
|
|
Dec 15 2007, 22:21
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Baser @ Dec 16 2007, 02:49)  При таком решении мне немного не понятно, как можно без дополнительных флагов обеспечивать выполнение периодических процедур главного цикла ОДИН раз за их номинальный период. МК в моменты простоя может все время крутиться в main и пролетать через них сотни раз за время одного "тика" прерывания. Дык у каждого процесса своя переменная заданного периода. Простым вычитанием двух беззнаковых переменных определяется закончился требуемый процессу интервал времени или еще нет. Код switch (mnuFlag) { case MNU_LEVEL1: mnu_time=tickTime; //фиксируем отметку времени mnuFlag=MNU_LEVEL2; break; case MNU_LEVEL2: if ((tickTime-mnu_time)<5000) break; //ждем окончания интервала времени 5сек else { ... mnuFlag=MNU_LEVEL3; } break; case MNU_LEVEL3: ... .... } Цитата(Baser @ Dec 16 2007, 02:49)  Ну, как показывает практика, даже пропадание 1-2-х 10 мс процедур никак не сказывается на нормальной обработке клавиатуры и дисплея. Ну да, со скоростью 100 раз в секунду ни одна супер-пупер-машинистка долбить по клаве не сможет. Цитата(Baser @ Dec 16 2007, 02:49)  Дребезг то на уровне 50-70 мс, сами понимаете. Это довольно плохие контакты. Я не припомню, чтобы мы применяли кнопки у которых дребезг превышал 10мс. Обычно не более единиц мс. Цитата(Baser @ Dec 16 2007, 02:49)  В первых своих работах я тоже делал обработку клавиатуры в прерывании, а потом перестал, потому что нету смысла: обработка неспешная и нечего ей занимать драгоценное время прерываний. Хозяин-барин. Все от задачи зависит. Некоторые задачи вообще полностью в "тиковом" прерывании выполняются (исключая конечно же начальную инициализацию).
|
|
|
|
|
Dec 16 2007, 10:01
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(alux @ Dec 14 2007, 18:53)  Проблема еще в том, что символы имеют переменную ширину.... Как сделать ввод чисел только в заданные места, т.е. пропускать '/' , ' ' , ':' ? наверное, завести массив структур на каждое редактируемое число, по структуре на редактируемое знакоместо. В структуре хранить смещение и размер редактируемого знакоместа (а возможно и допустимые значения). Размер, равный нулю, говорит о том, что знакоместа кончились.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Dec 16 2007, 11:47
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(Сергей Борщ @ Dec 16 2007, 14:01)  наверное, завести массив структур на каждое редактируемое число, по структуре на редактируемое знакоместо. В структуре хранить смещение и размер редактируемого знакоместа (а возможно и допустимые значения). Размер, равный нулю, говорит о том, что знакоместа кончились. Сложно все это как-то. Ширина символов у меня хранится в структуре шрифта. Эту ширину можно получить ф-цией CharWidth();Значение этой ф-ции прибавляю или отнимаю к текущей координате X, в которую необходимо переместить мигающий курсор и вводимый символ. Я думаю , может использовать ф-ции strtok или strtol. Только не знаю еще как. В общем, мне нужен пример ф-ции strEdit(); которая редактирует посимвольно строку в зависимости от нажатой кнопки. Неужели никто этого не делал? Только без всяких RTOS. Цитата(SasaVitebsk @ Dec 16 2007, 14:50)  PS: Выложил в библиотеки. Если есть какие вопросы, то можно здесь В каких библиотеках? P.S. Я немного упростил задачу. Время вывожу в формате: год : 2007 месяц : 12 дата : 14 день : 1 час : 13 минут : 30 Так даже наглядней будет. Строки переменных времени начинаются с коорд. X=50. Меняется коорд. Y для каждой переменной.
|
|
|
|
|
Dec 16 2007, 11:55
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Сергей Борщ @ Dec 16 2007, 12:01)  завести массив структур... Усложняешь, Сергей  Когда нужно было сделать подобный ввод, то не мудурствуя лукаво - была взята строка-шаблон; - выведена на дисплей; - курсор был воткнут на одно из редактируемых полей; - символ под курсором анализировался на digit/ASCII/separator, то не позволяло вводить символы не характерные для данного шаблона и позволяло перемещаться курсором сквозь символы не являющиеся символами характерными для данного поля; - при необходимости дополнительно можно было наложить дополнительные ограничения на перемещения курсора в пределах строки. - полученная таким редактированием строка-шаблон прогонялась обратно через sscanf() и .. и все. Все это оформлено ввиде достаточно универсальной функции редактирования строки работающей с указателем на редактируемую строку-шаблон, правой-левой границей в строке (это _не_ выделитель поля ввода), позицией курсора ( при первом запуске определеляет типа поля подлежащего редактированию) и, естественно, введенным символом. Все это использовалось и для ввода времени, и других параметров, и списков-таблиц. Вместе с раздумьями менее часа вся реализация.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Dec 16 2007, 12:16
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Дык алгоритм-то редактирования простейший. 1 уровень - "бегаем" по строке вправо-влево в заданных размером строки пределах. Если нажата кнопка, отличающаяся от "вправо"/"влево", то "проваливаемся" на второй уровень. 2 уровень - в зависимости от текущего положения курсора, редактируем какую-то конкретную переменную - year, month, day, hour, min, sec. Причем изменение ее (инкремент или декремент) идет на величину кратную положению курсора. Т.е. если редактируем вторую цифру минут, то декремент/инкремент переменной min при нажатии на кнопки "вверх"/"вниз" (или какие там у вас выделены для редактирования?) идет не на 1, а на 10. Естественно тут нужна проверка на выход за разрешенный для данной переменной диапазон значений. Например, 70 минут не бывает. Поэтому min может меняться только в пределах 0-59. При нажатии на любую другую кнопку, кроме выделенных для редактирования, возвращаемся на первый уровень.
|
|
|
|
|
Dec 16 2007, 12:47
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(rezident @ Dec 14 2007, 22:16)  Разделите логически в программе ввод, вывод и редактирование, а потом совместите их. Т.е. с помощью клавиатуры вы будете редактировать какую-то переменную время/дата. И эту же переменную выводить на экран после каждой модификации полностью. Не нужно будет ломать голову как совместить видимую позицию курсора и редактируемый символ. Переменную, наверное, необходимо сделать bin2bcd, чтобы редактировать каждую цифру числа. Пока не представляю как это все реализовать. Цитата(rezident @ Dec 16 2007, 16:16)  Дык алгоритм-то редактирования простейший. Не вижу ничего простейшего! Наоборот. Вы что, в мобильном телефоне тоже так время настраиваете? Т.е. подводите курсор на нужный символ, затем нажимаете на кнопку, отличающаяся от "вправо"/"влево". Затем кнопками "вправо"/"влево" редактируете значение времени??? Если это так, то я вам сочувствую... Если вы обратили внимание на мой код в первом посте, то у меня кнопки обрабатывается в одном уровне оператором switch. Т.е. если не нажата кнопка "вверх"/"вниз", то по умолчанию обрабатывается нажатие цифровых кнопок (если скан-код нажатой клавиши отличается от 0xff-нет нажатия).
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|