MK2 - Hardware - PIC18F4620 interface PCB

My Basic PIC18F 40 pin PDIP breakout PCB as used previously.  This one has a PIC16F4685 in it.

Purpose of this module is to interface commands received from Mach3 via the parallel port to our custom hardware

20120306 OK, have made a significant project change of direction and the controlling software is now KPlace per smtplace.com

KPlace directly controls the Head Z actuation & Vacuum through a Dynomotion KFLOP controller board, refer my KFLOP page in this project for details

This only allows for a single head, with automated feeders controlled by serial port.

This PIC18F PCB will receive a single BYTE command over a serial connection before activating the required feeder ie receive '0' activate feeder 0 etc etc

The PIC UART RX & TX pins will be connected to a FT232RL Serial to USB converter (A small PCB moduleI home 4 years ago that is lying around unused)

PIC18F4685 TX is on RC6 & RX is on RC7

#include <18F4685.h>
#include  <string.h>
#include  <math.h>
#include <stdlib.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES WDT128                   //Watch Dog Timer uses 1:128 Postscale
#FUSES H4                       //High speed osc with HW enabled 4X PLL
//#FUSES INTRC_IO                  //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
//#FUSES BORV25                   //Brownout reset at 2.5V
#FUSES PUT                      //Power Up Timer
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOIESO                   //Internal External Switch Over mode disabled
#FUSES NOFCMEN                  //Fail-safe clock monitor disabled
#FUSES NOPBADEN                 //PORTB pins are configured as digital I/O on RESET
#FUSES NOWRTC                   //configuration not registers write protected
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOEBTRB                  //Boot block not protected from table reads
#FUSES NOCPB                    //No Boot Block code protection
#FUSES NOLPT1OSC                //Timer1 configured for higher power operation
#FUSES MCLR                     //Master Clear pin enabled
//#FUSES XINST                    //Extended set extension and Indexed Addressing mode enabled

#use delay(clock=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7)

#define  FEEDERACTIVEDELAYMILLISECS 2500

int32    ISRupTimeSecs;
int16    ISRupTimeMilliSecs;

int32    upTimeSecs;
int16    upTimeMilliSecs;

int1     feederActive;
int16    feederActiveMilliSecs;

int      receiveByteCount;
int      receivedByte;
int      receivedDigit1;
int      receivedDigit2;
int    receivedFeeder;

int1  commandReceived;

int currentFeeder;

int i;




// 20120306

// Inputs
// RC6 & RC7 Serial command:
//   3 bytes:
//   - byte 1: 'f' or 'F'
//   - bytes 2-3: zero padded feeder no to activate

//Outputs
// D0-3/D4-7 feeder activation
// - D0-6 select the target feeder
// - D7 is a separate activate (active high) 
// E0 HeartBeat Led


void feederActivate(int feeder, int activate)
{
   if (activate)
   {
      output_D(feeder);
     output_D(feeder | 0b10000000);
      feederActiveMilliSecs = 0;
      feederActive = true;
   }
   else
   {
      output_D(feeder & 0b10000000);
      feederActive = false;
   }
}

#int_timer0
void timer0_isr(void)
{
   set_timer0(100); //256-156?
   if (++ISRupTimeMilliSecs == 1000)
   {
      ISRupTimeMilliSecs = 0;
      ISRupTimeSecs ++;
   }
   if (feederActive)
   {
      feederActiveMilliSecs++;
   }
}

#int_rda
void serial_isr() {
   receivedByte = getc();
   if (receivedByte == 'f' || receivedByte == 'F')
   {
      receiveByteCount = 1;
   }
   else if (receiveByteCount == 1)
   {
      receivedDigit1 = receivedByte;
      receiveByteCount = 2;
   }
   else if (receiveByteCount == 2)
   {   
      receivedDigit2 = receivedByte;
      receiveByteCount = 0;
      receivedFeeder = 0;
      if (receivedDigit1 >= '0' && receivedDigit1 <= '9')
      {
         receivedFeeder = 10 * (receivedDigit1 - '0');
         if (receivedDigit2 >= '0' && receivedDigit2 <= '9')
         {
            receivedFeeder = receivedFeeder + receivedDigit2 - '0';
            if (!feederActive)
            {
               commandReceived = true;
            }
         }
      }
   }
}


void main()
{
   //-------------------------------------
   //Startup sequence
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   
   receiveByteCount = 0; 
   feederActive= FALSE;
   commandReceived = false;
   currentFeeder = 0;

   upTimeMilliSecs = 0;
   upTimeSecs= 0;

   set_timer0(100);
   setup_timer_0(RTCC_INTERNAL|RTCC_8_BIT|RTCC_DIV_64);
   
   Output_D(0);
   Output_E(0);
   
   for (i = 0; i<32; i++)
   {
      output_bit(pin_e0,1);
      delay_ms(25);
      output_bit(pin_e0,0);
      delay_ms(25);
      putc('a');
      Output_D(i);
   }
   // ext_int_edge(1,H_TO_L);
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_TIMER0);
   enable_interrupts(int_rda);
   ///enable_interrupts(INT_EXT);
   
   currentFeeder = 0;

   // end startup sequence
   //-------------------------------------

   while(1)
   {
      uptimeSecs = ISRuptimeSecs;
      uptimeMilliSecs = ISRuptimeMilliSecs;
      if (uptimeSecs != ISRuptimeSecs)
      {
         // must've rolled over so get it again
         uptimeSecs = ISRuptimeSecs;
         uptimeMilliSecs = ISRuptimeMilliSecs;
      }
   
     output_bit(pin_e0,(upTimeMilliSecs < 500));

      // testing cycle all feeders up to n
      /*
      if (!feederActive)
      {
         currentFeeder = (currentFeeder == 17) ? 0 : ++currentFeeder;
         delay_ms(250);
         feederActivate(currentFeeder, true);
      }
      */
      
      if (feederActive && feederActiveMilliSecs >= FEEDERACTIVEDELAYMILLISECS)
      {
         feederActivate(currentFeeder, false);
      }
      
      
      if (commandReceived)
      {
        currentFeeder = receivedFeeder;
        feederActivate(currentFeeder, true);
        commandReceived = false;
      }
      
   }  // end while(1)
   
} //end main

 


Pre 20120306

UNRESOLVED:

The feeders on activating the index BTN pull back the current component cover.  If the index btn is not released within about 2 seconds, the component cover then moves back over the current component with no additional feed. If the index btn is released within the 2 second window a component feed action is initiated.

Do I:

1. Need to be signal both these actions from Mach3?

Initial answer: no

2. Need the PIC to manage the timing of the release action to be within the 2 seconds?

Initial answer?: yes


In this initial MK2 version it needs to accept commands to:

- action 4 heads up/down (pneumatic solenoid valves via 4 way Mosfet PCB)

- action 4 head vacuums (vacuum solenoid valves via 4 way Mosfet PCB)

- action up to n Siemens automated feeders

Later MK2 iterations may need to:

- Rotate head or PCB holder


Inputs:

Analysis

From the parallel port (assuming only Enable, X dir, x step, y dir, y step outputs are already used) we have 7 free signal lines available for driving this PCB.

I considered creating a simple serial protocol to send commands, but with 7 data lines, I can instead send a 6 line parallel command and clock it in using the 7th line. This will keep the Mach3 VBA macros for sending the commands simpler and easier to monitor / debug etc

This allows 2^6 commands = 64

Commands from MACH3:

Action First command last command  
Head n down 0 3  
Head n up 4 7  
Head n vacuum on 8 11  
Head n vacuum off 12 15  
Buzzer off 16 16  
Buzzer on 17 17  
Vacuum pump off 18 18  
Vacuum pump on 19 19  
unused 20 31  
Feeder n activate 32 63  

By using the 7th output to activate the PIC, the remaining 6 lines could potentially still be used for other purposes (ie A axis) just not simultaneously 

PIC pins

B0 = toggled to signal action current command (needs to be an interrupt)

B1 = unused as yet

B2-7 = current command:

- B4-7 = command bit 0-3

- B2-3 = command bit 4-5

 


Outputs

RA0-3 unused as yet

RA4-5 unused as yet

RE0: Heartbeat/Indicator LED

RE1: Buzzer

RE2: Vacuum pump on/off (possibly)

C0-3 = head z movement solenoids x 4

C4-7 = head vacuum solenoids x 4

D0-3, D4-7 = automated feeder activation

 


Firmware (using CCS C)

When B0 interrupt is activated we will set flags for command activation in the main loop.

We will also use Timer 0 to as a timer count every millisecond for deactivating any timed commands in the main loop and also for the heartbeat LED

Reminder to self on PIC18F4685 timer0:
- Timer frequency = system clock = instruction cycles
- There is no Period Register, by default is 8/16 bit mode values of 255 or 65535 on overflow but we can preset the Timer0 register in the ISR to effectively use a PR
- Crystal is 10MHZ * HSPLL = 40MHZ = 10MHZ system clock, doesn't divide nicely by powers of 2 (prescaler values) so some inaccuracy.. so looking for best trade off of low overhead / accuracy
Did up a spreadsheet.. best result (highest prescaler, highest PR):
- In 8 bit mode, prescaler at 64, fudge PR to 156:
Interval = 1 sec / (10,000,000 /  64 * 156 ) = 0.0009984 seconds = 0.16% error, pretty good

For the feeders, we need to activate the feeder & hold it for just before the timeout to initiate a feed.

- will need to track the time the feeder was activated & automatically deactivate it when the timeout period is reached
- assume only every one feeder active at a time

Below is a WIP just here as a backup

#include <18F4685.h>
#include  <string.h>
#include  <math.h>
#include <stdlib.h>
#FUSES NOWDT                    //No Watch Dog Timer
#FUSES WDT128                   //Watch Dog Timer uses 1:128 Postscale
#FUSES H4                       //High speed osc with HW enabled 4X PLL
//#FUSES INTRC_IO                  //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
//#FUSES BORV25                   //Brownout reset at 2.5V
#FUSES PUT                      //Power Up Timer
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOIESO                   //Internal External Switch Over mode disabled
#FUSES NOFCMEN                  //Fail-safe clock monitor disabled
#FUSES NOPBADEN                 //PORTB pins are configured as digital I/O on RESET
#FUSES NOWRTC                   //configuration not registers write protected
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOEBTRB                  //Boot block not protected from table reads
#FUSES NOCPB                    //No Boot Block code protection
#FUSES NOLPT1OSC                //Timer1 configured for higher power operation
#FUSES MCLR                     //Master Clear pin enabled
//#FUSES XINST                    //Extended set extension and Indexed Addressing mode enabled
#use delay(clock=40000000)
#define  FEEDERACTIVEDELAYMILLISECS 2000
int32    ISRupTimeSecs;
int16    ISRupTimeMilliSecs;
int32    upTimeSecs;
int16    upTimeMilliSecs;
int1     feederActive;
int16    feederActiveMilliSecs;
int1  commandReceived;
int   command;
int   currentHead;
int   currentFeeder;
int1  head0VacuumOn, head1VacuumOn, head2VacuumOn, head3VacuumOn;
// 201201227
// Inputs
// B0 is action current command
// B2-7 is current command bits 0-5
//Outputs
// C0-3 head pneumatic valves
// C4-7 head vacuum valves
// D0-3/D4-7 feeder activation
// - D0-6 selectthe target feeder
// - D7 is a separate activate (active high) 
// E0 HeartBeat Led
// E1 Buzzer
// E2 Mains Vacuum pump on / off
void buzzerOn(int1 on)
{
   output_bit(PIN_E1, on);
}
void mainVacuumOn(int1 on)
{
   output_bit(PIN_E2, on);
}
void headDown(int head, int1 down)
{
   switch (head)
   {
      case 0:
         output_bit(PIN_C0, down);
      case 1:
         output_bit(PIN_C1, down);
      case 2:
         output_bit(PIN_C2, down);
      case 3:
         output_bit(PIN_C3, down);
   }
}
void headVacuumOn(int head, int1 on)
{
   switch (head)
   {
      case 0:
         output_bit(PIN_C4, on);
      case 1:
         output_bit(PIN_C5, on);
      case 2:
         output_bit(PIN_C6, on);
      case 3:
         output_bit(PIN_C7, on);
   }
}
void feederActivate(int feeder, int1 activate)
{
   if (activate)
   {
      output_D(feeder);
      output_D(feeder & 0b100000000);
      feederActiveMilliSecs = 0;
      feederActive = true;
   }
   else
      output_D(0);
      feederActive = false;
}
void outputAllHeadVacuums(void)
{
   headVacuumOn(0, Head0VacuumOn);
   headVacuumOn(1, Head1VacuumOn);
   headVacuumOn(2, Head2VacuumOn);
   headVacuumOn(3, Head3VacuumOn);
}
void setAllHeadVacuums(int1 setting)
{
   Head0VacuumOn = Head1VacuumOn = Head2VacuumOn = Head3VacuumOn = setting;
   outputAllHeadVacuums();
}
#int_timer0
void timer0_isr(void)
{
   set_timer0(100); //256-156?
   if (++ISRupTimeMilliSecs == 1000)
   {
      ISRupTimeMilliSecs = 0;
      ISRupTimeSecs ++;
   }
   if (feederActive)
   {
      feederActiveMilliSecs++;
   }
}
#int_ext    //B0
void B0_isr(void)
{
   commandReceived = true;
   command = input_b() & 0b00111111; // only want 6 bits of command value
}
void main()
{
   //-------------------------------------
   //Startup sequence
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   
   feederActive= false;
   commandReceived = false;
   command = 0;
   upTimeMilliSecs = 0;
   upTimeSecs= 0;
   set_timer0(100);
   setup_timer_0(RTCC_INTERNAL|RTCC_8_BIT|RTCC_DIV_64);
   
   Output_C(0);
   Output_D(0);
   Output_E(0);
   // Cycle through all the head selects so we can see startup on the
   // the Stepper controller PCB & cycle all vacuum solenoids
   for(CurrentHead = 0; CurrentHead<4; ++CurrentHead)
      {
      Output_C(1<<CurrentHead);
      buzzerOn(CurrentHead&1);
      delay_ms(500);
      }
   Output_C(0);
   
   // ext_int_edge(1,H_TO_L);
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_TIMER0);
   enable_interrupts(INT_EXT);
   // end startup squence
   //-------------------------------------  
   
   while(1)
   {
      uptimeSecs = ISRuptimeSecs;
      uptimeMilliSecs = ISRuptimeMilliSecs;
      if (uptimeSecs != ISRuptimeSecs)
      {
         // must've rolled over so get it again
         uptimeSecs = ISRuptimeSecs;
         uptimeMilliSecs = ISRuptimeMilliSecs;
      }
   
      output_bit(pin_e2,(upTimeMilliSecs < 500));
      
      if (feederActive && feederActiveMilliSecs >= FEEDERACTIVEDELAYMILLISECS)
      {
         feederActivate(currentFeeder, false);
      }
      
      
      if (commandReceived)
      {
         if (command <= 3)
         {
            headDown(command, true);
         }
         else if (command <= 7)
         {
            headDown(command - 4, false);
         }
         else if (command <= 11)
         {
            headVacuumOn(command - 8, true); 
         }
         else if (command <= 15)
         {
            headVacuumOn(command - 12, false);
         }
         else if (command <= 17)
         {
            buzzerOn(command - 16);
         }
         else if (command <= 19)
         {
            mainVacuumOn(command - 18);
         }
         else if (command <= 31)
         {
            // unused
         }
         else if (command <= 63)
         {
            currentFeeder = command - 32;
            feederActivate(currentFeeder, true);
            feederActive = true;
            feederActiveMilliSecs = upTimeMilliSecs;
         }
      
      } // end command response processing
      
   }  // end while(1)
   
} //end main
#include <18F4685.h>
#include  
#include  
#include 

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES WDT128                   //Watch Dog Timer uses 1:128 Postscale
#FUSES H4                       //High speed osc with HW enabled 4X PLL
//#FUSES INTRC_IO                  //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
//#FUSES BORV25                   //Brownout reset at 2.5V
#FUSES PUT                      //Power Up Timer
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOIESO                   //Internal External Switch Over mode disabled
#FUSES NOFCMEN                  //Fail-safe clock monitor disabled
#FUSES NOPBADEN                 //PORTB pins are configured as digital I/O on RESET
#FUSES NOWRTC                   //configuration not registers write protected
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOEBTRB                  //Boot block not protected from table reads
#FUSES NOCPB                    //No Boot Block code protection
#FUSES NOLPT1OSC                //Timer1 configured for higher power operation
#FUSES MCLR                     //Master Clear pin enabled
//#FUSES XINST                    //Extended set extension and Indexed Addressing mode enabled

#use delay(clock=40000000)

#define  FEEDERACTIVEDELAYMILLISECS 2000

int32    ISRupTimeSecs;
int16    ISRupTimeMilliSecs;

int32    upTimeSecs;
int16    upTimeMilliSecs;

int1     feederActive;
int16    feederActiveMilliSecs;


int1  commandReceived;
int   command;

int   currentHead;
int   currentFeeder;

int1  head0VacuumOn, head1VacuumOn, head2VacuumOn, head3VacuumOn;

// 201201227

// Inputs
// B0 is action current command
// B2-7 is current command bits 0-5

//Outputs
// C0-3 head pneumatic valves
// C4-7 head vacuum valves
// D0-3/D4-7 feeder activation
// - D0-6 select the target feeder
// - D7 is a separate activate (active high) 
// E0 HeartBeat Led
// E1 Buzzer
// E2 Mains Vacuum pump on / off

void buzzerOn(int1 on)
{
   output_bit(PIN_E1, on);
}

void mainVacuumOn(int1 on)
{
   output_bit(PIN_E2, on);
}

void headDown(int head, int1 down)
{
   switch (head)
   {
      case 0:
         output_bit(PIN_C0, down);
      case 1:
         output_bit(PIN_C1, down);
      case 2:
         output_bit(PIN_C2, down);
      case 3:
         output_bit(PIN_C3, down);
   }
}

void headVacuumOn(int head, int1 on)
{
   switch (head)
   {
      case 0:
         output_bit(PIN_C4, on);
      case 1:
         output_bit(PIN_C5, on);
      case 2:
         output_bit(PIN_C6, on);
      case 3:
         output_bit(PIN_C7, on);
   }
}

void feederActivate(int feeder, int1 activate)
{
   if (activate)
   {
      output_D(feeder);
      output_D(feeder & 0b100000000);
      feederActiveMilliSecs = 0;
      feederActive = true;
   }
   else
      output_D(0);
      feederActive = false;
}


void outputAllHeadVacuums(void)
{
   headVacuumOn(0, Head0VacuumOn);
   headVacuumOn(1, Head1VacuumOn);
   headVacuumOn(2, Head2VacuumOn);
   headVacuumOn(3, Head3VacuumOn);
}

void setAllHeadVacuums(int1 setting)
{
   Head0VacuumOn = Head1VacuumOn = Head2VacuumOn = Head3VacuumOn = setting;
   outputAllHeadVacuums();
}

#int_timer0
void timer0_isr(void)
{
   set_timer0(100); //256-156?
   if (++ISRupTimeMilliSecs == 1000)
   {
      ISRupTimeMilliSecs = 0;
      ISRupTimeSecs ++;
   }
   if (feederActive)
   {
      feederActiveMilliSecs++;
   }
}

#int_ext    //B0
void B0_isr(void)
{
   commandReceived = true;
   command = input_b() & 0b00111111; // only want 6 bits of command value
}


void main()
{
   //-------------------------------------
   //Startup sequence
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   
   feederActive= false;
   commandReceived = false;
   command = 0;
   upTimeMilliSecs = 0;
   upTimeSecs= 0;

   set_timer0(100);
   setup_timer_0(RTCC_INTERNAL|RTCC_8_BIT|RTCC_DIV_64);
   
   Output_C(0);
   Output_D(0);
   Output_E(0);


   // Cycle through all the head selects so we can see startup on the
   // the Stepper controller PCB & cycle all vacuum solenoids
   for(CurrentHead = 0; CurrentHead<4; ++CurrentHead)
      {
      Output_C(1<= FEEDERACTIVEDELAYMILLISECS)
      {
         feederActivate(currentFeeder, false);
      }
      
      
      if (commandReceived)
      {
         if (command <= 3)
         {
            headDown(command, true);
         }
         else if (command <= 7)
         {
            headDown(command - 4, false);
         }
         else if (command <= 11)
         {
            headVacuumOn(command - 8, true); 
         }
         else if (command <= 15)
         {
            headVacuumOn(command - 12, false);
         }
         else if (command <= 17)
         {
            buzzerOn(command - 16);
         }
         else if (command <= 19)
         {
            mainVacuumOn(command - 18);
         }
         else if (command <= 31)
         {
            // unused
         }
         else if (command <= 63)
         {
            currentFeeder = command - 32;
            feederActivate(currentFeeder, true);
            feederActive = true;
            feederActiveMilliSecs = upTimeMilliSecs;
         }
      
      } // end command response processing
      
   }  // end while(1)
   
} //end main

 

xxxx