Обсуждаем контроллеры компании Atmel.
Ответить

Re: Семисегментный LED-индикатор. Эффекты анимации

Вс авг 14, 2022 20:35:05

Автор желает прочитать значение бита в исходном байте и перенести это значение в байт результата. То есть, бит-ориентированные действия желает провести.
На языке Си (поскольку он байт-ориентированный) чтение бита выполняется опять же через логическую операцию AND с байтом, в котором содержится бит, и маской, в которой записана 1 в позиции читаемого бита. Запись бита в байт производится логической операцией OR, если записывается 1, или операцией AND, если записывается 0. Однако, если записываемом бейте в позиции этого бита находится 0, то операцию AND можно не применять. Операция 0 OR 0 = 0.

Записать всё в сумме можно в таком варианте:
Код:
result |= ((DDRB & (1<<Rbit)) >> Rbit) << Wbit;

, где Rbit - номер позиции читаемого бита в байте DDRB,
Wbit - номер позиции записываемого бита в байт result.
(1<<Rbit) выдает маску из 0 во всех битах, кроме бита номер Rbit, который будет = 1. Эта маска будет наложена на байт DDRB, при этом результат операции будет содержать значение читаемого бита в позиции номер Rbit. Чтобы записать его в байт result в позиции Wbit, нужно провести сдвиги, сначала вправо на число позиций Rbit, затем влево на число позиций Wbit.

Второй вариант с несколько иным алгоритмом, используя проверку true/false при чтении бита.
Код:
result = (DDRB & (1 << Rbit)) ? result | (1 << Wbit) : result & ~(1 << Wbit);

Тут сложность в понимании того, что используется так называемый тренарный оператор условия. Суть в чем. То, что записано после знака "=" и перед знаком "?" является условием сравнения и аналогично записи if(DDRB & Rbit).
Следующие после "?" два выражения, разделенные знаком ":" являются действиями, исполняемыми по условию предыдущего сравнения. Первое выражение будет выполняется, если результат сравнения "истина" (true), второе выражение выполнится если результат сравнения "ложь" (false), и результат выражений запишется в первую переменную до знака "=", то есть в result. Напомню, что результат сравнения "истина" получается при любом ненулевом значении выражения, а результат "ложь" - при нулевом. То есть, если при выполнении побитовой операции AND получились все нули, то выражение ложно. Если хоть в какой-то позиции есть 1, то результат "истина".
Таким образом, если при чтении бита с номером Rbit там стоит 1, то в result она перенесется в позиции Wbit, а если прочитанный бит = 0, то в result запишется 0 в позиции Wbit.

И третий вариант записи - через структуры с восемью однобитовыми полями. В этом случае запись на Си будет более понятной, типа result.bit2 = DDRB.bit5;

Re: Семисегментный LED-индикатор. Эффекты анимации

Вс авг 14, 2022 20:49:21

MLX90640 писал(а):Тут сложность в понимании того, что используется так называемый тренарный оператор условия.
Что, проще нет что ли? Давайте предоставим все это компилятору.

Re: Семисегментный LED-индикатор. Эффекты анимации

Вс авг 14, 2022 21:10:12

Вы не дочитали до конца - я как раз и написал, что есть вариант со структурами и однобитными полями.

Re: Семисегментный LED-индикатор. Эффекты анимации

Вс авг 14, 2022 23:02:29

:) :)) :)))




правильно я Вас понял или нет, НО получилось !!! :music:
спасибо Вам за учебу, терпение и доброту :beer:

Re: Семисегментный LED-индикатор. Эффекты анимации

Вс авг 14, 2022 23:07:51

Молодца!!! Захотел. Сделал.

Re: Семисегментный LED-индикатор. Эффекты анимации

Ср авг 17, 2022 13:11:54

с анимацией цифр понятно :)))



а как чтоб они ездили туда-суда? :dont_know: :shock:



может кто подкинет идею, совет, предложение любой помощи буду рад. :beer:

Re: Семисегментный LED-индикатор. Эффекты анимации

Ср авг 17, 2022 13:40:37

А это так называемая "бегущая строка". И есть два пути. Второй из них заключается в том, чтобы при сдвиге цифр влево просто переносить байты цифр в буфере дисплея левее, начиная слева. Допустим, есть буфер
buf[4], в котором buf[0] - самый левый разряд, а buf[3] - самый правый. Так вот, каждый шаг анимации бегущей строки будет представлен шагами:
- копируем из buf[1] в buf[0],
- копируем из buf[2] в buf[1],
- копируем из buf[3] в buf[2],
- в buf[3] записываем "пустой символ".
И так - 4 раза. Всё число "уйдет" с дисплея влево. А чтобы новое число "вошло" справа налево, нужно в последнем пункте вместо "пустого" символа записать символ "вдвигаемого" числа, начиная с левого его края.

Первый же способ делается на указателях. В этом случае ничего не копируется, инкрементируется только указатель на начало отображения строки в дисплей, сдвигаясь по строке. То есть, допустим, есть строка chat txt[4+4], которую нужно сдвинуть по дисплею влево. Эта строка должна быть вдвое длиннее количества разрядов на дисплее, и в последних четырех байтах содержать "пустой" символ. Функция, принимающая строку для отображения в дисплей, будет брать строку txt[], смещаясь по ней на одну позицию вправо, от начала к концу.

Re: Семисегментный LED-индикатор. Эффекты анимации

Ср авг 17, 2022 14:42:08

Второй из них заключается в том, чтобы при сдвиге цифр влево просто переносить байты цифр в буфере дисплея левее, начиная слева. Допустим, есть буфер
buf[4], в котором buf[0] - самый левый разряд, а buf[3] - самый правый. Так вот, каждый шаг анимации бегущей строки будет представлен шагами:


да, этот способ я опробовал и он работает, но если бы выводить на экран два показания, скажем часы и температуру. а если выводить несколько показаний часы, несколько температур, день недели...
я не соображу как это написать вместо h1(часы) ,t1(температуру) d1(день )... :dont_know:




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



Код:
ISR(TIMER1_COMPA_vect) {      //обработчик прерывания по совпадению А таймера 1, счетчик сбрасывается в 0
 
 PORTD&=0b11110000;                   // потушить все (разряды - off)
 PORTB=digits[digit_out[cur_dig]]&maska;     // символ на экран
 PORTD |= (1<<cur_dig);    // засветить нужный разряд (бит знакоместа - on)

 cur_dig++; if (cur_dig > 3) {
    cur_dig = 0;

    }
  }


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

Re: Семисегментный LED-индикатор. Эффекты анимации

Ср авг 17, 2022 15:15:29

Для разрядов индикатора нужно создать отдельный буфер, не привязанный к часам-минутам или температуре. Вот так: byte buf[4]; Из этого буфера динамичская индикация берет очередное значение для зажигания сегментов в очередном разряде. И в этот же буфер бубут ложиться значения сегментов другой функцией, которая называется LED_Print и выполняет преобразование входного числа в комбинацию зажигаемых сегментов.
Вот так:
Код:
/* кодирование сегментов в байте */
#define SEG_A      (1 << 0)
#define SEG_B      (1 << 1)
#define SEG_C      (1 << 2)
#define SEG_D      (1 << 3)
#define SEG_E      (1 << 4)
#define SEG_F      (1 << 5)
#define SEG_G      (1 << 6)
#define SEG_dp      (1 << 7)

/* символы шрифта */
#define BLANK      0
#define CH_0      (SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F        )
#define CH_1      (        SEG_B | SEG_C                                )
#define CH_2      (SEG_A | SEG_B |         SEG_D | SEG_E         | SEG_G)
#define CH_3      (SEG_A | SEG_B | SEG_C | SEG_D                 | SEG_G)
#define CH_4      (        SEG_B | SEG_C                 | SEG_F | SEG_G)
#define CH_5      (SEG_A         | SEG_C | SEG_D         | SEG_F | SEG_G)
#define CH_6      (SEG_A         | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G)
#define CH_7      (SEG_A | SEG_B | SEG_C                                )
#define CH_8      (SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G)
#define CH_9      (SEG_A | SEG_B | SEG_C | SEG_D         | SEG_F | SEG_G)

#define CH_DASH      (                                                SEG_G)
#define CH_DNDASH   (                        SEG_D                        )

const uint8_t font[] =
{CH_0, CH_1, CH_2, CH_3, CH_4, CH_5, CH_6, CH_7, CH_8, CH_9};

#define NUM_DIG         4      // число разрядов в индикаторе
static uint8_t buf[NUM_DIG];  // буфер хранения данных о сегментах в разрядах

/** ----------
 * @brief   Преобразование текстовой строки в данные для сегментов
 *          индикатора.
 * @param *str - входная текстовая строка в кодировке ANSI
 * @param len - длина строки
 */
void DND_Print(char *str, uint8_t len)
{
   uint8_t digit = 0;
   uint8_t dotsign[NUM_DIG] = {0, 0, 0, 0};

   /* преобразование текстовой строки */
   while(len--)
   {
      switch(*str){
      /* цифры */
      case '0' ... '9':
         buf[digit] = font[*str - '0'];
         break;


      /* тире */
      case '-': buf[digit] = CH_DASH;
         break;
      case '_': buf[digit] = CH_DNDASH;
         break;
      /* десятичные точки*/
      case '.':
      case ',':
         /* для индикатора с разделительным двоеточием */
         if(digit == 2)
         {
            dotsign[3] = SEG_dp;
         }
         digit--;
         break;
      /* разделительные точки */
      case ':':
         /* для индикатора с разделительным двоеточием */
         if(digit == 2)
         {
            dotsign[2] = SEG_dp;
            dotsign[3] = SEG_dp;
         }
         digit--;
         break;
      default: buf[digit] = BLANK;
      }
   str++;            // следующий символ в строке
   digit++;         // следующий разряд
   if(digit > NUM_DIG) break;
   }

   /* добавление признака точек в разряды*/
   for(digit = 0; digit < NUM_DIG; digit++)
   {
      buf[digit] |= dotsign[digit];
   }
}


Работа динамической индикации обеспечивается вот этой функцией, вызываемой через равные промежутки времени по прерыванию таймера:
Код:
/** ----------
 * @brief   Поразрядное переключение шагов динамической индикации.
 */
#ifdef USE_SEGMENT_ANIMATION
  #include "fx.h"
#endif
void DND_DynaScan(void)
{
   static uint8_t digit = 0;

#ifdef USE_SEGMENT_ANIMATION
   uint8_t mask;

   mask = DND_SegAnimation_GetMask(digit);
   DND_DigitOut(digit, buf[digit] & mask);
#else
   DND_DigitOut(digit, buf[digit]);  // для варианта без анимации сегментов
#endif

   digit++;
   if(digit >= NUM_DIG) digit = 0;
}


А вывод текстовой строки на дисплей выглядит вот так:
Код:
DND_Print("1259", 4);  // фиксированная текстовая строка в формате ANSI

или
Код:
   char str[10];
   uint8_t len;

   len = sprintf(str, "%02d:%02d", current_time.tm_hour, current_time.tm_min);
   DND_Print(str, len);


функция sprintf является стандартной и выполняет форматированное преобразование чисел в ANSI-кодированный текст. В принципе, для нужд часов можно обойтись простым преобразованием числа в ANSI-строку любым доступным способом.

Добавлено after 10 minutes 42 seconds:
Да... Если всё написанное выше слишком сложно для понимания, то можно существенно упростить функции, сделав их по-топорному, вот так:

Код:
/* кодирование сегментов для цифр такое же как выше */
const uint8_t font[] =
{CH_0, CH_1, CH_2, CH_3, CH_4, CH_5, CH_6, CH_7, CH_8, CH_9};

/* Преобразование чисел от 0 до 9 в сегменты индикатора */
void DND_Print(byte d0, byte d1, byte d2, byte d3)
{
   buf[0] = font[d0];
   buf[1] = font[d1];
   buf[2] = font[d2];
   buf[3] = font[d3];
}

и тогда вы в эту функцию просто передаете 4 обезличенных числа в своих знакоместах, и не важно, десятки ли это минут или единицы градусов.
Последний раз редактировалось MLX90640 Ср авг 17, 2022 15:17:43, всего редактировалось 1 раз.

Re: Семисегментный LED-индикатор. Эффекты анимации

Ср авг 17, 2022 15:16:54

:shock: :o :roll: переварю/подумаю

Re: Семисегментный LED-индикатор. Эффекты анимации

Ср авг 17, 2022 15:18:55

Да, это довольно сложно для восприятия. Тут надо неплохо знать язык программирования Си. Впрочем, можно упростить. Я там добавил немного

Re: Семисегментный LED-индикатор. Эффекты анимации

Чт авг 18, 2022 03:43:58

Дело не в си, или каком другом языке. Алгоритмы. Потом реализация.

Re: Семисегментный LED-индикатор. Эффекты анимации

Чт авг 18, 2022 09:52:52

Да... Если всё написанное выше слишком сложно для понимания, то можно существенно упростить функции, сделав их по-топорному,

никак не въеду в суть "по-топорному" можно еще по подробнее разжевать этот метод, в чем тут смысл :facepalm:

Re: Семисегментный LED-индикатор. Эффекты анимации

Чт авг 18, 2022 13:08:33

А смысл как бы вот в том, что... Вот определили мы комбинации битов для зажигания сегментов в цифрах
#define CH_0 (SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F )
теперь, чтобы попроще сделать выбор этих комбинаций, создаем массив из десяти однобайтных переменных и сразу в этот массив кладем вышеопределенные комбинации:
const uint8_t font[10] = {CH_0, CH_1, CH_2, CH_3, CH_4, CH_5, CH_6, CH_7, CH_8, CH_9};
Таким образом, получается, что например 5-й элемент массива font[5] содержит комбинацию сегментов для цифры 5. И теперь получается прямая зависимость между числом единиц или десятков с комбинациями для зажигания сегментов в разрядах.
То есть, если например число десятков часов dHour = 5, то отобразить цифру 5 можно просто выбрав 5-й индекс в массиве, вот так:
Port = font[dHour];
В Port будет передано значение из пятого элемента массива font[].

Функция void DND_Print(byte d0, byte d1, byte d2, byte d3) принимает числовые значения d0, d1, d2, d3 в диапазоне 0-9 для каждого разряда (цифры, которые нужно на дисплей вывести), преобразует эти числа показанным выше методом в комбинации сегментов и кладет в массив переменных buf[4], из которого потом в нужные моменты переключения разрядов функция DND_DynaScan() забирает уже готовые комбинации для передачи в выходной порт для зажигания сегментов.
Такой алгоритм через промежуточный буфер buf[] сделан для того, чтобы разделить работу динамической индикации, непрерывно через интервалы читающей значения зажигаемых сегментов из buf[], и функции, преобразующей любые числа в комбинации сегментов. То есть, для этих функций не имеет значения, что именно отображается - часы с минутами, температура, дата или еще что.
Задачу разложения целого числа из переменной в поразрядное представление от 0 до 9 - это будет выполнять другая функция.

Re: Семисегментный LED-индикатор. Эффекты анимации

Чт авг 18, 2022 13:57:45

Тоже так делаю, но ради автоматизации знакогенератора - если окажется удобнее развести контакты порта по сегментам не по порядку, пожалуйста, разводи как удобно, но в коде, вот в этом месте, пропиши цифры в соответствии с разводкой, например:
Код:
#define SEG_A      (1 << 5)
#define SEG_B      (1 << 7)
#define SEG_C      (1 << 0)
#define SEG_D      (1 << 3)
#define SEG_E      (1 << 1)
#define SEG_F      (1 << 4)
#define SEG_G      (1 << 6)
#define SEG_dp     (1 << 2)
и весь знакогенератор,
(а у меня он обычно гораздо шире, чем 10 цифр... цифры лежат в массиве, остальное вызывается по дефайну, в т.ч. и сегменты по отдельности)
вобщем всё будет автоматически пересчитано, перерисовано и приведено в соответствие со схемой!

Добавлено after 1 minute 23 seconds:
поэтому я этот код выделил в отдельный файл и целиком его из проекта в проект таскаю... :tea:

Добавлено after 33 minutes 24 seconds:
Вот примерное содержание файлов:
SETUP.C
в этом файле служебные и отладочные настройки проекта (файл показан не весь, там дальше прописаны длительности задержек, количество импульсов и прочее... закомментированные номера - в соответствии с отладочной платой, подставленые - с прототипом)
Если проект делается для кого-то, то этот кто-то допускается только в этот файл.

7SEG.C
начало файла, работающего с 7 сегментным индикатором... далее идут процедуры гашения и зажигания разряда дисплея, они работают только с буфером дисплея и портами выводящими информацию на индикатор
остальной код, в т.ч. и код, реализующий спецэффекты, работает только с буфером.

Re: Семисегментный LED-индикатор. Эффекты анимации

Чт авг 18, 2022 13:59:03

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

Есть два способа адаптации под распиновку: либо менять цифры (положение битов) в знакогенераторе, либо записать функцию вывода таким образом, чтобы она преобразовывала входную комбинацию из знакогенератора в выходную комбинацию на порты. Годится любой способ, в зависимости от того, нужно ли сделать более оптимальную работу кода или более универсальную библиотеку. Для случая посегментной анимации пересчитывать положение битов в знакогерераторе и в картах сегментной анимации - это уж не шибко кошерно, потому что есть еще одно, частное ограничение - сегменты могут быть разведены не на одном порту, а на разных. На том же STM32 у вас не шибко то большой выбор последовательно расположенных портов на 8 выходов. Да и то, часть из нужных выходов могут быть занятыми другими не менее важными функциями. Например, SPI1 как назло вклинивается в PA5-PA7. Так что полюбасу понадобится функция "перепиновки", так сказать. Посему, знакогенератор желателен независимый от распиновки индикатора. Но это на вкус и цвет. Комуто хватает и просто заменить цифры в дефайнах знакогенератора.

Буквы на семисегментнике отображать не особо весело. Я работал с 16-сегментными ЖКД, вот там замечательно буквы показываются. Но тем не менее, на 7-сегментном тоже можно. Лично я использую массив const с символами цифр и букв, а за основу беру парсер ANSI-строки:
Код:
      switch(*str){
      /* цифры */
      case '0' ... '9':
         buf[digit] = font[*str - '0'];
         break;

#ifdef USE_LETTERS
      /* буквы */
      case 'A' ... 'U':
         buf[digit] = font[*str - 'A' + 10];
         break;
      case 'a' ... 'u':
         buf[digit] = font[*str - 'a' + 10];
         break;
#endif

      /* тире */
      case '-': buf[digit] = CH_DASH;
         break;
      case '_': buf[digit] = CH_DNDASH;
         break;
      /* десятичные точки*/
      case '.':
      case ',':
         /* для индикатора с разделительным двоеточием */
         if(digit == 2)
         {
            dotsign[3] = SEG_dp;
         }
         digit--;
         break;
      /* разделительные точки */
      case ':':
         /* для индикатора с разделительным двоеточием */
         if(digit == 2)
         {
            dotsign[2] = SEG_dp;
            dotsign[3] = SEG_dp;
         }
         digit--;
         break;
      default: buf[digit] = BLANK;

Re: Семисегментный LED-индикатор. Эффекты анимации

Чт авг 18, 2022 15:53:02

что касается портов вся ясно


читаем время дату


загоняем в буфер и ждем когда понадобится




понадобилось выводим

с датой и температурой также читаем- буфер- понадобилось выводим.
Функция void DND_Print(byte d0, byte d1, byte d2, byte d3) принимает числовые значения d0, d1, d2, d3


d0, d1, d2, d3 мне нужно воткнуть в буфер на пример так
----------
if (indicate == IND_CLOCK) { // час : мин
if (hour0) { d0 = (myhour >> 4) ? (myhour >> 4) : (PROBEL); }
else { d0 = myhour >> 4; }

d1 = myhour & 0x0F;
d2 = mymin >> 4;
d3 = mymin & 0x0F;
----------
// --------- что у нас будет в экранном буфере ? ------- (индикация даты) ----------
void view_date(void) {
if (indicate == IND_DATE) { // день - месяц
d0 = mydate >> 4;
d1 = mydate & 0x0F;
d2 = mymonth >> 4;
d3 = mymonth & 0x0F;
}
} //----------

а эффект туда-суда по экрану выводить так???
void effect_down (void) {
int i;
for (i =0; i<4;++i){

unsigned int AA[] = {PROBEL,PROBEL,PROBEL,d0};
unsigned int BB[] = {PROBEL,PROBEL,d0,d1};
unsigned int CC[] = {PROBEL,d0,d1,d2};
unsigned int DD[] = {d0,d1,d2,d3};


digit_out[0] = AA[i]; //разряды 1-4
digit_out[1] = BB[i];
digit_out[2] = CC[i];
digit_out[3] = DD[i];

delay_ms(300);

}
}
// ----------

только CodeVisionAVR ругается и не хочет принимать byte d0, byte d1, byte d2, byte d3
а Функцию void DND_Print(byte d0, byte d1, byte d2, byte d3) я не знаю куда приделать :dont_know:

Re: Семисегментный LED-индикатор. Эффекты анимации

Чт авг 18, 2022 16:20:03

Serzh2000, напиши где нибудь сверху
Код:
#define byte unsigned char

Re: Семисегментный LED-индикатор. Эффекты анимации

Чт авг 18, 2022 17:15:38

а зачем тебе всякие "минусы" в массив загонять? оставь их просто дефайнами...
1) если дефайн не вызывается, то он и места не занимает (а архив занимает)
2) получение числа из дефайна происходит в несколько раз быстрее, чем из массива
3) число типа чар подставляемое непосредственно занимает во флеше во много раз меньше места, чем код, позволяющий достать значение из массива.

Но! в массиве удобно и экономично алфавит хранить, чтобы к символам (цифрам) добираться без лишних условий.

Re: Семисегментный LED-индикатор. Эффекты анимации

Чт авг 18, 2022 17:39:16

Сорян, надо было char написать. Просто я тыщщу лет уже как не работаю с AVR.
Функция DND_Print() - это собственно функция, преобразующая четыре числа в комбинации сегментов. И ставить её туда, где требуется в этот момент вывести на дисплей новую информацию.
У нас просто разный стиль написания. Я пишу в таком стиле, для меня он удобен.

Опять же, почему я говорю за таблицу символов в виде массива const char font[], так потому, что так обеспечивается наиболее быстрое сопоставление входной строки и комбинации сегментов для её отображение. Нет перебора условий сравнения, есть прямая зависимость между входным числом и индексом массива. Табличный метод - нормальная практика. Представьте, что у вас 16-сегментный индикатор, полноценно отображающий почти полторы сотни символов. И перебором условий через if или switch вы сделаете ужос полный. А табличный метод - другое дело. Навряд ли 10 или даже 120 байт в программной или даже оперативной памяти отнимут последний кусок памяти. Впрочем, я уже давно не пишу для мелких AVR/PIC с ограниченными ресурсами.

По поводу бегущей строки. Опять же, нужна отдельная функция, которая будет вызываться в нужный момент и принимать в себя 4 числа для текущего отображения и внутри этой функции сдвигать их на одну позицию через равные промежутки времени. Можно их реализовать хоть через Delay. Потому что с прерываниями будет сложнее.
Вообще, все эти штуки делаются на так называемых конечных автоматах.
Ответить