Graphics using C and Kye's video driver
Dr_Acula
Posts: 5,484
This is a quick video demo of Catalina running Kye's video driver http://www.youtube.com/watch?v=Vw3E38Zna8U
A couple of pictures of our recent family expedition to the our local open park zoo http://www.zoossa.com.au/monarto-zoo
This is demonstrating:
1) Running a C on the propeller using external memory (programs up to 512k)
2) Stopping the Catalina video driver cogs
3) Loading in Kye's video driver into a cog from an sd card into one of these cogs and starting it
4) Drawing some pixels and boxes on the screen
5) Loading some pictures off the sd card and displaying them
I believe there is a lot of potential for C to create a GUI on the propeller. I am not sure Spin can do this because by the time you write an sd driver (1/3 of the hub ram) and a video buffer (19200 bytes) there is hardly any ram left for actual code.
So I think an answer is to move the program out into external memory, which Catalina C can do. This frees up virtually all the hub for a video buffer.
Coupled with this is the ability to load 'cogjects' - standalone 2k code that can be loaded into cogs and run. We have cogjects now for keyboard, serial, multiple text video, mouse and now graphics video and these can all be loaded and unloaded under software control.
Next step is to try to speed up the sd reads as this is a lot slower than Kye's sd code (which is able to read in frames at about 20 per second). Maybe Ross H might have some suggestions there?
A couple of pictures of our recent family expedition to the our local open park zoo http://www.zoossa.com.au/monarto-zoo
This is demonstrating:
1) Running a C on the propeller using external memory (programs up to 512k)
2) Stopping the Catalina video driver cogs
3) Loading in Kye's video driver into a cog from an sd card into one of these cogs and starting it
4) Drawing some pixels and boxes on the screen
5) Loading some pictures off the sd card and displaying them
I believe there is a lot of potential for C to create a GUI on the propeller. I am not sure Spin can do this because by the time you write an sd driver (1/3 of the hub ram) and a video buffer (19200 bytes) there is hardly any ram left for actual code.
So I think an answer is to move the program out into external memory, which Catalina C can do. This frees up virtually all the hub for a video buffer.
Coupled with this is the ability to load 'cogjects' - standalone 2k code that can be loaded into cogs and run. We have cogjects now for keyboard, serial, multiple text video, mouse and now graphics video and these can all be loaded and unloaded under software control.
Next step is to try to speed up the sd reads as this is a lot slower than Kye's sd code (which is able to read in frames at about 20 per second). Maybe Ross H might have some suggestions there?
/* PASM cogject demonstration, see also cogject example in spin*/ #include <stdio.h> unsigned long cogarray[511]; // external memory common cog array // start of C functions void clearscreen() // white text on dark blue background { int i; for (i=0;i<40;i++) { t_setpos(0,0,i); // move cursor to next line t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100 } } void sleep(int milliseconds) // sleep function { _waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296); } char peek(int address) // function implementation of peek { return *((char *)address); } void poke(int address, char value) // function implementation of poke { *((char *)address) = value; } void external_memory_cog_load(int cognumber, unsigned long cogdata[], unsigned long parameters_array[]) // load a cog from external memory { unsigned long hubcog[511]; // create a local array, this is in hub ram, not external ram int i; for(i=0;i<512;i++) { hubcog[i]=cogdata[i]; // move from external memory to a local array in hub } _coginit((int)parameters_array>>2, (int)hubcog>>2, cognumber); // load the cog } int EoF (FILE* stream) { register int c, status = ((c = fgetc(stream)) == EOF); ungetc(c,stream); return status; } void readcog(char *filename,unsigned long external_cog[]) // read in a .cog file into external memory array { int i; FILE *FP1; i = 0; if((FP1=fopen(filename,"rb"))==0) // open the file { fprintf(stderr,"Can't open file %s\n",filename); exit(1); } fseek(FP1,0,0); for(i=0;i<24;i++) { getc(FP1); // read in the first 24 bytes and discard } i = 0; while(!EoF(FP1) & (i<505)) // run until end of file or 511-6 { external_cog[i] = getc(FP1) | (getc(FP1)<<8) | (getc(FP1)<<16) | (getc(FP1)<<24); // get the long i+=1; } if(FP1) { fclose(FP1); // close the file FP1=NULL; } printf("external array cog first long = 0x%x \n",external_cog[0]); // hex value } void pix_clearscreen(unsigned long screen[]) { int i; for (i=0;i<4800;i++) { screen[i] = 0x00000000; // fill with black, use longs rather than bytes so 4 pixels per loop } } void pix_pixel(unsigned long screen[], int x, int y, char color) { poke((y*160+x+(unsigned long)&screen[0]),color); } void pix_readscreen(char *filename, unsigned long screen[]) // read a full screen 19200 byte file into the screen { int i; FILE *FP1; i = 0; FP1=fopen(filename,"rb"); // open the file fseek(FP1,0,0); for(i=0;i<=4800;i++) { screen[i] = getc(FP1) | (getc(FP1)<<8) | (getc(FP1)<<16) | (getc(FP1)<<24); // get the long } fclose(FP1); // close the file FP1=NULL; } char pix_color(char red,char green,char blue) // pass red,green,blue 0-3, returns a combined value { return ((red & 0x3) << 6 ) | ((green & 0x3) << 4) | ((blue & 0x3) << 2); } void pixenginestart(int cognumber, unsigned long cogarray[], unsigned long screen[], unsigned long pingroup, char kye_graphics_parameters[]) { /* pinGroup := ((pinGroup <# 3) #> 0) ' pingroup only used in this pub long[screen][2] := ($FF << (8 * pinGroup)) long[screen][3] := ($30_00_00_FF | (pinGroup << 9)) pinGroup := constant((25_175_000 + 1_600) / 4) ' pingroup recycled as a new var long[screen][4] := 1 repeat 32 pinGroup <<= 1 long[screen][4] <-= 1 if(pinGroup => clkfreq) pinGroup -= clkfreq long[screen][4] +=1 long[screen][0] := @displayindicator long[screen][1] := @syncindicator 'cogNumber := cognew(@initialization, screen) ' and displayindicatoraddress/syncindicatoraddress are cog variables cognumber := cognew(cogarray,screen) ' start cog waitcnt((((clkfreq / 1000 * 1) - 4296) #> 381) + cnt) ' Wait for designated time in ms eg 1000*1 is 1ms ' need a short delay otherwise the variables don't get passed to the cog */ // no need for first pingroup test, always will be 0,1,2,3 int i; char *ptr; screen[2] = (0xff << (8*pingroup)); //printf("value is %x \n",screen[2]); screen[3] = (0x300000ff | (pingroup << 9)); //printf("value is %x \n",screen[3]); pingroup = ((25175000 + 1600) / 4); //printf("value is %x \n",pingroup); screen[4] = 1; for(i=0;i<32;i++) { pingroup = pingroup << 1; screen[4] = (screen[4] << 1) | (screen[4] >> 31); //printf("value is %x \n",screen[4]); if (pingroup >= _clockfreq()) { pingroup -= _clockfreq(); screen[4] += 1; } } kye_graphics_parameters[0] = 1; // displayindicator kye_graphics_parameters[1] = 0; // syncindicator //printf("pointers \n"); ptr = &kye_graphics_parameters[0]; screen[0] = (unsigned long) ptr; //printf("value is %u \n",screen[0]); ptr = &kye_graphics_parameters[1]; screen[1] = (unsigned long) ptr; //printf("value is %u \n",screen[1]); //_cogstop(0); // not video _cogstop(1); // video shut down this driver // cog 2 is catalina _cogstop(3); // video shut down this driver //_cogstop(4); // not sure what this does, hangs prop when add this //_cogstop(5); // not video //_cogstop(6); // not video external_memory_cog_load(7,cogarray,screen); // load from external ram, pass some values in screen[] pix_clearscreen(screen); // clear the screen to black } void pix_colorbar(unsigned long screen[]) { int x; int c; x = 0; for (c=0;c<64;c++) { pix_pixel(screen,x+10,6,(c<<2)); // print all the colors in a bar x++; } } void pix_box_unfilled(unsigned long screen[], int startx, int starty, int endx, int endy, char color) { int row; int col; for(col=startx;col <= endx ;col++) { pix_pixel(screen,col,starty,color); // horizontal lines pix_pixel(screen,col,endy,color); } for(row=starty;row <= endy;row++) { pix_pixel(screen,startx,row,color); pix_pixel(screen,endx,row,color); } } void pix_box_fill(unsigned long screen[], int startx, int starty, int endx, int endy, char color) { int row; int col; for(row =starty;row <= endy; row++) { for(col=startx;col <=endx; col++) { pix_pixel(screen,col,row,color); // horizontal line } } } void kyegraphicsdemo(unsigned long screen[],char kye_graphics_parameters[]) // kye graphics demo { clearscreen(); // white on blue vga printf("Clock speed %u \n",_clockfreq()); // see page 28 of the propeller manual for other useful commands printf("Catalina running in cog number %i \n",_cogid()); // integer printf("Kye graphics demonstration \n"); sleep(2000); // 2 sec delay readcog("vgagraph.cog",cogarray); // read in kye's graphics driver pixenginestart(7,cogarray,screen,2,kye_graphics_parameters); // start the driver pix_pixel(screen,3,3,0x44); pix_pixel(screen,4,4,0xFF); pix_pixel(screen,5,5,pix_color(1,2,3)); // rgb each 0,1,2 or 3 pix_box_unfilled(screen,0,0,159,119,pix_color(3,0,0)); // red border for screen pix_colorbar(screen); // a color bar pix_box_unfilled(screen,10,10,30,30,pix_color(3,3,0)); // a yellow box pix_box_fill(screen,10,50,40,60,pix_color(3,0,3)); // magenta filled rectangle sleep(2000); pix_readscreen("wallaby.vga",screen); // display whole screen image pix_readscreen("Giraffe.vga",screen); // display whole screen image pix_readscreen("prop160.vga",screen); // display whole screen image } void main () { char kye_graphics_parameters[1]; // displayindicator and syncindicater are permanent hub variables unsigned long screen[160*120/4]; // graphics and text video buffer in hub - eg 19200 bytes kyegraphicsdemo(screen,kye_graphics_parameters); while (1); // endless loop as prop reboots on exit from main() }
Comments
Yes, I know the SD card driver I use is quite slow. It was my own adaption. When I get time I'll have a look at speeding it up.
Ross.
P.S. With release 3.0, it is much easier to identiy what cogs are doing what - see the new demo program test_plugin_names.c
I've got it down to about half a second by storing the picture in external memory. Got a tiny bit more speed using memcpy
I've added a button, text box and radio box - see attached photo. Next step is to capture a font. Catalina with external memory is great because things like fonts can be stored in external ram.
I've just realised though that if you only want buttons, text boxes, radio buttons etc, and no picture boxes, then a gray scale works fine. That means the color depth can go from 64 down to 4 (black, dark gray, light gray and white) and in theory, that means we could increase the number of pixels.
So - off to the obex to rummage around, and lo and behold, I managed to find one written by Kye. The demo he has was written at 160x120 and has a blue 3D thing bouncing around the screen. But this does not quite show off the full features of this driver.
I have tweaked this a little so it is 320x240 and with a light gray background and with some grayscale lines. Attached is a little demo - this will run on all the standard propeller boards with vga on pin group 2. Next step is to turn this into a cogject and port it into C.
I think Kye's driver can also do 640x480 in two colors - that may come in handy too.
The nice thing about cogjects and C is you can flip between all these video modes, and text video drivers all within the one program.
640x480 @ 1bpp = 38,400 bytes ... so it does not fit in the prop
640x240 @ 1bpp = 19,200 bytes
I would think there is likely to be some open source gui c code. That Prop Computer is getting closer
As for the graphics, perhaps a mix of tiles and text is possibly what we require, with these being combined by cogs. Not quite sure how to go about this (maybe a discussion point at the next UPEOZSA conference)
Ross: I presume the slow SD code is caused by the SD low level access. Perhaps Kye's pasm code could be adapted here??
@Cluso, I'm writing the buttons and radio boxes from scratch - doing a screen capture on standard windows buttons, blowing them up and then turning them into arrays. A little tedious, but most of them are pretty easy to do. I've got an automated font capture almost done - I'm going to try fixed width and variable width fonts.
I've got a bit stuck though with Kye's video driver. In general terms, when turning an obex into a cogject, there are no common variables between spin and pasm. The only link is via "par" so the first step is to comment out any DAT sections and see where all the errors are. Then pass variables in a list.
In Kye's 160 video driver I simply passed all the variables via the screen buffer, then in the cog code did a whole lot of rdlongs into the cog variables.
Kye's 320 video driver is very similar looking but it is not working. I've got the code written but I've had to come back a few steps to test things out one thing at a time. I've narrowed down the problem to a few lines of code:
This is different to the 160 driver as we have this new screenpointer variable.
To me this is quite confusing, as you start by passing from the 'main' program a pointer to the screen buffer. So that might pass a value like 2000. Then in this routine, you make a pointer equal to that value. Then you pass a pointer to a pointer??
I need to understand why he is doing this, because if you make one simple change, it garbles the screen:
I would have thought this would pass the same value as above?
As far as I can see, "newdisplaypointer" does not get changed in the startup routine. Is there something else clever going on, some other variable that is next to "newdisplaypointer" that changes it as well?
Any help here would be most appreciated.
I fixed this with the previous video driver by adding a delay just after the cognew
but this is not working for this video driver.
This is most confusing. Say I start my main with a declaration of a screen buffer
and then start the method with this and in the method is and then there is a line and then I want to fill up the screen buffer. Will this line below work? or is this pointing to something different?
Actually, what I am really confused about is where the screen buffer is. In the previous video driver we pass the value as above, and we have and we start the cog with in other words, we start with "screen", not with "@screen"
What I don't understand is why this new driver starts with an @ symbol. The value being passed to the method is a number which already has an @ in the main routine, so I can't grasp why there is a pointer to a pointer?
And I suspect because of this I'm not filling up the screen buffer, but some other random location in ram (and probably overwriting things).
I'm still confused about pointers. There are three of these in a row and I'm not sure why Kye does it that way.
I'll try a mailbox and see if I can get it working.
I'll start by putting everything into a mailbox (there are 14 longs in total that are passed), but some of these are stored in cog variables so maybe the mailbox can be made smaller once it is working.
Addit: Some working code. The only way to do this is to recompile after changing each line. There are too many variables otherwise. Also every time a variable is changed, you have to do a F3 and search for all other places it appears. Cogject programming is a very different style!
Thanks again to Kuroneko for finding that par rdlong in the main loop. I've changed the loop now so it stores the screen location in a new variable.