16 bit NTSC video Driver
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
Looks brilliant Perry, now to get some free time and figure out some cool stuff to do with it
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
MOV VSCL, vscl_burst ' 9 cycles of colorburst WAITVID dblank, sburst xxxxxxx mov frqb,#0 ' PJM xxxxxx mov frqb,#pwmlut ' PJM should beI normally use 108MHz clockbut 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!
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:
:loop RDword pixel, pixptr ' PJM'RDLONG pixel, pixptr ' 22 CLK mov luminance,pixel ' PJM ' +4 CLK shl luminance,l_Gain ' PJM ' +4 CLK add luminance,black ' PJM ' +4 CLK WAITVID pixel, #%%1 ' PJM +5 CLK MOV FRQb,luminance ' PJM +4 CLK ADD pixptr, #2 ' PJM +4 CLK DJNZ numpixels, #:loop ' +4 CLK = 51 CLKSo 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
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:
repeat i from 0 to 31 step 2 repeat j from 0 to 60 step 4 setcolor(j/4, i*4) cplot(i+10, j+20) setcolor(32, i*4) cplot(i+10, j+21)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:
PUB cplot (x, y) '80 x 96 graphics screen x //= 79 y //= 95 (SB 192) bitmap[y*80+x] := current_colorIMHO, 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
rdloop mov t2, ina ' read byte from SRAM \ ignores upper bits wrbyte t2, hubaddr ' copy byte to hub / add hubaddr, #1 ' inc hub pointer add outa, #(1 << 8) ' inc sram address djnz len, #rdloop ' loop for xxx bytesThis 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.