16 bit NTSC video Driver
Perry
Posts: 253
Thanks to Eric Ball's work with his 8bpp http://forums.parallax.com/showthread.php?134384-NTSC-8bpp-bitmap-display-driver&highlight=8bpp
I am offering for evaluation a 16bit NTSC video driver
This driver only uses 2 pins, on the demo board they would likely be 13 and 15.
pin 13 is the color signals capacitivly coupled to a Duty Dac from pin 15 to deliver luminence.
the demo shows 16 colors and 119 shades
Thanks to any spinner-heads who might try this
Perry
P.S.
having trouble with attachment so here are the two spin files.
first main test program: NTSC_16bit_demo.spin
and secondly the actual video driver: Pixelator_16bit_NTSC.spin
I am offering for evaluation a 16bit NTSC video driver
This driver only uses 2 pins, on the demo board they would likely be 13 and 15.
pin 13 is the color signals capacitivly coupled to a Duty Dac from pin 15 to deliver luminence.
the demo shows 16 colors and 119 shades
Thanks to any spinner-heads who might try this
Perry
P.S.
having trouble with attachment so here are the two spin files.
first main test program: NTSC_16bit_demo.spin
{{ 16bit NTSC video modifications Octber 2011 Perry James Mole pjm@hey-hello.ca ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ NTSC 8bpp bitmap TV driver demo (C) 2011-08-19 Eric Ball │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ TERMS OF USE: Parallax Object Exchange License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │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. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }} CON ' Set up the processor clock in the standard way for 80MHz ' _XINFREQ = 13_500_000 + 0000 ' _CLKMODE = XTAL1 + PLL8X _clkmode = xtal1 + pll16x _clkfreq = 80_000_000 ' Demoboard TV output on pins 13 and 15 TV_PIN = 12 { Notes from Grey TV driver: This driver uses PWM to generate output so a low pass filter is recommended: 0.001µF pin 13 ─────┐ pin 15 ──┳─╋── Composit Video output   value of this cap is small I'm unsure   The resistor values (124Ω series, 191Ω to ground) have an output impedance of 75 ohms and will drive a 75-ohm load at 1V P-P. The cap is there to filter the DUTY doody. - Phil Pilgrim http://forums.parallax.com/forums/default.aspx?f=25&m=340731&g=342216 However, in my experience, the RC network is not required. I have tested successfully using any of the Demoboard TV DAC resistors (although the higher resistance yields darker text) and with no resistors at all (although this is not recommended). } OBJ tv : "Pixelator_16bit_NTSC" '"NTSC 8bpp" VAR ' NTSC8bpp input parameters - 7 contiguous longs, reloaded each frame ' Note: NTSC8bpp input parameters not validated, minimum clockspeed 16.364MHz LONG i_ptr ' pointer to screen (bytes, long aligned), 0 = inactive LONG i_width ' number of columns, must be divisible by 4 (i_width*i_clocks < 3192-3894MHz/CLKFREQ) LONG i_height ' number of rows (max i_lines) LONG i_clocks ' pixel width in 16x colorburst clocks (256 => i_clocks > 515MHz/CLKFREQ) LONG i_lines ' number of active lines (max 482, does not need to be integer multiple of i_height) LONG i_pin ' must be divisible by 4, i_pin = 1.1kΩ, i_pin+1 = 560Ω, i_pin+2 = 270Ω LONG i_frqa ' 0 = calculate FRQA using CLKFREQ=long[0], MSB set = mask of calculated value, MSB clear = value to use long i_gain ' output video gain Added for Pixelator ' For "square" pixels 3*i_height*i_clocks = 14*i_lines ' i_clocks < 10 and i_lines < 2*i_height will generate color artifacts instead of extra detail ' Due to overscan not all of the screen is visible on all TVs. ' "Action Safe" i_width*i_clocks < 2750, i_lines < 433; "Title Safe" i_width*i_clocks < 2444, i_lines < 385 word bitmap[80*120] PUB start | color,i,j,ver tv.start( @i_ptr ) i_width := 80 i_height := 120 i_lines := 480 i_clocks := 36 i_pin := TV_PIN i_frqa := 0 i_ptr := @bitmap i_gain := 24 repeat j from 0 to 119 step 1 repeat i from 0 to 79 step 1 bitmap[i+j*80] := ((((i/4 & $F )<<4) + 8) << 8) + j*2
and secondly the actual video driver: Pixelator_16bit_NTSC.spin
{{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ NTSC 8bpp bitmap TV driver (C) 2011-06-16 Eric Ball │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ modifications for 16 bit Octber 2011 Perry James Mole most changes should be commented with ' PJM │ │ The NTSC8bpp TV driver displays a i_width x i_height image on a TV using standard 3pin TV DAC. │ │ The bitmap is padded horizontally and scaled vertically to i_lines. │ │ Pixels are stored in display order (left to right, top to bottom). │ │ │ │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ TERMS OF USE: Parallax Object Exchange License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │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. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }} VAR ' long cog {{ ' NTSC8bpp input parameters - 7 contiguous longs, reloaded each frame ' Note: NTSC8bpp input parameters not validated, minimum clockspeed 16.364MHz LONG i_ptr ' pointer to screen (bytes, long aligned), 0 = inactive LONG i_width ' number of columns, must be divisible by 4 (i_width*i_clocks < 3192-3894MHz/CLKFREQ) LONG i_height ' number of rows (max i_lines) square pixel = (14*i_height)/(3*i_lines) LONG i_clocks ' pixel width in 16x colorburst clocks (256 => i_clocks > 515MHz/CLKFREQ) LONG i_lines ' number of active lines (max 482, does not need to be integer multiple of i_height) LONG i_pin ' must be divisible by 4, i_pin = 1.1kΩ, i_pin+1 = 560Ω, i_pin+2 = 270Ω LONG i_frqa ' 0 = calculate FRQA using CLKFREQ=long[0], MSB set = mask of calculated value, MSB clear = value to use LONG l_Gain ' PJM added to control greyscale output dynamicaly ie contrast ' For "square" pixels 3*i_height*i_clocks = 14*i_lines ' i_clocks < 10 and i_lines < 2*i_height will generate color artifacts instead of extra detail ' Due to overscan not all of the screen is visible on all TVs. ' "Action Safe" i_width*i_clocks < 2750, i_lines < 433; "Title Safe" i_width*i_clocks < 2444, i_lines < 385 }} PUB start( parmptr ) stop RESULT := COGNEW( @cogstart, parmptr) cog := RESULT + 1 RETURN PUB stop COGSTOP( cog~ - 1 ) DAT {{ The purpose of this code is twofold. First, it can be used to display a byte per pixel (TV color) bitmap. Second, this code is intended to be a template which may be used by others to develop Propeller video drivers. Note: this code creates an 29.97Hz 525 line interlaced display. Rules for developing video drivers: 1. Start simple. Hardcode values and static display. 2. Add complexity and changes incrementally. Verify at each step. 3. If something doesn't work it's either because you have made an incorrect assumption or made a coding error. }} ORG 0 cogstart RDLONG i_ptr, PAR WZ IF_Z JMP #cogstart MOV taskptr, #taskone MOV numlines, #4 :dotasks JMPRET taskptr, taskptr DJNZ numlines, #:dotasks {{ An interlaced NTSC frame is made up of 525 lines: 9 lines vertical sync (6 equalizing pulses, 6 serration pulses, 6 equalizing pulses) 12 lines blank 241 lines active (top field / first line) half line (with sync pulse) 9 lines vertical sync (6 equalizing pulses, 6 serration pulses, 6 equalizing pulses) half line (without sync pulse) 12 lines blank 241 lines active (bottom field / last line) A non-interlaced NTSC frame is made up of 262 lines: 9 lines vertical sync (6 equalizing pulses, 6 serration pulses, 6 equalizing pulses) 12 lines blank 241 lines active }} frame MOV numlines, numblank0 WZ ' overscan blank lines MOV taskptr, #taskone ' reload parameters IF_NZ CALL #blank MOV nxtptr, i_ptr MOV linenum, i_lines MOV numlines, numodd CALL #active MOV numlines, numblank1 WZ ' overscan blank lines IF_NZ CALL #blank MOV VSCL, vscl_sync ' half line WAITVID dblank, #0 MOV FRQB, #0 ' PJM MOV VSCL, vscl_serr WAITVID dblank, sblank MOV FRQB, pwmlut ' PJM CALL #vsync MOV VSCL, vscl_half ' extend blank for 1/2 line WAITVID dblank, sblank MOV FRQB, #0 ' PJM { One of the limitations of TV.spin is it only allows integer vertical scaling. This calculation (and the matching calculation inside the active loop) scales a i_height bitmap to i_lines. Note: the first line is always displayed on the top field. } MOV nxtptr, i_ptr MOV linenum, i_lines SUB linenum, i_height WZ,WC ' set scaling for 2nd line IF_Z_OR_C ADD linenum, i_lines IF_Z_OR_C ADD nxtptr, i_width ' go to next line (single line res) IF_Z_OR_C ADD nxtptr, i_width ' go to next line (single line res) MOV numlines, numblank2 WZ ' overscan blank lines IF_NZ CALL #blank MOV numlines, numeven CALL #active MOV numlines, numblank3 WZ ' overscan blank lines IF_NZ CALL #blank CALL #vsync ' moved here to reduce instructions ' from active to equalizing JMP #frame {{ As each pixel may be any color, the pixels are effectively the color values. Therefor the code uses a "reverse WAITVID" which uses the pixel value for the color (destination) parameter and #%%3210 to output the colors in LSB first order. The downside of this method is it requires more RAM and the maximum resolution is clockspeed limited (which isn't a problem for composite NTSC which is bandwidth limited by the colorburst frequency). }} active MOV VSCL, vscl_sync ' 4.7usec @ -40 IRE (sync pulse) WAITVID dblank, #0 MOV FRQB, #0 ' PJM MOV pixptr, nxtptr ' precalculate pointer SUB linenum, i_height WZ,WC ' vertical scaling calculations IF_Z_OR_C ADD linenum, i_lines IF_Z_OR_C ADD nxtptr, w_width ' go to next line SUB linenum, i_height WZ,WC ' do twice for interlace IF_Z_OR_C ADD linenum, i_lines IF_Z_OR_C ADD nxtptr, w_width ' go to next line MOV numpixels, i_width MOV VSCL, vscl_s2cb ' 0.6usec @ 0 IRE (blank) WAITVID dblank, sblank MOV FRQB, pwmlut ' PJM MOV VSCL, vscl_burst ' 9 cycles of colorburst WAITVID dblank, sburst MOV FRQB, burst 'pwmlut ' PJM MOV VSCL, vscl_edge WAITVID dblank, sblank MOV FRQB, black ' PJM mov vs4,vscl_active ' PJM shr vs4,#2 ' PJM MOV VSCL, vs4 'vscl_active ' PJM ' i_clocks PLLA per pixel, 4 pixels per frame ' WAITVID pixel, #%%0 :loop RDword pixel, pixptr ' PJM'RDLONG pixel, pixptr ' 22 CLK ' WAITVID pixel, #%%3210 ' +6 CLK ' ADD pixptr, #4 ' +4 CLK mov luminance,pixel ' PJM shl luminance,l_Gain ' PJM add luminance,black ' PJM WAITVID pixel, #%%1 ' PJM MOV FRQb,luminance ' PJM ADD pixptr, #2 ' PJM DJNZ numpixels, #:loop ' +4 CLK = 36 CLK MOV VSCL, vscl_edge WAITVID dblank, sblank MOV FRQB, pwmlut ' PJM DJNZ numlines, #active ' next line active_ret RET {{ Video drivers are constrained by WAITVID to WAITVID timing. In the inner active display loop, this determines the maximum resolution at a given clock frequency. Other WAITVID to WAITVID intervals (e.g. front porch) determine the minimum clock frequency. For example the inner :loop in this code is 36 CLK and displays 4 pixels. Therefore with an 80MHz clock the minimum VSCL.PixelClocks (i_width) is 7. 36 / 80MHz * 57.272727MHz / 4 = 6.44 Similar calculations should be used between each pair of WAITVIDs to determine the minimum clock frequency (including initialization tasks). Note: the first HUBOP after a WAITVID should be counted as 22 CLK as it cannot be synchronized. WAITVID should be counted as 6 CLK as 5 CLK can cause intermittent errors with very tight timing. }} vsync MOV numlines, #6 :equal1 MOV VSCL, vscl_eqlo ' equalizing pulse (sync/2) WAITVID dblank, #0 mov frqb,#0 ' PJM MOV VSCL, vscl_eqhi WAITVID dblank, sblank mov frqb,pwmlut ' PJM DJNZ numlines, #:equal1 MOV numlines, #6 :serration MOV VSCL, vscl_serr ' serration pulse (sync) WAITVID dblank, #0 ' PJM mov frqb,#0 MOV VSCL, vscl_sync WAITVID dblank, sblank mov frqb,pwmlut ' PJM DJNZ numlines, #:serration MOV numlines, #6 :equal2 MOV VSCL, vscl_eqlo ' equalizing pulse (sync/2) WAITVID dblank, #0 mov frqb,#0 ' PJM MOV VSCL, vscl_eqhi WAITVID dblank, sblank mov frqb,pwmlut ' PJM DJNZ numlines, #:equal2 vsync_ret RET blank MOV VSCL, vscl_sync ' 4.7usec @ -40 IRE (sync pulse) WAITVID dblank, #0 ' PJM mov frqb,#0 MOV VSCL, vscl_s2cb ' 0.6usec @ 0 IRE (blank) WAITVID dblank, sblank mov frqb,pwmlut MOV VSCL, vscl_burst ' 9 cycles of colorburst WAITVID dblank, sburst mov frqb,#0 ' PJM MOV VSCL, vscl_half WAITVID dblank, sblank mov frqb,pwmlut ' PJM JMPRET taskptr, taskptr MOV VSCL, vscl_blank WAITVID dblank, sblank mov frqb,#0 ' PJM DJNZ numlines, #blank ' next line blank_ret RET d1 LONG |<9 ' destination = 1 taskone MOV pixptr, PAR ' load input parameters MOVD :i_loop, #i_ptr ' 242 CLK MOV numpixels, #8 ' ' PJM #7 add l_Gain to inputs :i_loop RDLONG i_ptr, pixptr ADD pixptr, #4 ADD :i_loop, d1 DJNZ numpixels, #:i_loop MOV W_width,i_width shL W_width,#1 CMP i_ptr, #0 WZ ' 0 = blank IF_Z JMP #standby ' 8 CLK MOV vscl_active, i_clocks ' vscl_active = i_clocks<<12 + i_clocks*4 SHL vscl_active, #12 ' 16 CLK ADD vscl_active, i_clocks SHL vscl_active, #2 MOV vscl_edge, vscl_edge0 ' vscl_edge = 1<<12 + (3192 - i_clocks*i_width)/2 MOV numerator, i_width ' 120 CLK MOV divisor, i_width MAX numerator, i_clocks MIN divisor, i_clocks :mul SHR divisor, #1 WZ,WC ' maximum 6 iterations IF_C SUB vscl_edge, numerator SHL numerator, #1 IF_NZ JMP #:mul SHR vscl_edge, #1 { The calculations here accomplish two tasks: ensure the blank lines are distributed correctly and add on the blank lines to the top of each field. } ' numeven := rounddown(i_lines / 2) MOV numeven, i_lines ' 60 CLK SHR numeven, #1 ' numodd := roundup(i_lines / 2) MOV numodd, i_lines SUB numodd, numeven ' numblank2 := numblank0 := int( (484-i_lines)/4 ) + 12 MOV numblank0, #484 SUB numblank0, i_lines SHR numblank0, #2 ADD numblank0, #12 MOV numblank2, numblank0 ' numblank1 := int( (482-i_lines)/4 ) MOV numblank1, #482 SUB numblank1, i_lines SHR numblank1, #2 ' numblank3 := numblank1 + i_lines // 2 MOV numblank3, i_lines AND numblank3, #1 ADD numblank3, numblank1 JMPRET taskptr, taskptr ' 242+8+16+120+60 = 446 CLK add i_pin,#3 ' ' PJM for Grey duty cycle MOVS CTRb, i_pin MOV pixel, #1 SHL pixel, i_pin mov DIRA, pixel sub i_pin,#3 ' PJM set pin back MOVI CTRb, #%0_00110_000 ' PJM use PLL%1 was #%0_00110_111 ' turn on PWM AND i_pin, #28 ' 16 CLK MOV pixel, #%0010 'PJM use only color pin '7 summing network output SHL pixel, i_pin or DIRA, pixel ' set pin mask MOV pixel, i_pin ' 36 CLK SHR pixel, #3 MOVD VCFG, pixel ' set VGroup AND i_pin, #4 WZ MOV pixel, #7 SHL pixel, i_pin MOVS VCFG, pixel ' set VPins IF_Z MOVI VCFG, #%0_10_1_0_1_000 ' composite baseband on VPins[2..0] IF_NZ MOVI VCFG, #%0_11_1_0_1_000 ' composite baseband on VPins[6..4] RDLONG divisor, #0 ' CLKFREQ MOV quotient, l9090909 ' 306 CLK MOV numerator, l7159090 :shlmax SHL quotient, #1 WC ' maximize the two values RCL numerator, #1 ' maximum 17 iterations (based on minimum 16MHz CLK) SHL divisor, #1 WC IF_NC JMP #:shlmax RCR divisor, #1 ' undo overshoot JMPRET taskptr, taskptr ' 16+36+306 = 358 CLK MOV numpixels, #16 ' do division (first half) :div1 CMPSUB numerator, divisor WC ' 264 CLK RCL quotient, #1 SHR divisor, #1 WZ DJNZ numpixels, #:div1 JMPRET taskptr, taskptr ' 264 CLK :div2 CMPSUB numerator, divisor WC ' complete division RCL quotient, #1 ' 264 CLK SHR divisor, #1 WZ IF_NZ JMP #:div2 { See http://www.linusakesson.net/programming/propeller/pllsync.php for synchronizing multiple video output cogs Otherwise just be aware that due to PLL startup time and the initial value of the internal frame counter being unknown that the timing and stability of the first frame is not predictable. } MOV VSCL, ivscl ' set VSCL to instant reload MOVI CTRA, #%0_00001_110 ' PLL internal mode, x8 CTRA = 57.2727272MHz (colorburst * 16) SHL i_frqa, #1 WC,WZ,NR ' test MSB/zero IF_NZ_AND_NC MOV quotient, i_frqa IF_C AND quotient, i_frqa MOV FRQA, quotient ' set CTRA to 7.159MHz, start the counters lasttask JMPRET taskptr, taskptr ' 256+24 = 280 CLK JMP #lasttask standby MOV DIRA, #0 MOV VCFG, #0 MOV CTRA, #0 MOV CTRb, #0 JMP #cogstart l7159090 LONG $6D3D32 ' 7159090 Hz l9090909 LONG $E8BA2E8B ' 0.909090909 Hz ivscl LONG 1<<12+1 ' initial delay vscl_sync LONG 1<<12+269 ' 4.7usec vscl_s2cb LONG 1<<12+304-269 ' sync to colorburst vscl_burst LONG 16<<12+16*9 ' 16 PLLA per cycle, 9 cycles of colorburst vscl_eqlo LONG 1<<12+135 ' sync/2 vscl_eqhi LONG 1<<12+1685 ' H/2 - sync/2 vscl_serr LONG 1<<12+1551 ' H/2 - sync vscl_half LONG 1<<12+1820 ' H/2 vscl_blank LONG 1<<12+1372 dblank LONG $8A0200 sblank LONG %%1111_1111_1111_1111 ' 16 pixels color 1 sburst LONG %%2222_2222_2222_2222 ' 16 pixels color 2 vscl_edge0 LONG 1<<13+3192 ' initial value (pre shifted) pwmlut LONG $4924a249 '$3d249249 ' PJM black LONG $4a24a249 '$3e93B84A ' PJM burst LONG $4f24a249 '$3f000000 ' PJM 'color_mask long $FFFFFFF0 ' PJM cog long 0 vscl_edge RES 1 ' border, 1 PLLA per pixel vscl_active RES 1 ' 4 pixels @ i_clocks PLLA per pixel i_ptr RES 1 ' pointer to screen (bytes) 0 = inactive i_width RES 1 ' number of longs (4 pixels per long) i_height RES 1 ' number of rows i_clocks RES 1 ' pixel width i_lines RES 1 ' number of active lines i_pin RES 1 ' pin number i_frqa RES 1 ' FRQA value / mask l_gain res 1 W_width RES 1 ' number of longs (4 pixels per long) numeven RES 1 ' number of even active lines numodd RES 1 ' number of odd active lines numblank0 RES 1 numblank1 RES 1 numblank2 RES 1 numblank3 RES 1 quotient RES 1 numerator RES 1 divisor RES 1 taskptr RES 1 ' initialization task jmpret pointer numlines RES 1 ' overall line counter linenum RES 1 ' line scaling counter numpixels RES 1 ' pixel counter pixptr RES 1 ' pointer to pixels nxtptr RES 1 ' pointer to next line pixel RES 1 ' pixel value luminance res 1 vs4 res 1 FIT 496
Comments
I'm surprised I'm the first to respond to this driver!
Fantastic colour range in this driver ( although at 5Mhz I get a lot of black flickery lines scrolling up the screen, so I swapped to a 6Mhz crystal on my demo board, and WOW, instant fix! )
Looks brilliant Perry, now to get some free time and figure out some cool stuff to do with it , what with this, and PhiPi's Colour Video Grabber!
This looks for interesting times ahead!
Baggers.
No R/C network here, and I see that it TVPin was given a value of 12 and my TV resistors are on pins 16,17,18 so I tried a variety of values from 15 to 18. With 15 I got a nice white to black fade, and with 17 I got some color bars.
Which is the one we are trying to display?
This is a very intriguing piece of code.
Thanks for the image, it should show much better than that!
to set pins for 16 use TV_PIN = 16 in the main code, the outputs used are then on pins 17 and 19
17 for chroma .001 cap to resistors
19 for luma
I just left out the duty cap in my test.
and use an alternate RCA connector to connect to TV
Thanks for your comments.
I have using this with "stupid video capture", I was playing again last night with "false color" movies as I was testing the code that I hope to drop Phil's color decoder into.
There seems to be at least one error in the first post
around line 270 in the video driver I normally use 108MHz clock
but this code works at 80Mhz for me except for static horizontal funnys that aren't seen in videos.
I have yet to study the part in the code that seems to time the instruction cycle! taskone?
And again I must add, the colour range with this driver is amazing! well done!
Have not tried very high horz sizes, The vertical bars at 5Mhz are probably "i_clocks" set too small.
My internet has been down lately, am visitinig a free access point!
Perry
Maximum resolution is a function of the inner loop:
So that's 51 CLK per pixel at 96MHz, or a minimum of 31 PLLA (i_clocks) per pixel, which works out to an i_width of around 89 pixels.
Having a great play with this right now. VERY impressive range of color. This is just a brilliant idea Perry. Very well done!
Would anybody be interested in a higher resolution luma only overlay?
I'm thinking of firing off another COG running another, simple 4 color tile driver to run right on the same pins. Been a while since I tried that. I think I can do it this time, particularly if it's luma only.
Another alternative would be to run the luma channel on this one at the current 80 or so pixels, but open up the waitvid so that a few pixels fit in there... Both methods could be combined. Too bad I don't have some kind of simple latch, where I could do "AND" with the pins... More could be done, but RAM would be at issue. Calling Dr_A.
This one works a pixel at a time, using waitvid for the color, then using the PWM for the luma. Why not just run the waitvid for a few pixels at a time? Since there are three pins, it's possible to "OR" the two outputs right? Different colors and a luma change for the brighter could be done. Run 4 pixels at a clip, keeping the PWM the same. I'm deffo gonna try that.
There also appeared to be some bright luma values in the border region. When I configured Eric's driver to be 80x96 with borders, they appeared bright. Modifying a value or two in the TV driver fixed it, I think. Will post that shortly. Anyone else experience this?
(attachment problems resolved... wierd)
See post below for WIP code posting and sample image.
As originally configured, the driver was running full screen, and IMHO the darker color ranges were well below "black". These value displayed are black and brighter. The Sony didn't like the below black signal at all, though the other devices seemed ok with it. Just a FYI. Well, mostly. The low color values dip a shade below black, but won't be at issue on any device I've ever seen.
The code here is configured for a 80x96 display, centered in the NTSC "safe area", leaving a black border. It can run wider, but not too much narrower. I'm using a 100Mhz Propeller.
My older, portable TV renders the graphics fairly well. Roll off the sharpness control a little, and it's basically useful. On my HD set, and the Sony WEGA, the PWM noise is very visible. IMHO, using the cap is recommended. I've gotta set one up for sure.
Edit: The little lower resolution LCD displays should render this nicely. Just thought I would add that. I have one smaller one, and it looks great.
The capture devices were not happy with this signal at all. One of the better captures attached. (and it's not very good, but does give you a sense of what the image looks like)
Got the color spaces sorted nicely. This archive contains a nice color map showing the range of values, basic dot plotter, clipped to the screen, and range constrained color definition.
Hues range from 0 to 15, all the basic hues a Propeller does with the built in video generator.
Each HUE has 128 intensity values.
2048 displayable colors are possible! Nice one Perry!
If the HUE is set to a value greater than 15, it's interpreted as INTENSITY only. Monochrome.
Each INTENSITY has 250 values.
250 displayable grey scales are also possible!
Resolution on this one is 80x96. It's not too big of a deal to run it at greater vertical resolutions, if desired. One page of graphics takes a little under 8k, leaving room for a higher resolution overlay. I'm thinking this may well be very useful for communicating levels, graphs and such, if some detail text can be included. Thinking on that...
I'm currently working on a line command, and am going to run this on a better circuit, also S-video. The PWM raw from the demo board is kind of noisy, which actually limits the color resolution to about 80 pixels. Some color combinations are better than that, but dot crawl and fringing gets significant on most combinations. Load the archive code to see a sample display demonstrating color limits on your display. Tweak your sharpness, contrast and such, changing background colors to see what I mean.
I'll post up S-video stuff shortly, once I get the PPDB wired properly.
Right now, it's possible to plot out basic data, suppose somebody could do a nice ball 'n bat game, or display pictures. Use the setcolor method included to figure out graphics palette. Somebody needs to combine this with Phil's video capture. It's ready to go!
.
.
.
A graphic adventure, top two thirds of the screen, using a bitmap, lower third using a charmap, maybe even half height rom text to save space?
Just a thought!
Great work though Perry and Potatohead kudos!
I'm still puzzling over hues. Looks like there are 32 distinct hues. If so, that's ~4k colors... Found the extra set while tinkering around with an attempt to do S-video on this one.
Got a more reasonable capture this time. Drawing darker color blocks interleaved with the bright palette samples cuts way down on the overall noise.
Hues range from 0 to 31, all other values from post above apply. Values above 31 are seen as INTENSITY only, with the 250 value limit.
I need some help understanding exactly how this one works. When I modified the TV driver to do S-video, I found colors and intensities combined. That was unexpected. I thought intensity was running on one pin, and color on the other... is that true?
Got a fairly good handle on what it DOES however.
Yeah, like the old Apple ][ games sometimes did. Wouldn't be a big deal to do text. Could grab lots of pictures off the SD card.
Until I get a better understanding of this one, I'm kind of limited though. It's totally possible to run more pixels in the waitvid, limiting intensity to the current 80 pixel resolution. Or... using another COG, with overlay video, or maybe just optimizing the PWM, better resolution could go that way too.
Noise is the biggest hassle. I need some small value caps. Some values of intensity are decidedly "sparkly", and the color dot-crawl is being exacerbated, due to that, I believe. Color resolution right now is low. 80 pixels is actually pushing it some.
@Perry
What is needed is chroma only on the high pin, PWM running through another pin, on the standard DAC. The Demo Board has both the high pin and the DAC connected together, which actually displays most anything you throw at it, or it can do the broadcast carrier, as intended.
I'm having trouble separating these two. Would love it if I could just get the LUMA signal out the composite connector, when chroma is routed to the high pin. I;m not quite seeing what you did here. Then again, it's late too. Gonna get sleep. Had a lot of fun though. Thanks for posting this!
The reason I ask is that with the limited propeller palette, if you run that through a dithering algorithm, many colors need to be artificially created by mixing a color, then a gray pixel, then a color pixel, and these pixels do not blend particularly well and at higher pixel density start to cause artifacts.
If you could produce a true mix of gray and a color then the lower number of pixels per line may not matter so much.
I'm not sure how you would do this in one byte though, as you need to store HSL values and might need more than one byte per pixel.
You should take a look with the code I have above. Very easy to do a coupla ramps to evaluate what that would look like. Not often we get to talk about a 3 - 4K color, any color anywhere type driver!
Something like:
Should draw a few pixels and or ramps for you to look at. Tweak the steps and or add cplot commands. (cplot = color plot, acts on last setcolor statement)
Increase the graphics buffer to 16K
Then adjust the screen driver parameters:
And the dot plotter:
IMHO, we need another video circuit to do what you are asking. Low resolution PWM could be done like this, using three COGs, maybe 4 pins total, outputting to YCbCr component video, or maybe RGB. The YCbCr format is Y = Intensity, Cb and Cr are blue and red color difference signals. Say you've got 50 percent grey on the Y, you could output a modest level difference on the Cb and or Cr channels to get saturation control, I think.
(goes off to look at that more closely)
I really like that format, and I have a display or two that will take it at up to HD resolutions. But... that input is not found on lower end displays, and isn't found at all, or much at all in various parts of the world. Why, I have no idea. I've done a quick test or two, then dropped it because it's a three cog solution to really be effective, and it's gonna take the RAM, and displays might not be all that easy, though I do believe there are converters out there. Maybe that makes sense? (goes off to look at those too)
Could also run the Y channel at a higher resolution than the difference channels too, allowing for intensity detail, but lower resolution color. Been thinking about doing that, just because it's fun. Need to order up some resistors and a cap or two, it seems. PWM noise is too great without the caps for display on anything with any real capability.
With RGB, we could do a similar thing, but without the ability to have intensity detail. Just fire off three of the PWM drivers, and figure out sync.
Is three COGs worth it?
Our lack of saturation control boils down to the color phase signal output by the prop video generator is just one fixed level. In composite land, we need to vary that, or move to a output form that gives us more direct control over the colors produced. Or... use clever dither techniques, like what I mentioned, or flicker it, alternating stuff across frames. (never did like that one, unless it's done on PAL, which actually renders the in-between color very nicely --one of the things I have always wanted to demonstrate on Prop, but we never got a good enough PAL signal for it to be worth doing)
One other option in NTSC land, given RAM isn't too much of a consideration, is to simply crank up the resolution, using words or longs per pixel. Eric has written a stable artifacting driver, which means we can do mixing, like I showed here: http://propeller.wikispaces.com/Colors That one ended up being unstable, because it's hard to know the state of the phase counter with respect to the pixel clock. There are hue shifts on propeller start that occur, meaning one can't always count on a given value boiling down to a given color on screen, without a lot of fiddling. That works because the composite signal breaks higher frequency content into color. Crank up the pixel clock, so that the pixels fall inside the colorburst clock, and make sure they are consistently timed, and the pixels literally become color info. This was the method used in the old Apple ][ for color graphics. It quite literally just had small pixels. Where a person places one determines the color. This idea is just expanding on that with lots of pixels to get lots of colors.
Did that one a long time ago, and didn't come back to it for that reason. Eric's recent NTSC efforts, inspired by the Apple, have the timings resolved, meaning that technique is on the table now. With more resolution, that method can produce more low saturation colors. Some of what that looks like can be seen on that driver screen capture.
I used 320 pixels in that one, with one half of a 160 horizontal resolution pixel being a standard propeller color, and the other half being one of the intensities. With artifacting, the horizontal resolution is fixed to the period of the color burst, which is 160 pixels in the "safe area" inside the borders, just FYI. Can be lower multiples too, or the active part of the screen can be changed. Possibilities are 40, 64, 80, 128, 160, etc... horizontally. Vertically, it's all about RAM, or tiles and RAM, whichever. Could be that we only need 640, if the standard waitvid hues are incorporated. Haven't tried it.
basically outputting color info, and luma info at a high rate, leaving the TV to average things out. A 640 pixel display will yield about 230 colors, a 1280 pixel one will output a lot! I never did the 1280 display trick, because it eats all the RAM... In C, maybe you don't care?
1280 pixels @ 4 colors = 80 waitvids / line, one word per pixel, for a total of 160 pixels horizontal. 320 bytes per scan line... That means a 30k buffer would yield a 160x90 or so pixel display. Would look fab though! Maybe I can knock up a sample of that, running at a modest resolution, or just using part of the screen. Would be NTSC, composite only though. Edit: There are a LOT of possibilities here. Some tinkering would be needed. Might not need 1280. Lots of ways to "mix" and "artifact" in the display. (on the to-do list now, because I love that kind of thing)
Prop video always comes down to RAM or pins, or both. Perry boosted what we can do though. I like this driver a lot. Opens the door to a lot of possibilities, and with the reference circuit too!
@Baggers: I love Prop I for precisely that reason. It's a great chip! So many possibilities too. Seems the more we think about it, the more it will do. Love that. What other device can support the kinds of conversations we have here regularly? Video on Prop is fun! (too damn much fun, I'm tired today!)
LOL!! Just thought about Perry and him getting Internet sorted. Bet he's gonna enjoy this thread!
I need to find some time to push the ram chip to its limits - at the moment it is half the speed of the video scan line but I think it can go faster. If you can pull data off an external chip it opens up more possibilities.
Researching NTSC is not as easy as I thought. I'm not even sure if some websites are correct - eg this wikipedia site http://en.wikipedia.org/wiki/Saturation_(color_theory) talks about colorfulness, chroma and saturation. They describe 'colorfulness' as the scale from gray to a full color. Then they talk about saturation and link to the HSL page where 'saturation' describes the scale from gray to full color. So what is 'saturation'?
Whatever color format you pick, there are always 3 values, not two. eg http://en.wikipedia.org/wiki/File:Hsl-hsv-colorpickers.svg looking at the picture on the top right with a fixed hue, there are still two dimensions there to describe all the colors.
The problem with some palettes is they are just 'too pretty', ie too colorful, ie, they only contain all the fully saturated colors and none of the ones with subtle mixes of gray.
Thinking about it in terms of the full color palette, you can have very fine graduations between various hues but if the graduation between 'gray' and 'color' is only binary, ie one graduation, it leaves out a lot of colors.
I'm not sure how to put this information back in? Is it the amplitude of the signal? I'm still not sure - I think amplitude is 'lightness', not 'grayness'.
Maybe there is another pwm you could run to alter things further? I wonder what you could do if you considered using all 8 cogs. And maybe even had two ram chips working independently and you could pull data off those ram chips in parallel using separate cogs.
I'd be interested to see what you could do with just a small tile in the middle of the screen.
Maybe this illustration will help:
The brightness is the signal level above "black" after lowpass filtering out the 3.57 MHz color component. The saturation is the amplitude of the 3.57 MHz chroma component, and the hue is the phase of the chroma component relative to the extrapolated phase of the color burst that occurs at the beginning of each line.
A signal that's totally gray will have no chroma component. IOW the 3.57 MHz subcarrier will be flat. A signal that's totally saturated will have a very high chroma subcarrier riding on the brightness signal.
Here's an image the illustrates the difference between saturation and brightness:
You can see that saturation is a measure of the "purity" of the color, whereas brightness is just a measure of how much light is emitted (or reflected).
-Phil
So you want to adjust the amplitude to adjust the saturation. If there are clever tricks being done with pwm to adjust things, can we do more clever tricks? Run another cog to generate a pwm and use that to control a transconductance amplifier like the CA3080? Hmm - need to track the average level for the lightness so this sets the virtual earth of the op amp, then adjust the gain. That would be a low pass filter with a time constant about the same as for displaying a pixel. Though I see the open loop bandwidth is only 2Mhz - possibly not fast enough? There are other faster chips I see. (I'm searching for wide bandwidth transconductance).
Ok, let me see. The Hue waveform is absolutely critical for timing and looking at the phase difference, would be too fast to store in an external ram (thinking sample-and-hold).
But the Brightness and Saturation could be. And that information need not even go through a prop.
Store Hue values in hub (could get up to 256x224). Store Saturation and Brightness in external ram (one chip and clock out alternate bytes, or two separate chips or one byte per pixel with 16 levels each of Sat/Brightness). The brightness goes to a level shifting op amp circuit and the saturation controls gain. Probably easier to do it in the other order because the earth is constant - so run gain first, then level shift.
These are old-school circuits that I understand. Simple R/2R D to A, maybe start with 16 levels of saturation and 16 levels of brightness. Can store that in one byte per pixel. 16 levels of gain for the transconductance op amp and then 16 levels of shift by using an op-amp adder.
Just need fast enough op amps. CA3280 transconductance amp has a 9Mhz bandwidth.
This can also be done with just pixels. Draw a grey pixel, for example, then put a really bright, smaller pixel right in the middle of it. The result will be a moderate saturation-ish pixel as the timing of the small pixel and it's high frequency nature triggers the color circuits, it's intensity impacts the purity of the color, as does it's size, while the grey pixel tends to "fill-in" where there would normally be a more fully developed color signal.
Frankly, we should fire off a thread about a NEW video circuit. The reference one has been, and will continue to be hacked all over the place. Very cool, but...
It might be a great discussion to have. Say we target two COGs. It's not like people would have to write for two, but design the circuit so that two could really be put to use. Get pins sorted, and discuss various modulation techniques we have available to us. With monochrome NTSC / PAL, we can do overlay, where two COGs write to the same pins, with VGA, and things like component, we could do that too, and do so in color, and make choices, like running color at a lower bitrate than monochrome. Or... if we had a gate, where a logical AND operation could happen electrically, the output of one COG could gate another one. This has it's uses in TV land, many of which apply in VGA land too.
One use would be to gate off a low resolution color signal, for example, allowing a dark intensity pixel to exist amidst a bright color. We can do the opposite now, as the pins are OR'ed together. Maybe that's not AND, but some other op. Something to be discussed.
So I thought I might come back to Hue. On the propeller, Hue comes in 16 discrete steps. Let's say we took a signal with full saturation and zero brightness. Add saturation and brightness later, but internal to the prop you have 16 Hues. Can you get more Hues by tweaking the phase?
The frequency is constant (3.57Mhz right?) and I was reading about phase shifts in R/C filters. There is a nice description about 1/3 of the way down this page http://www.geofex.com/article_folders/phasers/phase.html.
So if you have a constant frequency and and RC network and you adjust the resistor, then does that mean you can adjust the phase. I still need to test out whether the prop can read bytes off memory fast enough, but let's say it can, you read a byte and use 3 bits to work out the Hue. The other bits are set for full saturation. At the same time another byte is read off another ram chip and that contains data fed into a programmable resistor (?? a 4066 fast enough?) and then that alters the R in a RC filter and thus tweaks the phase.
Crazy ideas I know. I need to do more experiments on a breadboard. brb
It comes down to this loop
This is pulling the data off an sram as fast as possible and it is about 5/8 of the speed required. So even with one cog pulling data off a sram, the time it takes to do a wrbyte to move to hub is too long.
Maybe maybe one could have two srams running in parallel, each with their own pins and cogs but it gets complex. You just can't pull the data off an sram and pass it through a propeller fast enough.
I'm probably hijacking this thread too much, but I thought I might just throw the AD725 RGB to NTSC chip in the mix. http://www.analog.com/en/digital-to-analog-converters/video-encoders/ad725/products/product.html
Get the prop to generate the hsync and vsync signals. Use a Word rather than a Byte and you can have 5 bits for red, 5 for green and 5 for blue (two sram chips, or even sram with 16 data lines)
If you took that loop above and removed the wrbyte and one of the add instructions, could the propeller produce the timing clock fast enough?
In an ideal world, I think a pixel is 80 nanoseconds. The SRAM chip is 55ns so that works.
Even if that loop is fast enough, it may not divide in the right ratio. eg one loop might run too fast and you finish the line 3/4 of the way across the screen, and if you add a NOP it finishes off the screen and is too wide. The level of control is too coarse.
Maybe it is better to use the propeller counters and tell it to give you 256 pulses at a period of x? (Can the prop do that). Or use HC161 counters and clock out using a separate osc, and use the prop to do clever things like detect when the 161 has counted up to 256 and then stop the clock.
More silly thoughts. But if it gave the propeller a display of 32768 colors...
For this demo, one cog is running spin and one is running the TV driver and all the rest are free. And the only pins being used are the serial download, eeprom and 3 pins for TV. So there is a lot of flexibility.
The TV video driver cog is already updating a hub variable to say which line it is displaying, and the ram driver cog is using the change in this variable to sychronise the two, so having a third cog synch to that variable should be possible.