VGA Output from STM32F4 Discovery Board
I Love the web!
There is so many cool projects off there, and some, with a tweak or both,
Get me where I want to go quickly, saving a ton of time and effort.
Case in Point:the artektit page on VGA output using a 36-pin STM32.
The Artekit gang developed a minimalist VGA implementation using one of the boards they sell,
With a-a-MHz stm32f103 device on-board.
I say "minimalist" because it uses just the board and a VGA connector, but you get 800x600 monochrome VGA out.
Then they added code for doing simple graphics and the text generation, and all VGA timing are done using the DMA writes to a SPI Channel.
Their design have negligible impact on processor resources,
Could drop their VGA drivers into your project and see almost no hits to throughput.
Because of memory constraints in the chosen device, they only has a for 400x200 pixels.
They implement this by assigning a 10K frame buffer and then writing out of the pixels as lines of bytes for each.
Each pixel are sent twice and each are sent three times.
The result satisfies the monitor's timing for 800x600 VGA, and the effective graphics resolution is 400x200.
Enter the STM32F4 Discovery board.
It has plenty of the class for a full 800x600 frame buffer and runs far faster than the original ' 103 device.
All this is needed are to port their code from the ' 103 device to the STM32F4 processor and
Modify it to display a full 800x600 monochrome VGA.
VGA output from a $ board. With almost no CPU through-put hit.
What's not to like?
The porting effort
I started with a download of the Artekit files from the above link.
I have to say the code is impressive.
The comments told me most about what I needed to know, and the structure is well thought out.
I ' m only addressing items I changed or added to the Artekit project.
Some of this won ' t make sense unless you also read through the Artekit page; You ' ve been warned.
The first task was selecting the I/O pins to the S4D board.
Here's what I ended to with:
Artekit |
Discovery |
Notes |
PA1 |
PA1 |
Vertical Sync |
PA7 |
PB5 |
VGA pixel data (GREEN) |
PA8 |
PA8 |
Horizontal Sync |
GND |
GND |
Ground |
That ' s it! Four wires and you ' re good.
I switched my pixel data output to PB5.
I needed a MOSI signal from SPI1 and my only choices that supported DMA channels
Were PA8 and PB5, and PA8 was already assigned.
The VGA timing requirements is widely available on the web;
One of the best sites is tinyvga.com, which have pages devoted to many of the VGA resolutions.
The Artekit code uses a, and I stuck to that.
From the TINYVGA site, the important 800x600-Hz timing info is:
General timing
Screen refresh rate |
Hz |
Vertical Refresh |
35.15625 kHz |
Pixel freq. |
36.0 MHz |
Horizontal timing (line)
Polarity of horizontal sync pulse is positive.
Scanline part |
Pixels |
Time [Μs] |
Visible Area |
800 |
22.222222222222 |
Front Porch |
24 |
0.66666666666667 |
Sync Pulse |
72 |
2 |
Back Porch |
128 |
3.5555555555556 |
Whole Line |
1024 |
28.444444444444 |
Vertical Timing (frame)
Polarity of vertical sync pulse is positive.
Frame part |
Lines |
Time [MS] |
Visible Area |
600 |
17.066666666667 |
Front Porch |
1 |
0.028444444444444 |
Sync Pulse |
2 |
0.056888888888889 |
Back Porch |
22 |
0.62577777777778 |
Whole Frame |
625 |
17.777777777778 |
All of the setup for this timing happens in the file video.c.
The code uses the channels of TIM1.
Channel 1 generates the Hsync pulse and Channel 2 generates the Hsync plus back porch pulse.
Additionally, TIM1 is set as the master for TIM2.
The code also uses the channels of TIM2.
Channel 2 generates the Vsync pulse and Channel 3 generates the Vsync plus back porch pulse.
Because TIM2 is slaved to TIM1, it automatically fires in Lock-step with events generated by TIM1,
Ensuring that horizontal and vertical timing is properly synced.
Pixel data appears on MOSI of SPI1.
SPI1 is fed pixel data via DMA, which are triggered by completion of the Hsync plus back porch event on TIM1.
Each DMA transfer writes one line of pixel data to SPI1;
Data appears on MOSI with no timing gaps.
When each DMA transfer completes, a small amount of code in the IRQ handler
Updates the input pointer (m0ar) to point-to-either the next line of pixel data or,
If a full screen transfer just ended, the start of the frame buffer.
The IRQ handlers for DMA, TIM1 and TIM2 contain the only run-time code needed to keep the entire VGA output going;
We ' re talking about ten lines of code total.
All work to generate timing and move the pixel data are handled automatically by the timers and the DMA subsystem.
The pixel frequency is a fixed at-a-MHz, which in turn sets the SPI clock
And there is a limited number of prescalers in that clock range.
I set the MCU system clock to 144 MHz and then used A/2 Prescaler to get the need pixel clock.
Because the S4D has a much larger RAM pool than the original device,
I expanded the frame buffer to hold a full 800x600 monochrome display.
I also made slight modifications to the IRQ handlers to allow for the expanded pixel stream
and larger number of horizontal lines.
Because The original Artekit code is so well laid off, these changes were obvious and easy.
I left the Artekit demo code intact.
The demo looks almost exactly as its creators left it, except the text appears in the Upper-left corner rather than the CE Nter
And the text is quite a bit smaller though perfectly readable.
Here's a picture of the hardware:
Yes, I know, it looks pretty nasty. I ' ll clean it up on the next hardware revision.
The photo does show one small mod I made to the wiring.
The Artekit writeup talks about using a resistor divider on the GREEN input.
Basically, I connected PB5 to a 120-ohm resistor, with a PF capacitor wired across the resistor.
The other end of the resistor went to the GREEN input on the VGA connector.
Finally, I tied a 270-ohm resistor between the GREEN input and ground.
The Artekit page notes that they has driven a lot of monitors directly from the 3.3V output of their micro,
But it seemed to me using the the resistor divider would is a better choice.
Here are a picture of the output on a Dell monitor:
This was part of the Artekit demo program and gives your idea of the display quality.
You can just make out the original Artekit text near the upper-left corner.
Issues
I hit a few snags during this effort, not the least of which is my unfamiliarity with VGA signal generation and use of th E STM32 DMA subsystem.
One headache that came back almost immediately is the reason why I Haven ' t do any STM32 code in several months.
The ST Micros Std Peripheral Driver library just sucks.
Whoever thought it is a good idea to wrap a simple register change in a function call needs
To develop a few time-critical apps on the ATtiny and see what far, Concept gets you.
The only thing I ' ve seen that's worse is Arduino code.
At one point on my development, I needed to push a stream of bytes out the SPI,
So I used one of the library's functions to write to the DR register.
The function introduced NS gaps in the data stream.
Really? A-NS penalty for using the your library call?
Not gonna happen.
I switched to a direct write to the register using a macro and the gap disappeared.
I see a lot of STM32 code on the Web this uses library calls to initialize subsystems,
Then switches to macros inside functional code. That's pretty much what I do here.
And I hated doing even that, because I just knew the linker probably brought in 8K or so for bloat from invoking these call S.
I-ran into a problem with minor tearing (more like shimmering) of the display,
Even when the display is not being modified by the code
(So this is a issue with DMA accessing the frame buffer while the mainline code is accessing it).
I traced the problem to general memory accesses not related to the frame buffer.
I is using a spin-loop to generate delays, like this:
void sysdelayms (u32 dly) { int32_t n; int32_t J; for (n=0; n<dly; n++) { for (j=0; j<20000; j + +) ; // Rough guess, looking for 1 ms Delay }}
The above code caused the display to distort and tear while the mainline code is waiting for the delay to end.
If I moved the timing variables into registers and like this:
void sysdelayms (u32 dly) { register int32_t N; Register int32_t J; for (n=0; n<dly; n++) { for (j=0; j<20000; j + +) ; // Rough guess, looking for 1 ms Delay }}
The display became rock-solid and clean.
I don ' t know enough about the inner workings of the STM32F4-know why the earlier code affected the display.
If anyone has a answer, please drop me an email.
Summary
I ' ve bundled all of my code to a single zip file.
Like most of my embedded projects, I used Visual Studio as the IDE and the folder layout reflects that.
In order to set up this code to match your IDE, check through the Makefile (Vga_artekit.mak);
The comments should help you to edit the Makefile for your needs.
I ' m assuming here, you already has codesourcery lite++ installed on your machine
And you also has the ST Micros stm32f4 Discovery support files installed.
In my case, I installed the ST Micros code here:
C:\projects\STM32F4-Discovery_FW_V1.1.0\.
Note that I renamed any directories with spaces and their name to remove those spaces.
The spaces cause issues when I build within VS2008.
The code in this release are a working modification to the original Artekit code,
But I make no claims it is bug-free or even high quality.
It works, it shows an excellent display, but I know it needs more time and work from me.
I ' m releasing it so others can use it as a starting point for their projects.
As I update this code, I-ll try and keep this page updated, as well.
I started this project because I wanted VGA display of text from a embedded microcontroller for the least effort,
Smallest throughput hit, and lowest cost possible.
Thanks to Artekit and ST Micros, I now has the hardest part of the.
There ' s still plenty to do, of course.
I need to tweak the font file, wrap all of this code into a finished library,
Create text and graphics APIs for my other projects to use, and probably some other stuff.
But I am much farther along on my project than I could has hoped to being without the headstart given by Artekit.
Thanks, guys, for some excellent code and a terrific project!
Here is the zip file of my Project:vga_artekit.zip
VGA Output from STM32F4 Discovery Board