Author Topic: NTSC problem with ATMega328  (Read 1770 times)

0 Members and 1 Guest are viewing this topic.

Offline nycholdTopic starter

  • Newbie
  • Posts: 3
  • Country: us
NTSC problem with ATMega328
« on: October 21, 2021, 05:30:13 pm »
So let me just start by apologizing for the length of this post, but I want to include as much information as possible.

So a few months ago I began tinkering with creating a small, hobby (retro) computer for my own purposes and wanted to create some kind of graphic display.  I own Andre LaMothe's "Black Art of Game Console Development" book, and I know he talks about different display systems in there, so I took a stab at the "easiest" one.  He has schematics and diagrams, so I copied the design as much as I could, making a few chip substitutions to cut down on the number of analog multiplexers from 2 (74HC4051) to 1 (74HCT4067).  The timings of the two chips look similar, so I didn't think it would cause much of a problem.  And after a week or so of timing issues, it worked.

Sort of.

The test pattern created was a series of colored bars on the TV, but the colors didn't seem stable.  They would produce a small ripple or wave like effect that became very pronounced, then very subtle, in some instances disappearing completely.  This pattern came in waves, which lead me to believe the issue was noise.  Since the board I made was just the driver, and didn't include the ATMega328 on the board (I was using a generic, Chinese Arduino Nano clone), it would be very easy for some stray noise to get into the system and cause a jiggle or two.

So I pushed on, and created a new board design which included the Arduino Nano (just to program the ATMega328 easily) on the board itself.  I also added more colors because the color palette I had picked originally was crap.  The NTSC color wheel starts at yellow, but I made my calculations assuming it started at red like a standard HSV wheel.  Oh well, live and learn.  :-//

When I finished the programming of the board, I was shocked to see that I had gained a saturation problem.  Between colors 29 and 32, most of the color had been sucked out of the system.  I attribute this to noise being added due to the 74HCT244 as the oscillator's signal passes between the VIL and VIH thresholds, which gets progressively worse the more gates it has to go through.  Makes sense to me, but if any of you much more experienced guys has a clue, I'm all ears. :)  (Otherwise, I plan to deal with the issue by using Schmitt Trigger gates in the future.)

But my noise issue was also still there, and worse now.  Both the top and bottom of my board are ground-filled as much as possible, to help cut down on noise, so it should have gotten better, right?  Now, in addition to the ripple effect, I also have what looks like a rainbowing effect on some lines.  I tried the timings LaMothe mentioned in his book, as well as the Nintendo timings I found here: https://wiki.nesdev.org/w/index.php/NTSC_video as well as the timings I found here: https://www.avrfreaks.net/sites/default/files/ntsctime.pdf.  The Nintendo timings didn't work at all (though I may have messed them up) and both LaMothe's and AVR Freak's timings seem to exhibit the same problem.

Since the design I'm using is based on a copyrighted book, I'm not sure what the legality is on posting the designs directly, but for now, I'll just describe the boards (they're all pretty much the same):

A 3.579545 MHz crystal oscillator feeds directly into one of the inputs of a 74HCT244 where it ripples through all of the available inputs, as well as the inputs of 3 other chips except 1.  This adds delay in order to achieve the phase shift needed for NTSC color.  Then, those signals are all feed into 2 74HCT4067 chips, and the color value from the ATMega328 is used as the selector (plus an extra signal for LOW or HIGH bank).  The output of that is passed through into the empty input of a 74HCT244 (this was copied from the original design...I don't think it's necessary), then piped through a 75Ohm resistor.  A 1nF capacitor is used to smooth out the signal, in an attempt to get close to a sine wave, and is combined with an intensity signal (also coming from the ATMega328) through an R2R ladder (400 and 200 Ohms).  A couple of potentiometers allow for adjustment of the brightness and saturation (copied from the original design), but I couldn't get the values he suggested, so mine are as close as possible (1K and 10K, both linear).

I also use a 12ns RAM chip (W24257AK-12) to handle some of the color decoding, so that I can use a single port (PORTD) to change colors, but I highly doubt that's the culprit.  My first test board didn't even use it, but the issue has gotten worse, so who knows?

I've attached an image to show the issue I'm having, easily visible on the left and right sides of the image.  Here is the code for the ATMega328 (all assembly to ensure timing):

Code: [Select]
;; Arduino timing:
;;   16 MHz = 62.5ns per clock
;;   16 MHz / 3 = 187.5ns per pixel

;; [url]https://www.avrfreaks.net/sites/default/files/ntsctime.pdf[/url]
;; NSTC Timing:
;;   Normal pulses              253 EVEN LINES, 254 ODD LINES             14/15 TOP OVERSCAN      15 BOTTOM OVERSCAN
;;      SYNC                    4.7us         4700ns      25 pixels
;;      BREEZEWAY               1.6us         1687.5ns    9 pixels
;;      COLOR BURST             2.5us         2437.5ns    13 pixels
;;      BACK PORCH              2.1us         2062.5ns    11 pixels
;;      VIDEO DATA              51.2us        51187.5ns   273 pixels
;;        LEFT BORDER           1.5us         1500ns      8 pixels
;;        ACTIVE VIDEO          48us          48000ns     256 pixels
;;        RIGHT BORDER          1.6us         1687.5ns    9 pixels
;;      FRONT PORCH             1.5us         1500ns      8 pixels
;;
;;   Equalizing pulses
;;   BOTH                       3 LINES
;;      SYNC                    2.3us         2250ns      12 pixels
;;      BLANK                   29.5us        29625ns     158 pixels
;;      SYNC                    2.3us         2250ns      12 pixels
;;      BLANK                   29.5us        29625ns     158 pixels
;;   ODD                        1 LINE
;;      SYNC                    2.3us         2250ns      12 pixels
;;      BLANK                   29.5us        29625ns     158 pixels
;;      SYNC                    27.3us        27375ns     146 pixels
;;      BLANK                   4.5us         4500ns      24 pixels
;;
;;   Serration pulses
;;   EVEN                       3 LINES
;;      SYNC                    27.3us        27375ns     146 pixels
;;      BLANK                   4.5us         4500ns      24 pixels
;;      SYNC                    27.3us        27375ns     146 pixels
;;      BLANK                   4.5us         4500ns      24 pixels
;;   ODD                        1 LINE (2 LINES OF ABOVE)
;;      SYNC                    27.3us        27375ns     146 pixels
;;      BLANK                   4.5us         4500ns      24 pixels
;;      SYNC                    2.3us         2250ns      12 pixels
;;      BLANK                   29.5us        29625ns     158 pixels
;;
;;
;;  Frame process
;;      EVEN FRAME
;;          14 OVERSCAN
;;          224 ACTIVE VIDEO LINES
;;          15 OVERSCAN
;;          3 EQUALIZING PULSE
;;          1 EQUALIZING PULSE ODD
;;          2 SERRATING PULSE
;;          1 SERRATING PULSE ODD
;;          2 EQUALIZING PULSE
;;         263 LINES
;;      ODD FRAME
;;          15 OVERSCAN
;;          224 ACTIVE VIDEO
;;          15 OVERSCAN
;;          3 EQUALIZING PULSE
;;          3 SERRATING PULSE
;;          3 EQUALIZING PULSE
;;         262 LINES

.def wait1 = r16
.def wait2 = r17
.def lineCount = r18
.def color = r19
.def colorCount = r20
.def voltage = r21
.def colorIncrease = r22
.def ledStatus = r23
.def ledMask = r24

.set HSYNC = $00
.set VSYNC = $00
.set BLANK = $01
.set COLOR_BURST = $09
.set BLACK = $02
.set OVERSCAN_COUNT = 15
.set OVERSCAN_COUNT_EVEN = 14

;; Takes 1 parameter (x) and waits 3x clock cycles
;; Timing: 3x clock cycles -- x pixels
;; Proof:
;;     WAIT 6 => 6 pixels
;;                ldi     r16, 6                ;; 1 clock
;; loop:          subi    r16, 1                ;; 1 clock -- r16 = 5
;;                brne    loop                  ;; 2 clocks
;; loop:          subi    r16, 1                ;; 1 clock -- r16 = 4
;;                brne    loop                  ;; 2 clocks
;; loop:          subi    r16, 1                ;; 1 clock -- r16 = 3
;;                brne    loop                  ;; 2 clocks
;; loop:          subi    r16, 1                ;; 1 clock -- r16 = 2
;;                brne    loop                  ;; 2 clocks
;; loop:          subi    r16, 1                ;; 1 clock -- r16 = 1
;;                brne    loop                  ;; 2 clocks
;; loop:          subi    r16, 1                ;; 1 clock -- r16 = 0
;;                brne    loop                  ;; 1 clocks
;;                                              ;; 18 clocks = 3 * 6 => 6 pixels
.macro WAIT
                  ldi     wait1, @0             ;; 1 clock
  loop:           subi    wait1, 1              ;; 1 clock
                  brne    loop                  ;; 2 clocks, 1 if fall through
.endmacro

;; Takes 2 parameters (x, y) and waits 3x(y+1) clock cycles
;; Timing: 3x(y + 1) clock cycles -- x(y + 1) pixels
;; Proof:
;;     WAIT_LONG 6, 5   => 36 pixels
;;                ldi     r17, 6                ;; 1 clock
;; loop:          WAIT    5                     ;; 15 clocks
;;                subi    r17, 1                ;; 1 clock -- r17 = 5
;;                brne    loop                  ;; 2 clocks
;; loop:          WAIT    5                     ;; 15 clocks
;;                subi    r17, 1                ;; 1 clock -- r17 = 4
;;                brne    loop                  ;; 2 clocks
;; loop:          WAIT    5                     ;; 15 clocks
;;                subi    r17, 1                ;; 1 clock -- r17 = 3
;;                brne    loop                  ;; 2 clocks
;; loop:          WAIT    5                     ;; 15 clocks
;;                subi    r17, 1                ;; 1 clock -- r17 = 2
;;                brne    loop                  ;; 2 clocks
;; loop:          WAIT    5                     ;; 15 clocks
;;                subi    r17, 1                ;; 1 clock -- r17 = 1
;;                brne    loop                  ;; 2 clocks
;; loop:          WAIT    5                     ;; 15 clocks
;;                subi    r17, 1                ;; 1 clock -- r17 = 0
;;                brne    loop                  ;; 1 clock
;;                                              ;; 108 clocks = 3 * 36 => 36 pixels
.macro WAIT_LONG
                  ldi     wait2, @0             ;; 1 clock
  loop:           WAIT    @1                    ;; 3 * @1 clocks
                  subi    wait2, 1              ;; 1 clock
                  brne    loop                  ;; 2 clocks, 1 if fall through
.endmacro

;; Takes 1 parameter (voltage)
;; Timing: 2 clock cycles
.macro SET_VOLTAGE
                  ldi     voltage, @0
                  out     PORTD, voltage
.endmacro

;; Takes 1 parameter (colorburst)
;; Timing: 186 clock cycles -- 62 pixels
.macro LINE_START
;;      SYNC                    4.7us         4700ns      25 pixels
;;      BREEZEWAY               1.6us         1687.5ns    9 pixels
;;      COLOR BURST             2.5us         2437.5ns    13 pixels
;;      BACK PORCH              2.1us         2062.5ns    11 pixels
;;      VIDEO DATA              51.2us        51187.5ns   273 pixels
;;        LEFT BORDER           1.5us         1500ns      8 pixels
;;        ACTIVE VIDEO          48us          48000ns     256 pixels
;;        RIGHT BORDER          1.6us         1687.5ns    9 pixels
;;      FRONT PORCH             1.5us         1500ns      8 pixels

                  ;; HSYNC -- 25 pixels
                  nop                                       ;; 1 clock
                  SET_VOLTAGE HSYNC                         ;; 2 clocks -- pixel
                  WAIT 25                                   ;; 72 clocks -- 24 pixels, HSYNC now over

                  ;; BREEZEWAY -- 9 pixels
                  nop                                       ;; 1 clock
                  SET_VOLTAGE BLANK                         ;; 2 clocks -- pixel
                  WAIT 8                                    ;; 24 clocks -- 8 pixels, BREEZEWAY now over

                  ;; COLOR_BURST -- 13 pixels
                  nop                                       ;; 1 clock
                  SET_VOLTAGE @0                            ;; 2 clocks -- pixel
                  WAIT 12                                   ;; 36 clocks -- 12 pixels, COLOR_BURST now over

                  ;; BACK PORCH -- 11 pixels
                  nop                                       ;; 1 clock
                  SET_VOLTAGE BLANK                         ;; 2 clocks -- pixel
                  WAIT 10                                   ;; 30 clocks -- 10 pixels, BACK PORCH now over

                  ;; 8 clock black pixels for left border
                  nop                                       ;; 1 clock
                  SET_VOLTAGE BLACK                         ;; 2 clocks -- pixel
                  WAIT 7                                    ;; 21 clocks -- 7 pixels, LEFT BORDER now over
.endmacro


;; Takes 1 parameter (color + intensity)
.macro INTENSITY_LINE
                  ;; 256 pixels of 1 intensity
                  nop                                       ;; 1 clock
                  SET_VOLTAGE @0                            ;; 2 clocks -- pixel
                  WAIT 255                                  ;; 255 more pixels, INTENSITY_LINE now over
.endmacro



;; Takes 1 parameter (intensity)
.macro DISPLAY_LINE
                  ;; 8 pixels of 32 colors = 256 pixels total
                  ldi     colorIncrease, $08                ;; 1 clock
                  ldi     color, @0                         ;; 1 clock
                  out     PORTD, color                      ;; 1 clock -- pixel

                  ldi     colorCount, 31                    ;; 1 clock
                  nop                                       ;; 1 clock
                  nop                                       ;; 1 clock -- pixel
                  WAIT    6                                 ;; 18 clocks -- 6 pixels, next color

  loop:           nop                                       ;; 1 clock
                  add     color, colorIncrease              ;; 1 clock
                  out     PORTD, color                      ;; 1 clock -- pixel
                  WAIT    6                                 ;; 18 clocks -- 6 pixels
                  subi    colorCount, 1                     ;; 1 clock
                  brne    loop                              ;; 2 clocks, 1 fall through

                  nop                                       ;; 1 clock -- pixel, DISPLAY_VIDEO now over
.endmacro


// Takes no parameters, ends 1 pixel early, for looping
.macro LINE_END
;;        RIGHT BORDER          1.6us         1687.5ns    9 pixels
;;      FRONT PORCH             1.5us         1500ns      8 pixels
                  ;; 9 black pixels for border
                  nop                                       ;; 1 clock
                  SET_VOLTAGE BLACK                         ;; 2 clocks -- pixel
                  WAIT 8                                    ;; 24 clocks -- 8 pixels

                  ;; FRONT PORCH, 8 pixels (really 7 for external looping)
                  nop                                       ;; 1 clock
                  SET_VOLTAGE BLANK                         ;; 2 clocks -- pixel
                  WAIT 6                                    ;; 18 clocks -- 6 pixels

                  ;; Skip last pixel
.endmacro


;; Takes no parameters
.macro BLACK_VIDEO
                  ;; 256 black pixels
                  nop                                       ;; 1 clock
                  SET_VOLTAGE BLACK                         ;; 2 clocks -- pixel
                  WAIT 255                                  ;; 765 clocks -- 255 pixels, BLACK_VIDEO now over
.endmacro


;; Takes no parameters, lineCount must be set before using, leaves 1 unused clock cycle
.macro OVERSCAN
  loop:           LINE_START BLACK
                  BLACK_VIDEO
                  LINE_END
                  subi      lineCount, 1                    ;; 1 clock
                  brne      loop                            ;; 2 clocks, 1 if fall through

                  ;; 1 unused clock
.endmacro


;; Takes 2 parameters (linecount, intensity), requires 1 extra clock cycle
.macro INTENSITY_BAR
                  ldi       lineCount, @0                   ;; 1 clock (extra clocks burned)

loop:             LINE_START BLACK
                  INTENSITY_LINE @1
                  LINE_END                                  ;; 3 unused clocks
                  subi      lineCount, 1                    ;; 1 clock
                  brne      loop                            ;; 2 clocks, 1 if fall through

                  ;; 1 unused clock
.endmacro


;; Takes 2 parameters (linecount, intensity), requires 1 extra clock cycle
.macro COLOR_BAR
                  ldi       lineCount, @0                   ;; 1 clock (extra clocks burned)

  loop:           LINE_START COLOR_BURST
                  DISPLAY_LINE @1
                  LINE_END                                  ;; 3 unused clocks
                  subi      lineCount, 1                    ;; 1 clock
                  brne      loop                            ;; 2 clocks, 1 if fall through

                  ;; 1 unused clock
.endmacro


;; Takes 4 parameters (syncTime1, blankTime1, syncTime2, blankTime2)
.macro VSYNC_PULSE
                  nop                                       ;; 1 clock
                  SET_VOLTAGE VSYNC                         ;; 2 clocks -- pixel
                  WAIT @0                                   ;; syncTime1 - 1 pixels

                  nop                                       ;; 1 clock
                  SET_VOLTAGE BLANK                         ;; 2 clocks -- pixel
                  WAIT @1                                   ;; blankTime1 - 1 pixels

                  nop                                       ;; 1 clock
                  SET_VOLTAGE VSYNC                         ;; 2 clocks -- pixel
                  WAIT @2                                   ;; syncTime2 - 1 pixels

                  nop                                       ;; 1 clock
                  SET_VOLTAGE BLANK                         ;; 2 clocks -- pixel
                  WAIT @3                                   ;; blankTime2 - 1 pixels
.endmacro


;;   Equalizing pulses
;;   BOTH                       3 LINES
;;      SYNC                    2.3us         2250ns      12 pixels
;;      BLANK                   29.5us        29625ns     158 pixels
;;      SYNC                    2.3us         2250ns      12 pixels
;;      BLANK                   29.5us        29625ns     158 pixels
;;   ODD                        1 LINE
;;      SYNC                    2.3us         2250ns      12 pixels
;;      BLANK                   29.5us        29625ns     158 pixels
;;      SYNC                    27.3us        27375ns     146 pixels
;;      BLANK                   4.5us         4500ns      24 pixels
;;
;;   Serration pulses
;;   EVEN                       3 LINES
;;      SYNC                    27.3us        27375ns     146 pixels
;;      BLANK                   4.5us         4500ns      24 pixels
;;      SYNC                    27.3us        27375ns     146 pixels
;;      BLANK                   4.5us         4500ns      24 pixels
;;   ODD                        1 LINE (2 LINES OF ABOVE)
;;      SYNC                    27.3us        27375ns     146 pixels
;;      BLANK                   4.5us         4500ns      24 pixels
;;      SYNC                    2.3us         2250ns      12 pixels
;;      BLANK                   29.5us        29625ns     158 pixels

;; Leaves three clock cycles unused
.macro VSYNC_ODD
;;          3 EQUALIZING PULSE
                  VSYNC_PULSE 11, 157, 11, 157
                  VSYNC_PULSE 11, 157, 11, 157
                  VSYNC_PULSE 11, 157, 11, 157

;;          3 SERRATING PULSE
                  VSYNC_PULSE 145, 23, 145, 23
                  VSYNC_PULSE 145, 23, 145, 23
                  VSYNC_PULSE 145, 23, 145, 23

;;          3 EQUALIZING PULSE
                  VSYNC_PULSE 11, 157, 11, 157
                  VSYNC_PULSE 11, 157, 11, 157
                  VSYNC_PULSE 11, 157, 11, 156              ;; Leave 1 pixel accounted for
.endmacro

;; Leaves three clock cycles unused
.macro VSYNC_EVEN
;;          3 EQUALIZING PULSE
                  VSYNC_PULSE 11, 157, 11, 157
                  VSYNC_PULSE 11, 157, 11, 157
                  VSYNC_PULSE 11, 157, 11, 157

;;          1 EQUALIZING PULSE ODD
                  VSYNC_PULSE 11, 157, 145, 23

;;          2 SERRATING PULSE
                  VSYNC_PULSE 145, 23, 145, 23
                  VSYNC_PULSE 145, 23, 145, 23

;;          1 SERRATING PULSE ODD
                  VSYNC_PULSE 145, 23, 11, 157

;;          2 EQUALIZING PULSE
                  VSYNC_PULSE 11, 157, 11, 157
                  VSYNC_PULSE 11, 157, 11, 156        ;; Leave 1 pixel accounted for
.endmacro

; Replace with your application code
start:            jmp     frame                             ;; RESET
                  jmp     halt                              ;; IRQ0 handler
                  jmp     halt                              ;; IRQ1 handler
                  jmp     halt                              ;; PCINT0 handler
                  jmp     halt                              ;; PCINT1 handler
                  jmp     halt                              ;; PCINT2 handler
                  jmp     halt                              ;; WDT (watchdog) handler
                  jmp     halt                              ;; TIM2_COMPA handler
                  jmp     halt                              ;; TIM2_COMPB handler
                  jmp     halt                              ;; TIM2_OVF handler
                  jmp     halt                              ;; TIM1_CAPT handler
                  jmp     halt                              ;; TIM2_COMPA handler
                  jmp     halt                              ;; TIM2_COMPB handler
                  jmp     halt                              ;; TIM2_OVF handler
                  jmp     halt                              ;; SPI_STC handler
                  jmp     halt                              ;; USART_RXC handler
                  jmp     halt                              ;; USART_UDRE handler
                  jmp     halt                              ;; USART_TXC handler
                  jmp     halt                              ;; ADC handler
                  jmp     halt                              ;; EE_RDY handler
                  jmp     halt                              ;; ANA_COMP handler
                  jmp     halt                              ;; TWI handler
                  jmp     halt                              ;; SPM_RDY handler
                  ;; [url]https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf[/url]
                  ;; page 50

halt:             jmp     halt

frame:            ;; Set up port D
                  clr     voltage
                  sts     UCSR0B, voltage

                  ldi     voltage, 0xff                       ;; All outputs
                  out     DDRD, voltage

                  ;; Set up port B
                  ldi     ledMask, $20                      ;; bit 5 = Arduino D13 = onboard LED
                  out     DDRB, ledMask
                  ;;ldi     ledStatus, $20
                  ;;out     PORTB, ledMask

                  ldi     lineCount, OVERSCAN_COUNT_EVEN
                  jmp     display_loop

;; WAIT TEST
                  ;; 2665 pixels ~ 1kHz (but leave 2 for looping)
                  ;; 2663 = 2662 + 1 = 22 * 121 + 1
blink_1khz:       WAIT_LONG 22, 120                   ;; 2662 pixels
                  nop
                  nop
                  nop                                 ;; 2663 pixels
                  nop
                  eor       ledStatus, ledMask
                  out       PORTB, ledStatus          ;; 2664 pixels
                  jmp       blink_1khz                ;; 2665 pixels

;; OVERSCAN TEST1
                  ;; 1 clock + 338 pixels - 1 clock + 5 clocks = 339 px + 2 clocks = 63,687.5 ns
                  ;; 15,701.6 kHz / 2 =
                  ;; 7,850.83 kHz
overscan_test1:   ldi       lineCount, 1
                  OVERSCAN
                  eor       ledStatus, ledMask
                  out       PORTB, ledStatus
                  jmp       overscan_test1

;; OVERSCAN TEST2
                  ;; 438.3 Hz
overscan_test2:   ldi       lineCount, OVERSCAN_COUNT
                  OVERSCAN
                  eor       ledStatus, ledMask
                  out       PORTB, ledStatus
                  jmp       overscan_test2

;; DISPLAY TEST
                  ;; 337 pixels + 4 clocks = 338 + 1 clocks =
                  ;; 7.874.0157 kHz
display_test:     LINE_START COLOR_BURST
                  DISPLAY_LINE 15
                  LINE_END
                  eor       ledStatus, ledMask
                  out       PORTB, ledStatus
                  jmp       display_test

;; COLOR_BAR TEST
                  ;; 357.9 Hz
colorbar_test:    COLOR_BAR 36, 15
                  eor       ledStatus, ledMask
                  out       PORTB, ledStatus
                  jmp       colorbar_test

;; VSYNC TEST
                  ;; 1.312 kHz
;;vsync_test:       VSYNC_PULSE
;;                  eor       ledStatus, ledMask
;;                  out       PORTB, ledStatus
;;                  jmp       vsync_test

display_loop:
even_frame:
                  OVERSCAN                                    ;; 1 unused clock cycle

                  ;; 224 lines by 6 intensities = 37 lines per color bar (+1 for top and bottom)

                  COLOR_BAR 38, 7                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 37, 6                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 37, 5                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 37, 4                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 37, 3                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 38, 2                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle

                  ldi     lineCount, OVERSCAN_COUNT           ;; 1 clock cycle, timing caught up
                  OVERSCAN                                    ;; 1 unused clock cycle
                  ldi     lineCount, OVERSCAN_COUNT           ;; 1 clock cycle (required for first odd OVERSCAN)

                  VSYNC_EVEN                                  ;; Perform vsync pulses for even frame
                                                              ;; 3 unused clock cycles
                  nop                                         ;; 1 clock
                  nop                                         ;; 1 clock
                  nop                                         ;; 1 clock

odd_frame:
                  OVERSCAN                                    ;; 1 unused clock cycle

                  ;; 224 lines by 6 intensities = 37 lines per color bar (+1 for top and bottom)

                  COLOR_BAR 38, 7                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 37, 6                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 37, 5                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 37, 4                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 37, 3                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle
                  COLOR_BAR 38, 2                             ;; requires 1 extra clock cycle, leaves 1 unused clock cycle

                  ldi     lineCount, OVERSCAN_COUNT           ;; 1 clock cycle, timing caught up
                  OVERSCAN                                    ;; 1 unused clock cycle
                  ldi     lineCount, OVERSCAN_COUNT_EVEN      ;; 1 clock cycle (required for first even OVERSCAN)

                  VSYNC_ODD                                   ;; Perform vsync pulses for odd frame
                                                              ;; 3 unused clock cycles
                  jmp display_loop



If you are confused why there are "ODD" lines in the EVEN frames, it's because I adjusted the timing from the PDF linked above to create tighter code.

Frankly, at this point, I'm at a loss.  I don't know if this belongs in the "beginner" section, but I think I'm missing something totally obvious.  Help? :D
 

Offline Renate

  • Super Contributor
  • ***
  • Posts: 1460
  • Country: us
Re: NTSC problem with ATMega328
« Reply #1 on: October 21, 2021, 08:27:25 pm »
I haven't looked closely enough to see what you are doing, but...
You might not be having a coherent subcarrier phase.
There are supposed to be 227.5 subcarrier cycles/line (in NTSC).
If you sync your scope on line you should see the burst is mirrored (high peaks and low peaks).
Still, that doesn't tell you if your line length is correct.
 

Online Benta

  • Super Contributor
  • ***
  • Posts: 5871
  • Country: de
Re: NTSC problem with ATMega328
« Reply #2 on: October 21, 2021, 08:49:08 pm »
I'm in no mood the read through all your code.
But I rather suspect a hardware design problem, and as your schematic is "Top Secret", I can't help any further here.
Do you at least have a black-and-white schematic without the terrible copyright?


« Last Edit: October 21, 2021, 08:52:35 pm by Benta »
 

Offline SL4P

  • Super Contributor
  • ***
  • Posts: 2318
  • Country: au
  • There's more value if you figure it out yourself!
Re: NTSC problem with ATMega328
« Reply #3 on: October 22, 2021, 12:19:27 am »
As it iappears to be a vertical issue, remember that’s ‘low frequency’ vs the high frequency of line rate…
Guessing that you’re running a 60Hz monitor, and there are 6? bands, you’re probably looking for something around 360Hz…?
That may help isolate the problem…

The vertical ‘lines’ seem straight, so that suggests the horizontal / line rate is stable.

The other thing is, if your output field/frame rate is off, you may be writing the raster more than once per field/frame ?

Nice looking output though !
Don't ask a question if you aren't willing to listen to the answer.
 

Offline Renate

  • Super Contributor
  • ***
  • Posts: 1460
  • Country: us
Re: NTSC problem with ATMega328
« Reply #4 on: October 22, 2021, 12:49:22 am »
How are you dividing a 3.58 Xtal by 227.5?
Digital televisions don't take wacky timing.
I think it's taking 1/10 of a line to resync on the color burst?
I'd start with a 14.318 Xtal.
 

Offline nycholdTopic starter

  • Newbie
  • Posts: 3
  • Country: us
Re: NTSC problem with ATMega328
« Reply #5 on: October 22, 2021, 02:09:32 am »
I've looked up what 227.5 has to do with NTSC and I just can't figure it out.  I know I output 525 lines every "frame" as per the spec (263 on "even" frames, 262 on "odd" frames).  I have a typo in the code I posted where I mention 253 and 254 lines, but that's all it is...a typo.  Since I'm using a 3.58 MHz crystal, and am generating the exact number of lines necessary, I'm guessing I have this 227.5 subcarrier?  But I have no idea how to verify that.

Digital televisions don't take wacky timing.

It's interesting you mention this because it actually works on a digital TV.  On an old analog TV, I get no color at all, just black and white, and all the same "ripple" artifacts.  I think my color burst signal is just not close enough to a sine wave to generate color, an issue I was going to work on later.

I'm in no mood the read through all your code.
But I rather suspect a hardware design problem, and as your schematic is "Top Secret", I can't help any further here.
Do you at least have a black-and-white schematic without the terrible copyright?

I'm attaching my schematics for this board.  Since this is for educational purposes (namely my own, but also for any one else reading) and I have no intention of selling it, I believe it falls under Fair Use.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7732
  • Country: ca
Re: NTSC problem with ATMega328
« Reply #6 on: October 22, 2021, 02:35:30 am »
Funny you are not clocking the atmega at 14.31818MHz, exactly 910 clocks for each horizontal sync.

I know in way in the past, I over-clocked an old 20MHz PIC with 28.63636MHz and with nothing but a resistor DAC on a 4 bit or 8 bit port, with nothing more than an NPN follower amp to drive an NTSC cable, I generated a full legit color signal text on screen display.  Adding an EPM7128 CPLD, I removed a ton of the dedicated timing functions inside the MCU and with an analog switch, I then had a super-impose genlock feature by having the EPM7128 running the 14.31818 crystal with a PLL tuning logic to lock onto the incoming external NTCS circuit and also feed the XTAL input of the MCU.

Today with 4x PLLs inside PICs and interupts, this would be easy with the standard 14.31818MHz to generate quite a bit of patterns, or with an external rom with 2 bit binary counter feeding the LSB holding fatter Luma and Chroma patterns with preset levels for the sync positions, it would be easy to get everything dead on timing Y/C and CVBS output.  Though using something like the EPM7128 CPLD in place of the rom would offer a background sync generator in place of any bad timing coming from the MCU.

Note that the old weird 240p of old game consoles do run at 262 lines missing that 1/2 line giving you just above 60.05hz progressive and that many modern TV/Video grabbers which use cheap NTSC ADC grabber logic which may have problems decoding that signal.  They are expecting that 525 interlaced signal.
 

Online oPossum

  • Super Contributor
  • ***
  • Posts: 1415
  • Country: us
  • Very dangerous - may attack at any time
Re: NTSC problem with ATMega328
« Reply #7 on: October 22, 2021, 03:42:50 am »
3.579545 MHz (colorburst) / 227.5 = 15.734 kHz (horizontal frequency)

The colorburst phase will alternate by 180 degrees every other line (as it should) because of the 1/2 of a colorburst cycle per line.

An LCD monitor may be sensitive to this. Using the same clock for the AVR and colorbust (divided as necessary) may solve the problem you are having.

Edit: Fix cb freq typo - thanks Brian
« Last Edit: October 22, 2021, 11:29:31 am by oPossum »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7732
  • Country: ca
Re: NTSC problem with ATMega328
« Reply #8 on: October 22, 2021, 07:05:59 am »
3.579545 MHz (colorburst) / 227.5 = 15.734 kHz (horizontal frequency)

The colorburst phase will alternate every other line (as it should) because there are an odd number of colorburst cycles per line.

An LCD monitor may be sensitive to this. Using the same clock for the AVR and colorbust may solve the problem you are having.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7732
  • Country: ca
Re: NTSC problem with ATMega328
« Reply #9 on: October 22, 2021, 04:20:28 pm »
@nychold, if you have an eprom, why in the world are you generating all those delays and 3.57MHz crystal?

What a waste.
Use a 28.63636Mhz crystal, with a 3 bit counter feeding the A0, A1 and A2 of the eeprom.
Feed your desired output color and syncs/blk porch/color burst enable into the upper address.
Feed the 8 bit out into an 8 bit dac with a RLC low pass notch filter to notch out the 28.63636Mhz reference.
You may use a 74HC574 to buffer the eprom, clocked on the 28.63636Mhz crystal to clean the read data, with a resistor dac on the 8 bit output.
Generate all your display colors with 3.579545MHz phases into the upper address with with the color phases calculated across every 8 words.  Don't forget to encode the sync and colorburst levels as well.

Do the math correctly when generating the video levels from an 8 bit dac and your output will be as high quality at a mid-tier (around 2000's) digital video encoder with 27MHz dacs.  You can even go to 57.27272MHz with the first 4 bits of the eprom to encode your NTSC colors making that RLC notch filter easier to implement.

I will stop here.
« Last Edit: October 22, 2021, 04:24:47 pm by BrianHG »
 

Offline nycholdTopic starter

  • Newbie
  • Posts: 3
  • Country: us
Re: NTSC problem with ATMega328
« Reply #10 on: October 22, 2021, 04:51:40 pm »
I question that 15.734 kHz horizontal refresh rate.  Not because I know better, but because the timings never match that on any of the specifications I've seen.  Take this page from Maxim:

https://www.maximintegrated.com/en/design/technical-documents/tutorials/7/734.html

There's mention that the US standard for NTSC is supposed to be 15.734 kHz, but then they show the wave form and it doesn't match:

Front Porch = 1.5us
Sync Tip = 4.7us
Breezeway = 0.6us
Color Burst = 2.5us
Back Porch = 1.6us

The sum of that is exactly equal to the 10.9us they mention for horizontal blanking, so there aren't any obvious rounding issues.  Then, when you combine that with the 52.6us of active video, you get 63.5us.  1000000 / 63.5 = 15748 or 15.748 kHz.  It's not off by much, 14 Hz, but it suggests that either the timings are wrong, the graph is wrong, or there's quite a bit of slop in the protocol.

The timings in my original test code produced a blinking LED at 7.874 kHz, which is exactly half of 15.748 kHz, so it matches (more or less) the diagram shown.  It doesn't, match the 15.734 kHz, but neither does their diagram.
 

Offline Renate

  • Super Contributor
  • ***
  • Posts: 1460
  • Country: us
Re: NTSC problem with ATMega328
« Reply #11 on: October 22, 2021, 05:31:05 pm »
In ye olde days a television could not correlate color subcarrier because they were two unrelated phase locked loops.
Now that processing is fancier we do count color subcarriers in a line and we do expect them to be synchronized.
Only that way can we implement comb filters and other processing.

If you run everything off a 14.318 Xtal you can probably swing it a bit without annoying your digital television.
But you can't play fast and loose with the relationship between color/H/V.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7732
  • Country: ca
Re: NTSC problem with ATMega328
« Reply #12 on: October 22, 2021, 05:37:00 pm »
If your monitor sample the NTSC input, there may be no room for error unless you guarantee it can lock cleanly onto a non TBC'ed all analog VHS videotape signal.  And I've seen many having color decoding problems with VHS tape.

The formula is a H-SYNC every 227.5 clocks at 3.57954545454545454545454545MHz = 15734.26573Hz HS-HS.
Or, 910 clocks at 14.31818181818MHz.
Since many generators use the 14.31818181818MHz standard, here is the clock count using that frequency:
from HS-HS = 910 clocks, IE count from 0-909 and loop.
Front porch = 4 clocks
Sync           = 66 clocks
Back porch before color burst = 12 clocks
back porch with color burst     = 36 clocks
back porch after color burst    = 24 clocks
active video display                = 768 clocks

Total = 910 clocks.
These are from notes around 20years old.

Sorry, lost the pixel counts for stabilization pulses and V-sync pattern.
You will need to get a scope which can lock onto fields and analyze a true NTSC broadcast signal.
« Last Edit: October 22, 2021, 06:09:17 pm by BrianHG »
 

Online Benta

  • Super Contributor
  • ***
  • Posts: 5871
  • Country: de
Re: NTSC problem with ATMega328
« Reply #13 on: October 22, 2021, 05:40:21 pm »
The hypothesis that the color carrier frequency is an integer multiple of the horizontal line frequency sounds nice, but is wrong!

Quite the opposite.

Both for NTSC and PAL, the color frequency was carefully chosen to NOT be a multiple of horizontal to avoid interference and beating.

 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7732
  • Country: ca
Re: NTSC problem with ATMega328
« Reply #14 on: October 22, 2021, 05:49:30 pm »
The hypothesis that the color carrier frequency is an integer multiple of the horizontal line frequency sounds nice, but is wrong!

Quite the opposite.

Both for NTSC and PAL, the color frequency was carefully chosen to NOT be a multiple of horizontal to avoid interference and beating.
Then why that perfect scrolling comb pattern.  Also why can it be computed out with video line memory to a ok quality extent, and even better quality with Z video frame memory unless it didn't divide in every 2 or 4 lines evenly.

Do not get confused with the allowed drift/error in timing between the B&W video sync & frame with relation to the color sub-carrier which is what you got with non-time base corrected analog VHS recordings.  Many TVs today did not configure their NTSC analog sampling front-end's I2C controls to support analog VTR signals and their color with analog VHS tapes can flutter the color across the display unless the engineers who made these NTSC samplers have tested their TV with an old analog VHS deck, in cheap ELP mode, you cannot guarantee good solid still color decoding on such a signal.
 

Online Benta

  • Super Contributor
  • ***
  • Posts: 5871
  • Country: de
Re: NTSC problem with ATMega328
« Reply #15 on: October 22, 2021, 06:03:02 pm »
I'm not confused about anything.

I can highly recommend "Video Demystified" by Keith Jack, but it'll set you back around $75 unless you buy it used. The best book on video I know of.

The color carrier for NTSC was carefully selected to NOT be a multiple of 15625 Hz and 60 Hz. Same thing for PAL (4.43361875 MHz), except here the goal was to avoid multiples of 50 Hz and 15625 Hz.

Think about it: otherwise why choose such wacky frequencies? 15625 Hz x 64 is 1 MHz; much simpler.

« Last Edit: October 22, 2021, 06:15:18 pm by Benta »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7732
  • Country: ca
Re: NTSC problem with ATMega328
« Reply #16 on: October 22, 2021, 06:25:30 pm »
I'm not confused about anything.

I can highly recommend "Video Demystified" by Keith Jack, but it'ii set you back around $75 unless you buy it used. The best book on video I know of.

The color carrier for NTSC was carefully selected to NOT be a multiple of 15625 Hz and 60 Hz. Same thing for PAL (4.43361875 MHz), except here the goal was to avoid multiples of 50 Hz and 15625 Hz.

Think about it: otherwise why choose such wacky frequencies? 15625 Hz x 64 is 1 MHz; much simpler.
That is not the NTSC Hsync frequency.
It is 15734Hz.
Even wikipedia got it right: https://en.wikipedia.org/wiki/NTSC
Stating exactly 15.734 kHz.

As stated above, the normal color subcarrier frequency of 3.579545MHz  (the 45 supposed to be infinite repeat, but it doesn't need to be that accurate.)  Also correctly stated in wikipedia.

Divide that 3.579545 MHz clock by 227.5 and you get the 15.734 kHz.

Look at any close-up scope shot of an NTSC sync and color burst signal and you will see a 2 sine waves at perfect 180 degree phase overlapped illustrating that the color burst clock does indeed divide by evenly every 2 horizontal lines of video.

Heck, go looking for a NTSC encoder FPGA HDL core which generates an NTSC signal and you will see the clock cycles extrapolated.  Even the easily gen-lock able Amiga computers had a 4x NTSC color clock at 14.31818MHz.  Even my VCR's had such crystals in them, both in the analog section and in the TBC section.  This includes my laser-disc's TBC as well.

The only new-comer clock frequency is the 27MHz used for cross compatibility with NTSC/PAL/SECAM decoders and encoders because it can divide with an even fraction into the 2 video format color sub-carrier frequencies as well as offer 720 sample active pixel per line for both standards.
 

Offline Renate

  • Super Contributor
  • ***
  • Posts: 1460
  • Country: us
Re: NTSC problem with ATMega328
« Reply #17 on: October 22, 2021, 06:28:34 pm »
The number that was/is set in stone is 4.5 MHz, the sound carrier.
You could screw around with any number but not that.
So, since the color subcarrier spectrum was near the audio subcarrier that meant that it shouldn't clash.
That meant that the baseband spectrum should clash.
4.5 MHz / 525 / 30 = ~285.7
Therefore use 286 (closest integer).
4.5 MHz / 286 = ~15734.3
Now pick out a color subcarrier at random that is a half multiple of line rate, fits nicely between baseband and audio, has small prime factors (so your master sync generator is not too complex).
 

Online oPossum

  • Super Contributor
  • ***
  • Posts: 1415
  • Country: us
  • Very dangerous - may attack at any time
Re: NTSC problem with ATMega328
« Reply #18 on: October 22, 2021, 06:41:36 pm »
@nychold, if you have an eprom, why in the world are you generating all those delays and 3.57MHz crystal?

What a waste.
Use a 28.63636Mhz crystal, with a 3 bit counter feeding the A0, A1 and A2 of the eeprom.

That would require a ~30 ns EEPROM. Does such exist in friendly form?

A shift register clocked at 28.636 MHz could probably be done with fast 74xx such as 74LVC or 74AHC. That wouldn't be dependent on gate delays.
 

Online oPossum

  • Super Contributor
  • ***
  • Posts: 1415
  • Country: us
  • Very dangerous - may attack at any time
Re: NTSC problem with ATMega328
« Reply #19 on: October 22, 2021, 06:43:24 pm »
I question that 15.734 kHz horizontal refresh rate.  Not because I know better, but because the timings never match that on any of the specifications I've seen.  Take this page from Maxim:

https://www.maximintegrated.com/en/design/technical-documents/tutorials/7/734.html

There's mention that the US standard for NTSC is supposed to be 15.734 kHz, but then they show the wave form and it doesn't match:

Front Porch = 1.5us
Sync Tip = 4.7us
Breezeway = 0.6us
Color Burst = 2.5us
Back Porch = 1.6us

More detailed references attached...
« Last Edit: October 23, 2021, 07:19:29 am by oPossum »
 
The following users thanked this post: BrianHG, CNe7532294

Online oPossum

  • Super Contributor
  • ***
  • Posts: 1415
  • Country: us
  • Very dangerous - may attack at any time
Re: NTSC problem with ATMega328
« Reply #20 on: October 22, 2021, 06:47:27 pm »
The color carrier for NTSC was carefully selected to NOT be a multiple of 15625 Hz and 60 Hz. Same thing for PAL (4.43361875 MHz), except here the goal was to avoid multiples of 50 Hz and 15625 Hz.

The color carrier falls between the 227th and 228th harmonic of Hsync. That is 'not harmonically related'.
 

Online oPossum

  • Super Contributor
  • ***
  • Posts: 1415
  • Country: us
  • Very dangerous - may attack at any time
Re: NTSC problem with ATMega328
« Reply #21 on: October 22, 2021, 06:52:16 pm »
Look at any close-up scope shot of an NTSC sync and color burst signal and you will see a 2 sine waves at perfect 180 degree phase overlapped illustrating that the color burst clock does indeed divide by evenly every 2 horizontal lines of video.

Heck, go looking for a NTSC encoder FPGA HDL core which generates an NTSC signal and you will see the clock cycles extrapolated.  Even the easily gen-lock able Amiga computers had a 4x NTSC color clock at 14.31818MHz.  Even my VCR's had such crystals in them, both in the analog section and in the TBC section.  This includes my laser-disc's TBC as well.

The only new-comer clock frequency is the 27MHz used for cross compatibility with NTSC/PAL/SECAM decoders and encoders because it can divide with an even fraction into the 2 video format color sub-carrier frequencies as well as offer 720 sample active pixel per line for both standards.

Yep. Grab any pro or consumer video product and look at it on a waveform monitor. You will see what I posted in reply #7. That is from a DVD player.

Open up something that uses digital video and you will typically find a.... 27 MHz xtal.

27 MHz / 1716 == 3.579545 MHz / 227.5 == 15.734 kHz

« Last Edit: October 22, 2021, 06:59:11 pm by oPossum »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7732
  • Country: ca
Re: NTSC problem with ATMega328
« Reply #22 on: October 22, 2021, 07:09:45 pm »
27Mhz *35 /66 = 14.3181818181818181818181818181818... MHz.
Divide that by 4 and get the 3.579545 MHz.

Nice solid numbers if you want to perfectly PLL a 27MHz into a 14.31818MHz or the other way around.
Or, /33 if you want the 28.63636MHz used in Amiga computers and some older TBC circuits.

great for FPGA PLLs when cross converting 27MHz and NTSC sub-carrier frequencies.
« Last Edit: October 22, 2021, 07:22:37 pm by BrianHG »
 
The following users thanked this post: oPossum

Online Benta

  • Super Contributor
  • ***
  • Posts: 5871
  • Country: de
Re: NTSC problem with ATMega328
« Reply #23 on: October 22, 2021, 07:19:13 pm »
The color carrier for NTSC was carefully selected to NOT be a multiple of 15625 Hz and 60 Hz. Same thing for PAL (4.43361875 MHz), except here the goal was to avoid multiples of 50 Hz and 15625 Hz.

The color carrier falls between the 227th and 228th harmonic of Hsync. That is 'not harmonically related'.
Exactly. That was my point. There is no integer relationship between color carrier and HSync. The relationship is 227.5, as others have mentioned.
BTW, "227th and 228th harmonic" would be somewhat higher.   ::)

 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf