Saturday, April 16, 2011

How it's done volume 6: When and how to fire the coils


Earlier we talked about the need for SCRs. We also talked about having an infrared see-through barrel. Hopefully you did both. Because if you did, it's easy to pick up cut-beam sensors and big beefy SCRs from digikey. I'll assume that you've done so. I'll also assume that you're using shielded wire. That's critical to cut down on the EM noise made by the charger and the coils firing.

Some coil guns will place sensors right before every coil and fire that coil when the round is about to enter it. Other guns will simply use a series of pre-determined delays from when the round first fires. Those times are often determined experimentally.

Don't do either of these, do both. If you have the sensor right before every coil, when the coils are firing slow compared to the projectile velocity, you'll be firing those coils too late. If you're always getting stuck around 40 or 50m/s this is almost certainty your problem. If you take the other track and have timers from the beginning, you'll have issues with timer drift as you add more and more coils. For instance, if the second coil doesn't give as much power as expected, the round will be too late for the 3rd coil and then even later for the 4th coil and so on till you're essentially using the other coils only some of the times and become the victim of chaos.

The alternative is to have many sensors and fire coils in advance based on times. If you look at the code that I have, sometimes I'm waiting for a sensor to fire and firing one coil but then waiting and firing the next coil without waiting for a sensor. By the end, the computer is actually firing coils a few sensors in advance. So the projectile may hit sensor 5 and the computer will wait a small time then fire coil 8.

The values here must be determined experimentally. Far too much chaos to apply math to this one. That said, it's very reliable. Each round is within 5% of the previous round. I have some confidence that I could even use the same basic construction and not have to change these constants or at least not have to change them much.

A long as you're doing this experimentally, use a computer. Don't try building this into circuits. A cheap PIC chip will have enough power to handle it. And get a simple one. The Arduino is a nice piece of equipment, but if it's actually running Java that means you're got a garbage handler. It could kick in any time and mess up the timing. Get a simple computer with no operating system, write in C.

I use the almost-disposable DM164120-2 demo board which comes with a PIC16F887 MCU. It takes an hour or two to setup the environment for it. There are some IO pins that don't-work/I-can't-get-to-work. However, I've used even the more complex things like interrupts with no difficulty.

This board comes with a bunch of LEDs on PORTD so I made that the output port. I arbitrarily made port C the input port and took one of the pins from PORTB as the input for the charge signal. You'll notice some code to prevent the system from firing while charging. This happens when either there's a user error or more commonly the EM noise from the charger gives the computer bad readings about the sensors and it thinks the time has come to fire.

You'll also notice I don't use interrupts. Interrupts on this device are too slow from the work that we're doing. At 70m/s it takes just 1.4uS for our projectile to travel a millimeter and a couple of millimeters can make a difference as to how much power you get from a coil.


#include
__CONFIG(INTCLK & WDTDIS & PWRTDIS & MCLRDIS & UNPROTECT & DUNPROTECT & BORDIS & IESODIS & FCMDIS & LVPDIS);
__CONFIG(BORV40); // 2nd config word

#define FIRE_TIMEOUT 0xFF0


void wait(int a, int b)
{
int i,j;
for(i = 0; i < a; i++)
for(j = 0; j < b; j++);
}

void test_count()
{
int time;
time = 0;
TRISD = 0x00;
while(1==1)
{
time = time+1;
PORTD = time;
wait(0x01,0xFF);
}
}

void showC()
{
int tmrOff = 0b00110100;
T1CON = tmrOff;
ADCON0 = 0xFF;
ANSELH = 0; //Turn off PortB AtoD
TRISC = 0xFF;
TRISD = 0x00;
TRISB = 0xFF;
while(1==1)
{
PORTD = PORTC;
}

}

void delayFire()
{
int time;
int i;
int tmrOff = 0b00110100;
int holdFire;
T1CON = tmrOff;
ADCON0 = 0xFF;
ANSELH = 0; //Turn off PortB AtoD
TRISC = 0xFF;
TRISD = 0x00;
TRISB = 0xFF;
time = 0;
while( 1==1)
{
//If the 'charge signal' is on, it should not be easy to disable
if( (PORTB & 0x04) > 0x00)
{
PORTD = 0x00000000;//Don't fire
wait(0x01,0x0A);
i = 0;
if( (PORTB & 0x04) > 0x00)
{
//It better be cleared consistantly and for a long time before we'll consider firing
while(i<0x3F) { wait(0x01,0x2F); if((PORTB & 0x02) > 0x00)
i = 0;
else
i = i+1;
}
}
}
else //Consider firing
{
//Gate 1
if( (PORTC & 0x01) == 0x00)
{
//wait(0x01,0x01);
PORTD = 0b00000001;


//Gate 2
wait(0x01,0x60);
PORTD = 0b00000011;

//Gate 3
//I is the timeout. It's used only when not firing (not fast enough for firing)
//Only really used for clearing issues on startup or power cycle.
i = 0;
while( ((PORTC & 0x02) != 0x00) && i < FIRE_TIMEOUT)
i = i + 1;
if( i < FIRE_TIMEOUT )
{
wait(0x01,0x37);
PORTD = 0b00000110;
}

//Gate 4
i = 0;
while( ((PORTC & 0x04) != 0x00) && i < FIRE_TIMEOUT)
i = i + 1;
if( i < FIRE_TIMEOUT )
{
wait(0x01,0x2A); //tested 3/30
PORTD = 0b00001100;
}

//Gate 5
i = 0;
while( ((PORTC & 0x08) != 0x00) && i < FIRE_TIMEOUT)
i = i + 1;
if( i < FIRE_TIMEOUT )
{
wait(0x01,0x13);
PORTD = 0b00011000;
}

//Gate 6
wait(0x01,0x30);
PORTD = 0b00110000;

//Gate 7
i = 0;
while( ((PORTC & 0x10) != 0x00) && i < FIRE_TIMEOUT)
i = i + 1;
if( i < FIRE_TIMEOUT )
{
i = 0;
while(i < 0x20)
i++;
PORTD = 0b01100000;
}
wait(0x01,0xF0);
}
else
PORTD = 0b10000000;
}
}
}

void main()
{
//test_count();
//test_a();
//showC();
delayFire();
}

No comments:

Post a Comment