Jump to content
-=FISHER=-

Управление меню с помощью трёх кнопок (Си)

Recommended Posts

11 минут назад, -=FISHER=- сказал:

Обычно я это делаю вот такими функциями

Только настоятельно рекомендую делать из static - в целях экономии памяти. И подавление дребезга (если _delay_ms(3) служит этому) лучше делать прямо в get_button.

12 минут назад, -=FISHER=- сказал:

как при таком подходе мне отловить длительно нажатие?

Да так же, как и ранее: в get_code анализируйте длительность сохранения одинакового кода, возвращаемого get_bгttons, и реагируйте соответственно.

 

Дополнительно и альтернативно предлагаю рассмотреть вариант с программными (или аппаратными, если есть) таймерами, вы раньше делали такое уже.

Настраиваете один такой таймер на 100 Гц, и в его функции опрашиваете состояние пинов кнопок, помещая результат в глобальную переменную. Это и будет состояние кнопок, дополнительно уже не надо дребезг давить, он уже будет подавлен.

uint8_t btn_state; // это та самая переменная с кодом нажатых кнопок из таймерной функции

typedef enum{
  CMD_NONE,
  CMD_SET_HOUR,
  CMD_SET_MIN,
  ...
} command_t;

command_t get_command(void){
  static uint8_t prev_btn;

  if(btn_state != prev_btn){
    prev_btn = btn_state;
    if(btn_state == 0){
      stop_timer(LONG_PRESS_TMR);
      return CMD_NONE;
    }
    start_timer(LONG_PRESS_TMR);
    // здесь надо вычислить команду по значению btn_state и вернуть его
    // для примера я считаю, что команда совпадает со значением btn_state
    return btn_state;
  }
  if(!timeout_timer(LONG_PRESS_TMR)) return CMD_NONE;
  // здесь надо вычислить команду по длительному удержанию кнопок в btn_state и вернуть эту команду
  // заодно можно сделать автоповтор путем перезапуска таймера
  return ...
}

Написал сгоряча такую функцию - вроде должна работать...


Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites
14 часов назад, -=FISHER=- сказал:

но я совсем не могу придумать, как при таком подходе мне отловить длительно нажатие?

 

добавить повторный опрос портов через заданный интервал, если значение не изменилось значит долгое нажатие и изменяем значение code простым добавлением какого либо числа большего чем максимальное значение к примеру 0х0F(если используется младшие разряды порта и не больше 4-х), либо 1 при условии НЕ использования 0-го разряда порта.    Потом в функции по значению определяете имя команды. Что-то вроде этого (в синтаксисе могут быть ошибки).

uint8_t get_button(void)
{
    return PINB;
}

uint8_t get_code(void) //опрос портов
{
    code=get_button();
    _delay_ms(3);
    if(code!=get_button()){code=ERROR;}
    else {_delay_ms(10);
        if(code == get_button()){ code = code + 0xF0; //или code++;
         }; };
    return code;
}

//*****************************************

coDcoM = get_code();
switch (coDcoM) {
    case 0x01: command=KN1;  // короткое нажатие
    break;
    case 0xF1: command=KN1L;  // долгое нажатие
    break;
       .........
    }; 

 

Share this post


Link to post
Share on other sites

Изготовление 2-х слойных плат от 2$, а 4-х слойных от 5$!

Быстрое изготовление прототипа платы всего за 24 часа! Прямая доставка с нашей фабрики!

Смотрите видео о фабрике JLCPCB: https://youtu.be/_XCznQFV-Mw

Посетите первую электронную выставку JLCPCB https://jlcpcb.com/E-exhibition чтобы получить купоны и выиграть iPhone 12, 3D-принтер и так далее...

16 часов назад, ARV сказал:

Написал сгоряча такую функцию - вроде должна работать...

Мне кажется что при такой логике, сначала в любом случае выполнится действие от короткого нажатия, а потом если кнопку не отпускать то от длинного. Поправьте пожалуйста, если не прав.

Мне кажется нужно выполнять действие от короткого нажатия при отпускании кнопки, а от длинного при нажатии.


Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites

Вебинар «Параметры выше, цена ниже. Обновление в линейке AC/DC- и DC/DC-преобразователей MORNSUN» (26.01.2021)

Приглашаем 26 января на бесплатный вебинар, посвящённый преимуществам и отличиям новых источников питания и DC/DC-преобразователей Mornsun. На вебинаре будут рассмотрены изолированные и неизолированные DC/DC-преобразователи последнего, четвертого, поколения (R4) и компактные модульные источники питания второго и третьего поколений (семейства LS/R3 и LD/R2) на плату. Рассмотрим новую группу продукции – встраиваемые источники питания в кожухе.

Подробнее

Именно так. Я ранее писал, что вариант "реакция на отпускание" проще, но для пользователя менее удобен. Представьте себе, как бы писали текст, если бы буквы появлялись при отпускании клавиш...


Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites

Вебинар "Новый BlueNRG-LP с Bluetooth 5.2 и Long Range — волшебная палочка разработчика IoT" (04/02/2021)

Приглашаем 4 февраля на бесплатный вебинар о BlueNRG-LP - новом программируемом чипе SoC STMicroelectronics. На вебинаре будут детально рассмотрены новые возможности, особенности подключения, аппаратные и программные средства для разработки, а также практические примеры работы с микросхемой.

Подробнее

33 минуты назад, -=FISHER=- сказал:

Мне кажется нужно выполнять действие от короткого нажатия при отпускании кнопки, а от длинного при нажатии

Даже любопытно, каким именно способом вы обнаружите начало длинного нажатия, чтобы среагировать на него в момент нажатия :) 


Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites

Плата STEVAL-IDB011V1 – тестируем идеи на новом BLE 5.2-чипе BlueNRG-LP

Новая система на кристалле BlueNRG-LP производства STMicroelectronics предназначена для устройств интернета вещей(IoT ) и не только, отвечает стандарту BLE 5.2 и поддерживает MESH-сети. Микросхема содержит малопотребляющий MCU Cortex-M0+. Отладка STEVAL-IDB011V1 позволит сэкономить время на разработку новых устройств.

Подробнее

1 час назад, ARV сказал:

Даже любопытно, каким именно способом вы обнаружите начало длинного нажатия,

По-моему это как раз просто, нажали кнопку - начали считать длинное нажатие, досчитал допустим до 0.5 секунды - выполнили действие, а когда отпустили, если не было длинного нажатия, значит произошло короткое.

1 час назад, ARV сказал:

Именно так.

Это вы на первое мое "мне кажется" написали или на второе?

1 час назад, ARV сказал:

 Я ранее писал, что вариант "реакция на отпускание" проще, но для пользователя менее удобен.

Я согласен полностью, но мне кажется что ещё более неудобно, если пользователь хочет выполнить действие от длительного нажатия, но сначала получает от короткого, а потом от длинного. 

Или я чего-то не понял, поясните пожалуйста.


Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites
19 минут назад, -=FISHER=- сказал:

По-моему это как раз просто, нажали кнопку - начали считать длинное нажатие, досчитал допустим до 0.5 секунды - выполнили действие, а когда отпустили, если не было длинного нажатия, значит произошло короткое.

Ничего не понял. Условие: короткое нажатие при отпускании, а длинное в момент нажатия - вы же так писали ранее? Но короткое тоже начинается с нажатия! Как же вы отличие эти нажатия друг от друга?

Я надавил кнопку - в этот момент, по вашему, МК должен прочесть мои мысли и, если я собрался жать долго, то сразу сработать, а если я собрался отпустить кнопку быстро, то МК должен дождаться этого момента и потом уж срабатывать. :)

19 минут назад, -=FISHER=- сказал:

ещё более неудобно, если пользователь хочет выполнить действие от длительного нажатия, но сначала получает от короткого, а потом от длинного. 

А вы продумайте интерфейс так, чтобы это не было неудобным. В клавиатуре компьютера такое поведение же вас устраивает?

Edited by ARV

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites
17 минут назад, ARV сказал:

Ничего не понял.

1. Нажали кнопку - считаем время, если кнопка нажата более 0.5 секунды, выполняем действие по длинному нажатию.

2. Отпустили кнопку - смотрим, сколько кнопка была в нажатом состоянии, если меньше полсекунды, то выполняем действие по короткому нажатию, если же полсекунды, то ничего не делаем.


Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites

То есть всё-таки оба варианта по отпусканию... Но 0,5 сек это совсем не долгое нажатие. Долгое это не менее 2 сек.


Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites

@ARV Я кажется почти придумал как избавиться от длинных нажатий: кнопка set заходит в меню, далее + и - навигация по пунктам меню, что бы зайти в выбранный пункт нажимаем опять set, далее + и - настраиваем выбранный параметр. Потом опять нажимаем set, выходим в меню, пролистываем кнопкой + до последнего пункта и выходим из меню и попадаем в режим отображения.

Все нажатия короткие. Достаточно эргономично?..

6 минут назад, ARV сказал:

То есть всё-таки оба варианта по отпусканию... 

Да почему же? Длинное нажатие срабатывает когда кнопка удерживается нажатой определенное время.


Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites

Я бы на вашем месте с многоуровневым меню вообще не заморачивался. Нажали Set - что-то замигало, надо изменить - меняем плюсом и минусом, не надо - жмем Set ещё раз, начинает мигать что-то другое... И так далее.

Вместо загадочных символов показывать сразу эффект, например...


Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites

@ARV А ещё Вы можете пояснить момент с тем, что если опрашивать состояние кнопок в прерывании с частотой 100 Гц, то антидребегз в таком случае не нужен?


Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites

Так за 10 мс дребезг успеет окончиться...


Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites
3 часа назад, -=FISHER=- сказал:

если же полсекунды, то ничего не делаем

зачем же? пусть будет больше или равно.

2 часа назад, ARV сказал:

Я бы на вашем месте с многоуровневым меню вообще не заморачивался. Нажали Set - что-то замигало, надо изменить - меняем плюсом и минусом

именно это я ему выше расписал - не меню, а просто список параметров.

27 минут назад, -=FISHER=- сказал:

если опрашивать состояние кнопок в прерывании с частотой 100 Гц, то антидребегз в таком случае не нужен?

да, бороться с дребезгом не нужно.

я при опросе кнопок увеличиваю счетчик кнопки. даже если из-за дребезга будет пропуск нажатого состояния (и то в самый момент нажатия), счетчик все равно накопит некоторое число.

но у меня длинное нажатие сделано 1 секунда или более.

потом в главном цикле проверяю счетчик кнопки. и у меня 3 варианта проверки:

1. при отпущенной кнопке - если есть в счетчике число, то это было короткое нажатие, и выполняем работу по короткому нажатию.

2. при нажатой кнопке - если число >= 100, то было длинное нажатие, и выполняем работу по длинному нажатию.

3. при нажатой кнопке - если число меньше 100, то ничего не делаем (а вдруг это еще длится короткое нажатие?).

таким образом, у меня короткое нажатие - от 1 до 99. правда, трудно себе представить, что кто-то умудрился сделать нажатие менее 20 мс.

3 часа назад, -=FISHER=- сказал:

Я кажется почти придумал как избавиться от длинных нажатий: кнопка set заходит в меню, далее + и - навигация по пунктам меню, что бы зайти в выбранный пункт нажимаем опять set, далее + и - настраиваем выбранный параметр. Потом опять нажимаем set, выходим в меню

именно это я тебе выше подробно расписал. причем, вообще без длинных нажатий.

и, в данном случае, выход не из меню, а из настройки параметра.


Мудрость приходит вместе с импотенцией...

Share this post


Link to post
Share on other sites

Понимаете, в чем дело, я хочу, чтобы было именно меню.

То есть нажимая + и - в обычном режиме отображения, чтобы по кругу менялись часы, температура и вольтметр.

А если нажать SET, то открывается меню и от + и - уже по кругу меняются пункты меню, с названиями настроек.

Там соответственно на выбранной настройке нажимаем SET и открывается мигающие численное значение параметра ну или мигающие часы.

А что касается антидребезга, ведь нужно все равно какой-то счётчик заводить, чтобы после первого же вхождения в get_code не срабатывало действие, а только после второго или третьего предположим. Или нет? Нельзя же допускать запуска процедуры после первого же полученного кода?


Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites
13 часов назад, -=FISHER=- сказал:

То есть нажимая + и - в обычном режиме отображения, чтобы по кругу менялись часы, температура и вольтметр.

А если нажать SET, то открывается меню и от + и - уже по кругу меняются пункты меню, с названиями настроек.

ну и чем отличается твое желания от описанного мною алгоритма работы?

13 часов назад, -=FISHER=- сказал:

Нельзя же допускать запуска процедуры после первого же полученного кода?

если не будет длинных нажатий, то можно запускать от первого найденного нажатия. но при этом нужно ставить флаг блокировки клавиатуры, чтобы не было повторной обработки нажатой кнопки.

а флаг блокировки клавиатуры сбрасывать только когда все кнопки отпущены.


Мудрость приходит вместе с импотенцией...

Share this post


Link to post
Share on other sites
1 час назад, Starichok сказал:

если не будет длинных нажатий, то можно запускать от первого найденного нажатия. но при этом нужно ставить флаг блокировки клавиатуры, чтобы не было повторной обработки нажатой кнопки.

а флаг блокировки клавиатуры сбрасывать только когда все кнопки отпущены

Я короче не правильно понимал дребезг контактов. Ведь сама по себе кнопка нажиматься не может, поэтому вполне можно реагировать сразу на первое же нажатие. Другое дело что во время первого нажатия от дребезга в течении короткого времени появляются ещё хаотичные отжатия и нажатия, пока кнопка не устаканится в нажатом положении. И если проверять состояние кнопки раз в 10 мс, то получается что пока придёт время следующей проверки, кнопка уже устаканится.


Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites

кста энкодер заменяет 3 кнопки. Конструкцию можно сделать более компактной. Другое дело, что обработка энкодера немного отлична от кнопок

Share this post


Link to post
Share on other sites
16 минут назад, fuckir сказал:

кста энкодер заменяет 3 кнопки.

Да вы правы, но я делаю плату в уже существующие автомобильные часы, а там именно 3 кнопки


Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites

Тогда вам следует изучить MicroMenu в качестве примера одного из подходов в создании многоуровневых меню, или примеры вашего покорного слуги - ссыли ранее давал, но можно и из других моих проектов, правда, придетс поискать :)


Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites
On 4/9/2020 at 1:41 PM, -=FISHER=- said:

как сделать чтобы кнопка понимала что в настройках минут, нужно менять только от 0 до 59

Можно вот так, например. В главном цикле 20 раз в секунду опрашиваем кнопки get_button() и передаём код нажатой кнопки для исполнения в do_button().

Spoiler

#define BUT_UP        1 << 0     // кнопка на PIN0
#define BUT_DOWN    1 << 1    // кнопка на PIN1
#define BUT_SET        1 << 2    // кнопка на PIN2
#define ANY_K        (BUT_UP | BUT_DOWN | BUT_UP)

// Перечисляем все возможные состояния
enum Mode{MODE_TIME_VIEW, MODE_VOLT_VIEW, MODE_TEMP_VIEW, MODE_SET_HOURS, MODE_SET_MINUTES, ....}

int mode = MODE_TIME_VIEW;

int main()
{
    int but;
    
    while(1)
    {
        but = get_button();
        do_button(but);
        delay_ms(50);
    }
}

int get_button()    // для кнопок при нажатии PIN = 1, при отпускании PIN = 0
{
    static int old_but = 0;
    int res;
    int new_but;

    new_but = PINB & ANY_K;        // считать состояние кнопок
    res = old_but ^ new_but;     // изменилось состояние кнопок?
    res &= new_but;                // выделяем фронт нажатия
    old_but = new_but;            // сохраняем состояние кнопок для следующего раза
    
    return res; // возвращает 0 если нет фронта нажатия, или возвращает BUT_UP, BUT_DOWN, BUT_SET
}

void do_button(int but)
{

    if(but == 0) return;
    
    switch(mode)
    {
         case MODE_TIME_VIEW:
            if(but == BUT_UP) mode = MODE_VOLT_VIEW;
            if(but == BUT_DOWN) mode = MODE_TEMP_VIEW;
            if(but == BUT_SET) mode = MODE_SET_HOURS;
            break;                

        case MODE_TEMP_VIEW:
            .......
            break;
            
        case MODE_VOLT_VIEW:
            ......
             break;    
            
       case MODE_SET_HOURS:
            if(but == BUT_UP) 
            {
                if(hours<23)hours++;
                else hours = 0;
            }
            if(but == BUT_DOWN)
            {
                if(hours>0)hours--;
                else hours = 23;
            }
            if(but == BUT_SET) mode = MODE_SET_MINUTES;
            break;

        case MODE_SET_MINUTES:
            if(but == BUT_UP)
            {
                if(minutes<59) minutes++;
                else minutes == 0;
            }
            if(but == BUT_DOWN)
            {
                if(minutes>0)minutes--;
                else minutes = 59;
            if(but == BUT_SET) mode = MODE_TIME_VIEW;
            break;
        
        ......
    }
}


 

 

Share this post


Link to post
Share on other sites

1 - Почитайте этот цикл. Цикл статей Татарчевского

2 - http://easyelectronics.ru/organizaciya-drevovidnogo-menyu.html

Меню я сделал так: названия взял с easyelectronics, но за основу взял оригинал 1 версия. Есть 2 версия. Деталей уже не помню, потому как несколько лет назад наткнулся. Сейчас пользуюсь собственной переработкой этих проектов. Могу выложить проект-пример, но на это нужно время.

Share this post


Link to post
Share on other sites

С обработкой нажатий разобрался, использовал функцию, которую прошлым летом мне показал @ARV. За что ему огромное спасибо ещё раз!))

Скрытый текст

uint8_t get_button(void)
{
	static unsigned char but_code;
		
	but_code=PIN_BTN & BUTTONS;
	_delay_ms(5);
	if(but_code==(PIN_BTN & BUTTONS))
	{
		return but_code;
	}
	else
	{
		return ERROR;
	}
}

char Key_Repeater(char kk) //функция повторения нажатий
{
	static char old_kk; //предыдущее сочетание клавишь
	char result = 0; //результат возврата
	if(kk==NO_BUTTON_PRESS) //если ни одна кнопка не нажата
	{
		StopTimer(&Timer_KK);
	}
	else
	{
		if(old_kk!=kk) //если пришло новое нажатие
		{
			StartTimer(&Timer_KK, ACTIVE, 0.5*second, NULL); //запустим таймер с периодом 0,5 сек
			result = kk; //при первом входе сразу же вернули полученое сочетание клавишь
		}
		else //если данное сочетание пришло не впервые
		{
			if(TimeoutTimer(&Timer_KK)) //если время таймера вышло
			{
				result = kk; //повторим сочетание клавишь через 1 сек
				StartTimer(&Timer_KK, ACTIVE, 0.3*second, NULL); //запустим новый таймер с периодом 0,3 сек
			}
		}
	}
	old_kk = kk; //запомним предыдущее нажатие
	return result;
}

int main(void)
{
	while(1)
    {
    	key = Key_Repeater(get_button()); 
    }
}

 

Но теперь уперся в другое "узкое место" моей программы, а именно вывод символов на четырёхразрядный семисегментный индикатор. Для его управления я использую драйвер MAX7219.

Дак вот, основная проблема в том что в разных режимах, существуют разные правила отображения:

  • в режиме часов - нужно моргать точкой между часами и минутами;
  • в режиме температуры, отображать символ градуса и выводить минус при отрицательном значении;
  • в режиме вольтметра выводить символ U;
  • в режиме отображения пунктов меню, просто выводить определённые символы;
  • в режиме настройки моргать параметром.

MAX7219 подключен по SPI и символы я вывожу с помощью двух основных функций:

Скрытый текст

unsigned char segchar (unsigned char seg, unsigned char number) //функция перевода цифры в символ на дисплее
{
	unsigned char result;
	switch(number)
	{
		case 1: result = 0b00110000; break; //цифра 1
		case 2: result = 0b01101101; break; //цифра 2
		case 3: result = 0b01111001; break; //цифра 3
		case 4: result = 0b00110011; break; //цифра 4
		case 5: result = 0b01011011; break; //цифра 5
		case 6: result = 0b01011111; break; //цифра 6
		case 7: result = 0b01110000; break; //цифра 7
		case 8: result = 0b01111111; break; //цифра 8
		case 9: result = 0b01111011; break; //цифра 9
		case 0: result = 0b01111110; break; //цифра 0
		
		case 10: result = 0b00000001; break; // знак -
		case 11: result = 0b00000000; break; // пустое место
		case 12: result = 0b01100011; break; // символ для показаний температуры
		case 13: result = 0b00000000; break; //знак пустоты
	}
	
	switch(mode) //в зависимости от режима, меняем правила отображения
	{
		case MODETIMEVIEW:
		{
			if(animation_flag)
			{
				
				if((seg==2)&&(animation_flag==1)){result=0;} //если четвертый разряд и режим анимации, выведем пустоту
				if((seg==3)&&((animation_flag==1)||(animation_flag==2))){result=0;} //если четвертый разряд и режим анимации, выведем пустот
				if((seg==4)&&(number==0)){result=0;} //если в четвертом разряде приходит цифра 0, выведем пустоту на дисплей
			}
			else
			{
				if((seg==4)&&(number==0)){result=0;} //если в четвертом разряде приходит цифра 0, выведем пустоту на дисплей
				if (seg==3){if(PIN_SWQ&(1<<SQW)){result|=(1<<7);}} //если пришло время мигнуть точкой, подмешаем её в символ				
			}
			break;			
		}
		
		case MODETEMPVIEW:
		{
			if(seg==1){result = 0b01100011;} //допишем значек градуса после показаний температуры
		
			if(seg==3){if((number==0)&&(Cminus==0)){result=0;}} //если в третьем разряде приходит цифра  0, выведем пустоту на дисплей
			
			if(seg==4){if((number==0)&&(Cminus==0)){result=0;}} //если в четвертом разряде приходит цифра 0, выведем пустоту на дисплей			
	
			break;	
		}
			
		case MODEVOLTVIEW:
		{
			if (seg==1){result = 0b00111110;} //допишем букву U после значения напряжения
						
			if (seg==3){result|=(1<<7);} //точка разделитель десятой части вольта
			
			if ((seg==4)&&(number==0)){result=0;} //если в четвертом разряде приходит 0, то пишем пустое место			
			break;	
		}
			
	}
		return result;
}

void ledprint_7219 (unsigned int number) //функция, которая выводит в каждый разряд свой символ из числа
{
	Send_7219(1, segchar(1, number%10));
	Send_7219(2, segchar(2, number%100/10));
	Send_7219(3, segchar(3, number%1000/100));
	Send_7219(4, segchar(4, number/1000));
}

 

Однако мне кажется что функция segchar уже на данном этапе чрезмерно перегружена и уродлива)) (или нет?) может быть вы подкинете парочку идей как сделать этот участок кода более логичным красивым и коротким?

Edited by -=FISHER=-

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites

Снова рискну предложить вариант, ставший для меня основным.

Следует разделить функцию обновления индикатора и функцию вывода на индикатор. Принцип тот же, как и в любом дисплее: дисплей сам показывает то, что на него вывели, знать не знает, зачем.

Итак, делаем "экранную область" - массив scr, содержащий коды символов, отображаемых на индикаторе. Делаем знакогенератор - массив zgen, содержащий битовые карты светящихся сегментов для каждого символа. Делаем функцию, которая берет символы из экранной области, по их кодам извлекает битовые карты сегментов из знакогенератора и отправляет это на дисплей:

void refresh_scr(void){
  for(uint8_t i=0; i<SCR_SZ; i++)
    spi_send(zgen[scr[i]]);
}

Функцию refresh_scr можно вызывать по таймеру, предположим, 10 раз в секунду, тогда можно считать, что любой символ, который мы поместили в экранную область тут же (с задержкой не более 0,1 сек) появляется на индикаторе.

Такой подход позволяет просто выводить в экранную область то, что надо: захотели вывести минус в третьем разряде индикатора - записали в массив экранной области в третью ячейку код символа минус - и всё: scr[2] = CHAR_MINUS;

Мигание - отдельная тема. Если мигать должно целое знакоместо, то, при количестве знакомест не более 8, можно использовать отдельный байт (назовем его blink), превратив его биты в маску мигающих разрядов. Функция вывода экранной области на индикатор для этого случая должна измениться:

void refresh_scr(void){
  static uint8_t entry;
  uint8_t mask = 1;
  for(uint8_t i=0; i<SCR_SZ; i++){
    spi_send(zgen[(blink & mask) && entry ? CHAR_SPACE : scr[i]]);
    mask <<= 1;
  }
  entry = !entry;
}

Она будет просматривать эти биты, и для помеченных разрядов выводить "через раз" либо заданный символ, либо пробел - вот вам и автоматическое мигание. То есть если вы захотите замигать вторым разрядом индикатора, вы просто делаете blink |= _BV(1); - и он будет мигать до тех пор, пока этот бит в blink установлен. 

Как вам идея?

Edited by ARV

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Share this post


Link to post
Share on other sites
1 час назад, ARV сказал:

Как вам идея?

Похоже идея отличная. Давайте я по порядку буду разбирать.

1 час назад, ARV сказал:

Итак, делаем "экранную область" - массив scr, содержащий коды символов, отображаемых на индикаторе. Делаем знакогенератор - массив zgen, содержащий битовые карты светящихся сегментов для каждого символа.

 

Скрытый текст

#define DG 4 //кол-во используемых разрядов

uint8_t scr[DG], //"экранная область"
		zgen[]=  //"знакогенератор"
		{
			0b00110000, //цифра 1
			0b01101101, //цифра 2
			0b01111001, //цифра 3
			0b00110011, //цифра 4
			0b01011011, //цифра 5
			0b01011111, //цифра 6
			0b01110000, //цифра 7
			0b01111111, //цифра 8
			0b01111011, //цифра 9
			0b01111110, //цифра 0
		
			0b00000001, // знак -
			0b01100011, // символ для показаний температуры
         	 	0b00111110, //символ напряжения U
			0b00000000 //знак пустоты			
		};

 

Я правильно написал то что Вы предложили?

Edited by -=FISHER=-

Мы все учились по-немногу, чему-нибудь и как-нибудь...

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Сообщения

    • КТ972А должен подойти , но главное  превышать ток базы  ...   В  цепь базы установи резистор мин мально 1 Ком ... а  процем надо видеть схему  и тога  будет ясно точно что к чему  .... 
    • как то так : "Книга "1000 и одна микроконтроллерная схема" – купить ... www.ozon.ru › context › detail В наличии Книга "1000 и одна микроконтроллерная схема", ДМК Пресс в интернет-магазине ... В ней содержатся электрические схемы сопряжения микроконтроллеров с внешними устройствами. ... Автор на обложке: С. М. Рюмик."
    • Вопрос от новичка по подбору транзисторов под attiny/atmega. Вот захожу на сайт радиодеталей, и там выбор из 500 транзисторов, а подходящих мне по мощности/току/напряжению около 300 моделей. Как мне понять, с каким транзисторами работать будет нормально МК, а какие не подойдут. А то ж там биполярные, полевые, в даташитах графики всякие умные у каждого ... слишком сложно. Как это всё понять и научиться выбирать в пределах часа времени. Научите, пожалуйста. Сейчас вот подключаю нагрузку через attiny13, 500мА, 6В, и даже не знаю подойдет ли рядом валяющийся КТ972А. Нужен универсальный совет на все случаи, типа: мне рыба не нужна, дайте удочку.
    • Планирую пробник стабилитронов из такого преобразователя. Выход до 300V
    • Забыл написать что, мне не обязательно на выходе иметь импульс такой же длительности как входной, это будет просто цифровой вход микроконтроллера.    Про таймер, не совсем понял логику, можно модель таймера для чтения даташита?
    • @Vladimir_L Немного изменил программу в части управления кнопками. Проверяйте. У меня всё работает как надо.   FM_M8_N5110_scan_v8_1.hex

  • Модуль часов реального времени на основе DS1307

×
×
  • Create New...