8-900-374-94-44
[email protected]
Slide Image
Меню

Динамическая индикация семисегментный индикатор – Семисегментный индикатор. Динамическая индикация

Семисегментный индикатор и динамическая индикация на AVR микроконтроллере ATmega8

В разных конструкциях бывает оправдано использовать семисегментные светодиодные индикаторы, дешево и сердито по сравнению с символьными ЖКИ. Светодиодный индикатор представляет собой восемь светодиодов (7 для представления цифры и 1 для точки) расположенные в виде слегка наклоненной цифры:
В разных конструкциях бывает оправдано использовать семисегментные светодиодные индикаторы, дешево и сердито по сравнению с символьными ЖКИ. Светодиодный индикатор представляет собой восемь светодиодов (7 для представления цифры и 1 для точки) расположенные в виде слегка наклоненной цифры:

Светодиоды внутри имеют общий анод (ОА) или общий катод (ОК). То есть, для управления одной цифрой нужно 8 выводов микроконтроллера. А что же делать, когда нужно управлять, например, четырьмя цифрами? Использовать микроконтроллер с 4*8=32 выводами? Не экономично.

Для такого случая придумали динамическую индикацию. Для этого соединяем выводы, которые отвечают за включение сегментов в общую шину, а общими анодами (катодами) будем управлять через транзисторы. В отдельный момент времени горит только одна цифра. Таким образом быстро перебирая цифры на дисплее (как кадры в фильме) мы получим эффект постоянно горящего изображения. В замедленном варианте, как это происходит, можно посмотреть на картинке:


А вот ускоренная в 25 раз картинка, уже начинают вырисовываться контуры «12.34»:

Используя принцип динамической индикации мы сможем управлять четырьмя цифрами при помощи 8+4=12 выводов. Использование же 2-х сдвиговых регистров HC595 может сократить это число до 4. Рассмотрим схему подключения к микроконтроллеру:

Управлять же индикатором будем с помощью микроконтроллера ATmega8. Резисторы R5-R13 – ограничительные на 470 Ом. R1-R4 – по 1кОм. Транзисторы Q1-Q4 – любые PNP типа, я использовал BC807 в планарном исполнении. Конденсаторы С5,С7 – электролиты по 100 и 200мкф соответственно,
С4,С6
– керамика по 0,1мкф. Так как индикатор с общим анодом, то соответственно включение разряда/сегмента производиться низким уровнем.

Для индикаторов с общим катодом схема аналогична, только транзисторы следует взять NPN структуры, и управляться индикатор будет высоким уровнем.
Продемонстрируем сказанное, напишем программу, которая будет перебирать числа от 0 до 9999 и выводить их на семисегментный индикатор.

  1. #include <avr/io.h>

  2. #include <avr/interrupt.h>

  3. // 0 1 2 3 4 5 6 7 8 9

  4. const unsigned char codes[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

  5.  

  6. unsigned char data[4]={0x00,0x00,0x00,0x00};

  7. unsigned char counter=0;

  8.  

  9. void pause (unsigned int a)

  10. { unsigned int i; for (i=a;i>0;i--); }

  11.  

  12. void init_timer (void)

  13. { TIMSK=(1<<TOIE0); //Enable timer overflow interrupt

  14. TCCR0=(0<<CS00)|(1<<CS01)|(0<<CS02); //Prescaller = /1

  15. }

  16.  

  17. void convert_data (unsigned int x)

  18. {unsigned int temp,res;

  19. temp=x;

  20. res=temp/1000; //Calculate 1000-s

  21. data[3]=codes[res];

  22. temp=temp-res*1000;

  23.  

  24. res=temp/100; //Calculate 100-s

  25. data[2]=codes[res];

  26. temp=temp-res*100;

  27.  

  28. res=temp/10; //Calculaate 10-s

  29. data[1]=codes[res];

  30. temp=temp-res*10;

  31.  

  32. data[0]=codes[temp]; //Calculate 1-s

  33. }

  34.  

  35. ISR (TIMER0_OVF_vect)

  36. {PORTD=0xff;

  37. PORTB=~_BV(counter); //Enable digit

  38. PORTD=~data[counter]; //Write code

  39. counter=(counter+1)%4; //Increment digit counter

  40.  

  41. TCNT0=0x00; //Clear timer

  42. }

  43.  

  44. int main(void)

  45. { unsigned int x=0;

  46. DDRD=0xff;

  47. PORTD=0x00;

  48. DDRB=0x0f;

  49. PORTB=0x0f;

  50.  

  51. pause(1000); //Settle pause

  52. init_timer(); //Init timer

  53. sei(); //Interrupt enable

  54. while(1)

  55. {convert_data(x); //Conver data to codes

  56. if (x<9999) x=x+1; //Increment data

  57. else x=0;

  58. pause(30000);

  59. }

  60. return 1;

  61. }


Код очень простой. В массиве codes находятся коды, которые следует выводить на порт, чтобы получить желаемую цифру. Смена активной цифры производиться по прерыванию от переполнения таймера 0. А функция convert_data(int x) раскладывает число х по разрядам, и записывает соответствующие коды в массив data, данные из которого выводятся непосредственно на индикатор, при срабатывание прерывания.
Что из этого вышло, можно глянуть на рисунках:

Исходный код можно скачать в виде проекта под AVR Studio 4
Также, может кому пригодиться, платка для моего табло в формате .lay для программы Sprint Layout 5

avrlab.com

РадиоКот :: Динамическая индикация

РадиоКот >Обучалка >Микроконтроллеры и ПЛИС >Микроконтроллеры AVR - пишем, компилируем, прошиваем... >

Динамическая индикация

Итак, девочки и мальчики, сегодня дядя Сережа расскажет о том, как соорудить динамическую индикацию в домашних условиях, че это ваще такое, и зачем оно нам.

Не вдаваясь в нудные предисловия скажем так:

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

На этом принципе, как вы догадываетесь, построено кино и телевидение. Не слишком занудно?

Поконкретнее.

Скажем, нам нужно организовать вывод какого-то числа на 7-сегментные индикаторы. Ну, например, мы делаем часы, или термометр, или еще чего-нибудь. Давайте посчитаем, сколько понадобится проводов, чтобы подключить индикаторы.

Кстати, индикаторы выглядят примерно вот так:

Уделим пару слов увиденному.

Перед нами – самый обычный светодиодный 7-сегментный индикатор. Вопрос: почему 7-сегментный, когда сегментов на самом деле – восемь? Ответ: потому что 8-й сегмент – это точка, которая не входит в изображение цифры и вообще, является необязательной. Бывают индикаторы и без точек.

Для нумерации сегментов используются латинские буквы от a до h . У всех всех всех 7-сегментных индикаторов в нашем бренном мире, сегменты нумеруются в том порядке, как это показано на рисунке, и никак иначе.

Теперь смотрим на схемы. Во-первых, почему их две? Да потому что индикаторы бывают разные. Бывают – с общим катодом (ОК), бывают – с общим анодом (ОА). Среди буржуйских более распространены, как ни странно, индикаторы с ОА. Но встречаются и с ОК. Так что – нужно быть осторожным, чтоб не спутать. Хотя, нам-то все равно. Нашими индикаторами будет управлять контроллер. А его можно запрограммировать как на работу с ОК так и с ОА. Тут сложностей нет.

У каждого индикатора – 9 ножек. С 1 по 8 – выводы сегментов, 9-я – общий. Допустим, мы хотим отображать 4-разрядное число. Надо 4 индикатора.

Арифметика маленького Пети: 8*4 = 32. То есть, нам потребуется 32 провода (не считая общие) Неслабо? Ничуть. Тем более, учитывая, что у большинства контроллеров количество каналов ввода/вывода как раз равно 32. Как-то меня не очень греет перспектива угрохать все выводы контроллера на индикацию. А вас? Ведь надо ж еще куда-то подключить кнопочки, ручечки, датчички и пр. пр. пр. а мало ли чего? Кстати, у любимого нами AT 90 s 2313 всего 15 каналов ввода/вывода. Как здесь прикажете быть? Вот тут нам и поможет динамическая индикация. Делаем очень просто: подключаем все индикаторы параллельно. Точнее, сажаем выводы сегментов на общую шину. А общие провода оставляем раздельно.

Смотрим небольшой поясняющий мультик:

Че мы делаем?

Мы последовательно подаем напряжение на адресные входы индикаторов, и одновременно выдаем в шину данных 7-сегментный код, соответствующий индикатору, активному в данный момент.

Все. Прекращаю кормить Вас баснями. Сейчас мы пишем прогу. В этой проге мы выведем на 4-разрядный индикатор число 1234. Вот тут то нам и понадобится узнанное в предыдущей главе об оперативной памяти.

Итак, я хочу, чтобы значения цифр, выводимых на индикацию, лежали в ячейках памяти. Поскольку у индикатора 4 разряда – значит цифр тоже будет 4, и соответственно, столько же ячеек памяти. Дабы не заморачиваться, мы объединяем все четыре ячейки в переменную Digit . Итак, пишем:



.include "d:avravrasmappnotes2313def.inc"


	.def	Temp1=R16
	.def	Temp2=R17
	.def	Temp3=R18
	.def	Temp4=R19
	.def	Temp=R20

.dseg

Digit:     .byte 4


Написали.
Теперь самое время определиться с портами.

Поскольку у нас на порт B уже повешены светодиоды – наверно не будет большим криминалом повесить туда еще и сегменты индикаторов.

Порт D пока что пустует – ну дык значит посадим туда общие провода. Рисуем схему:

Схему прошивки, как и договаривались, не показываем, но подразумеваем ее наличие.

В данной схеме используется два сдвоенных индикатора C562. Эти индикаторы – с общим катодом (об этом говорит буква C в названии). Будем иметь это ввиду при написании программы.

Вы можете поставить совершенно любые индикаторы, какие будут под рукой. Можно, например, поставить один счетверенный, или четыре одинарных. Также, можно ставить индикаторы с общим анодом – в программе только придется дописать пару строчек – совершенно ничего сложного…

Резисторы в шине данных взяты меньшего номинала, чем были. Зачем? Чтоб ярче светили светодиоды. Поскольку индикация динамическая, то каждый индикатор работает только 1/4 общего времени работы схемы. Глаз это воспринимает как пропорциональное уменьшение яркости свечения (т.е. – тоже в 4 раза.). Чтобы как-то компенсировать этот недостаток, на светодиоды подается завышенный ток (больше максимально допустимого). В статическом режиме этот ток может причинить светодиодам вред. Однако в динамическом режиме, кратковременные воздействия тока не столь фатальны.

Ну вот, вроде все. Теперь пишем программулину дальше.



.cseg
.org 0

              rjmp RESET    ; Reset Handler
              rjmp EXT_INT0 ; IRQ0 Handler
              rjmp EXT_INT1 ; IRQ1 Handler
              rjmp TIM_CAPT1 ; Timer1 Capture Handler
              rjmp TIM_COMP1 ; Timer1 Compare Handler
              rjmp TIM_OVF1 ; Timer1 Overflow Handler
              rjmp TIM_OVF0 ; Timer0 Overflow Handler
              rjmp UART_RXC ; UART RX Complete Handler
              rjmp UART_DRE ; UDR Empty Handler
              rjmp UART_TXC ; UART TX Complete Handler
              rjmp ANA_COMP ; Analog Comparator Handler



EXT_INT0  :	ret
EXT_INT1  :	ret
TIM_CAPT1 :	ret
TIM_OVF0  :	ret
TIM_OVF1  :   ret
UART_RXC  :   ret
UART_DRE  :   ret
UART_TXC  :   ret
ANA_COMP  :   ret
TIM_COMP1 :   ret



reset:        ldi Temp1,RamEnd	;инициализация стека
              out SPL,Temp1

              cli

              ldi Temp,0b11111111 ;настройка портов
              out ddrb,Temp

              ldi Temp,0b00001111
              out ddrd,Temp

              ldi Temp,4
              sts Digit  ,Temp      ;загрузка начальных сначений
              ldi Temp,3
              sts Digit+1,Temp
              ldi Temp,2
              sts Digit+2,Temp
              ldi Temp,1
              sts Digit+3,Temp


;*********************************************************
;MAIN
;*********************************************************

IndicCycle:   rcall Display        ;цикл индикации
              rjmp IndicCycle

;*********************************************************
Display:
;последовательный вывод на индикацию содержимого
;переменной Digit

              lds Temp1,Digit      ;загружаем 0-ю ячейку
              ldi Temp,0b00001110  ;активируем 0-й разряд
                                   ;индикации
              out PortD,Temp
              rcall Decoder        ;вызываем 7-сегм. декодер
              out PortB,Temp1      ;выводим значение в порт
              rcall Delay1         ;ждем

              lds Temp1,Digit+1    ;и .т.д
              ldi Temp,0b00001101
              out PortD,Temp
              rcall Decoder
              out PortB,Temp1
              rcall Delay1

              lds Temp1,Digit+2
              ldi Temp,0b00001011
              out PortD,Temp
              rcall Decoder
              out PortB,Temp1
              rcall Delay1

              lds Temp1,Digit+3
              ldi Temp,0b00000111
              out PortD,Temp
              rcall Decoder
              out PortB,Temp1
              rcall Delay1

              ret


;*********************************************************
Decoder:
;преобразование двоичного числа
;в код 7-сегментного индикатора

              ldi ZL,Low(DcMatrix*2)   ;инициализация массива
              ldi ZH,High(DcMatrix*2)

              ldi Temp2,0             ;прибавление переменной
              add ZL,Temp1            ;к 0-му адресу массива
              adc ZH,Temp2

              lpm                     ;загрузка значения
              mov Temp1,r0
              ret

DcMatrix:
;массив - таблица истинности декодера
              ;     hgfedcba   hgfedcba
              .db 0b00111111,0b00000110	;0,1
              .db 0b01011011,0b01001111	;2,3
              .db 0b01100110,0b01101101	;4,5
              .db 0b01111101,0b00000111	;6,7
              .db 0b01111111,0b01101111	;8,9

;*********************************************************
Delay1:
;цикл задержки

              push Temp1
              push Temp2


              ldi Temp1,0
              ldi Temp2,50

d11:          dec Temp1
              brne d11
              dec Temp2
              brne d11

              pop Temp2
              pop Temp1

              ret


Очень внимательно читаем программу.

Сначала мы настраиваем прерывания, порты и стек. Это стандартно.
Затем, мы записываем в переменную Digit число 1234.
Потом мы выходим в главный цикл программы. из этого цикла вызывается функция Display , которая, собственно, и занимается динамическим выведением на индикацию числа из переменной Digit . Функция состоит из 4-х похожих кусков. Эти куски различаются лишь номером вызываемой ячейки Digit и разрядом текущего индикатора.
Из каждого кусочка вызывается функция Decoder – 7-сегментный декодер. Этот декодер преобразует значение из ячейки Digit в код 7-сегментного индикатора, и передает этот код обратно функции Display , которая выводит его на индикацию.

Вот так, в общих чертах, все просто и понятно.

Далее мы сделаем из этого дела секундомер.

<<--Вспомним пройденное----Поехали дальше-->>


Как вам эта статья?

Заработало ли это устройство у вас?

www.radiokot.ru

Динамическая индикация. Подключение светодиодной матрицы к микроконтроллеру | RadioLaba.ru

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Реализация динамической индикации для матрицы с разрешением 8х8

;Частота тактового генератора для примера 4 МГц, машинный цикл 1 мкс

 

              org         0000h            ;начать выполнение программы с адреса 0000h

              goto        Start            ;переход на метку Start

          

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Подпрограмма обработки прерываний

 

              org         0004h            ;начать выполнение подпрограммы с адреса 0004h

      

              movwf       W_TEMP           ;сохранение значений ключевых регистров

              swapf       STATUS,W         ;

              clrf        STATUS           ;

              movwf       STATUS_TEMP      ;

                                           ;  

              movf        FSR,W            ;сохранение текущего значения регистра FSR

              movwf       FSR_osn          ;в регистр FSR_osn

              movf        FSR_prer,W       ;восстановление ранее сохраненного значения

              movwf       FSR              ;регистра FSR из регистра FSR_prer

                                           ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

                                           ;загрузка содержимого регистра stolb в микросхему

                                           ;74HC595 (последовательный сдвиговый регистр)

              movf        stolb,W          ;копирование содержимого регистра stolb

              movwf       var              ;в регистр var

              movlw       .8               ;запись числа 8 в регистр scetbit, для отсчета

              movwf       scetbit          ;переданных битов

met2          btfsc       var,0            ;устанавливаем вывод ds в соответствии с

              bsf         ds               ;значением 7-го бита регистра var

              btfss       var,0            ;

              bcf         ds               ;

              bsf         sh_cp            ;тактируем вывод sh_cp, для защелкивания данных

              bcf         sh_cp            ;

              rrf         var,F            ;сдвиг регистра var вправо, для подготовки

                                           ;следующего бита

              decfsz      scetbit,F        ;декремент с условием регистра scetbit

              goto        met2             ;scetbit не равен нулю: переход на метку met2

                                           ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

                                           ;загрузка содержимого регистра INDF в микросхему

                                           ;74HC595 (последовательный сдвиговый регистр)

              movf        INDF,W           ;копирование содержимого регистра INDF

              movwf       var              ;в регистр var                                      

              movlw       .8               ;запись числа 8 в регистр scetbit, для отсчета

              movwf       scetbit          ;переданных битов

met1          btfsc       var,7            ;устанавливаем вывод ds в соответствии с

              bsf         ds               ;значением 7-го бита регистра var

              btfss       var,7            ;

              bcf         ds               ;

              bsf         sh_cp            ;тактируем вывод sh_cp, для защелкивания данных

              bcf         sh_cp            ;

              rlf         var,F            ;сдвиг регистра var влево, для подготовки

                                           ;следующего бита

              decfsz      scetbit,F        ;декремент с условием регистра scetbit

              goto        met1             ;scetbit не равен нулю: переход на метку met1            

                                           ;

              bsf         st_cp            ;тактируем вывод st_cp, для передачи загруженных

              bcf         st_cp            ;байтов на выходные линии микросхем 74HC595

                                           ;

              bcf         STATUS,C         ;сброс бита C регистра статус перед сдвигом

              rrf         stolb,F          ;сдвиг влево регистра stolb

                                           ;

              incf        FSR,F            ;инкремент регистра FSR, подготовка следующего

                                           ;регистра для отправки данных на 74HC595

              decfsz      shet,F           ;декремент с условием регистра shet

              goto        exxit            ;регистр shet не равен 0: переход на exxit

              movlw       data1            ;регистр shet равен 0: запись адреса первого

              movwf       FSR              ;регистра хранения иннформации в регистр FSR

              movlw       .8               ;запись числа 8 в регистр shet, для ведения

              movwf       shet             ;счета столбцов

              movlw       b'10000000'      ;запись двоичного числа 10000000 в

              movwf       stolb            ;регистр stolb, для включения 1-го столбца

                                           ;

exxit         bcf         INTCON,T0IF      ;сброс флага прерывания по переполнению TMR0

              movlw       .124             ;запись числа 124 в регистр таймера TMR0

              movwf       TMR0             ;

                                           ;  

              movf        FSR,W            ;сохранение текущего значения регистра FSR

              movwf       FSR_prer         ;в регистр FSR_prer

              movf        FSR_osn ,W       ;восстановление ранее сохраненного значения

              movwf       FSR              ;регистра FSR из регистра FSR_osn

                                           ;

              swapf       STATUS_TEMP,W    ;восстановление содержимого ключевых регистров

              movwf       STATUS           ;

              swapf       W_TEMP,F         ;

              swapf       W_TEMP,W         ;

                                           ;

              retfie                       ;выход из подпрограммы прерывания

 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Основная программа

                                

            

Start         .................            ;первоначальная настройка регистров    

              .................            ;специального назначения

              .................

 

              

              bsf         STATUS,RP0       ;запись двоичного числа 11010011 в регистр

              movlw       b'11010010'      ;OPTION_REG, тем самым устанавливаем внутренний

              movwf       OPTION_REG       ;источник тактового сигнала для TMR0

              bcf         STATUS,RP0       ;включаем предделитель перед TMR0

                                           ;устанавливаем коэффициент предделителя 1:8

                                           ;

              movlw       .8               ;запись числа 8 в регистр shet, перед запуском

              movwf       shet             ;прерываний по переполнению TMR0, выполняется

                                           ;однократно, после включения питания

              movlw       b'10000000'      ;запись двоичного числа 10000000 в

              movwf       stolb            ;регистр stolb, для включения 1-го столбца

                                           ;выполняется однократно, после включения питания

                                           ;

              movlw       data1            ;запись адреса первого регистра (регистры хранения  

              movwf       FSR_prer         ;информации) в регистр FSR_prer, выполняется

                                           ;однократно, после включения питания

                                           ;

              movlw       .8               ;очистка 8-ми регистров вывода информации на

              movwf       tmp              ;матрицу, равнозначно выключению

              movlw       data1            ;матрицы

              movwf       FSR              ;

met3          clrf        INDF             ;

              incf        FSR,F            ;

              decfsz      tmp,F            ;

              goto        met3             ;

                                           ;

              bcf         INTCON,T0IF      ;сброс флага прерывания по переполнению TMR0

              bsf         INTCON,T0IE      ;разрешение прерываний по переполнению TMR0

              bsf         INTCON,GIE       ;разрешение глобальных прерываний

                                           ;

m1            movlw       data1            ;пример вывода буквы R

              movwf       FSR              ;

              movlw       b'00000000'      ;

              movwf       INDF             ;

              incf        FSR,F            ;

              movlw       b'01111111'      ;

              movwf       INDF             ;

              incf        FSR,F            ;

              movlw       b'00001001'      ;

              movwf       INDF             ;

              incf        FSR,F            ;                        

              movlw       b'00011001'      ;

              movwf       INDF             ;

              incf        FSR,F            ;

              movlw       b'00101001'      ;

              movwf       INDF             ;

              incf        FSR,F            ;

              movlw       b'01000110'      ;

              movwf       INDF             ;

              incf        FSR,F            ;

              movlw       b'00000000'      ;

              movwf       INDF             ;

              incf        FSR,F            ;                                

              movlw       b'00000000'      ;

              movwf       INDF             ;

                                           ;

              .................            ;    

              .................            ;

              .................            ;

                                           ;

              end                          ;конец всей программы

                                           ;

radiolaba.ru

Семисегментный индикатор. Организация динамической индикации - Как подключить - AVR project.ru

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

Dim W As Integer
Dim N1 As Byte
Dim N2 As Byte
Dim N3 As Byte
Dim N4 As Byte
Dim M1 As Integer
Dim M2 As Integer
Dim M3 As Integer
Dim M4 As Integer


 После объявления переменных настраиваем порты на выход которые будут использоваться для подключения индикатора:

DDRC = &B11111111
DDRD = &B11111111

 Здесь я поставил на выход все ноги порта C и D, но можно оставить только те которые будут использоваться в качестве выходов, к примеру так:

DDRC=&B00001111 и DDRD = &B01111111 (четыре первых ноги порта С под аноды и шесть первых порта D под сегменты).

 

Затем присваиваем переменной W то значение, которое собираемся вывести на индикатор:

W = 1234

 В основном цикле программы присваиваем переменным М значение переменной W, я делаю так:

M1 = W
M2 = M1
M3 = M1
M4 = M1


 Это не паранойя)), это сделано с той целью, чтобы в во всех переменных М лежало одно и тоже число, так как во время операции присваивания легко может ворваться прерывание (если такое имеется и не отключено), в обработчике которого переменная W может измениться. И в случае если присваивание шло таким образом: М1=W, M2=W, M3=W, M4=W в переменных М будут лежать разные значения что приведет к каше в показаниях.

 

 После присвоения переменным значения начинаем работать с
каждой из них, преобразуя таким образом, чтобы в переменную N попало то значение, которое будет
отображаться на индикаторе: в переменной N1 должна оказаться «1», в N2 – «2», в N3 – «3», а в N4 – «4».

M1 = M1 / 1000        ' M1 = 1234/1000 = 1,234                            
N1 = Abs (m1)         ' N1 = Abs (1,234) = 1

 Abs – функция возвращающая целое число переменной.  В переменную N1 попала единица, что собственно и требовалось.

 Для присвоения двойки переменной N2 операция будет немного сложнее:

M2= M2 Mod 1000          ' M2 =1234 Mod 1000 = 234              
M2 = M2 / 100            ' M2 = 234 / 100 = 2,34
N2= Abs (m2)             ' N2 = Abs (2,34) = 2

 Для начала функцией Mod мы возвращаем переменной первые три
цифры числа (остаток от деления на 1000), а дальше все как в первом случае.

 С двумя последними разрядами практически тоже самое:

M3 = M3 Mod100                                  
M3 = M3 / 10
N3 = Abs(m3)
 
M4 = M4 Mod 10                                    
N4= Abs(m4)

 Теперь в наших переменных лежат те значения, которые мы хотим отобразить, самое время микроконтроллеру подрыгать ногами и вывести эти значения на индикатор, для этого вызываем подпрограмму обработки индикации:

   

Gosub Led

 Процессор перепрыгнет на подпрограмму с меткой Led:

Led:

  Portc =&B00001000    

 Здесь подаем высокий уровень на PORTC.3 , к этой ноге у нас подсоединен  анод первого разряда. Затем выбираем, какие сегменты необходимо зажечь, чтобы отобразить значение первой переменной. Она у нас единица поэтому ноль будет на ногах Portd.1и Portd.2, что соответствует сегментам B и С индикатора.

Select Case N1
         Case 0 : Portd = &B11000000
         Case 1 : Portd = &B11111001                  
         Case 2 : Portd = &B10100100
         Case 3 : Portd = &B10110000
         Case 4 : Portd = &B10011001
         Case 5 : Portd = &B10010010
         Case 6 : Portd = &B10000010
         Case 7 : Portd = &B11111000
         Case 8 : Portd = &B10000000
         Case 9 : Portd = &B10010000
      End Select
    Waitms 5  

 После того как зажгли нужные сегменты ждем 5 мс и переходим к отображению следующих чисел:

Portc = &B00000100                            
       Select Case N2
         Case 0 : Portd = &B11000000
         Case 1 : Portd = &B11111001
         Case 2 : Portd = &B10100100
         Case 3 : Portd = &B10110000
         Case 4 : Portd = &B10011001
         Case 5 : Portd = &B10010010
         Case 6 : Portd = &B10000010
         Case 7 : Portd = &B11111000
         Case 8 : Portd = &B10000000
         Case 9 : Portd = &B10010000
      End Select
 
    Waitms 5
 
 
       Portc = &B00000010                                  
 
      Select Case N3
         Case 0 : Portd = &B11000000
         Case 1 : Portd = &B11111001
         Case 2 : Portd = &B10100100
         Case 3 : Portd = &B10110000
         Case 4 : Portd = &B10011001
         Case 5 : Portd = &B10010010
         Case 6 : Portd = &B10000010
         Case 7 : Portd = &B11111000
         Case 8 : Portd = &B10000000
         Case 9 : Portd = &B10010000
      End Select
 
    Waitms 5
 
 
       Portc = &B00000001                                
 
      Select Case N4
         Case 0 : Portd = &B11000000
         Case 1 : Portd = &B11111001
         Case 2 : Portd = &B10100100
         Case 3 : Portd = &B10110000
         Case 4 : Portd = &B10011001
         Case 5 : Portd = &B10010010
         Case 6 : Portd = &B10000010
         Case 7 : Portd = &B11111000
         Case 8 : Portd = &B10000000
         Case 9 : Portd = &B10010000
      End Select
 
    Waitms 5

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

Вот что получим в итоге:


 За счет маленькой задержки переключения не будут заметны человеческому глазу и мы увидим целое число 1234. 

Скачать исходник и проект в протеусе можно ниже:


avrproject.ru

Управление семисегментными индикаторами по одному проводу

Часто бывает ситуация, когда надо выполнить простую задачу с которой справится даже грошовый контроллер вроде ATTiny12, но вот незадача — нужна индикация, а под какой нибудь семисегментный индикатор придется убить дофига выводов, а их всего восемь, из которых два питание, а один Reset. Можно, конечно, взять просто МК потолще, но мне больше по душе разные схемотехнические извраты. Вот и тут камрад Kalobyte подкинул ссылку на прикольную схему управления тройным (а в перспективе хоть десятерным) семисегментным индикатором по одному проводу.

Индикатор зажигается посредством сдвигового регистра 74HC164, всего таких регистров можно навесить очень много, просто соединив по цепи несколько регистров. Если отбросить RESET, то для проталкивания данных в регистр нужны две линии — Data и Clock. Путем небольшого изврата можно эти две линии обьединить в одну.

Для начала немного теории
Обычная RC цепочка. Отличается тем, что напряжение на конденсаторе не может измениться мгновенно. Время заряда и разряда зависит от емкости кондера и сопротивления резистора. Оценить его можно по постоянной времени T=R*C , это время за которое заряд изменится примерно втрое (в е=2.7 раз).


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

Если подадим короткий импульс, по времени намного меньше чем постоянная времени, то напряжение на конденсаторе изменится совершенно незначительно. Так, дрыгнется да и только. То же относительно короткой паузы. Незначительный провал и все возвращается на круги своя. Чуете куда я клоню?

Правильно!
У нас у регистра есть линия данных (Data) и линия строба (Clock). Когда уровень на Clock меняется с нуля на единицу (передний фронт) с линии Data считывается текущий уровень. Их можно разнести по разные стороны RC фильтра. И одни сигналы передавать узкими, другие широкими импульсами.

А теперь думаем. Строб един для всех регистров, а данные различные для разных битов. Поэтому строб будет всегда один, а данные будут меняться. Делаем строб узким и снимаем его до RC цепи. Узкие импульсы не могут пройти сквозь RC цепь, а данные передаем широкими импульсами, которые спокойно перезаряжают конденсатор. Данные мы снимаем после RC цепи.

Получается вот такая схема:


Сигналы Reset я даже рисовать не стал — они там намертво на +5 повешаны. Сброс регистров осуществляется загоном в него нулей. Регистов тут два, но как я уже писал, можно навесить их еще очень много, лишь бы хватило скорости их заполнять.

Осталось теперь хитрым образом формировать биты на линии. Чтобы было понятней, я нарисовал поясняющую картинку, на которой показана передача байта 10011010.


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

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

А вот так выглядит осциллограмма реальной передачи

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

Ссылки по теме:

easyelectronics.ru

Динамическая индикация 9 разрядного индикатора по последовательной шине.

РадиоКот >Лаборатория >Цифровые устройства >

Динамическая индикация 9 разрядного индикатора по последовательной шине.

Всем привет.
Попался мне вот этот 9 разрядный семисегментный индикатор с последовательной шиной, выполненный на двух микросхемах 74HC595D. Индикатор стоял в телефоне с АОН русь 27, до того как по нем прошлась гроза, да так что, восстановлению телефон не подлежал, индикатору тоже досталось, сгорели все 2 микрухи.

Проверить индикатор вообще оказалось просто. При рабочих микросхемах и поданном питании, если докоснуться к линиям данных, на индикаторе бегает всякая ерунда(при условии что линии висят в воздухе). Индикатор с динамической индикацией, то есть для отображения информации последовательно зажигаем каждую цифру на определенное время, например на 2 милисекунды. Сложностей с ним почти нет, если не учитывать что выходов у 74HC595D восемь, а цифр на индикаторе девять. Девятая цифра подключена к выводу Q7" последовательный выход данных. Какие выводы и для чего они нужны, читайте в DATASHEET на микросхему.

Здесь я просто расскажу как я решил проблему вывода 8 и 9 цифры. Работать с этими микросхемами просто. Для того чтобы загрузить данные, устанавливаем бит данных на выводе DS serial data input, и сдвигаем (загружаем) положительным импульсом SHcp shift register clock input. Для сохранения и вывода данных в регистре подаем положительный импульс на STcp storage register clock input. Неувязка получается при выводе 8 и 9 цифры. Индикатор здесь с общим катодом, чтобы включить, например 4 цифру, нужно загрузить число 0b11110111, при этом на выходе Q7" будет сдвинутое значение из Q6, в данном случае 1, то что нужно. Значение Q6 по положительному импульсу SHcp сдвигается в Q7". При выводе восьмой цифры ноль сдвигается в Q7 и Q7" (Q7"=1 0b10111111 Q7"=0 0b01111111), в итоге у нас включены 8 и 9 разряд, импульсом STcp сохраняем и выводим это значение, теперь если дать импульс SHcp копейка из Q6 переместится в Q7", 9 разряд потушен! По программе импульсы STcp и SHcp идут синхронно. Для включения 9 разряда нужно загрузить число 0b11111111, вывести его импульсом STcp, затем загружаем в регистр нули до тех пор, пока ноль из Q6 не переместится в Q7".

Может быть все это можно сделать проще. Вот у меня индикатор заработал и я на этом успокоился. Сейчас он работает в часах. Хочу сказать об одном маленьком минусе. В часах есть возможность регулировки яркости индикатора. Период включения каждой цифры 2 ms. Что бы уменьшить яркость индикатора цифру можно включать например на 1 ms, оставшиеся время она будет потушена. При этом яркость свечения снижается. Если пользоваться таким способом, то из за того что 9 разряд (Q7") самый нагруженный, на низкой яркости индикатора яркость девятой цифры чуть меньше. Про исходник. Сразу предупреждаю программист я никакой, поэтому просьба не ругать. А вот файлы для PROTEUS.

Вопросы складываем тут.


Как вам эта статья?

Заработало ли это устройство у вас?

www.radiokot.ru

Arduino. Динамическая индикация | CUSTOMELECTRONICS.RU

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

Подготовка к работе

В предыдущей статье мы разобрали пример работы с одним разрядом. В ней использовалась статическая индикация. Увеличить количество разрядов можно, если использовать еще один дисплей и еще семь выводов микроконтроллера, но гораздо выгоднее отображать разряды индикатора по очереди с высокой скоростью. Так, чтобы это стало не заметно для глаза.
Для этого на индикаторе должны быть объединены аноды разрядов и катоды сегментов.

Индикатор с динамической индикацией

Все примеры выполнены с использованием EduBoard и TutorShield. На нашем шилде именно такой индикатор. Для его использования установите перемычки, выделенные на рисунке:

Перемычки для включения индикатора

Принципиальная схема подключения индикатора:

Принципиальная схема подключения индикатора

Первый пример

Для начала выведите два разных числа на индикаторе, используя следующий код:

#define DIG1 4
#define DIG2 5
#define A 6
#define B 7
#define C 8
#define D 9
#define E 10
#define FF 11
#define G 12
#define TAKT 1000

void setup() {                
  pinMode(A, OUTPUT); pinMode(B, OUTPUT);
  pinMode(C, OUTPUT); pinMode(D, OUTPUT);
  pinMode(E, OUTPUT); pinMode(FF, OUTPUT);
  pinMode(G, OUTPUT); pinMode(DIG1, OUTPUT);
  pinMode(DIG2, OUTPUT);
  digitalWrite(A,HIGH); digitalWrite(B,HIGH);
  digitalWrite(C,HIGH); digitalWrite(D,HIGH);
  digitalWrite(E,HIGH); digitalWrite(FF,HIGH);
  digitalWrite(G,HIGH); digitalWrite(DIG1,HIGH);
  digitalWrite(DIG2,HIGH);
}

void loop() {
  digitalWrite(DIG1,LOW);
  Show(3);
  delay(TAKT);
  Clean();
  digitalWrite(DIG1,HIGH);
  digitalWrite(DIG2,LOW);
  Show(4);
  delay(TAKT);
  Clean();
  digitalWrite(DIG2,HIGH);
}

void Show(int digit) {
  switch(digit) {
    case 0: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW); digitalWrite(D,LOW);
      digitalWrite(E,LOW); digitalWrite(FF,LOW);
    }
    break;
    case 1: {
      digitalWrite(B,LOW); digitalWrite(C,LOW);
    }
    break;
    case 2: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(D,LOW); digitalWrite(E,LOW);
      digitalWrite(G,LOW);
    }
    break;
    case 3: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW); digitalWrite(D,LOW);
      digitalWrite(G,LOW);
    }
    break;
    case 4: {
      digitalWrite(B,LOW); digitalWrite(C,LOW);
      digitalWrite(FF,LOW); digitalWrite(G,LOW);
    }
    break;
    case 5: {
      digitalWrite(A,LOW); digitalWrite(C,LOW);
      digitalWrite(D,LOW); digitalWrite(FF,LOW);
      digitalWrite(G,LOW);
    }
    break;
    case 6: {
      digitalWrite(A,LOW); digitalWrite(C,LOW);
      digitalWrite(D,LOW); digitalWrite(E,LOW);
      digitalWrite(FF,LOW); digitalWrite(G,LOW);
    }
    break;
    case 7: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW);
    }
    break;
    case 8: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW); digitalWrite(D,LOW);
      digitalWrite(E,LOW); digitalWrite(FF,LOW);
      digitalWrite(G,LOW);
    }
    break;
    case 9: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW); digitalWrite(D,LOW);
      digitalWrite(FF,LOW); digitalWrite(G,LOW);
    }
    break;
  }
}

void Clean() {
    digitalWrite(A,HIGH); digitalWrite(B,HIGH);
    digitalWrite(C,HIGH); digitalWrite(D,HIGH);
    digitalWrite(E,HIGH); digitalWrite(FF,HIGH);
    digitalWrite(G,HIGH);
}

Основной цикл этого примера линеен и прост. Сначала в первом разряде на одну секунду выводится цифра 3, затем во втором 4.
Если уменьшить интервал TAKT, то можно добиться того, чтобы за счет инерционности зрения казалось, что цифра 34 горит непрерывно. Какую длительность поставить? С одной стороны она должна быть как можно больше, чтобы осталось время на выполнение других задач, с другой стороны как можно меньше, чтобы глаз не видел мерцания.
Эмпирическим путем установлено, что обновления изображения с частотой 50 раз в секунду вполне достаточно для приемлемого восприятия. Это значит, что идентификатор TAKT должен иметь значение меньше 10мс. Попробуйте запустить программу с этим числом, а затем попробуйте увеличить его до 20мс и понаблюдайте за разницей.
Не забудьте после оставить значение TAKT 10.

Управление яркостью

При динамической индикации разряды горят не на протяжении всего времени и светятся лишь на половину (при двух разрядах). Это нужно учитывать при подборе токоограничивающих резисторов индикатора.
Также можно управлять яркостью. В предыдущем примере добавьте еще один идентификатор BRIGHT и замените основной цикл:

#define BRIGHT 2

void loop() {
  digitalWrite(DIG1,LOW);
  Show(3);
  delay(BRIGHT);
  Clean();
  digitalWrite(DIG1,HIGH);
  delay(TAKT-BRIGHT);
  digitalWrite(DIG2,LOW);
  Show(4);
  delay(BRIGHT);
  Clean();
  digitalWrite(DIG2,HIGH);
  delay(TAKT-BRIGHT);
}

Переопределение идентификатора BRIGHT от 0 до 10 будет приводить к изменению яркости. На время BRIGHT индикатор будет включен, а на время TAKT-BRIGHT выключен. Надо понимать, что эта константа будет справедлива только для ситуаций, когда TAKT равен 10мс.

Упрощение использования

На данный момент программа в основном цикле занимается только тем, что выводит числа. Для дальнейшего ее упрощения нужно понимать для чего будет использоваться сегментный индикатор.
С применением индикатора можно сделать, например, вольтметр. В нем блок АЦП микроконтроллера периодически делает замер и сохраняет результат в память. Это результат можно из памяти вытащить и показать на индикаторе. То есть желательно иметь функцию, которая будет разделять двухразрядные числа две цифры. Назовем эту функцию DisplayMath():

void DisplayMath(int data) {
  dig1 = dig2 = 0;
  if (data < 100) {
    while (data >= 10) {
      data -= 10;
      dig1++;
    }
    dig2 = data;
  }
}

В эту функцию будет передаваться число, которое нужно разобрать по разрядам. Результат обработки функция записывает в глобальные переменные dig1 и dig2.
Для начала происходит проверка входных данных. Если переданное значение больше 100, то функция не будет делать ничего и переменные dig1 и dig2 обнулятся. Если меньше 100, то запускается пересчет.
Далее локальная переменная data декрементируется с шагом 10 и подсчитывается количество итераций. Например, если функции передано значение 48 цикл while будет выполнен 4 раза и концу его выполнения переменные будут иметь состояние dig1=4, data=8. Далее остается только записать остаток data в переменную dig2.
В итоге после запуска этой функции в переменных dig1 и dig2 окажутся значения разрядов.
Сам вывод цифр на дисплей тоже лучше вынести в отдельную функцию. Назовем ее DisplayShow():

void DisplayShow() {
  digitalWrite(DIG1,LOW);
  Show(dig1);
  delay(BRIGHT);
  Clean();
  digitalWrite(DIG1,HIGH);
  delay(TAKT-BRIGHT);
  digitalWrite(DIG2,LOW);
  Show(dig2);
  delay(BRIGHT);
  Clean();
  digitalWrite(DIG2,HIGH);
  delay(TAKT-BRIGHT);
}

Окончательный код с использованием этих функций:

#define DIG1 4
#define DIG2 5
#define A 6
#define B 7
#define C 8
#define D 9
#define E 10
#define FF 11
#define G 12
#define TAKT 10
#define BRIGHT 2

int dig1 = 0;
int dig2 = 0;

void setup() {                
  pinMode(A, OUTPUT); pinMode(B, OUTPUT);
  pinMode(C, OUTPUT); pinMode(D, OUTPUT);
  pinMode(E, OUTPUT); pinMode(FF, OUTPUT);
  pinMode(G, OUTPUT); pinMode(DIG1, OUTPUT);
  pinMode(DIG2, OUTPUT);
  digitalWrite(A,HIGH); digitalWrite(B,HIGH);
  digitalWrite(C,HIGH); digitalWrite(D,HIGH);
  digitalWrite(E,HIGH); digitalWrite(FF,HIGH);
  digitalWrite(G,HIGH); digitalWrite(DIG1,HIGH);
  digitalWrite(DIG2,HIGH);
}

void loop() {
  DisplayMath(34);
  DisplayShow();
}

void DisplayMath(int data) {
  dig1 = dig2 = 0;
  if (data < 100) {
    while (data >= 10) {
      data -= 10;
      dig1++;
    }
    dig2 = data;
  }
}

void DisplayShow() {
  digitalWrite(DIG1,LOW);
  Show(dig1);
  delay(BRIGHT);
  Clean();
  digitalWrite(DIG1,HIGH);
  delay(TAKT-BRIGHT);
  digitalWrite(DIG2,LOW);
  Show(dig2);
  delay(BRIGHT);
  Clean();
  digitalWrite(DIG2,HIGH);
  delay(TAKT-BRIGHT);
}

void Show(int digit) {
  switch(digit) {
    case 0: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW); digitalWrite(D,LOW);
      digitalWrite(E,LOW); digitalWrite(FF,LOW);
    }
    break;
    case 1: {
      digitalWrite(B,LOW); digitalWrite(C,LOW);
    }
    break;
    case 2: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(D,LOW); digitalWrite(E,LOW);
      digitalWrite(G,LOW);
    }
    break;
    case 3: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW); digitalWrite(D,LOW);
      digitalWrite(G,LOW);
    }
    break;
    case 4: {
      digitalWrite(B,LOW); digitalWrite(C,LOW);
      digitalWrite(FF,LOW); digitalWrite(G,LOW);
    }
    break;
    case 5: {
      digitalWrite(A,LOW); digitalWrite(C,LOW);
      digitalWrite(D,LOW); digitalWrite(FF,LOW);
      digitalWrite(G,LOW);
    }
    break;
    case 6: {
      digitalWrite(A,LOW); digitalWrite(C,LOW);
      digitalWrite(D,LOW); digitalWrite(E,LOW);
      digitalWrite(FF,LOW); digitalWrite(G,LOW);
    }
    break;
    case 7: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW);
    }
    break;
    case 8: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW); digitalWrite(D,LOW);
      digitalWrite(E,LOW); digitalWrite(FF,LOW);
      digitalWrite(G,LOW);
    }
    break;
    case 9: {
      digitalWrite(A,LOW); digitalWrite(B,LOW);
      digitalWrite(C,LOW); digitalWrite(D,LOW);
      digitalWrite(FF,LOW); digitalWrite(G,LOW);
    }
    break;
  }
}

void Clean() {
    digitalWrite(A,HIGH); digitalWrite(B,HIGH);
    digitalWrite(C,HIGH); digitalWrite(D,HIGH);
    digitalWrite(E,HIGH); digitalWrite(FF,HIGH);
    digitalWrite(G,HIGH);
}

Обратите внимание насколько прост основной цикл loop(). В нем всего две строки. Если вы измените значение 34 на любое другое, то оно будет отображено на индикаторе.
Для демонстрации работы индикатора запустим счетчик с произвольной скоростью и будем выводить его состояние. Замените основной цикл и добавьте две переменных:

int value = 0;
int i = 0;

void loop() {
  i++;
  if (i == 7) {
    i = 0;
    value ++;
    if (value == 100) value = 0;
  }
  DisplayMath(value);
  DisplayShow();
}

Вы увидите как на экране побегут цифры от 0 до 99.

Заключение

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

Индивидуальные задания

  1. Увеличьте модуль счета. Подключите четыре дополнительных светодиода на выводах A0-A5 и допишите программу так, чтобы счетчик считал от 0 до 699.
  2. Если в старшем разряде 0, то его нет смысла показывать. Доработайте программу так, чтобы старший разряд был погашен, если нужно показать число меньше 10
  3. Сделайте так, чтобы если функции DisplayMath() передано число больше 99 на дисплее отображалось -0 (overflow, переполнение)

Остальные статьи цикла можно найти здесь.

Мы будем очень рады, если вы поддержите наш ресурс и посетите магазин наших товаров shop.customelectronics.ru.

www.customelectronics.ru

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *