Обсуждаем контроллеры компании 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 писал(а):Тут сложность в понимании того, что используется так называемый тренарный оператор условия.
Что, проще нет что ли? Давайте предоставим все это компилятору.
Спойлер
Код:
union BytByte {
struct {
unsigned char b0: 1;
unsigned char b1: 1;
unsigned char b2: 1;
unsigned char b3: 1;
unsigned char b4: 1;
unsigned char b5: 1;
unsigned char b6: 1;
unsigned char b7: 1;
} bit;
volatile uint8_t byte;
}__attribute__((packed));

union BytByte myBByte;

union BytByte rezultat;

   myBByte.byte = DDRB;

   rezultat.bit.b0 = myBByte.bit.b1;
   rezultat.bit.b7= myBByte.bit.b0;
   rezultat.bit.b1 = myBByte.bit.b2;
   // и так далее
   rezultat.byte // здесь будет результат

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

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

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

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

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

:) :)) :)))


Спойлер//ATMEGA328

//ВЫВОДЫ ATMEGA328 НА СЕГМЕНТЫ
#define A 1
#define B 2
#define C 4
#define D 8
#define E 16
#define F 32
#define G 64

int h1,h0,m1,m0; // h1 - десятки часов, h0 - еденицы часов и так далее
unsigned char digit_out[4], // буфер экрана (нумерация от 0 до 3 плюс четвертый невидимый разряд)
cur_dig; // тек.высвечиваемый символ (позиция)

unsigned char digits[] = {
(A+B+C+D+E+F), // 0
(B+C), // 1
(A+B+D+E+G), // 2
(A+B+C+D+G), // 3
(B+C+F+G), // 4
(A+C+D+F+G), // 5
(A+C+D+E+F+G), // 6
(A+B+C), // 7
(A+B+C+D+E+F+G), // 8
(A+B+C+D+F+G) // 9
};

byte mask = 0xFF;
void setup() {
DDRD = 0xFF; // порт D на OUTPUT
DDRB = 0xFF; // порт B на OUTPUT
cli();
TCCR1A = 0b00000000; // OC1A Отключен
TCCR1B = 0b00001011; // делитель 64, сброс при совпадении 1A (режим СТС)
OCR1A = 0x041; // 5ms
TIMSK1 = 0b0000010; // запусе прерывания А таймера 1
sei();

}
void loop() {


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
h1=2; //тут будем читать потом время
h0=1;
m1=5;
m0=8;
for (int i =0; i<7;++i){ // это сколько кадров

if (i==0) mask=F; // первый эффект
if (i==1) mask=F+A;
if (i==2) mask=F+A+B;
if (i==3) mask=F+A+B+G;
if (i==4) mask=F+A+B+G+E;
if (i==5) mask=F+A+B+G+E+D;
if (i==6) mask=F+A+B+G+E+D+C;
if (i==7) mask=F+A+B+G+E+D+C+D;

digit_out[0] = h1; // первай разряд
digit_out[1] = h0; // второй разряд
digit_out[2] = m1; // третий разряд
digit_out[3] = m0; // четвертый разряд


delay(20);
}
delay(100);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

h1=2; //тут будем читать потом время
h0=0;
m1=4;
m0=7;
for (int i =0; i<5;++i){ // это сколько кадров

if (i==0) mask=A; // второй эффект
if (i==1) mask=A+F+B;
if (i==2) mask=A+F+B+G;
if (i==3) mask=A+F+B+G+E+C;
if (i==4) mask=A+F+B+G+E+C+D;


digit_out[0] = h1; // первай разряд
digit_out[1] = h0; // второй разряд
digit_out[2] = m1; // третий разряд
digit_out[3] = m0; // четвертый разряд


delay(40);
}
delay(100);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ISR(TIMER1_COMPA_vect) { //обработчик прерывания по совпадению А таймера 1, счетчик сбрасывается в 0

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

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

}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


правильно я Вас понял или нет, НО получилось !!! :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:

Спойлерfor (byte i =0; i<4;++i){ // это сколько кадров

unsigned int AA[] = { 0, 0, 0,h1}; // h1- десятки часов
unsigned int BB[] = { 0, 0,h1,h0}; // h0- еденицы часов
unsigned int CC[] = { 0,h1,h0,m1}; // m1- десятки минут
unsigned int DD[] = {h1,h0,m1,m0}; // m0- еденицы минут


digit_out[0] = AA[i]; //высветить первый разряд
digit_out[1] = BB[i]; //высветить второй разряд
digit_out[2] = CC[i]; //высветить третий разряд
digit_out[3] = DD[i]; //высветить четвертый разряд

delay(100);
}



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



Код:
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
Спойлер
Код:
//Setup
#define DisablePWM       //отключение ШИМ выходов (мне для протеуса)
#define EnDebug          //включение порта отладки (reset) мне для протеуса

//полярность дисплеев
#define DISPLAY_U_CC   //#define DISPLAY_U_CA или #define DISPLAY_U_CC при отсутствии - автоопределение
#define DISPLAY_I_CC   //#define DISPLAY_I_CA или #define DISPLAY_I_CC при отсутствии - автоопределение
//соответствие сегмента и номера ноги порта D
#define Seg_A       5  //0
#define Seg_B       0  //1
#define Seg_C       1  //2
#define Seg_D       6  //3
#define Seg_E       7  //4
#define Seg_F       2  //5
#define Seg_G       3  //6
#define Seg_P       4  //7

//#define ShowLogo       //показать лого
..........
..........
в этом файле служебные и отладочные настройки проекта (файл показан не весь, там дальше прописаны длительности задержек, количество импульсов и прочее... закомментированные номера - в соответствии с отладочной платой, подставленые - с прототипом)
Если проект делается для кого-то, то этот кто-то допускается только в этот файл.

7SEG.C
Спойлер
Код:
//Digits
//Описание цифр семисегментного индикатора и функции, связанные с экраном

//расстановка единиц в маске
#define _A_  (1<<Seg_A)
#define _B_  (1<<Seg_B)
#define _C_  (1<<Seg_C)
#define _D_  (1<<Seg_D)
#define _E_  (1<<Seg_E)
#define _F_  (1<<Seg_F)
#define _G_  (1<<Seg_G)
#define _P_  (1<<Seg_P)
#define ___  (0       )

//знакогенератор
#define _0  (_A_|_B_|_C_|_D_|_E_|_F_|___)
#define _1  (___|_B_|_C_|___|___|___|___)
#define _2  (_A_|_B_|___|_D_|_E_|___|_G_)
#define _3  (_A_|_B_|_C_|_D_|___|___|_G_)
#define _4  (___|_B_|_C_|___|___|_F_|_G_)
#define _5  (_A_|___|_C_|_D_|___|_F_|_G_)
#define _6  (_A_|___|_C_|_D_|_E_|_F_|_G_)
#define _7  (_A_|_B_|_C_|___|___|___|___)
#define _8  (_A_|_B_|_C_|_D_|_E_|_F_|_G_)
#define _9  (_A_|_B_|_C_|_D_|___|_F_|_G_)
#define _A  (_A_|_B_|_C_|___|_E_|_F_|_G_)
#define _B  (___|___|_C_|_D_|_E_|_F_|_G_)
#define _c  (___|___|___|_D_|_E_|___|_G_)
#define _C  (_A_|___|___|_D_|_E_|_F_|___)
#define _D  (___|_B_|_C_|_D_|_E_|___|_G_)
#define _E  (_A_|___|___|_D_|_E_|_F_|_G_)
#define _F  (_A_|___|___|___|_E_|_F_|_G_)
#define _G  (_A_|_B_|___|___|___|_F_|_G_)   //°
#define _U  (___|_B_|_C_|_D_|_E_|_F_|___)   
#define _t  (___|___|___|_D_|_E_|_F_|_G_)   
#define _P  (_A_|_B_|___|___|_E_|_F_|_G_)
#define _r  (___|___|___|___|_E_|___|_G_)   
#define _o  (___|___|_C_|_D_|_E_|___|_G_)
#define _n  (___|___|_C_|___|_E_|___|_G_)   
#define _Z  (___|___|___|___|___|___|___)   //выкюченный разряд
#define _N  (___|___|___|___|___|___|_G_)   // "-"

const unsigned char DIG[]={_0,_1,_2,_3,_4,_5,_6,_7,_8,_9};
//flash unsigned char DIG[]={_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,_C,_d,_e,_f};

#if (defined(DISPLAY_U_CC) && defined(DISPLAY_U_CA))
#error "Conflict on DISPLAY_U mode"
#endif
#if (defined(DISPLAY_I_CC) && defined(DISPLAY_I_CA))
#error "Conflict on DISPLAY_I mode"
#endif
.......
.......
начало файла, работающего с 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

что касается портов вся ясно
Спойлер#define A 1
#define B 4
#define C 16
#define D 64
#define E 128
#define F 2
#define G 8

static flash unsigned char digits[] = {
(A+B+C+D+E+F), // 0
(B+C), // 1
(A+B+D+E+G), // 2
(A+B+C+D+G), // 3
(B+C+F+G), // 4
(A+C+D+F+G), // 5
(A+C+D+E+F+G), // 6
(A+B+C), // 7
(A+B+C+D+E+F+G), // 8
(A+B+C+D+F+G), // 9
(A+B+C+E+F+G), // A - 10
(C+D+E+F+G), // b - 11
(A+D+E+F), // C - 12
(B+C+D+E+G), // d - 13
(A+D+E+F+G), // E - 14
(A+E+F+G), // F - 15
(G), // 16 - знак минус
(A+B+F+G), // 17 - символ градус цельсия
(0), // 18 - пусто
(C+E+G), // n
(D+E+F+G) // t
};

#define MINUS 16
#define GRADUS 17
#define PROBEL 18
#define SYMBOL_A 10
#define SYMBOL_B 11
#define SYMBOL_C 12
#define SYMBOL_D 13
#define SYMBOL_E 14
#define SYMBOL_F 15
#define SYMBOL_N 19
#define SYMBOL_T 20


читаем время дату
Спойлер// ---------- прочесть время из DS1307 ----------
void read_time(void) {
rtc_get_time(&myhour, &mymin, &mysec);
rtc_get_date(&mydate, &mymonth, &myyear);

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


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

Спойлер//---------- экранный буфер --- (индикация времени )--------
void view_time(void) {


if (indicate == IND_CLOCK) { // час : мин
if (hour0) { digit_out[0] = (myhour >> 4) ? (myhour >> 4) : (PROBEL); }
else { digit_out[0] = myhour >> 4; }

digit_out[1] = myhour & 0x0F;
digit_out[2] = mymin >> 4;
digit_out[3] = mymin & 0x0F;
}

Спойлер// --------- экранный буфер------- (индикация даты) ----------
void view_date(void) {
if (indicate == IND_CLOCK) { // день - месяц
digit_out[0] = mydate >> 4;
digit_out[1] = mydate & 0x0F;
digit_out[2] = mymonth >> 4;
digit_out[3] = mymonth & 0x0F;
}
} //----------


понадобилось выводим
Спойлер//======= индикация времени ======
//==============
if ((indicate == IND_CLOCK) && ((t_ind_date!=0) || ((devices >= 1) && (t_ind_term != 0)))) {
effect_down(); // эффект
read_time(); // читаем время
view_time(); //в буфере

zpt_4=0;
zpt_BLINK=1; // включить мигающую запятую

effect_up(); // эффект
}

if (indicate == IND_CLOCK) {
zpt_BLINK=1; // включить мигающую запятую

for (j=0;j<tc1;j++){ // сколько секунд показывать "время"
read_time(); // читаем время
view_time(); // в буфере
set_ind(); // дает задержку в 0,2 сек
set_yarkost(); // яркось 0-10

if (indicate != IND_CLOCK) break; // если был выход из индикации времени
}
} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

с датой и температурой также читаем- буфер- понадобилось выводим.
Функция 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. Потому что с прерываниями будет сложнее.
Вообще, все эти штуки делаются на так называемых конечных автоматах.
Ответить