![]() |
Forum Index : Microcontroller and PC projects : VGA on the VGAMite: how it works
Author | Message | ||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
Not invented by me I'm afraid but really clever Timer 1 is set to run at the system speed (100.7MHz). This then generates the horizontal synch pulse using PWM. The clever bit is that timer 1 is also set to do an output compare at the end of the horizontal blanking period. This compare generates an interrupt. Finally timer 1 rollover is used as the clock source for timer 2. Timer 2 is now running at the horizontal synch rate. This then generates a vertical synch pulse using PWM. Timer 2 also uses output compare to generate an interrupt at the end of the vertical blanking period. The interrupt at the end of the vertical blanking period just sets the line counter to 0. The interrupt at the end of the horizontal blanking period just triggers a DMA transfer from the memory holding the next line to SPI3. SPI3 is set to run at one quarter of the system speed (25.175MHz) DMA completion triggers an interrupt. This interrupt updates the DMA memory location to point to the next line of data ready for the DMA to be triggered by the timer 1 interrupt So simple, but so clever because the code in the interrupt routines is trivial and therefore the CPU load is minimal and because timer 2 is driven by timer 1 rollover the synch pulses are perfectly timed and locked together. Not doable on things like the Picomite as it doesn't have the sophisticated timers needed so generating VGA becomes very processor intensive and/or uses multiple PIO processes void TIM1_CC_IRQHandler(void) { if(valid) DMA1_Stream5->CR |= DMA_SxCR_EN; // start the DMA TIM1->SR &= ~TIM_IT_CC2; // clear the timer interrupt bit } void TIM2_IRQHandler(void) valid=1; // enable output after the first vertical blanking period after reboot vline=0; // reset the line counter TIM2->SR &= ~TIM_IT_CC2; // clear the timer interrupt bit } void DMA1_Stream5_IRQHandler(void) { DMA1->HIFCR DMA_HIFCR_CHTIF5|DMA_HIFCR_CFEIF5|DMA_HIFCR_CTEIF5|DMA_HIFCR_CDMEIF5; //clear any DMA interrupt flags except end of transfer if(DMA1->HISR & DMA_HISR_TCIF5) {// If the interrupt is end of transfer DMA1->HIFCR = DMA_HIFCR_CTCIF5; //clear the end of transfer interrupt flag DMA1_Stream5->CR &= ~DMA_SxCR_EN; // disable the stream DMA1_Stream5->M0AR=(uint32_t)&fb[vline][0]; //point DMA to the next line vline++; //increment the line counter if(vline>=480) vline=480; //if in frame blanking just replay a blank line } } Edited 2021-11-29 20:53 by matherp |
||||
robert.rozee Guru ![]() Joined: 31/12/2012 Location: New ZealandPosts: 2442 |
out of curiosity, would it be possible to generate video using a set of character bitmaps and just an 80x24 array of bytes holding character codes for each character position? this would use minimal quantities of RAM (80x24 = 1920 bytes), even if the character bitmaps were also held in RAM (24 lines -> 20*256 = 5120 bytes). one approach could be to build each scan line on-the-fly, although doing it this way would be quite CPU intensive, and play havoc with the workings of everything else in mmbasic! not saying this should be done, just curious if it is technically possible. cheers, rob :-) |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
Doubt it, you would only have 6.3uS to build each line during line blanking |
||||
Mixtel90![]() Guru ![]() Joined: 05/10/2019 Location: United KingdomPosts: 7937 |
If I understand you correctly, that's basically how the old method of character generation worked. Video ram stored character codes, not pixels. A character generator ROM stored the pixel patterns for the characters. Each scan looked at successive lines of the characters (read from the ROM), so top row of a 40x16 screen would have 320 pixels. The next 7 scans would complete the first row of characters. First the top line of each character then the second line then the third etc. Then you start on the next row of characters. It works well, but you can only use block graphics. Machines like the Nascom, TRS-80 etc. use this method. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
Volhout Guru ![]() Joined: 05/03/2018 Location: NetherlandsPosts: 5089 |
What Peter explains is method to map memory (RAM) on a VGA screen. Almost without involvement of the CPU. If you would like to map characters to VGA (in stead of bitmapped RAM) you need the character (ROM) in between. That is what a "video chip" does. It maps a character ROM in between RAM and VGA output. Since we don't have a dedicated video chip inside the STM, you would have to do this using the ARM. Basicaly real-time translating to characters. Although this might be possible, you would end up in a system where the ARM is more or less generating the video signal on it's own, busy during the whole active part of the video (everything outside the HSYNC and VSYNC). Something like a ZX81 in slow mode. No good idea.... Volhout PicomiteVGA PETSCII ROBOTS |
||||
Mixtel90![]() Guru ![]() Joined: 05/10/2019 Location: United KingdomPosts: 7937 |
True. :) It's a very early way to get characters onto a screen. I think I first saw it in The Cheap Video Cookbook (the circuit is almost directly copied for the Nascom-1 video). It had the advantage at that time that it used only TTL logic & counters to copy the video RAM onto the screen. It was switching the character generator RAM access between CPU and row counter access that caused the famous screen flash on some early machines. This early technique still works nicely on a FPGA. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |