Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
itai_n
PostPosted: Jun 12, 2012 - 06:40 PM
Rookie


Joined: Jul 19, 2011
Posts: 47
Location: Haifa, Israel

After having some difficulties using DMA, I'm happy to post a working program here. Things are not complete yet but possibly useful as a reference.

Note - DMA is using DMA_CH_TRIGSRC_TCD0_CCA_gc to output the first byte in a line. This gives a consistent timing for the whole line. An interrupt handler (TCD0_CCA_vect) is changing the trigger to DMA_CH_TRIGSRC_USARTD1_DRE_gc to output 15 more bytes.

This would be impossible without a logic analyser. I use the Open Workbench Logic Sniffer. My best spent $50.

Comments and suggestions are welcome.

Code:
// TV Out for xmega
// (C) Itai Nahshon 2012
// nahshon <at> actcom <dot> co <dot> il

#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#define XTAL16      // use 16 MHz crystal
void Config32MHzClock(void);

#define TIMER_PRESCALER 64
// #define TICKS(n)   ((uint16_t)(((uint32_t)(n)*((F_CPU/1000000)))/(TIMER_PRESCALER)))
#define TICKS(n)   ((n)/2)      // Microseconds to timer 'ticks' converter

#define TIMER   TCD0
#define PORT   PORTD
#define USART   USARTD1
#define DMAX   DMA.CH0

// Hardware used:
// Boston Android EVAL-04 XMega Development Board + 16 MHz crystal (and 2x 10pf capacitors)
//
// Output signals:
// PORTD0 - Used to flash LED
// PORTD1 - Video sync output (connect with 1 KOhm resistor to video)
// PORTD7 - Video signal output (connect with 330 Ohm resistor to video)
// PORTD5 - Video clock
// PORTD4 - Debugoutput (to see interrupt handler timing on a logic analyzer)
uint8_t blank_bits[] = { [0 ... 15] 0xff };
#define DATASIZE 16

/*
 * Logo.xbm contains a 128x64 pixel B/W image atored as single array of 1024 bytes, LSB first.
 * Created with The Gimp, saved as XBM file.
 * static unsigned char Logo_bits[1024];
 */
#include "Logo.xbm"

/*
 * PAL timing, as described here:
 * http://martin.hinner.info/vga/pal.html
 */
#define NORMAL_LINE_SYNC   TICKS(4)
#define NORMAL_LINE_LEN      TICKS(64)
#define NORMAL_LINE_COUNT   305
#define SYNC_HALFLINE_LEN   TICKS(32)

#define VIDEO_START_IN_LINE   TICKS(16)   // when to start DMA

const uint16_t Field1_sync[] = {
   [ 0 ...  5] TICKS(2),   // Pre-equalizing
   [ 6 ... 10] TICKS(30),   // Vertical sync
   [11 ... 15] TICKS(2),   // Post-equalizing
};
#define FILED1_SYNC_LEN   16

const uint16_t Field2_sync[] = {
   [ 0 ...  4] TICKS(2),   // Pre-equalizing
   [ 5 ...  9] TICKS(30),   // Vertical sync
   [10 ... 13] TICKS(2),   // Post-equalizing
};
#define FILED2_SYNC_LEN   14

int main(void) {
   Config32MHzClock();

   PORT.DIR = 0xff;
   PORT.PIN1CTRL |= PORT_INVEN_bm;
   PORT.PIN7CTRL |= PORT_INVEN_bm;

   // Initialize Usart as SPI Master
   USART.BAUDCTRLA = 4;         // 3.2 Mbit/sec
   USART.BAUDCTRLB = 0;
   USART.CTRLA = 0;         // No interrupt
   USART.CTRLB = USART_TXEN_bm;      // tx only
   USART.CTRLC = USART_CMODE_MSPI_gc|_BV(2);// SPI, lsb first, rising edge;

   // Initialize DMA
   DMA.CTRL = 0;
   DMA.CTRL = DMA_RESET_bm;      // reset DMA controller
   while (DMA.CTRL & DMA_RESET_bm)      // wait reset complete
      ;

   // configure DMA controller
   DMA.CTRL = DMA_ENABLE_bm;
   // channel 0
   // **** TODO: reset dma channels (?)
   DMA.CH0.REPCNT = 1;
   DMA.CH0.CTRLA = DMA_CH_BURSTLEN_1BYTE_gc /*| DMA_CH_SINGLE_bm*/ | DMA_CH_REPEAT_bm;
   DMA.CH0.ADDRCTRL = DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTDIR_FIXED_gc;
   DMA.CH0.DESTADDR0 = (( (uint16_t) &USART.DATA) >> 0) & 0xFF;
   DMA.CH0.DESTADDR1 = (( (uint16_t) &USART.DATA) >> 8) & 0xFF;
   DMA.CH0.DESTADDR2 = 0;
   // Interrupts
   //DMA.CH0.CTRLB = DMA_CH_ERRINTLVL_HI_gc|DMA_CH_TRNINTLVL_HI_gc;
   DMA.CH0.CTRLB = 0;   // No interrupt!

   // Initialize timer
   TIMER.CTRLA = TC_CLKSEL_DIV64_gc;
   TIMER.CTRLB = TC_WGMODE_SS_gc;
   TIMER.PER = NORMAL_LINE_LEN-1;

   TIMER.CCA = VIDEO_START_IN_LINE;
   TIMER.CCB = NORMAL_LINE_SYNC;

   // Interrupt on overflow and CCA
   TIMER.INTCTRLA = TC_OVFINTLVL_HI_gc;
   TIMER.INTCTRLB = TC_CCAINTLVL_HI_gc;

   // Enable timer
   TIMER.CTRLB = TC0_CCBEN_bm|TC_WGMODE_SS_gc;

   // Enable the DMA
//   DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;

   // Enable interrupts
   PMIC.CTRL = (PMIC_HILVLEN_bm|PMIC_MEDLVLEX_bm|PMIC_LOLVLEX_bm);
   sei();

   // Now blink LED at 2Hz, use timer TCC0
   TCC0.CTRLA = 0x7;   // clk/1024
   while(1) {
      PORTD.OUT ^= _BV(0);
      while(TCC0.CNT < 7812) // roughly 250ms
         asm("nop");
      TCC0.CNT=0; // reset
   }
}

uint16_t mode_limit[] = { NORMAL_LINE_COUNT, FILED2_SYNC_LEN, NORMAL_LINE_COUNT, FILED1_SYNC_LEN };
uint16_t count = 0;
uint8_t mode = 0;

ISR(TCD0_OVF_vect) {
   PORT.OUT |= 0x10;   // Debug!

   if((mode & 0x01) == 0) {
      // Normal line, prepare to display some data
      uint8_t *linedata;
      uint8_t effective_line = (count - 29) / 4;

      if(effective_line >= 64)
         linedata = blank_bits;   // below 29 or above 29+4*64
      else
         linedata = &Logo_bits[DATASIZE * effective_line];

      // Prepare timer
      TIMER.PER = NORMAL_LINE_LEN-1;
      TIMER.CCA = VIDEO_START_IN_LINE;
      TIMER.CCB = NORMAL_LINE_SYNC;

      // Prepare DMA to start on CCB;
      DMA.CH0.CTRLB |= DMA_CH_ERRIF_bm;
      DMA.CH0.CTRLB |= DMA_CH_TRNIF_bm;
      DMA.CH0.CTRLA &= DMA_CH_ENABLE_bm;

      //DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_TCD0_CCB_gc;
      DMA.CH0.REPCNT = 1;
      DMA.CH0.CTRLA = DMA_CH_BURSTLEN_1BYTE_gc | DMA_CH_SINGLE_bm | DMA_CH_REPEAT_bm;
      //DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_USARTD1_DRE_gc;
      DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_TCD0_CCA_gc;
      DMA.CH0.TRFCNT = DATASIZE;
      DMA.CH0.SRCADDR0 = (( (uint16_t) &linedata[0]) >> 0) & 0xFF;
      DMA.CH0.SRCADDR1 = (( (uint16_t) &linedata[0]) >> 8) & 0xFF;
      DMA.CH0.SRCADDR2 = 0;
      DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
   }
   else {
      if(mode & 0x02)
         TIMER.CCB = Field1_sync[count];
      else
         TIMER.CCB = Field2_sync[count];

      TIMER.PER = SYNC_HALFLINE_LEN-1;
      // Large number! (so we don't get the interrupt)
      TIMER.CCA = TICKS(99);
   }

   if(++count >= mode_limit[mode]) {
      count = 0;
      mode = (mode + 1) & 0x03;
   }

   PORT.OUT &= ~0x10;   // Debug!
}

ISR(TCD0_CCA_vect) {
   PORT.OUT |= 0x10;   // Debug!

   // Change trigger of DMA channel
   DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_USARTD1_DRE_gc;

   PORT.OUT &= ~0x10;   // Debug!
}

void Config32MHzClock(void)
{
#ifdef XTAL16
   // External 16 MHz crystal
   CCP = CCP_IOREG_gc;
   CLK.LOCK = 0;

   // External 16,000,000kHz oscillator initialization
   OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc|OSC_XOSCSEL_XTAL_16KCLK_gc;
   // Enable the oscillator
   OSC.CTRL |= OSC_XOSCEN_bm;

   // Wait for the external oscillator to stabilize
   while (!(OSC.STATUS & OSC_XOSCRDY_bm))
      ;

   // Enable PLL and set it to multiply by 2 to get 32MHZ
   OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc|OSC_PLLFAC1_bm;
   OSC.CTRL |= OSC_PLLEN_bm;

    // Wait for the PLL to stabilize
   while (!(OSC.STATUS & OSC_PLLRDY_bm))
      ;

   // Select PLL for the system clock source
   CCP = CCP_IOREG_gc;
   CLK.CTRL = CLK_SCLKSEL_PLL_gc;

    // Disable unused clocks
   CCP = CCP_IOREG_gc;
   OSC.CTRL = OSC_XOSCEN_bm|OSC_PLLEN_bm;

   // No prescaler
   CCP = CCP_IOREG_gc;
   CLK.PSCTRL = 0;

   CCP = CCP_IOREG_gc;
   CLK.LOCK = 1;
#else
   // Internal 32 MHz oscilator
   CCP = CCP_IOREG_gc; //Security Signature to modify clock
   // initialize clock source to be 32MHz internal oscillator (no PLL)
   OSC.CTRL = OSC_RC32MEN_bm; // enable internal 32MHz oscillator
   while(!(OSC.STATUS & OSC_RC32MRDY_bm)); // wait for oscillator ready
   CCP = CCP_IOREG_gc; //Security Signature to modify clock
   CLK.CTRL = 0x01; //select sysclock 32MHz osc
#endif
}
 
 View user's profile Send private message  
Reply with quote Back to top
AtomicZombie
PostPosted: Jun 12, 2012 - 10:07 PM
Posting Freak


Joined: Feb 13, 2007
Posts: 1025
Location: Gillies, Ontario

Nice to see another video project, thanks. Do you have a screenshot of the output?

Brad

_________________
I Like to Build Bikes : http://www.AtomicZombie.com
I Like to Hack Stuff : http://www.LucidScience.com
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
itai_n
PostPosted: Jun 13, 2012 - 06:14 PM
Rookie


Joined: Jul 19, 2011
Posts: 47
Location: Haifa, Israel

Sorry, no screen shot.
Image is just static, so it would be boring...
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Jun 13, 2012 - 06:48 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62290
Location: (using avr-gcc in) Finchingfield, Essex, England

Quote:

so it would be boring...

Not for micro engineers who can appreciate how amazing the achievement is!

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
itai_n
PostPosted: Jun 14, 2012 - 12:45 AM
Rookie


Joined: Jul 19, 2011
Posts: 47
Location: Haifa, Israel

Very Happy
I've added code for a Spektrum satellite receiver.
Here's the video:
http://www.youtube.com/watch?v=wFvacqHAQWk
 
 View user's profile Send private message  
Reply with quote Back to top
itai_n
PostPosted: Jun 18, 2012 - 03:15 AM
Rookie


Joined: Jul 19, 2011
Posts: 47
Location: Haifa, Israel

I now have some OSD ability. Increased resolution to 212X128 meaning I use 3392 (of the available 4K) just for the frame buffer.

I found that interrupt response times are not consistent enough to handle horizontal timing. Need to find a solution better than polling in long-long loops.

http://youtu.be/Oh-u3cDeg0M
 
 View user's profile Send private message  
Reply with quote Back to top
indianajones11
PostPosted: Aug 01, 2012 - 05:12 AM
Raving lunatic


Joined: Nov 28, 2004
Posts: 3552
Location: San Diego, Ca

Quote:
I found that interrupt response times are not consistent enough to handle horizontal timing. Need to find a solution better than polling in long-long loops.
Maybe it'll work better if you convert key code sections to asm and link those routines into your C code.

_________________
1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1
 
 View user's profile Send private message  
Reply with quote Back to top
condemned
PostPosted: Aug 01, 2012 - 11:00 AM
Hangaround


Joined: Sep 04, 2007
Posts: 356
Location: Oxford (England)

Here are a couple of ideas to 'fix' the interrupt response times:
1. Go to sleep before the interrupt fires.
This means that no instruction is running when the interrupt fires and the latency is fixed.

2. Compare the Timer
Switch your timer to clock 1:1 with the CPU.
When you get into your interrupt routine, read the timer and have a short, variable, delay based on what you read.

See this thread for a brief discussion on the latter.
Here's another thread about normalising interrupt timing.
I've got a TV-output XMega project on my XMega example code page.

Note that if you're using an external source for hsync, then you'll never get perfect results simply because your clock will never be quite in sync with the source. On an XMega, you'll probably be able to get close enough.

Also note that you might hit issues with the USART bit clock - it seems to keep the underlying bit clock running even when it's not transmitting, hence all transmissions are half-bit aligned with one another, so even if you get your interrupt nicely jitter-free, the USART output could still scupper you! You've probably avoided this problem by having your scanline width an integer multiple of the USART clock.

_________________
Nigel Batten
www.batsocks.co.uk
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
mojo-chan
PostPosted: Aug 01, 2012 - 01:39 PM
Resident


Joined: Jan 24, 2008
Posts: 517


The only way to get a perfectly accurate sync with an external video clock is to use a PLL. Well, there is one other way actually, connect a timing crystal to the hsync via a 1M resistor so that it is "encouraged" to sync with it Smile
 
 View user's profile Send private message  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits