oPossum 1,083 Posted June 28, 2011 Share Posted June 28, 2011 // main.c #include "msp430g2211.h" #include "string.h" void video_init(void); unsigned pix[14], nxt[14]; unsigned get_pix(int x, int y) { if(x < 0 || x > 15 || y < 0 || y > 13) return 0; return (pix[y] >> x) & 1; } void main(void) { int x, y, n; WDTCTL = WDTPW + WDTHOLD; // No watchdog BCSCTL1 = XT2OFF + 15; // DCO 16 Mhz DCOCTL = 0x86; // // P1DIR = 0xF2; // I/O assignment P1REN = 0x0D; // Pull up resistor P1OUT = 0x0F; // P1SEL = 0x50; // Enable Timer A output, SMCLK output video_init(); // Begin video generation // Initial GOL field pix[0] = pix[2] = pix[4] = pix[6] = pix[8] = pix[10] = pix[12] = 0x5555; pix[1] = pix[3] = pix[5] = pix[7] = pix[9] = pix[11] = pix[13] = 0xAAAA; pix[0] = 0x0000; pix[1] = 0x000E; pix[2] = 0x0000; pix[3] = 0; memcpy(nxt, pix, sizeof(nxt)); for(; { for(y = 0; y < 14; ++y) { for(x = 0; x < 16; ++x) { n = get_pix(x - 1, y - 1) + get_pix(x , y - 1) + get_pix(x + 1, y - 1) + get_pix(x - 1, y ) + /* me */ get_pix(x + 1, y ) + get_pix(x - 1, y + 1) + get_pix(x , y + 1) + get_pix(x + 1, y + 1); if(get_pix(x, y)) { if(n < 2 || n > 3) nxt[y] &= ~(1 << x); } else { if(n == 3) nxt[y] |= (1 << x); } } } __delay_cycles(5000000); memcpy(pix, nxt, sizeof(pix)); } } ; video.asm .cdecls C, LIST, "msp430g2211.h" .text .global video_init .global pix .bss sync_state, 2 .bss sync_lines, 2 .bss video_state, 2 .bss vid_line, 2 video_init ; --- Initialize Video Generation --- clr &sync_state ; Init state machine mov #1, sync_lines ; clr &video_state ; mov #1016, &TACCR0 ; Setup Timer A period for 63.5 us mov #75, &TACCR1 ; Setup Timer A compare for video sync mov #0x0210, &TACTL ; Timer A config: SMCLK, count up mov #0x0060, &TACCTL1 ; Setup Timer A set/reset output mode bis #0x0010, &TACCTL0 ; Enable PWM interupt eint ; Enable interupts ret ; ; video_isr ; --- Video ISR --- dec &sync_lines ; Decrement sync lines jne vid_state ; Skip sync if not zero... add &sync_state, PC ; Jump to sync handler... ; - Field 1/3 jmp vid_eq ; Pre Equalizing jmp vid_vsync ; Vertical Sync jmp vid_eq ; Post Equalizing jmp vid_vbi ; VBI jmp vid_blank_9 ; 9 blank lines jmp vid_active ; Active Video jmp vid_blank_9 ; 9 blank lines jmp vid_half ; Video half line ; - Field 2/4 jmp vid_eq ; Pre Equalizing jmp vid_vsync ; Vertical Sync jmp vid_eq ; Post Equalizing jmp vid_half ; Video half line jmp vid_vbi ; VBI jmp vid_blank_9 ; 9 blank lines jmp vid_active ; Active Video jmp vid_blank_9l ; 9 blank lines vid_state ; -- Active Video Generation add &video_state, PC ; reti ; No active video jmp vid_field ; Field ; vid_eq ; Equalizing pulses - 6 half lines mov #508, &TACCR0 ; Half line mov #38, &TACCR1 ; Equalizing pulse duration mov #6, &sync_lines ; 6 half lines clr &video_state ; No video generation during sync incd &sync_state ; Next state reti ; ; vid_vsync ; Vertical sync - 6 half lines mov #508 - 75, &TACCR1 ; Sync pulse duration mov #6, &sync_lines ; 6 half lines incd &sync_state ; Next state reti ; ; vid_half ; Video half line - 1 half line mov #508, &TACCR0 ; Half line mov #75, &TACCR1 ; Sync pulse duration mov #1, &sync_lines ; 1 half line incd &sync_state ; Next state clr &video_state ; No video generation during half line and sync reti ; ; vid_vbi ; Vertical blanking interval - 11 lines mov #1016, &TACCR0 ; Full line mov #75, &TACCR1 ; Sync pulse duration mov #11, &sync_lines ; 11 lines incd &sync_state ; Next state reti ; ; vid_active ; Active video - 224 lines mov #224, &sync_lines ; 224 lines incd &sync_state ; Next state mov #2, &video_state ; Genearte video clr &vid_line ; Clear video line count jmp vid_field ; Generate video... ; vid_blank_9 ; 9 blank lines mov #9, &sync_lines ; incd &sync_state ; clr &video_state ; reti ; ; vid_blank_9l ; 9 blank lines (last in field) mov #9, &sync_lines ; clr &sync_state ; clr &video_state ; reti ; ; vid_field ; push R10 ; push R12 ; push R14 ; ; mov &TAR, R10 ; cmp #176, R10 ; jlo $ - 8 ; and #7, R10 ; rla R10 ; add R10, PC ; nop ; nop ; nop ; nop ; nop ; nop ; nop ; ; mov &vid_line, R12 ; inc &vid_line ; rra R12 ; rra R12 ; rra R12 ; ;bic #1, R12 ; add #pix, R12 ; mov @R12, R12 ; mov #16, R14 ; pixloop rlc R12 ; jc pixon ; bic.b #0x80, &P1OUT ; jmp nexpix ; pixon bis.b #0x80, &P1OUT ; jmp nexpix ; nexpix mov #10, R10 ; dec R10 ; jne $ - 2 ; dec R14 ; jne pixloop ; nop ; nop ; nop ; bic.b #0x80, &P1OUT ; ; pop R14 ; pop R12 ; pop R10 ; ; reti ; ; ; Interrupt Vectors .sect ".int09" ; TACCR0 CCIFG .short video_isr ; ; .end ; I can explain the video code if anyone is interested. It is very flexible and does proper interlaced video and sync (equalization and serration). RobG, bluehash, tripwire and 3 others 6 Quote Link to post Share on other sites
RobG 1,892 Posted June 28, 2011 Share Posted June 28, 2011 Nice! I am thinking 46" multimeter now. Quote Link to post Share on other sites
zeke 693 Posted June 28, 2011 Share Posted June 28, 2011 +1 for code monkey song Quote Link to post Share on other sites
NatureTM 100 Posted June 28, 2011 Share Posted June 28, 2011 That amazing synth and now this TV ouput game? Quit outdoing all my projects! Kidding! Exciting stuff. I'm looking forward to learning from your posts. You're awesome. Quote Link to post Share on other sites
RobG 1,892 Posted June 29, 2011 Share Posted June 29, 2011 As I said in my previous post, it would be great to put that video generation code to some serious (and not so serious) use.So I took oPossum's asm code, added some ADC stuff, and here it is, 46" 4ch 10bit binary meter For those interested in msp video games, checkout slaa177 bluehash 1 Quote Link to post Share on other sites
bluehash 1,581 Posted June 29, 2011 Share Posted June 29, 2011 Your guys are all on steroids. Just keep doing what you are doing. :mrgreen: Quote Link to post Share on other sites
RobG 1,892 Posted June 29, 2011 Share Posted June 29, 2011 An improved meter.This is fun, it's like getting a new toy.oPossum, here's a challenge for you. Change your asm to take sync, not generate, so we can overlay video. #include "msp430g2231.h" #include "string.h" void video_init(void); unsigned char charIndex = 0; unsigned char rowIndex = 0; unsigned int adcValue = 0; unsigned int digit = 0; unsigned int pix[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; unsigned int tmp[5] = {0,0,0,0,0}; unsigned const int digits[5][10] = {{0x7000,0x2000,0x7000,0x7000,0x5000,0x7000,0x7000,0x7000,0x7000,0x7000}, {0x5000,0x6000,0x1000,0x1000,0x5000,0x4000,0x4000,0x1000,0x5000,0x5000}, {0x5000,0x2000,0x7000,0x7000,0x7000,0x7000,0x7000,0x1000,0x7000,0x7000}, {0x5000,0x2000,0x4000,0x1000,0x1000,0x1000,0x5000,0x1000,0x5000,0x1000}, {0x7000,0x2000,0x7000,0x7000,0x1000,0x7000,0x7000,0x1000,0x7000,0x7000}}; void main(void) { WDTCTL = WDTPW + WDTHOLD; // No watchdog BCSCTL1 = XT2OFF + 15; // DCO 16 Mhz DCOCTL = 0x86; // ADC10CTL0 = ADC10SHT_2 + ADC10ON + ADC10IE; ADC10CTL1 = INCH_1; // ADC10AE0 |= BIT1; // P1DIR = 0xF0; // I/O assignment P1SEL = 0x50; // Enable Timer A output, SMCLK output video_init(); // Begin video generation for(; { __delay_cycles(1000000); adcValue = ADC10MEM; charIndex = 0; while(charIndex < 4) { digit = adcValue % 10; adcValue /= 10; rowIndex = 0; while(rowIndex < 5) { tmp[rowIndex] >>= 4; tmp[rowIndex] |= digits[rowIndex][digit]; rowIndex++; } charIndex++; } memcpy(pix, tmp, sizeof(tmp)); ADC10CTL0 |= ENC + ADC10SC; } } // ADC10 interrupt service routine #pragma vector=ADC10_VECTOR __interrupt void ADC10_ISR(void) { ADC10CTL0 &= ~ENC; } oPossum 1 Quote Link to post Share on other sites
oPossum 1,083 Posted June 29, 2011 Author Share Posted June 29, 2011 I think video overlay is best done by dedicated chips. Creating a MCU clock that is phase locked to the video sync and/or colorburst is non-trivial. The video will shake (jitter) without phase lock. I have used the Zilog Z86229 chip and it works well. Thanks for the DVM code. I have modified it to write directly to the video buffer and not use division or mod (they are slow and use a lot of code). The font has been packed in to fewer words. #include "msp430g2231.h" #include "string.h" void video_init(void); unsigned pix[14]; static const unsigned digits[5][3] = { // 0123 4567 89-- { 0x7277, 0x5777, 0x7700 }, { 0x5611, 0x5441, 0x5500 }, { 0x5277, 0x7771, 0x7700 }, { 0x5241, 0x1151, 0x5100 }, { 0x7277, 0x1771, 0x7700 } }; void main(void) { unsigned adc; int pos, shift; register unsigned digit, mask; register unsigned *pp; register const unsigned *dp; WDTCTL = WDTPW + WDTHOLD; // No watchdog BCSCTL1 = XT2OFF + 15; // DCO 16 Mhz DCOCTL = 0x86; // ADC10AE0 = BIT1; // Analog in on P1.1 (A1) ADC10CTL1 = INCH_1; // Select A1 // Configure ADC ADC10CTL0 = ADC10SHT_2 | ADC10SR | ADC10ON | ENC; P1DIR = 0xF0; // I/O assignment P1SEL = 0x50; // Enable Timer A output, SMCLK output memset(pix, 0, sizeof(pix)); // Clear screen video_init(); // Begin video generation for(; { __delay_cycles(500000); ADC10CTL0 |= ADC10SC; // Start ADC conversion while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion complete adc = ADC10MEM; // Get result // Show on screen mask = 0xF000; pos = 0; do { digit = 0; while(adc >= 1000) ++digit, adc -= 1000; adc = (adc + (adc << 2)) << 1; shift = (pos++ - (digit & 3)) << 2; dp = &digits[0][digit >> 2]; pp = pix + 4; digit = 5; while(digit--) *pp = (~mask & *pp) | \ (mask & ((shift < 0) ? (*dp << -shift) : (*dp >> shift))), ++pp, dp += 3; } while(mask >>= 4); } } RobG 1 Quote Link to post Share on other sites
RobG 1,892 Posted June 29, 2011 Share Posted June 29, 2011 I was thinking more like a proof of concept project. Sync separator like LM1881 could be used if syncing in software becomes too difficult. Quote Link to post Share on other sites
oPossum 1,083 Posted June 30, 2011 Author Share Posted June 30, 2011 Here is the video code written in C for those who don't understand assembly. The ISR uses a state machine to change the sync timing as the frame progresses. The Timer A compare registers are updated as needed to produce proper NTSC sync timing. A few states will also do setup for the actual video generation. This method is very simple and flexible. The C code was a bit too slow to get the equalization pulse timing exact, so the sync pulse is a litter wider than it should be (50 clocks vs. 38 clocks). The ISR... __interrupt void video_isr(void) // Video ISR { // if(--sync_lines) { // Decrement the sync line count, if not zero yet then if(video_state) active_video(); // generate video if video_state is non-zero } else { // Sync line count is now zero, so update the sync timing switch(sync_state) { // default: // Restart state machine sync_state = 0; // case 0: case 2: case 8: case 10: // Equalizing pulses - 6 half lines TACCR1 = 50; TACCR0 = 508; sync_lines = 6; break; case 1: case 9: // Vertical sync - 6 half lines TACCR1 = 508 - 75; sync_lines = 6; break; case 3: case 12: // VBI - 11 lines TACCR1 = 75; TACCR0 = 1016; sync_lines = 11; break; case 4: case 6: case 13: case 15: // 9 blank lines sync_lines = 9; video_state = 0; break; case 5: case 14: // Active video - 224 lines sync_lines = 224; video_state = 1; vid_line = 0; active_video(); break; case 7: case 11: // Half line - 1 half line TACCR1 = 75; TACCR0 = 508; sync_lines = 1; break; } // ++sync_state; // Move to next state } // } // Complete code... (equivalent to video.asm) // video.c #include "msp430g2211.h" extern unsigned pix[14]; static unsigned sync_state; static unsigned sync_lines; static unsigned video_state; static unsigned vid_line; void video_init(void) { sync_state = 0; sync_lines = 1; video_state = 0; TACCR0 = 1016; TACCR1 = 75; TACTL = 0x0210; TACCTL1 = 0x0060; TACCTL0 |= 0x0010; _EINT(); } void active_video(void) { register unsigned b, n, d; #if 1 //__asm(" mov &TAR, R12"); __asm(" mov &0x0170, R12"); __asm(" cmp #144, R12"); __asm(" jlo $ - 8"); __asm(" and #7, R12"); __asm(" rla R12"); __asm(" add R12, PC"); __asm(" nop"); __asm(" nop"); __asm(" nop"); __asm(" nop"); __asm(" nop"); __asm(" nop"); __asm(" nop"); #else while(TAR < 160); #endif b = pix[vid_line++ >> 4]; n = 16; do { if(b & 0x8000) P1OUT |= 0x80; else { P1OUT &= ~0x80; __asm(" nop"); __asm(" nop"); } b <<= 1; d = 10; while(--d); --n; } while(n); P1OUT &= ~0x80; } #pragma vector = TIMERA0_VECTOR __interrupt void video_isr(void) // Video ISR { // if(--sync_lines) { // Decrement the sync line count, if not zero yet then if(video_state) active_video(); // generate video if video_state is non-zero } else { // Sync line count is now zero, so update the sync timing switch(sync_state) { // default: // Restart state machine sync_state = 0; // case 0: case 2: case 8: case 10: // Equalizing pulses - 6 half lines TACCR1 = 50; TACCR0 = 508; sync_lines = 6; break; case 1: case 9: // Vertical sync - 6 half lines TACCR1 = 508 - 75; sync_lines = 6; break; case 3: case 12: // VBI - 11 lines TACCR1 = 75; TACCR0 = 1016; sync_lines = 11; break; case 4: case 6: case 13: case 15: // 9 blank lines sync_lines = 9; video_state = 0; break; case 5: case 14: // Active video - 224 lines sync_lines = 224; video_state = 1; vid_line = 0; active_video(); break; case 7: case 11: // Half line - 1 half line TACCR1 = 75; TACCR0 = 508; sync_lines = 1; break; } // ++sync_state; // Move to next state } // } // jsolarski 1 Quote Link to post Share on other sites
bromatt 0 Posted July 21, 2011 Share Posted July 21, 2011 Any ideas on how to connect the launchpad s upto a monitor, I did try this over the weekend and got so far but looks like i didn't have any sync? Matt Quote Link to post Share on other sites
oPossum 1,083 Posted July 22, 2011 Author Share Posted July 22, 2011 ... bromatt 1 Quote Link to post Share on other sites
bromatt 0 Posted July 22, 2011 Share Posted July 22, 2011 Greatstuff, be trying that this evening, hopefully improve on the attached, which was what I had before.. Thanks!! Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.