Dikoy 0 April 4, 2018 Posted April 4, 2018 (edited) · Report post Мучаю исходник http://www.vga-avr.narod.ru/main_rus.html "Простой VGA/Видео адаптер" В проекте есть прерывание, в котором выполняется горизонтальная и вертикальная синхронизации, а также настройки указателей отрисовки. Прерывание это нормировано по тактам, даже в ветвлениях if-ов нопами выровнено время выполнения. Я не знаю зачем автор так сделал, но если добавить хоть 1 nop в любое место, то вся синхронизация падает. Проблема в том, что автор использует для выдачи сигналов синхронизации команды PORTD = 4; и PORTD = 0; Что, естественно, приводит к невозможности использовать порт D для чего бы то ни было ещё. Вторая проблема - PORTD = 4 на самом деле означает не только установку третьего бита, но и сброс второго. Третий бит это вертикальная синхронизация, второй - горизонтальная. И происходить эта операция должна одновременно. Я пробовал менять на PORTD |= (1<<3); PORTD &= ~(1<<2); Но всё тут же падает и из-за не одновременности вывода сигналов, и из-за возросшего времени выполнения. Не помогает также и замена на ассемблерные cbi/sbi. Единственный вариант, который мне видится, это char temp; temp = PORTD; temp |= (1<<3); temp &= ~(1<<2); PORTD = temp; Но даже char temp; temp = PORTD; Уже приводит к искажению картинки из-за увеличившегося времени выполнения прерывания. В общем, с этим надо что-то делать... Либо соптимизировать прерывание и выкроить такты, либо управлять портом как-то иначе, например, через указатель, но сомневаюсь что по числу тактов будет выигрыш. Вот текст прерывания: //Global definitions for VGA render #define vga_field_line_count 525 //standart VGA quantity lines #define vga_symbols_per_row 20 //symbols quantity per horizontal #define vga_row_count 20 //symbols quantity per vertical #define vga_symbol_height 24 //rendered symbol height #define TIMER_LIMIT 0xC3 //set count, One VGA line 31.77us //All VGA sincronize made here.. SIGNAL(SIG_OVERFLOW0) { TCNT0 = TIMER_LIMIT; //reload counter value 0xC3 //set count, One VGA line 31.77us //******Syncronization Handler******** //Count number of lines if (++linecount == vga_field_line_count) { linecount = 0; //clear pointers for render display buffer raw_render = 0; y_line_render = 0; } // Вертикальный и горизонтальный синхроимпульсы должны быть одновременно, поэтому дёргать битами порта раздельно по cbi/sbi не получится. // кроме того, тело прерывания крайне чувствительно ко времени исполнения, и добавление любой команды сбивает синхронизацию. //Make Vsync length 2 VGA lines if ((linecount == 10 )||(linecount == 11 )) { //Make here vertical syncronization & HSYNC syncro level on PORTD = 0; //vsync_on } else { //.. & HSYNC syncro level on PORTD = 4;// vsync_off } video_enable_flg = true; if (linecount < 45) { video_enable_flg = false; //Add to avoid flickering at top display NOP; // 15 nops NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; } else { //Forming current string for rendering if (++y_line_render == vga_symbol_height) { raw_render++; y_line_render = 0; } else { NOP; // 8 nops NOP; NOP; NOP; NOP; NOP; NOP; NOP; } } hsync_off; //HSYNC syncro level off sbi(PORTD,3) //******Syncronization Handler******** } Ассемблер: //All VGA sincronize made here.. SIGNAL(SIG_OVERFLOW0) { de4: 1f 92 push r1 de6: 0f 92 push r0 de8: 0f b6 in r0, 0x3f; 63 dea: 0f 92 push r0 dec: 11 24 eor r1, r1 dee: 2f 93 push r18 df0: 8f 93 push r24 df2: 9f 93 push r25 //unsigned char port_buffer; TCNT0 = TIMER_LIMIT; //reload counter value df4: 83 ec ldi r24, 0xC3; 195 df6: 82 bf out 0x32, r24; 50 //******Syncronization Handler******** //Count number of lines if (++linecount == vga_field_line_count) { df8: 80 91 77 00 lds r24, 0x0077 dfc: 90 91 78 00 lds r25, 0x0078 e00: 01 96 adiw r24, 0x01; 1 e02: 90 93 78 00 sts 0x0078, r25 e06: 80 93 77 00 sts 0x0077, r24 e0a: 80 91 77 00 lds r24, 0x0077 e0e: 90 91 78 00 lds r25, 0x0078 e12: 8d 50 subi r24, 0x0D; 13 e14: 92 40 sbci r25, 0x02; 2 e16: 41 f4 brne .+16; 0xe28 <__vector_11+0x44> linecount = 0; e18: 10 92 78 00 sts 0x0078, r1 e1c: 10 92 77 00 sts 0x0077, r1 //clear pointers for render display buffer raw_render = 0; e20: 10 92 73 03 sts 0x0373, r1 y_line_render = 0; e24: 10 92 79 00 sts 0x0079, r1 // Вертикальный и горизонтальный синхроимпульсы должны быть одновременно, поэтому дёргать битами порта раздельно по cbi/sbi не получится. // кроме того, тело прерывания крайне чувствительно ко времени исполнения, и добавление любой команды сбивает синхронизацию. // я не смог победить этот глюк и использовал костыль с промежуточной переменной. //Make Vsync length 2 VGA lines if ((linecount == 10 )||(linecount == 11 )) { e28: 80 91 77 00 lds r24, 0x0077 e2c: 90 91 78 00 lds r25, 0x0078 e30: 0a 97 sbiw r24, 0x0a; 10 e32: 31 f0 breq .+12; 0xe40 <__vector_11+0x5c> e34: 80 91 77 00 lds r24, 0x0077 e38: 90 91 78 00 lds r25, 0x0078 e3c: 0b 97 sbiw r24, 0x0b; 11 e3e: 11 f4 brne .+4 ; 0xe44 <__vector_11+0x60> //Make here vertical syncronization & HSYNC syncro level on PORTD = 0; //vsync_on e40: 12 ba out 0x12, r1; 18 e42: 02 c0 rjmp .+4 ; 0xe48 <__vector_11+0x64> } else { //.. & HSYNC syncro level on PORTD = 4;// (PORTD | 0x04); //vsync_off e44: 84 e0 ldi r24, 0x04; 4 e46: 82 bb out 0x12, r24; 18 } video_enable_flg = true; e48: 81 e0 ldi r24, 0x01; 1 e4a: 80 93 74 03 sts 0x0374, r24 if (linecount < 45) { e4e: 80 91 77 00 lds r24, 0x0077 e52: 90 91 78 00 lds r25, 0x0078 e56: 8d 97 sbiw r24, 0x2d; 45 e58: 90 f4 brcc .+36; 0xe7e <__vector_11+0x9a> video_enable_flg = false; e5a: 10 92 74 03 sts 0x0374, r1 ... NOP; NOP; NOP; NOP; NOP; NOP; e7a: 00 00 nop e7c: 19 c0 rjmp .+50; 0xeb0 <__vector_11+0xcc> } else { //Forming current string for rendering if (++y_line_render == vga_symbol_height) { e7e: 80 91 79 00 lds r24, 0x0079 e82: 8f 5f subi r24, 0xFF; 255 e84: 80 93 79 00 sts 0x0079, r24 e88: 80 91 79 00 lds r24, 0x0079 e8c: 88 31 cpi r24, 0x18; 24 e8e: 41 f4 brne .+16; 0xea0 <__vector_11+0xbc> raw_render++; e90: 80 91 73 03 lds r24, 0x0373 e94: 8f 5f subi r24, 0xFF; 255 e96: 80 93 73 03 sts 0x0373, r24 y_line_render = 0; e9a: 10 92 79 00 sts 0x0079, r1 e9e: 08 c0 rjmp .+16; 0xeb0 <__vector_11+0xcc> ... NOP; } } hsync_off; //HSYNC syncro level off sbi(PORTD,3) eb0: 93 9a sbi 0x12, 3; 18 //******Syncronization Handler******** } eb2: 9f 91 pop r25 eb4: 8f 91 pop r24 eb6: 2f 91 pop r18 eb8: 0f 90 pop r0 eba: 0f be out 0x3f, r0; 63 ebc: 0f 90 pop r0 ebe: 1f 90 pop r1 ec0: 18 95 reti 00000ec2 <display_Mega>: Edited April 4, 2018 by Dikoy [codebox] для длинного кода. [code]-для короткого!!! Quote Share this post Link to post Share on other sites More sharing options...
Сергей Борщ 60 April 4, 2018 Posted April 4, 2018 · Report post Ассемблер:Не вижу умножения или других действий с R0. Можно его не сохранять на стеке. Quote Share this post Link to post Share on other sites More sharing options...
Dikoy 0 April 4, 2018 Posted April 4, 2018 · Report post Возможно... Только как это сделать в WINAVR? Запретить сохранять регистр? Ещё мысль: if (linecount < 45) с его нопами можно использовать для if ((linecount == 10 )||(linecount == 11 )) { //Make here vertical syncronization & HSYNC syncro level on PORTD = 0; //vsync_on } else { //.. & HSYNC syncro level on PORTD = 4;// vsync_off Ибо 10 и 11 меньше 45, тогда как минимум 15 нопов экономится. И не будет ли быстрее записать этот код как if (linecount == 10 ) PORTD = 0; //vsync_on if (linecount == 12 ) PORTD = 4;// vsync_off Quote Share this post Link to post Share on other sites More sharing options...
Dikoy 0 April 4, 2018 Posted April 4, 2018 · Report post А есть для авр ассемблера что-то для быстрого подсчёта тактов? Например, от сих и до сих. А то на бумажке считать несколько уныло... Quote Share this post Link to post Share on other sites More sharing options...
zombi 0 April 4, 2018 Posted April 4, 2018 · Report post А есть для авр ассемблера что-то для быстрого подсчёта тактов? Например, от сих и до сих. А то на бумажке считать несколько уныло... Думаю должна быть возможность загрузить в студию прошивку. Установить PC на нужный адрес "от сих" точку останова на "до сих" запустить эмуляцию и смотреть сколько тактов потребовалось. :bb-offtopic: формирование синхросигналов VGA на СИ - это жесть Quote Share this post Link to post Share on other sites More sharing options...
prottoss 0 April 4, 2018 Posted April 4, 2018 · Report post А есть для авр ассемблера что-то для быстрого подсчёта тактов? Например, от сих и до сих. А то на бумажке считать несколько уныло... симулятор AVRStudio Думаю должна быть возможность загрузить в студию прошивку. Установить PC на нужный адрес "от сих" точку останова на "до сих" запустить эмуляцию и смотреть сколько тактов потребовалось. Проще прям набить на ассемблере исследуемый код и замерить выполнение Quote Share this post Link to post Share on other sites More sharing options...
Dikoy 0 April 4, 2018 Posted April 4, 2018 · Report post :bb-offtopic: формирование синхросигналов VGA на СИ - это жесть Жесть, это на ардуине https://forum.arduino.cc/index.php?topic=102181.0 Quote Share this post Link to post Share on other sites More sharing options...
Dikoy 0 April 4, 2018 Posted April 4, 2018 (edited) · Report post В общем, победил. Код сравнения выполнялся 3+3+2+1+3+3+2+1+1+2+1+1 = 23 (24, 25) циклов. //Make Vsync length 2 VGA lines if ((linecount == 10 )||(linecount == 11 )) { //Make here vertical syncronization & HSYNC syncro level on PORTD = 0; //vsync_on } else { //.. & HSYNC syncro level on PORTD = 4;// vsync_off } Переписал его так и уложился в тот же тактаж: hsync_on; // cbi(PORTD,3) if (linecount == 10 ) cbi(PORTD,2); //PORTD = 0; if (linecount == 12 ) sbi(PORTD,2); //PORTD = 4; Этого хватило. Работает, порты отпустило. Сейчас тело прерывания выглядит вот так. Вдруг кто ещё будет это чудовище повторять. //All VGA sincronize made here.. SIGNAL(SIG_OVERFLOW0) { TCNT0 = TIMER_LIMIT; //reload counter value //******Syncronization Handler******** video_enable_flg = true; //Count number of lines if (++linecount == vga_field_line_count) { // == 525 linecount = 0; //clear pointers for render display buffer raw_render = 0; y_line_render = 0; } hsync_on; // cbi(PORTD,3) if (linecount == 10 ) cbi(PORTD,2); //PORTD = 0; if (linecount == 12 ) sbi(PORTD,2); //PORTD = 4; if (linecount < 45) { //Add to avoid flickering at top display NOP; // 15 nops NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; video_enable_flg = false; } else { //video_enable_flg = true; //Forming current string for rendering if (++y_line_render == vga_symbol_height) { raw_render++; y_line_render = 0; } else { NOP; // 8 nops NOP; NOP; NOP; NOP; NOP; NOP; NOP; } // } hsync_off; //HSYNC syncro level off sbi(PORTD,3) //******Syncronization Handler******** } В принципе, остаюсь при мнении что перенос синхронизации за if (linecount < 45) позволит ещё сократить время выполнения прерывания, как минимум на 15 тактов, но это потребует подбора нопов в противовесе (после else) а мне лень. Проект и так из "давай быстро прилепим вывод на монитор к стенду" перерос в 2 дня отладки... Но если у кого будет желание - уверен так можно. Edited April 4, 2018 by Dikoy Quote Share this post Link to post Share on other sites More sharing options...
zombi 0 April 4, 2018 Posted April 4, 2018 · Report post Жесть, это на ардуине https://forum.arduino.cc/index.php?topic=102181.0 Да хоть на чём угодно. Но я не понимаю или не вижу: где и как происходит выравнивание фронта синхросигнала в зависимости от времени выполнения прерываемой команды? Quote Share this post Link to post Share on other sites More sharing options...
Dikoy 0 April 4, 2018 Posted April 4, 2018 (edited) · Report post где и как происходит выравнивание фронта синхросигнала в зависимости от времени выполнения прерываемой команды? Оооо! Там такая содомия в коде творится! В мейнлупе то же самое происходит - вывод строки сделан в цикле и не дай кришна убрать один ноп! Если б я знал заранее что будет такая жесть, не начинал бы это дело. Написал бы своё на СТМ или использовал ЖК панель. Но попробовал скомпилированный код автора, работает, ок, думаю, допишу вывод своих данных, это не сложно... :rolleyes: **** Кстати о содомии. То же самое там происходит и с портом В. Но там всё проще. Меняем ногодрыг на sbi/cbi: #define video_off cbi(DDRB,5) // DDRB=0x90 #define video_on sbi(DDRB,5) // DDRB=0xB0 Убираем по одному нопу после video_on; и до video_off;, т.к. команды sbi/cbi выполняются по 2 такта, а присвоение, преобразуемое в LDI - только 1 такт. //Cycle for render line i = vga_symbols_per_row; while(i--) { SPDR = pgm_read_byte_near(_ptr1 + (* _ptr++)*vga_symbol_height/2); video_on; NOP; } //Delay for draw last symbol NOP; NOP; NOP; NOP; NOP; video_off; Edited April 4, 2018 by Dikoy Quote Share this post Link to post Share on other sites More sharing options...
zombi 0 April 4, 2018 Posted April 4, 2018 · Report post Оооо! Там такая содомия в коде творится! Да это то понятно. Вы наверное относитесь к людям которые не ищут простых и/или лёгких путей. Но я немного о другом спрашивал. Прерывание таймера может произойти во время выполнения команды длительностью 1,2 или 3 такта. Это где-то учитывается? Quote Share this post Link to post Share on other sites More sharing options...
demiurg_spb 0 April 4, 2018 Posted April 4, 2018 · Report post Может Вам пригодится... Для многих АВРок есть возможность тоглить (инвертировать) состояние GPIO путём записи в регистр PINx. Пример инверсии нулевого и седьмого бита в порту Б: PINB = (1<<7)|(1<<0); Вот список моделей для которых это можно #if defined(__AVR_AT90PWM1__) \ || defined(__AVR_AT90PWM2__) \ || defined(__AVR_AT90PWM2B__) \ || defined(__AVR_AT90PWM3__) \ || defined(__AVR_AT90PWM3B__) \ || defined(__AVR_AT90PWM216__) \ || defined(__AVR_AT90PWM316__) \ || defined(__AVR_AT90PWM81__) \ || defined(__AVR_AT90USB82__) \ || defined(__AVR_AT90USB162__) \ || defined(__AVR_ATmega8U2__) \ || defined(__AVR_ATmega16U2__) \ || defined(__AVR_ATmega32U2__) \ || defined(__AVR_ATmega16U4__) \ || defined(__AVR_ATmega32U4__) \ || defined(__AVR_ATmega32U6__) \ || defined(__AVR_AT90USB646__) \ || defined(__AVR_AT90USB647__) \ || defined(__AVR_AT90USB1286__) \ || defined(__AVR_AT90USB1287__) \ || defined(__AVR_AT90CAN32__) \ || defined(__AVR_AT90CAN64__) \ || defined(__AVR_AT90CAN128__) \ || defined(__AVR_ATmega16M1__) \ || defined(__AVR_ATmega32M1__) \ || defined(__AVR_ATmega64M1__) \ || defined(__AVR_ATmega32C1__) \ || defined(__AVR_ATmega64C1__) \ || defined(__AVR_ATmega48__) || defined(__AVR_ATmega48A__) \ || defined(__AVR_ATmega88__) || defined(__AVR_ATmega88A__) \ || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168A__) \ || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega48PA__) \ || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega88PA__) \ || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega168PA__) \ || defined(__AVR_ATmega328__) \ || defined(__AVR_ATmega328P__) \ || defined(__AVR_ATmega640__) \ || defined(__AVR_ATmega1280__) \ || defined(__AVR_ATmega1281__) \ || defined(__AVR_ATmega2560__) \ || defined(__AVR_ATmega2561__) \ || defined(__AVR_ATmega164A__) \ || defined(__AVR_ATmega164P__) || defined(__AVR_ATmega164PA__) \ || defined(__AVR_ATmega324A__) \ || defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324PA__) \ || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) \ || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) \ || defined(__AVR_ATmega1284P__) \ || defined(__AVR_ATmega165__) || defined(__AVR_ATmega165A__) \ || defined(__AVR_ATmega165P__) || defined(__AVR_ATmega165PA__) \ || defined(__AVR_ATmega325__) || defined(__AVR_ATmega325A__) \ || defined(__AVR_ATmega325P__) \ || defined(__AVR_ATmega3250__) \ || defined(__AVR_ATmega3250P__) \ || defined(__AVR_ATmega645__) || defined(__AVR_ATmega645A__) \ || defined(__AVR_ATmega645P__) \ || defined(__AVR_ATmega6450__) || defined(__AVR_ATmega6450A__) \ || defined(__AVR_ATmega6450P__) \ || defined(__AVR_ATmega169__) || defined(__AVR_ATmega169A__) \ || defined(__AVR_ATmega169P__) || defined(__AVR_ATmega169PA__) \ || defined(__AVR_ATmega329__) || defined(__AVR_ATmega329A__) \ || defined(__AVR_ATmega329P__) || defined(__AVR_ATmega329PA__) \ || defined(__AVR_ATmega3290__) \ || defined(__AVR_ATmega3290P__) \ || defined(__AVR_ATmega649__) || defined(__AVR_ATmega649A__) \ || defined(__AVR_ATmega6490__) || defined(__AVR_ATmega6490A__) \ || defined(__AVR_ATmega6490P__) \ || defined(__AVR_ATmega649P__) \ || defined(__AVR_ATmega406__) \ || defined(__AVR_ATmega8HVA__) \ || defined(__AVR_ATmega16HVA__) \ || defined(__AVR_ATmega16HVA2__) \ || defined(__AVR_ATmega16HVB__) \ || defined(__AVR_ATmega32HVB__) \ || defined(__AVR_ATmega64HVE__) \ || defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny261A__) \ || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny461A__) \ || defined(__AVR_ATtiny861__) || defined(__AVR_ATtiny861A__) \ || defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny2313A__) \ || defined(__AVR_ATtiny4313__) \ || defined(__AVR_ATtiny13__) || defined(__AVR_ATtiny13A__) \ || defined(__AVR_ATtiny25__) \ || defined(__AVR_ATtiny45__) \ || defined(__AVR_ATtiny85__) \ || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny24A__) \ || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny44A__) \ || defined(__AVR_ATtiny84__) \ || defined(__AVR_ATtiny43U__) \ || defined(__AVR_ATtiny48__) || defined(__AVR_ATtiny88__) \ || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) # define PIN_TOGGLE_BY_PIN_WRITE 1 #else # define PIN_TOGGLE_BY_PIN_WRITE 0 #endif Quote Share this post Link to post Share on other sites More sharing options...
V_G 8 April 4, 2018 Posted April 4, 2018 · Report post Прерывание таймера может произойти во время выполнения команды длительностью 1,2 или 3 такта. Это где-то учитывается? Сильно подозреваю, что если вывод информации в строке жестко привязан к импульсу строчной синхронизации, то небольшое гуляние самого импульса синхронизации никак не скажется на качестве картинки Quote Share this post Link to post Share on other sites More sharing options...
Dikoy 0 April 4, 2018 Posted April 4, 2018 (edited) · Report post Да это то понятно. Вы наверное относитесь к людям которые не ищут простых и/или лёгких путей. Но я немного о другом спрашивал. Прерывание таймера может произойти во время выполнения команды длительностью 1,2 или 3 такта. Это где-то учитывается? Здесь скорее "кроилово привело к попадалову" - желание сэкономить время и использовать готовый проект как базу привело к бОльшим трудозатратам, чем сделать то же на СТМ. Но не суть. Работает. То, что мне нужно - делает. Прибор одноразовый, для одного конкретного стенда и то для отладки. Поэтому допустимо немного припустить планку. Так-то железка годная, только писали её неправильно. Там ещё выжать можно и скорости, и ресурса. Нигде это не учитывается. Но предыдущий оратор прав - колебания начала отрисовки на пару тактов ни на что не влияют. Картинка довольно чёткая. Даже на ЭЛТ мониторе выпуска 2001 года. На ЖК ЛОСе, современном, вообще очень чёткая картинка. Видимо, монитор хорошо подстраивается. Edited April 4, 2018 by Dikoy Quote Share this post Link to post Share on other sites More sharing options...