Вывод символов на vga монитор.
- Войдите на сайт для отправки комментариев
Пнд, 06/06/2022 - 11:27
Была задумка заменить "севшие" и неисправные кинескопы в УЦИ на светодиодные индикаторы.
Чтобы меньше паять(напаялся уже),взял готовые 8-ми разрядные цифровые с SPI интерфейсом(MAX7219).
Такой дисплей получился:

Когда процесс пошёл ,увидел ,что индикация для станка микроскопическая,
а другие варианты-морока со сборкой и дорого для экспериментов.
И на производстве не заинтересовались-обходились же до сих пор.
Забросил. А осадок остался-не завершённое дело.
Пришла идея,вот бы поженить SPI и VGA-и там и там последовательный поток информации.
В интернетовском лесу нашёл вот это https://www.gammon.com.au/forum/?id=11608
В программе не разобрался-какая-то иностранщина(а дедушка новичок),
но с переводчиком синхронизацию и настройки таймеров осилил.
А дальше-есть цель, средства(ардуино+старый монитор) и предлинный больничный.
подключение ардуино к монитору:

Голый принцип использования SPI:
[code] //arduino-1.8.7-windows // плата Arduino Nano v 3.0 ATmega328P 16 мгц // Голый принцип использования SPI volatile int l=0; //счётчик строк горизонтальной развёртки void setup() { pinMode (3, OUTPUT); //OC2B-->Pin3 строчная синхронизация pinMode (10, OUTPUT); //OC1B-->Pin10 кадровая синхронизация (вывод pin10(PB2)настроен на выход-не участвует в аппаратном SPI) pinMode(11, OUTPUT); //видео //-------------------Настройка SPI------------------------------------------ SPCR=0b01010100; //включен ,старшим битом вперёд,ведущий, режим1(по последнему фронту SCK // фиксация посл. бита на MOSI- поэтому последний бит должен=0) SPSR &=~(1<<0); // тактовая 16мгц/4 //----------------- выключение таймера 0 --------------------------------------- TIMSK0 = 0; OCR0A = 0; OCR0B = 0; //-------------------- Таймер 1 для кадровой синхронизации ------------------ TCCR1A = 0b00110011; // включен выход OC1B(Pin10),режим ШИМ (OCR1A) TCCR1B = 0b00011101; // 16мгц/1024-предделитель OCR1A = 259; //регистр сравнения A-выполняет роль TOP -период кадровой примерно=16.6мс OCR1B = 0; //регистр сравнения B-по совпадению вкл. OC1B(в 1),после TCNT1=0 выкл. OC1B(в 0) // OCR1B = 0, но на сравнение уходит 1 такт-получается 64 мкс ширина кадрового синхроимпульса TIMSK1 = bit (TOIE1); // разрешает прерывание по переполнению счётного регистра ( TOP=OCR1A=259) TIFR1 = bit (TOV1); // при записи 1 флаг сбрасывается(там и так 0) //----------------------- Таймер 2 для строчной синхронизации ------------------------ TCCR2A = 0b00110011; // включен выход OC2B(Pin3),режим ШИМ (OCR2A) TCCR2B = 0b00001010; // предделитель 16мгц/8 OCR2A = 63; //регистр сравнения A-период строчной прим.=32мкс OCR2B = 7; //регистр сравнения B-синхроимпульс прим.=4мкс TIMSK2 = bit (TOIE2); // разр. прер. по переполнению счётного регистра(TOP=OCR2A=63) TIFR2 = bit (TOV2); // там и так 0 } //***********конец void setup()************ void loop() {} // пустая крутится ISR (TIMER1_OVF_vect)//----------------обработчик прерывания от таймера1------------------------- { l=0; //счётчик строк гориз. развёртки delayMicroseconds(3000); //иммитация полезной программы TIFR2 |=(1<<TOV2); // сбросим флаг пропущенных прерываний от Т2 } //конец обработчика таймера1 ISR (TIMER2_OVF_vect) //---------------обработчик прерывания от таймера2 --------------------- { delayMicroseconds(7); // для выравнивания по горизонтали l=l+1; // счёт начинается после окончания обработчика ISR (TIMER1_OVF_vect)) if (l<100||l>200) return ; // ограничение отображения сверху,снизу //-------------------отображение ------------------------------- SPSR &=~(1<<0); // тактовая 16мгц/4 SPDR=0b11111110; // засвечивается вертикальная полоса, размер по горизонтали от тактовой SPI delayMicroseconds(4); // задержка позволяет дождаться полного вывода предыдущего и // развести символы по горизонтали SPDR=0b10101010; // следующий вывод delayMicroseconds(4); SPSR|=(1<<0); // меняем тактовую частоту SPI(x2) после задержки(полоса уже) SPDR=0b10101010; // следующий вывод-и так заполняем всю строку }//конец обработчика от таймера2 [/code]
Если коротко-в начале кадра готовится информация(в кадровом обработчике),
ничего не выводится(другие прерывания же запрещены).
По окончанию кадрового обработчика начинают включатся строчные и
информация в соответствии с знакоместом и матрицей выстреливается на экран.
уже в конце упёрся в тупик- вертикальные края символов немного смазанные.
Подозреваю ,что из loop вход в обработчик занимает разное время.
А у старшего товарища Nickа Gammonа на картинке всё чётко.
И подсмотрел,как у него запускается строка - а из спящего режима.Всё закрыл тему.
Картинка получилась чёткая,дедушка доволен.
Теперь для дисплея пилить,строгать и паять по минимуму.
строка символов:

[code] //arduino-1.8.7-windows // плата Arduino Nano v 3.0 ATmega328P 16 мгц // Отображение символьной информации на VGA монитор. #include <avr/sleep.h> //работа со спящими режимами volatile byte g=0; // счётчик строк горизонтальной развёртки в пикселе volatile byte t=0; // счётчик пикселей в символе по вертикали(см. Zngenerator[16][15]) volatile int l=0; //счётчик строк горизонтальной развёртки byte m=100; // m корректируем смещение изображения по вертикали byte k=50; // фиксация времени на обработчик T1(можно корректировать смещение изображения по вертикали) //--------------------------------массив знакогенератора 0...9 и т.д. -------------------------------- // полная матрица 8х15,знаки 7х10, с 11 по 15 пустые байты для промежутка между символами по вертикали,когда несколько строк. // В двоичном виде нагляднее написать шрифт,нулевой бит всегда=0(т.к. последний выводимый остаётся на выходе) const byte Zngenerator[16][15]={{0b01111100,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // это 0 {0b00001000,0b00011000,0b00101000,0b00001000,0b00001000,0b00001000,0b00001000,0b00001000,0b00001000,0b00001000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 1 {0b11111110,0b10000010,0b10000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000,0b10000000,0b11111110,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 2 {0b11111100,0b00000010,0b00000010,0b00000010,0b01111100,0b00000010,0b00000010,0b00000010,0b00000010,0b11111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 3 {0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b11111110,0b00000010,0b00000010,0b00000010,0b00000010,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 4 {0b11111100,0b10000000,0b10000000,0b10000000,0b11111100,0b00000010,0b00000010,0b00000010,0b00000010,0b11111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 5 {0b01111100,0b10000000,0b10000000,0b10000000,0b11111100,0b10000010,0b10000010,0b10000010,0b10000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 6 {0b11111110,0b00000010,0b00000100,0b00001000,0b00010000,0b00100000,0b00100000,0b00100000,0b00100000,0b00100000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 7 {0b01111100,0b10000010,0b10000010,0b10000010,0b01111100,0b10000010,0b10000010,0b10000010,0b10000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 8 {0b01111100,0b10000010,0b10000010,0b10000010,0b10000010,0b01111110,0b00000010,0b00000010,0b00000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 9 {0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, //чёрный {0b00000000,0b00000000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, //две точки {0b00000000,0b00000000,0b00000000,0b00000000,0b11111110,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000},//это (-)->12 {0b00000000,0b00010000,0b00010000,0b00010000,0b11111110,0b00010000,0b00010000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, //это(+)->13 {0b00000000,0b10000010,0b01000100,0b00101000,0b00010000,0b00101000,0b01000100,0b10000010,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000},// x->14 {0b00000000,0b10000010,0b01000100,0b00101000,0b00010000,0b00010000,0b00010000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000},// y->15 } ; // конец массива byte data1[9]={14,12,1,2,3,4,5,6,7}; // отображаемые данные void setup() { //noInterrupts(); pinMode (3, OUTPUT); //OC2B-->Pin3 строчная синхронизация pinMode (10, OUTPUT); //OC1B-->Pin10 кадровая синхронизация (вывод pin10(PB2)настроен на выход-не участвует в аппаратном SPI) pinMode(11, OUTPUT); //видео //-------------------Настройка SPI------------------------------------------ SPCR=0b01010100; //включен ,старшим битом вперёд,ведущий, режим1(по последнему фронту SCK // фиксация посл. бита на MOSI- поэтому последний бит должен=0) SPSR &=~(1<<0); // тактовая 16мгц/4 //----------------- выключение таймера 0 --------------------------------------- TIMSK0 = 0; OCR0A = 0; OCR0B = 0; //-------------------- Таймер 1 для кадровой синхронизации ------------------ TCCR1A = 0b00110011; // включен выход OC1B(Pin10),режим ШИМ (OCR1A) TCCR1B = 0b00011101; // 16мгц/1024-предделитель OCR1A = 259; //регистр сравнения A-выполняет роль TOP -период кадровой примерно=16.6мс OCR1B = 0; //регистр сравнения B-по совпадению вкл. OC1B(=1),после TCNT1=0 выкл. OC1B(=0) // OCR1B = 0, но на сравнение уходит 1 такт-получается 64 мкс ширина кадрового синхроимпульса TIMSK1 = bit (TOIE1); // разрешает прерывание по переполнению счётного регистра ( TOP=OCR1A=259) TIFR1 = bit (TOV1); // при записи 1 флаг сбрасывается(там и так 0) //----------------------- Таймер 2 для строчной синхронизации ------------------------ TCCR2A = 0b00110011; // включен выход OC2B(Pin3),режим ШИМ (OCR2A) TCCR2B = 0b00001010; // предделитель 16мгц/8 OCR2A = 63; //регистр сравнения A-период строчной прим.=32мкс OCR2B = 7; //регистр сравнения B-синхроимпульс прим.=4мкс TIMSK2 = bit (TOIE2); // разр. прер. по переполнению счётного регистра(TOP=OCR2A=63) TIFR2 = bit (TOV2); // там и так 0 set_sleep_mode (SLEEP_MODE_IDLE); // такой режим сна будет использоваться // interrupts(); } //***********конец void setup()************ void loop() { sleep_mode (); //Спим. Программа останавливается(Таймеры работают) stroka (); // по прерыванию Т2 выход из спящего сразу на эту подпрограмму( } // конец void loop() ISR (TIMER1_OVF_vect)//----------------обработчик прерывания от таймера1------------------------- { l=0; //счётчик строк гориз. развёртки t=0; //счётчик пикселей(прямоугольников) в символе по вертикали g=0; // счётчик строк горизонтальной развёртки в пикселе //************блок подготовки информации для отображения(в начале каждого кадра) // расчёт и заполнение массива data1 (или внешними данными) delayMicroseconds(3000); //--------иммитация блока подготовки выводимой инф.- // чем длительнее прогр. тем уже зона отображ. после неё // вертикальная синхронизация может нарушатся из-за разного времени выполнения do{} //обработчика таймера1-сделаем его фиксированным привязав к таймеру1(к кол-ву тактов) while (TCNT1L<k); // такт Т1 примерно 64мкс,64хk(50)=3200мкс-это заведомо больше delayMicroseconds(3000) TIFR2 |=(1<<TOV2); // сбросим флаг пропущенных прерываний от Т2 - может //нарушится строчная синхронизация,если нет режима sleep_mode } //******конец обработчика таймера1******* EMPTY_INTERRUPT(TIMER2_OVF_vect); // обработчик без действий-только вывести из сна /* ISR (TIMER2_OVF_vect) //---------------обработчик прерывания от таймера2 --------------------- //{stroka (); } // если stroka () здесь,а не в void loop() (не используется sleep_mode)-> // то не чёткие вертикальные границы символов */ //-----------------------------подпрограмма обработки строки------------------------------- void stroka () { delayMicroseconds(5); // для выравнивания по горизонтали l=l+1; // счёт начинается после окончания обработчика ISR (TIMER1_OVF_vect)) if (l<=m)return ; // смещение отображения по вертикали(зона отображения от m до 180+m строк) if(l>180+m) return ; // ограничение отображения снизу (матрица 8х15,12 строк в пикселе-12*15=180) //-------------------отображение ------------------------------- for(byte x=0;x<9;x++){ SPDR=Zngenerator[data1[x]][t]; delayMicroseconds(2); // время,чтобы вытащить байт из массива плюс задержки позволяют asm("nop"); // дождаться полного вывода предыдущего и asm("nop"); // развести символы по горизонтали asm("nop"); asm("nop"); } control (11); //передаём в функцию размер пикселя(кирпичика)это будет 12 строк гориз. развёртки //если несколько символьных строк разного размера-функция одна,параметр меняется }//конец void stroka ()-возвращаемся в void loop(), а там спим //---------------конец вывода изобр. строки ,проверки условий на следующую строку--------------------- void control (byte y) { g=g+1; // счётчик строк горизонтальной развёртки в пикселе if(g>y){ //размер пикселя по вертикали в строках гориз. развёртки g=0; // пиксель нарисовали t=t+1; // на следующий пиксель } if(t>14){ // всю строку символов вывели(все пиксели отработали)? t=0; } } [/code]
три строки символов:

[code] //arduino-1.8.7-windows // плата Arduino Nano v 3.0 ATmega328P 16 мгц #include <avr/sleep.h> //работа со спящими режимами byte k=0; byte o=0; //для 0.5 секунд int f=0; // минуты int c=0; // счётчик кадров в минуту для коррекции времени byte q=0; // используется для мигания (:) byte g=0; // счётчик строк горизонтальной развёртки в пикселе byte t=0; // счётчик пикселей в символе по вертикали(см. Zngenerator[16][15]) int l=0; //счётчик строк горизонтальной развёртки byte m=50; // m корректируем смещение изображения по вертикали volatile int w; // счётчик символьных строк //--------------------------------массив знакогенератора 0...9 и т.д. -------------------------------- // полная матрица 8х15,знаки 7х10, с 11 по 15 пустые байты для промежутка между символами по вертикали // в двоичном виде нагляднее написать шрифт,нулевой бит всегда=0(т.к. последний выводимый остаётся висеть) const byte Zngenerator[16][15]={{0b01111100,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // это 0 {0b00001000,0b00011000,0b00101000,0b00001000,0b00001000,0b00001000,0b00001000,0b00001000,0b00001000,0b00001000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 1 {0b11111110,0b10000010,0b10000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000,0b10000000,0b11111110,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 2 {0b11111100,0b00000010,0b00000010,0b00000010,0b01111100,0b00000010,0b00000010,0b00000010,0b00000010,0b11111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 3 {0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b11111110,0b00000010,0b00000010,0b00000010,0b00000010,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 4 {0b11111100,0b10000000,0b10000000,0b10000000,0b11111100,0b00000010,0b00000010,0b00000010,0b00000010,0b11111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 5 {0b01111100,0b10000000,0b10000000,0b10000000,0b11111100,0b10000010,0b10000010,0b10000010,0b10000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 6 {0b11111110,0b00000010,0b00000100,0b00001000,0b00010000,0b00100000,0b00100000,0b00100000,0b00100000,0b00100000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 7 {0b01111100,0b10000010,0b10000010,0b10000010,0b01111100,0b10000010,0b10000010,0b10000010,0b10000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 8 {0b01111100,0b10000010,0b10000010,0b10000010,0b10000010,0b01111110,0b00000010,0b00000010,0b00000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 9 {0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, //чёрный {0b00000000,0b00000000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, //две точки {0b00000000,0b00000000,0b00000000,0b00000000,0b11111110,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000},//это (-)->12 {0b00000000,0b00010000,0b00010000,0b00010000,0b11111110,0b00010000,0b00010000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, //это(+)->13 {0b00000000,0b10000010,0b01000100,0b00101000,0b00010000,0b00101000,0b01000100,0b10000010,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000},// x->14 {0b00000000,0b10000010,0b01000100,0b00101000,0b00010000,0b00010000,0b00010000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000},// y->15 } ; // конец массива byte data1[8]={14,12,0,1,2,3,4,5}; // отображаемые данные byte data2[15]={15,13,6,7,8,9,1,2,3,4,5,6,7,8,9}; void setup() { set_sleep_mode (SLEEP_MODE_IDLE); // такой режим сна будет использоватся pinMode (3, OUTPUT); //OC2B-->Pin3 строчная синхронизация pinMode (10, OUTPUT); //OC1B-->Pin10 кадровая синхронизация pinMode(11, OUTPUT); //видео digitalWrite(6,LOW); // в pin6 записываем 0-будем исключать один из цветов //-------------------Настройка SPI------------------------------------------ SPCR=0b01010100; //включен ,старшим битом вперёд,ведущий, режим1 //SPSR &=~(1<<0); //множитель x2 изменяем дальше в программе //----------------- выключение таймера 0 --------------------------------------- TIMSK0 = 0; OCR0A = 0; OCR0B = 0; //-------------------- Таймер 1 для кадровой синхронизации ------------------ TCCR1A = 0b00110011; // включен выход OC1B(Pin10),режим ШИМ (OCR1A) TCCR1B = 0b00011101; // 16мгц/1024-предделитель OCR1A = 259; //регистр сравнения A-выполняет роль TOP -период кадровой примерно=16.6мс OCR1B = 0; //регистр сравнения B-по совпадению вкл. OC1B(в 1),после TCNT1=0 выкл. OC1B(в 0) // OCR1B = 0, но на сравнение уходит 1 такт-получается 64 мкс ширина кадрового синхроимпульса TIMSK1 = bit (TOIE1); // разрешает прерывание по переполнению счётного регистра ( TOP=OCR1A=259) TIFR1 = bit (TOV1); // при записи 1 флаг сбрасывается(там и так 0) //----------------------- Таймер 2 для строчной синхронизации ------------------------ TCCR2A = 0b00110011; // включен выход OC2B(Pin3),режим ШИМ (OCR2A) TCCR2B = 0b00001010; // предделитель 16мгц/8 OCR2A = 63; //регистр сравнения A-период строчной прим.=32мкс OCR2B = 7; //регистр сравнения B-синхроимпульс прим.=4мкс TIMSK2 = bit (TOIE2); // разр. прер. по переполнению счётного регистра(TOP=OCR2A=63) TIFR2 = bit (TOV2); // там и так 0 } //***********конец void setup()************ void loop() { sleep_mode (); //Спим. Программа останавливается(Таймеры работают.) stroka (); // по прерыванию таймера 2 выход из спящего сразу на эту подпрограмму } // конец void loop() ISR (TIMER1_OVF_vect)//----------------обработчик прерывания от таймера1------------------------- { l = 0; //счётчик строк гориз. развёртки t=0; //счётчик пикселей(прямоугольников) в символе по вертикали g=0; w=0; // счётчик символьных строк //************блок подготовки информации для отображения(в начале каждого кадра) // расчёт и заполнение массивов data1,data2 (или внешними данными) delayMicroseconds(3000); //--------иммитация блока подготовки выводимой инф.-ЧЕМ ДЛИТЕЛЬНЕЕ ПРОГР. ТЕМ УЖЕ ЗОНА ОТОБРАЖ. после неё o=o+1; if(o==30){ // переменная (o) для мигания с секундным периодом(0.5сек+0.5сек) q=q^0b00000001; // q для чередования кода 10 и 11(смотри строки 10 и 11 массива знакогенератора) o=0; } c=c+1; if(c==3587){ // для коррекции ошибки(кадровая не ровно 60гц) c=0; f=f+1; // минуты if(f==10)f=0; } do{ // вертикальная синхронизация будет нарушатся из-за разного времени выполнения k=TCNT1L; //обработчика таймера1-сделаем его фиксированным привязав к таймеру1 }while (k<50); // такт Т1 примерно 64мкс. 64х50=3200мкс-это заведомо больше delayMicroseconds(3000) TIFR2 |=(1<<TOV2); // сбросим флаг пропущенных прерываний от Т2 //нарушится строчная синхронизация,если нет режима sleep_mode } //******конец обработчика таймера1******* ISR (TIMER2_OVF_vect) //---------------обработчик прерывания от таймера2 --------------------- { } // пустой обработчик чтобы только выйти из сна} //-----------------------------подпрограмма обработки строки------------------------------- void stroka () { delayMicroseconds(4); // для выравнивания по горизонтали l=l+1; // счёт начинается после окончания обработчика ISR (TIMER1_OVF_vect)) if (l<m)return ; // смещение отображения по вертикали(зона от m до 360+m строк) if(l>360+m)return ; // ограничение отображения снизу //-------------------отображение-------------------------------- switch (w) { case 0: // первая симв. строка с тактом 16мгц/4 SPSR &=~(1<<0); // меняем тактовую частоту SPI- убираем(x2) for(byte x=0;x<8;x++){ SPDR=Zngenerator[data1[x]][t]; delayMicroseconds(2); // время,чтобы вытащить байт из массива плюс задержки позволяют asm("nop"); // дождаться полного вывода предыдущего и развести символы по горизонтали asm("nop"); asm("nop"); asm("nop"); } control (11); // размер пикселя(кирпичика) 12 строк гориз. развёртки break; case 1: // вторая симв. строка с тактом 16мгц/8 SPSR|=(1<<0); // меняем тактовую частоту SPI(x2) for(byte x=0;x<15;x++){ SPDR=Zngenerator[data2[x]][t]; delayMicroseconds(1); // выводится быстрее-уменьшить задержку } control (5); //выводится быстрее-символы уже-надо и по высоте уменьшить break; case 2: // третья симв. строка с тактом 16мгц/8 // вывод не из массива delayMicroseconds(2); SPSR|=(1<<0); // меняем тактовую частоту SPI(x2) SPDR=Zngenerator[1][t]; // запись в регистр данных SPI и аппаратный вывод байта // число 1 delayMicroseconds(3); // задержка нужна чтобы дождаться полного вывода предыдущего // иначе потеряется // здесь не надо вытаскивать из массива-задержки уже другие SPDR=0b11111110; // засвечивается прямоугольник delayMicroseconds(2); asm("nop"); DDRD |=(1<<6); //pin6 перекл. на выход(а там 0 установили заранее) SPDR=Zngenerator[3][t]; // вывод числа 3 с изменённым цветом delayMicroseconds(2); asm("nop"); DDRD &=~(1<<6); //pin6 перекл. на вход - восстанавливаем цвета SPDR=Zngenerator[10+q][t]; // чередуется 10(пусто) и 11(:)-мигает двуеточие delayMicroseconds(2); asm("nop"); SPDR=Zngenerator[5][t]; delayMicroseconds(2); asm("nop"); SPDR=Zngenerator[f][t]; //вывод минут-счётчик в кадровом обработчике control (5); //выводится быстрее-символы уже-надо и по высоте уменьшить break; default: // если больше 3 строк символов-в начало w=0; } }//конец void stroka ()-возвращаемся в void loop(), а там спим //---------------конец вывода изобр. строки ,проверки условий на следующую строку--------------------- void control (byte y) { g=g+1; // счётчик строк горизонтальной развёртки в пикселе if(g>y){ //размер пикселя по вертикали (переменный) в строках гориз. развёртки g=0; // пиксель нарисовали t=t+1; // на следующий пиксель } if(t>14){ // всю строку символов вывели(все пиксели отработали)? t=0; w++; // на следующую строку символов } } [/code]
часы(от кадровой синхронизации):

[code] // arduino-1.8.7-windows // плата Arduino Nano v 3.0 // часы #include <SPI.h> #include <avr/sleep.h> //работа со спящими режимами // pin11-->Видеосигнал через выход SPI // pin3-->горизонт. синхроимпульс // pin10-->кадровый синхроимпульс byte k=0; byte o=0; //счётный имп. секунд int c=0; // счётный имп. минут byte f=0; //минуты byte h=0; //дес. минут byte n=0; // часы byte z=0; //дес. часов volatile byte q=0; volatile byte g=0; // счётчик строк горизонтальной развёртки в пикселе volatile byte t=0; // счётчик пикселей в символе по вертикали volatile int l=0; //счётчик строк горизонтальной развёртки const int m=200; //сдвиг изображения по вертикали //--------------------------------массив знакогенератора 0...9,: -------------------------------- // полная матрица 8х15,знаки 7х10, с 11 по 15 резервные байты- можно убрать скорректировав парамеры массива и if(l>=m+210), if(t>14) // в двоичном виде удобнее(нагляднее)вручную написать шрифт,нулевой бит всегда=0(т.к. последний выводимый остаётся висеть) const byte Zngenerator[12][15]={{0b01111100,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // это 0 {0b00001000,0b00011000,0b00101000,0b00001000,0b00001000,0b00001000,0b00001000,0b00001000,0b00001000,0b00001000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 1 {0b11111110,0b10000010,0b10000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000,0b10000000,0b11111110,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 2 {0b11111100,0b00000010,0b00000010,0b00000010,0b01111100,0b00000010,0b00000010,0b00000010,0b00000010,0b11111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 3 {0b10000010,0b10000010,0b10000010,0b10000010,0b10000010,0b11111110,0b00000010,0b00000010,0b00000010,0b00000010,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 4 {0b11111100,0b10000000,0b10000000,0b10000000,0b11111100,0b00000010,0b00000010,0b00000010,0b00000010,0b11111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 5 {0b01111100,0b10000000,0b10000000,0b10000000,0b11111100,0b10000010,0b10000010,0b10000010,0b10000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 6 {0b11111110,0b00000010,0b00000100,0b00001000,0b00010000,0b00100000,0b00100000,0b00100000,0b00100000,0b00100000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 7 {0b01111100,0b10000010,0b10000010,0b10000010,0b01111100,0b10000010,0b10000010,0b10000010,0b10000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 8 {0b01111100,0b10000010,0b10000010,0b10000010,0b10000010,0b01111110,0b00000010,0b00000010,0b00000010,0b01111100,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, // 9 {0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, //чёрный {0b00000000,0b00000000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00010000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000}, //две точки } ; // конец массива void setup() { set_sleep_mode (SLEEP_MODE_IDLE); // такой режим сна будет использоватся //pinMode(8,OUTPUT); //digitalWrite(8, HIGH); //pin8 на выход,если есть SPI pinMode(11, OUTPUT); pinMode(14,INPUT_PULLUP); // pin14-pin17(pinA0-pinA3) для кнопок коррекции pinMode(15,INPUT_PULLUP); pinMode(16,INPUT_PULLUP); pinMode(17,INPUT_PULLUP); //-------------------Настройка SPI------------------------------------------ SPI.begin(); SPI.setBitOrder(MSBFIRST); //старшим битом вперёд SPI.setClockDivider(SPI_CLOCK_DIV8); // делитель на 8 SPI.setDataMode(SPI_MODE1); //----------------- выключение таймера0--------------------------------------- TIMSK0 = 0; OCR0A = 0; OCR0B = 0; //-------------------- Таймер 1 для кадровой синхронизации ------------------ pinMode (10, OUTPUT); //OC1B-->Pin10 TCCR1A = 0b00110011; // рабочий выход OC1B(Pin10),режим ШИМ (OCR1A) TCCR1B = 0b00011101; //clk/1024-такт=64мкс OCR1A = 259; //регистр сравнения A-это будет макс. значением счётного регистра TCNT1(вместо 65535)-период кадровой прим.=16.6мс OCR1B = 0; //регистр сравнения B-по совпадению OC1B(Pin10)= 1,после TCNT1=0 OC1B=0 так форм. синхроимпульс прим.=64мкс // OCR1B = 0, а на сравнение 1 такт-получается 64 мкс ширина кадрового синхроимпульса //TIFR1 = bit (TOV1); TIMSK1 = bit (TOIE1); // разрешает прерывание по переполнению счётного регистра(259) //----------------------- Таймер 2 для строчной синхронизации ------------------------ pinMode (3, OUTPUT); //OC2B-->Pin3 TCCR2A = 0b00110011; TCCR2B = 0b00001010; // clk/8=0.5мкс OCR2A = 63; //регистр сравнения A-период строчной прим.=32мкс OCR2B = 7; //регистр сравнения B-синхроимпульс прим.=4мкс //TIFR2 = bit (TOV2); TIMSK2 = bit (TOIE2); // разр. прер. по переполнению счётного регистра(63) } //***********конец void setup()************ void loop() { sleep_mode (); //Спим (Прерывания продолжают работать.) Программа останавливается stroka (); // по прерыванию таймера 2 вых. из спящего сразу на эту подпрограмму } // конец void loop() ISR (TIMER1_OVF_vect)//----------------обработчик прерывания от таймера1------------------------- { l = 0; //счётчик строк t=0; //счётчик пикселей в символе по вертикали g=0; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<расчёт переменных для отображения часов >>>>>>>>>>>>>>>>>>>>>>>>>>>>> // дес. часов-z, часы-n,дес. минут-h,минуты-f // используя 60гц расчитывать мин., часы o=o+1; if(o==30){ // переменная (o) для мигания с секундным периодом(0.5сек+0.5сек) q=~q&0b00000001; //q для чередования кода 10 и 11(смотри строки 10 и 11 массива знакогенератора) //q=q^0b00000001; o=0; // -------установка часов от кнопок----------------------- if (digitalRead(14) == LOW) { //установка минут-pin A0 f=f+1; if(f==10){ f=0; } } if (digitalRead(15) == LOW) { //установка дес. минут-pin A1 h=h+1; if(h==6){ h=0; } } if (digitalRead(16) == LOW) { //установка часов-pin A2 n=n+1; if(n==4&&z==2){ //если дес.часов=2 часы=4 то часы=0 n=0; } else{ if(n==10){ n=0; } } } if (digitalRead(17) == LOW) { //установка дес. часов-pin A3 z=z+1; if(n>=4&&z==2){ //если дес.часов=2 часы>4 то часы=3 n=3; } if(z==3){ z=0; } } } // --------------------непосредственно обсчёт часов-------------------------------- c=c+1; if(c==3587){ // минутный счёт >>>>>>>>>>>>> для коррекции ошибки меняем значение (c) // сейчас оно уже скорректировано и не равно 3600(частота кадров х 60 сек) c=0; f=f+1; if(f==10){ f=0; h=h+1; } if(h==6){ h=0; n=n+1; } if(n==4&&z==2){ //если дес.часов=2 часы=4 то часы=0 n=0; z=0; } else{ if(n==10){ n=0; z=z+1; //дальше дес. часов+1 ,если ==2 } } } //------- конец обсчёта переменных часов-------------- do{ // вертикальная синхронизация будет нарушатся из-за разного времени выполнения k=TCNT1L; //обработчика таймера1-сделаем его фиксированным привязав к таймеру1(к кол-ву тактов) }while (k<5); // менять k до синхронизации или заведомо больше выпол. программы(такт Т1 прим.=64мкс) // здесь в обработчике только обсчёт часов TIFR2 |=(1<<TOV2); // сбросим флаг прерывания от Т2 иначе начнёт сразу обрабатываться(флаг уже установлен ) //нарушится строчная синхронизация,если нет режима sleep_mode } //******конец обработчика таймера1******* ISR (TIMER2_OVF_vect) //****************обработчик прерывания от таймера2 ************** { } // пустой обработчик чтобы только выйти из сна //-----------------------------подпрограмма обработки строки------------------------------- void stroka () { delayMicroseconds(3); // для выравнивания по горизонтали l=l+1; // счётчик строк гориз. разв. if (l<=m)return ; // запрет отображения сверху if(l>=m+240)return ; //от l=m до l=m+240 отображается полезная информация // 16-размер пикселя в строках,8х15-полная матрица,7х10-её полезная часть // 16*15=240-под всю матрицу //-------------------вывод на индикацию времени-------------------------------- SPDR=Zngenerator[z][t]; // запись в регистр SPI байта из массива знакогенератора и //его последовательный вывод на монитор //здесь номер строки знакогенератора равен числу(z) дес. часов delayMicroseconds(5); //ждём конца вывода + ещё для отдаления от предыдущего SPDR=Zngenerator[n][t]; delayMicroseconds(5); SPDR=Zngenerator[10+q][t]; //чередование кодов 10(пусто) и 11(:) delayMicroseconds(5); SPDR=Zngenerator[h][t]; delayMicroseconds(5); SPDR=Zngenerator[f][t]; //---------------конец вывода изобр. строки ,проверки условий на следующую строку--------------------- g=g+1; // счётчик строк горизонтальной развёртки в пикселе if(g>15){ //размер пикселя по вертикали-16(с 0 по 15) строк гориз. развёртки g=0; t=t+1; // на след. строку символов } if(t>14){ // всю строку символов вывели? t=0; } } //---------- конец подпрограммы stroka-возвращаемся в void loop(), а там спим [/code]
Менял частоты развёрток синхронизации(таймер 2 8-ми битный-точно не подобрать ).
Поменять T1 и T2 местами не получится-из сна выводит только T2.
Эту настройку мониторы захватывали стабильно:
OCR1A = 207; 75гц
OCR1B = 0;
OCR2A = 51; 37500гц
OCR2B = 7;
Символы увеличатся-чтобы строка уместилась в развёртку уменьшить количество выводимых.
Время выполнения stroka() должно быть меньше времени строчной развёртки,
чтобы не пропустить следующий синхроимпульс
Остался невыясненным нюанс. Если убрать сдвиг по вертикали-m=0(после
кадрового обработчика нет пустых строк),первая строка на отображении со смещением.
Если запретить прерывание от T2(TIMSK2=0), символы не отображаются,но
верхняя смещённая строка остаётся(одна)-что поднимает из сна на одну строчку?
(без sleep_mode такого нет)
:)
Интересная тема, сейчас много таких мониторов скапливается без дела. Был бы полезен скетч-пример с функциями ( или библиотека) для вывода данных по аналогии экранчика SSD1306 128*64 пикселя. Примерно такой:
Функции: вставка картинки (из "прогмем"), рисование точки, отрезка, окружности...
:)
Интересная тема, сейчас много таких мониторов скапливается без дела. Был бы полезен скетч-пример с функциями ( или библиотека) для вывода данных по аналогии экранчика SSD1306 128*64 пикселя. Примерно
С ATmega328 особо не разбежишься.
Это да, нашлась "библиотека по мотивам"... 120*60 пикселей, похоже предел без использования дополнительной платы Ардуино. Странно, что китайцы ещё не сделали "видеокарту под Ардуино".
https://github.com/smaffer/vgax
На всякий случай напомню: http://arduino.ru/forum/proekty/s-stm32f103-na-televizor-polnyi-tv-signal
BluePill 512х240 пикселей.
Да, вопрос функционального назначения важен. Экранчики таких размеров - многоглазы, например, демонстрационный вольтметр на уроках...
...отрезал болгаркой от материнки и собрал по схеме...
...библиотека полна сюрпризов...
Гифка не грузится теперь, хоть и весит 1,74 Мб :(
Тоже сделал часы на " vga.делее", по другому библиотека не даёт :)
Чисто из спортивного интереса, только + чего то коряво печатается, сбой в конвертере авторском видать :)
шрифт или глифы
прописные лень рисовать.
В принципе получилось что часы уходят как точно не подбирай в час на 2-3 секунды. Для "делай" хорошо, в сутки раз подводить надо. Для сторожа в фойе самое то :), заодно можно негласно контролировать его выход на работу.
Доброго дня уважаемый. Подскажите как вы нарисовали вольтметр. И активный он? И я так понимаю это вывод через VGA.
Который день ищу информацию как можно сделать анимационные приборы измерения электрических величин.
Спасибо заранее
Огромное спасибо
Здравствуйте в очередной раз. Объясните мне пожалуйста.
Перечитал много информации и везде написано что в монохром разрешение 800х600, цветной 480х320, а в реале 120х60 монохром. Попробовал залить несколько проектов в ардуино нано v3, в том числе и прошивки с ваших постов. И что то меня очень расстроило.
Можно ли вообще выводить текст/переменные в размере 800х600 точек?
Спасибо за ранее
Средства данной библиотеки ограничены 120*60 в 3 цветах. Другими аппаратными и программными средствами думаю можно.
3 цвета-то откуда?
3 цвета-то откуда?
Вру, 4 цвета - один фоновый.
RGB вот три цвета
:)
Во истину, так!
3 цвета-то откуда?
Вру, 4 цвета - один фоновый.
4 цвета - это 2 бита.
120*60*2/8=1800 байт. 224 байта на все остальное, включая глобальные переменные и стек, это не слишком мало?
Ну других библиотек под УНО нет.