Showing posts with label radio conrol. Show all posts
Showing posts with label radio conrol. Show all posts

Monday

Arduino: High Speed (8Mhz) Signaling: NTSC TV Output



I recently purchased a cheap 7" color monitor with intentions of using it for FPV flying, but still have not received the transmitting equipment. Of course, in waiting, I began to wonder about interaction with an Arduino, and a few searches led me to the TVOut library, which achieves NTSC video output (B/W) using an Arduino. I downloaded the library, and the more I read through the code, the more I was intrigued by how exactly such fast and accurate timing was acheived.

To give an idea of the requirements, the horizonal sync signal occurs about 15750 cycles/second (every 63.5us), which is relatively easy for an Arduino to produce in itself. In between these signals however, the picture is drawn, which requires much faster signaling and more precise timing, into the Mhz+ range.

In addition to the TVOut library, I also found a simpler form of code here that uses the USART (Serial port) to form the video signal, using each bit in every byte sent to form individual pixels. The main problem I found with this code was timing. It uses sleep mode to help time the video display accurately, but there were still some issues with small video glitches that I could not resolve when replicating the code. It also uses manual writing of the hsync pulse, where the other method uses a timer generated signal.

The first thing to do was to understand the makeup of an NTSC signal. This page provided a straightforward explanation, along with the timing requirements. Then it was on to recreating a hybrid of the code mentioned above, in the simplest possible format. The first part was pretty straightforward, creating horizontal and vertical sync signals. An entire frame consists of 259 horizontal sync signals, and 3 vertical sync signals, which are simply inverted horizontal sync signals to make 262 lines. The following code will produce a sync signal on pin 12, which results in a blank black screen.


#define hLinesPerFrame  262
#define vSync 247
#define hSync 25

volatile unsigned int isrCounter = 0;

void setup(){
  pinMode(12,OUTPUT);
 

  //This sets up the timer to produce the horizontal sync pulses
  //ICR1 is the timer TOP value, OCR1B controls the hSync pulse width
  //OCR1A will control the timing of the video display
  ICR1 = 1019;

  OCR1A = 1;
  OCR1B = 30;
  TCCR1A =  _BV(WGM11) | _BV(COM1B1) | _BV(COM1B0);
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
  TIMSK1 = _BV(OCIE1A); 
}


ISR(TIMER1_COMPA_vect){
  ++isrCounter;

  switch(isrCounter){         
          case vSync: OCR1B = 1019-hSync; break;

          case vSync+3: OCR1B = hSync; break;
          case hLinesPerFrame:  isrCounter = 0; break;
        }
  }


void loop(){ 

}

So that part is easy enough, a few lines of code, a 1k resistor from pin 12, and we have a basic NTSC signal with vertical and horizontal sync. Actually creating video will be a bit more complicated, and I've chosen an Arduino Mega due to the amount of SRAM (8K) available, which is needed for higher resolutions.

What I found in experimenting with different methods for timing, is that the timing of interrupts on a standard Arduino board can vary quite a bit. Using sleep mode to idle the board prior to triggering works 98-99% of the time, but still leaves a few interupts coming a bit late, producing glitches in the video. The method used in the TVOut library appears to work the best, as it monitors the counter to trigger at exactly the same time every time. Of course, it is written in assembly, and I made a slight modification to exit immediatly if the wait time has been missed. My explanation may be a slight bit off technically, but should be pretty close:

static void inline wait_until(uint8_t time) {

__asm__ __volatile__ (
    "sub    %[time], %[tcnt1l]\n\t" //Subtract the low byte of TCNT1 from the wait time
    "brmi   102f\n\t"               //If result is negative, we missed it. Branch to 102

 "100:\n\t"                     
    "subi    %[time], 3\n\t"         //Subtract 3 from time
    "brcc    100b\n\t"               //Loop until we get a negative
    "subi    %[time], 0-3\n\t"       //Add 3 back to our value
    "breq    101f\n\t"               //If equal to 0, branch to 101 and delay 1/16 of a uS (No OPeration)
    "dec    %[time]\n\t"            //Decrement our value by 1
    "breq    102f\n\t"               //If equal to 0, branch to 102 and don't delay
    "rjmp    102f\n"                 //Else Jump to the end
 "101:\n\t"
    "nop\n"
 "102:\n"

     :                                 //Output variables (none)
     : [time] "a" (time),              //Input variables
       [tcnt1l] "a" (TCNT1L)

);
 

}

That takes care of the basic signaling, and provides accurate enough timing to begin looking at displaying the video. I chose to use the USART to draw the video, as it simplifies things quite a bit when compared to using functions written in assembly, and should be able to use a baud rate of 8Mhz in Master SPI mode according to the datasheet. The USART also includes a double-buffering capability, which seems to make it more suitable here than using SPI.




We can calculate our BAUD rate, and the required setting for the UBBRn register at maximum speed:

16Mhz / 2(UBRRn + 1)  -->  16 / 2(0 + 1)  -->  16 / 2 -->  BAUD: 8Mhz  UBRRn: 0

Now, to configure the USART, only two lines of code are required in setup() :

  UCSR1C = _BV(UMSEL01) | _BV(UMSEL00); //Set USART to Master SPI mode
  UBRR1 = 0;                            //Baud rate = 8Mhz



Then to create a buffer for the pixel data of 5400 bytes:
    int totalBytes = (400/8) * 108;   
  byte data[totalBytes];

Now, all there is to do is start drawing pixels at the correct time, which (simplified) is done with the following lines of code, which get added to the TIMER1_COMPA_vector interrupt shown above.

wait_until(153);                      //See the assembly function above

UCSR1B |= _BV(TXEN1);                 //Enable USART TX

for (int x =0; x < 400/8; x++){       //Load bytes until we have drawn 1 horizontal line:
  while ( !(UCSR1A & _BV(UDRE1))){ }  //Wait until the USART is ready for more data
  UDR1 = data[x+start];               //Load a byte
}

  
UCSR1B = 0 ;
                                             //Disable the USART TX




...And thats all there is to it. Of course, the last part of it requires a bit more code to control the screen position and count lines, etc, but those are the fundamentals of producing NTSC video with an Arduino. A complete sketch is posted below.

Notes:
a: The timing of the video display in relation to the horizontal sync pulse is very important. It specifically triggers before the h-sync pulse ends so as to have enough time to smooth out variations in triggering.
b: The loading of data into the buffer (UDR1) must be done as quickly as possible, and requires efficient code to work at 8Mhz.
c: Using the USART requires accurate screen positioning due to the pulse created whenever it is enabled. This creates a vertical line, which can be positioned off screen via the wait timing.
d: This code does not use the defined standard timing for parts of the signal, due to better results with slightly modified settings. (ie: 2uS horizontal pulse instead of 4.7uS)
e: The maximum resolution is mainly limited by the memory available, as 1 byte is required for every 8 pixels. (400 x 108 uses 5.4KB)

Summary:
For me, this was an interesting look at the maximum limitations of signaling using an Arduino, and gave me an opportunity to learn a little more about assembly code as well. I found a great tutorial here showing how to view your Arduino code in assembly, and it was a great help in beginning to understand the code behind the code.
All in all, I am pretty happy with the results, as the methods used above seem pretty comparable to the TVOut libary, but with a bit higher resolution. The main benefits of using the USART are simplified code, as the baud rate can be changed to scale the video horizontally, and feeding it data is very straightforward.

The code used to create the above video can be found here and borrows heavily from the code it is based on.






Sunday

A Smaller Custom Arduino Quadcopter


The new 13.5" (34cm) custom quad             

  I've enjoyed building and flying my first custom Quadcopter based on an Arduino and FreeIMU, so decided to build another smaller version. The original really needs some wide open spaces to open up and fly, and I wanted something I could fly in more confined areas. This one is quite a bit smaller, measuring in at 13.5" compared to 21" and is much lighter. It is very similar to the first electronically, but uses a GY-86 module and Arduino Nano for the FC board. The IMU module has a gyro, barometer, compass, and accelerometer. It uses the same APC220 transceiver for control, but a standard RC unit is recommended in most cases.

Video from one of the initial test flights 

  As seen in the pics, the FC board is very simple, and I used the complex design method of 'connect the dots'. Just below the orange transceiver, jumpers can be seen which are used to disconnect it from the hardware serial port when connecting to USB for programming. The other pins shown just beside the Arduino are for connecting the ESCs, one of which powers the board. The Nano is mounted on header pins, so can be easily removed. This one was modelled somewhat after the first, but uses 10mm square carbon fiber booms and no aluminium mounts. The setup in MultiWii is pretty much the same, but with the GY-86 chosen under sensor definitions.


  The 1.5mm sheet was cut into two squares measuring 1.75" x 5" for the main body, and holes were drilled through the booms to bolt them directly between the sheets. Drilling through the booms may not provide the most strength, but with a small well balanced quad, I'm hoping it will hold up. The motor mounts required both a top and bottom plate as well due to the thin layer of carbon fiber in the centre of the boom. Drilling through the carbon fiber sheets completely dulled a standard drill bit as well.

  The two quads side-by-side

  A quick test flight with a 3S 1000maH battery and 3-blade 5045 props provided about 6 minutes of casual flight time, but it should be better with a 2S battery and 6" props according to eCalc. The tool also says the props will stall, but it hovers and moves nicely so not sure about that. The FC board is attached directly to the frame using some nylon standoffs, but vibration is minimal after balancing.
  I had a lot of difficulty trying to balance such small props individually, so admitted defeat and went directly to dynamic balancing. I came across laser balancing in this video and I always keep some spare lasers around (who doesn't?) so I tried it, and the balance was the best I've achieved. I balanced the motors before adding the props/adaptors and re-balancing.

 

  I had a few issues with these motors, since the mounts are a bit flimsy, and will break if there is excessive vibration. I've since attached the motors using hard rubber between the metal mounts and the carbon fiber to provide some give, and it seems to be working ok so far. Something also seems to have gotten into one of them, tearing apart the wiring, but it was easy to rewind with new magnet wire. Luckily, I bought 5 motors, but I may need to purchase a different type of motor if troubles keep popping up.

Main Parts List:
Quad:
4X HobbyKing Donkey ST2004-1550kv Brushless Motor
4X Propeller Adaptor (Colet Type) 3MM
2X Counter Rotating Three Blade 5x4.5 Propeller
2X Counter Rotating Three Blade 6x4.5 Propeller
4X Turnigy Multistar 10 Amp Multi-rotor Brushless ESC
2X 1000mAh 3S 25C Lipo Pack
2X 1000mAh 2S 25C Lipo Pack
1X 10mm Carbon Fiber Square Tube - 750mm
1X Woven Carbon Fiber Sheet 300x100 (1.5MM Thick)

FC Board:
1X Arduino Nano
1X GY-86 10DOF IMU Module: MS5611 HMC5883L MPU6050
1X APC220 Wireless Serial Data Module

FC Board PinOut:
pin3: front motor
pin9: rear motor
pin10: right motor
pin11: left motor
A4: GY-86 SDA
A5: GY-86 SCL
TX: APC220 RX
RX: APC220 TX   

Creating a Customized Zephyr Project + Arduino core

 Creating a Customized Zephyr Project + Arduino core Working outside them boxes   I currently have 2 Arduino Uno Q boards in my possession, ...