VGA on Mbed

-deleted-
28 Mar 2011

Hi Guys,

Thought I should share this with people.

I have just made the MBED output VGA video.

Its very hacked as normal an needs work, but I have code to share with people unlike the Componentless VGA cookbook which has no code.

Please note this is for the LPC1768.

If anyone is interested I have happy to post code.

28 Mar 2011

Nice enough. What resolution?

Lerche

28 Mar 2011

Hello David,

This is a very interesting subject please do post your code. What resolution and number of colour have you gotten from the mbed. I am attempting to make a simple photo player that will take photos from an SDcard and display then on the VGA monitor, ideally I need to run 3 monitors with the picture stretched across all three. Does this from you experience sound possible?

Regards Dave.

-deleted-
28 Mar 2011

Hi Dave,

From my current experience I would say no, at least not with an Mbed on its own.

I believe you would need external RAM, SRAM would be the easiest to work with.

An I also believe you would need to place the LPC1768 on a PCB/Prototyping Socket to get all the I/O you would need.

I can imagine how you can acomplish such a task but its by no means simple and no really doable using the Mbed module.

This is only my opinion though. It would greatly depend on the resolution of your images.

But I dont feel the MBed is upto being able to read pictures from an SDCARD and display them on a VGA monitor never mind 3 of them.

If you would like any more information feel free to ask an ill explain in more depth.

Regards, Dave.

-deleted-
28 Mar 2011

Below is the main code I used.

It is VERY messy and I cant take anywhere near all credit as I used the RIT Demo Library (albeit changing the library files) as a starting point.

I will be improving it an tidying it up but I thought I would post the main code for any one quite desperate to see the code as I would have been before I wrote this myself.

I still dont fully understand how it all works, particularly the interrupt handles and things to do with pointers.

It is one of those C things that still eludes me. if anyone would be kinda enough to explain in detail exactly what is happening, I would greatly appreciate it.



#include "mbed.h"
#include "RIT.h"
//AnalogOut sig(p18);
DigitalOut rit_led(LED1);
RIT rit(61);//RIT timer for firing the interrupt every 635nS

int rithit = 0;//Interrupt counter used for HSync timing
int linecount = 0;//Line Counter for VSync

void RIT_IRQHandler(void)
{
    
    rithit++; 
    linecount++;
}

int main()
{//Start of Main

//sig.write(40);
    LPC_GPIO0->FIODIR = 0x00000000;//Define Port 0 Direction as all Outputs
    LPC_GPIO0->FIODIR = 0x07800000 | 0x00000000; 
    LPC_GPIO0->FIOMASK = 0xF87FFFFF;//Define Port 0 Mask
    LPC_GPIO0->FIOCLR = 0x03000000;//Set Port 0 bits 23,24 and 25 to 0    
    
    rit.append(RIT_IRQHandler);
    rit.enable();
    
    while(1)//Infinite loop to continuously send HSync and VSync signals
    {// Start of While Loop
    
    if  (linecount < 50)
    {
    LPC_GPIO0->FIOCLR = 0x02000000;
    }

    else
    {
    LPC_GPIO0->FIOSET = 0x02000000;
    }
    
    
    if (rithit < 4)
    {
    LPC_GPIO0->FIOCLR = 0x01800000;
    }
    
    else
    {
    LPC_GPIO0->FIOSET = 0x01800000;
    }
    
    
        if (rithit < 25)
    {
    __nop();
    __nop();
    __nop();
    }
    
    else
    {
    rithit = 0;
    __nop();
    __nop();
    }
    
    
    
            if (linecount < 13250)
    {
    __nop();
    __nop();
    __nop();
    }
    
    else
    {
    linecount = 0;
    __nop();
    __nop();
    }

    }//End of While Loop

}//End of Main



-deleted-
28 Mar 2011

RIT Library cpp file containing my minor alterations to the timing allowing me to play with timings under 1us.

#include "LPC17xx.h"
#include "RIT.h"
#include "mbed.h"

//Note: Code Ideas taken from BLDC and ADC Library.

//TODO Document RIT.h
//TODO Publish as Library

RIT *RIT::instance;

//------------------------------------------------------------------------------
//Public Stuff.
//------------------------------------------------------------------------------

RIT::RIT(uint32_t ms) {
    //Default NULL global custom isr
    _rit_g_isr = NULL;

    // Set RIT interrupt priority
    NVIC_SetPriority(RIT_IRQn, 11);

    // Power the RIT
    power_enable();

    // Select perhiperal clk
    select_clk();

    // Set Clock
    setup_ms(ms);

    // Set counter clear/reset after interrupt
    LPC_RIT->RICTRL |= (2L); //RITENCLR

    // Connect the RIT interrupt to the interrupt handler
    instance = this;
    NVIC_SetVector(RIT_IRQn, (uint32_t)&_ritisr);

    // Disable the RIT interrupt
    disable();
}

//Setup Timing in ms.
void RIT::setup_ms(uint32_t ms) {
    LPC_RIT->RICOMPVAL = (uint32_t)(ms-1);
    LPC_RIT->RICOUNTER = 0;
}

//Setup Timing in us.
void RIT::setup_us(uint32_t us) {
    LPC_RIT->RICOMPVAL = (uint32_t)(((SYSCLK / ONEMHZ) * us)-1);
    LPC_RIT->RICOUNTER = 0;
}

//Attach custom interrupt handler replacing default
void RIT::attach(void(*fptr)(void)) {
    //* Attach IRQ
    NVIC_SetVector(ADC_IRQn, (uint32_t)fptr);
}

//Restore default interrupt handler
void RIT::detach(void) {
    //* Attach IRQ
    instance = this;

    NVIC_SetVector(ADC_IRQn, (uint32_t)&_ritisr);
}

//Unappend global interrupt handler to function isr
void RIT::append(void(*fptr)()) {
    _rit_g_isr = fptr;
}

//Detach global interrupt handler to function isr
void RIT::unappend() {
    _rit_g_isr = NULL;
}

void RIT::enable(void) {
    // Enable RIT interrupt
    NVIC_EnableIRQ(RIT_IRQn);

    // Enable the RIT
    LPC_RIT->RICTRL |= (8L); //RITEN;
}

void RIT::disable(void) {
    // Disable RIT interrupt
    NVIC_DisableIRQ(RIT_IRQn);

    // Disable the RIT
    LPC_RIT->RICTRL &= ~(8L); //RITEN;
}

void RIT::power_enable(void) {
    // Power the TRIT
    LPC_SC->PCONP |= (1L<<16); //PCRIT
}

void RIT::power_disable(void) {
    // Powerdown the RIT
    LPC_SC->PCONP &= ~(1L<<16); //PCRIT
}
//------------------------------------------------------------------------------
//Private Stuff.
//------------------------------------------------------------------------------

void RIT::_ritisr(void) {
    instance->ritisr();
}

void RIT::ritisr(void) {
    // RIT interrupt occured
    if ((LPC_RIT->RICTRL & 1L) == 1L) //RITINT
        // Clear the RIT interrupt
        LPC_RIT->RICTRL |= 1L;
    
    //Call User defined interrupt handlers if any
    if (_rit_g_isr != NULL)
        _rit_g_isr();
}

void RIT::select_clk(void) {
    // Timer0 perhiperal clock select (01: PCLK Peripheral = CCLK)
    LPC_SC->PCLKSEL1 &= ~(3L << 26); // Clear PCLK_RIT bits;
    LPC_SC->PCLKSEL1 |=  (1L << 26); // Set PCLK_RIT bits to 0x01;
}

One last thing to note guys is that the main code is very timing sensitive.

The nops are carefully places to balance out all the signals and as it stands changing one thing that may seem insigificant will knock out the video.

also currently it is componentless VGA.

I have it hooked up to an hp1825 without any resistors.

I did try with resistors to produce the 0.7V and a series resistor but I couldnt get any color that way.

as soon as I drove the red pin directly with a digital I/O it worked with color.

Thanks

-deleted-
29 Mar 2011

Last post for tonight,

A question of resolution was asked, currently stands at 13 x 480 displaying a scrolling color bar pattern using 3 bit color.

more messy code, really to play with this code you need a nice scope to measure the positive an negative widths etc. This is because one small change has a change reaction on the rest. If its possible to set multiple RIT's going or have multiple handlers (I think is the correct term but maybe wrong) then the code could be made much more efficient and less sensitive.

/* mbed Repetitive Interrupt Timer Library
 * Copyright (c) 2011 wvd_vegt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "mbed.h"
#include "RIT.h"
//AnalogOut sig(p18);
DigitalOut led1(LED1);
DigitalOut led2(LED2);
RIT rit(61);//RIT timer for firing the interrupt every 635nS

int rithit = 0;//Interrupt counter used for HSync timing
int linecount = 0;//Line Counter for VSync
int col = 0;
void RIT_IRQHandler(void)
{
    
    rithit++; 
    linecount++;
}

int main()
{//Start of Main

//sig.write(40);
    LPC_GPIO0->FIODIR = 0x00000000;//Define Port 0 Direction as all Outputs
    LPC_GPIO0->FIODIR = 0x07800000 | 0x00000000; 
    LPC_GPIO0->FIOMASK = 0xF87FFFFF;//Define Port 0 Mask
    LPC_GPIO0->FIOCLR = 0x03000000;//Set Port 0 bits 23,24 and 25 to 0    
    
     LPC_GPIO2->FIODIR = 0x00000000;//Define Port 0 Direction as all Outputs
    LPC_GPIO2->FIODIR = 0x00000007 | 0x00000000; 
    LPC_GPIO2->FIOMASK = 0xFFFFFFF8;//Define Port 0 Mask
    LPC_GPIO2->FIOCLR = 0x00000000;//Set Port 0 bits 23,24 and 25 to 0    
    
    led1 = 1;
    rit.append(RIT_IRQHandler);
    rit.enable();
    
    while(1)//Infinite loop to continuously send HSync and VSync signals
    {// Start of While Loop
    
    if  (linecount < 36)
    {
    LPC_GPIO0->FIOCLR = 0x02000000;
    }

    else
    {
    LPC_GPIO0->FIOSET = 0x02000000;
}
    
    if (rithit < 5)
    {
    LPC_GPIO0->FIOCLR = 0x01800000;
    col++;
    }
    
    else
    {
    LPC_GPIO0->FIOSET = 0x01800000;
    col++;
    }
    
    
        if (rithit < 33)
    {
    LPC_GPIO2->FIOPIN = col;


    //wait(0.2);
    //led1 =0;

    }
    
    else
    {
    rithit = 0;
    __nop();
    __nop();
        __nop();
col++;
  
    }
    
    
    
            if (linecount < 17750)
    {
__nop();  
    __nop();
    //led1 = 1;

    }
    
    else
    {
    linecount = 0;
    col = 0;
    __nop();

    }

    }//End of While Loop

}//End of Main

I currently have the wiring connected for 3 I/Os to R,G,B another 2 for H/VSync, and Red GND, Sync GND, and GND on the VGA connector all connector to GND on the MBed. There are no resistors being used so the MBed is driving the monitor directly.

31 Mar 2011

David,

You wrote -

Quote:

I still don't fully understand how it all works, particularly the interrupt handles and things to do with pointers.

An interrupt handler is just a sub-routine that gets called by the hardware whenever the conditions for an interrupt occur, and the interrupt's corresponding hardware register has been set to that subroutine's entry address.

The interrupt handler is not very special, but there are differences about the environment in which it is called that must be considered. For example, the CPU could be doing anything just before the handler is called. It could be between any of the statements in the user's code for the main routine or a called subroutine. It could be in the middle of a statment evaluating an expression. It could be anywhere within in a called library routine.

When the hardware calls the handler, it does not supply any arguments, and cannot use any returned value. So the handler must be a subroutine (not a function) that takes no arguments, and returns no values (type 'void' result). The hardware-generated call also pushes Registers 1 and 2 onto the stack just before the subroutine begins execution. The other special circumstance is that the interrupt which called the handler has blocked any other interrupts until the handler subroutine returns. The handler therefore needs to be declared with the 'Interrupt' attribute, so that the compiler knows about the pushed registers, and uses a special 'return from interrupt' op-code that pops Registers 2 and 1, and unblocks the other interrupts.

Large handler routines block the other interrupts for a long time (which is usually a bad thing). A handler should not call any library routines that depend (internally) on having the other interrupts running (not blocked). Therefore most handlers just manipulate a few global variables (test, set/clear, increment, move, etc.), then quickly return. A loop in the main routine can then set flags for the handler to check, check the globals for what actually needs to be done, and call any needed (large/complex) routines.

As far as pointers go, they are just variables that are used to hold the address of other objects. The actual contents of the particular object that the pointer is pointing 'at' are indicated by a '*' prefix operator, a structure component '->' in-fix operator, or an array index suffix '[<integer expression>]'. Increment (or decrementing) a pointer makes it point to the next (or previous) item in an array of such items. The pointer variable must have a type that matches the kind of object it is meant to point to, either in it's declaration, or by 'casting'.

I have done some editing of your VGA code to help explain how I think it is supposed to work. I have not compiled or run it. Like you, I have left various un-used/alternate code lines in comments. My main change was to move all of the main 'while (1) {...} code into the interrupt handler. This eliminates any sources of timing jitter, and does away with the need for fiddly '__noop()' timing adjustments. The handler runs at every 61 cycles of the 96MHz CPU clock. Any un-used clock cycles are eaten by the main 'while (1) { ; }' loop, with nothing to cause any timing jitter.

Note that it is possible for an interrupt handler to change the values in the interrupt hardware's controlling registers, so that the timing to the next call is varied, or so that a different handler will be called next time. This can be used to break-up the interrupt code into several different (and shorter) code blocks, and to export some of the logic functions into a form of 'state-machine'. It might be possible to run an 80 x 60 'block-pixel' display pattern that way. Other I/O could be relegated to the horizontal-retrace states, and to the 'video' states during vertical-retrace.

/* mbed Repetitive Interrupt Timer Library
 * Copyright (c) 2011 wvd_vegt
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
** Re-organized and commented by FJS 2011 March 29 -
** For amusment purposes only :-).
*/

// VGA color-bar signal generator program -

#include "mbed.h"
#include "RIT.h"

// Built-in constants yeild 26 (visible) color bars x 480 line
// resolution with 48 Khz x 89 Hz timing

// AnalogOut sig(p18);	// for 48 KHz sample rate audio out?

// DigitalOut led1(LED1);
// DigitalOut led2(LED2);

RIT rit(61); // Repetitive Interrupt Timer for firing the
             // interrupt every 635nS

// Globals retain values between interrupts -
int rithit = 0;	// Interrupt counter used for Sync and Color-bar timing
int vcount = 0;	// Counter for VSync
int col = 0;	// 3 bit RGB color value

// Unlike the usual situation, we want to put most of the
// work (with simple tests and assignments) into the interrupt
// routine to keep them phase-locked to interrupts from RIT -

// Should this should be marked with the property 'Interrupt'?
void RIT_IRQHandler(void) {
// register int new_color; // need temporary variable?

    // simple if/else clauses produce conditional instructions
    // for the ARM without any jumps/branches -

    // VSync lo interval is not locked to HSync -
    if  (++vcount < 36)	{ 		// set VSync lo or hi
	// TBD - make color black during retraces?  Vertical framing?
	LPC_GPIO0->FIOCLR = 0x02000000; // (61*35)/96 MHz => 22.240 us lo
    } else {
	// TBD - change RIT interrupt routine to next one in
	// chain, for vertical timing effects?
	LPC_GPIO0->FIOSET = 0x02000000;
    }

    if (++rithit < 5) {			// set HSync lo or hi
	// change RIT interval to HSync value?
	// change RIT interrupt routine to next state in chain?
        LPC_GPIO0->FIOCLR = 0x01800000; // 4/33 => 12% (2.542 us) lo
	// TBD - make color black during retrace?  Horizontal framing?
	// col = -1;	// color will be black (0) & locked to HSync
    } else {
	// change RIT interval to Color-Bar value?
        LPC_GPIO0->FIOSET = 0x01800000;
    }

    // TBD - Horizontal framing?
    ++col;    		// new RGB color per column; [1 .. 33] % 8.
    
    if (rithit < 33) {	// 96 Mhz/(61*33) = 47.69 KHz Horiz Freq.
	// Change RIT interval for video columns?

        LPC_GPIO2->FIOPIN = col; // set pins to 3 bit (1 of 8) color.

        // wait(0.2);
        // led1 =0;

    } else {
	// Change RIT interval for right-hand Horizontal Framing?
	// Set right-hand Horizontal Framing color on pins?

        rithit = 0;	// end of line, wrap rit/column count.

	{   // Always lock colors to Hsync? -
            // col = 0;	// end of line, sync color count?

	    // ++col;  	// or spin colors some more?

	    // or allow locked colors to be shifted sometimes? -
	    --col;  // lock colors to HSync (33 - 1 = 32 = 8 * 4)
	};

	// TBD - 48 KHz sample-rate audio out?

        // __nop();		// need to waste some time?
        // __nop();
        // __nop();
    }

    // re-set vcount to lock to the HSync rate, or to yeild 2:1 interlace
    // watch out for off-by-one errors?
    // (538 * 33) locks VSync rate to HSync? -
    if (vcount < (538 * 33)) { // 96 MHz/(61*33*538) = 88.643 Vert. Freq.
        ; //  __nop();		// need to waste some time?

        // led1 = 1;

    } else {
        vcount = 0;
        // col = 0; // Lock color to VSync (even when not locked to HSync)?
	// TBD - spin colors every few seconds (count VSyncs?)...
    }
    // TBD - spin colors every few seconds (count VSyncs?)...

    // return;
}

// TBD - Multiple RIT interrupt handlers chained round-robin for vertical
// and/or horizontal timings.


int main() {				// Start of Main

// sig.write(40);

    // Set-up hardware -
    LPC_GPIO0->FIODIR = 0x00000000;  // Define Port 0 Direction as all Outputs
    LPC_GPIO0->FIODIR = 0x07800000 | 0x00000000; 
    LPC_GPIO0->FIOMASK = 0xF87FFFFF; // Define Port 0 Mask bits 23 .. 26
    LPC_GPIO0->FIOCLR = 0x03000000;  // Set Port 0 bits 24 and 25 to 0    
    
    LPC_GPIO2->FIODIR = 0x00000000;  // Define Port 2 Direction as all Outputs
    LPC_GPIO2->FIODIR = 0x00000007 | 0x00000000; 
    LPC_GPIO2->FIOMASK = 0xFFFFFFF8; // Define Port 2 Mask bits 0 .. 2
    LPC_GPIO2->FIOCLR = 0x00000007;  // Set Port 2 bits 0, 1, and 2 to 0    
    
    // led1 = 1;

    // Set-up (initial) RIT interrupt -
    rit.append(RIT_IRQHandler);
    rit.enable();
    
    // There will be no Sync jitter if
    // RIT_interrupt_execution_time + loop_time == RIT interval -

    // if an empty 'while (1) {;}' loop translates into an aligned set of
    // Thumb codes for {jump_relative -2, no-op}, then that loop
    // can execute in a single clock cycle, and cannot produce jitter.

    while(1) // Infinite loop to continuously send HSync and VSync signals
    { // Start of While Loop    

	// interrupts handle all sync and color, locked to RIT.

    	// __nop(); // need to waste some time to avoid Sync jitter?

	// Make CPU halt/wait until interrupted?
	;

    } //End of While Loop

} //End of Main

Fred

31 Mar 2011

David,

I see in your RIT library posting where you have 're-purposed' the setup_ms() interface to use plain old clock counts. I take it that the 'finished' library might rename it to something like 'setup_clks()', and use the new name in the default instance?

Also, there are still a few references to 'ADC_IRQn' that I think are meant to be 'RIT_IRQn'? I'm not certain how your program could work, unless your compiled code actually uses a corrected library. Or are 'ADC_IRQn' and 'RIT_IRQn' aliases for the same interrupt vector table slot?