Home 9 The Art of Technology 9 PIC10F322 XC8 Code : Build a Trigger-Activated Dual Alternating Flasher for Model Trains

Creating a Trigger-Activated Dual Alternating Flasher with PIC10F322 for Model Train Level Crossing Lights

This guide provides code for the microcontroller to create a trigger-activated dual alternating flasher, also known as train level crossing lights. This circuit modification ensures that the lights only begin flashing when an external trigger signal is detected, such as when a model train crosses into the grade block.

PIC10F322 Project Overview

In a previous project, we built a dual alternating flasher where the lights would flash back and forth as soon as power was supplied. In this modification, the circuit remains inactive until the input pin A3 is taken low. When this happens, the lights will alternate until the pin is brought back high. This setup allows for an external trigger to activate the circuit, making it perfect for model train applications.

The Circuit

dual crossing trigger

XC8 Code for Trigger-Activated Dual Alternating Flasher

Here's the complete XC8 code to create a trigger-activated dual alternating flasher with the PIC10F322 microcontroller:

 * File:  cross_trigger.c
 * Author: Jamie Starling - GizoFoundry.com 
 * Created on:  September 7, 2021, 7:45 PM
 * Code/Circuit provided as-is.


//Device Configuration
#pragma config FOSC = INTOSC  // Oscillator Selection 
#pragma config BOREN = ON    // Brown-out Reset
#pragma config WDTE = OFF    // Watchdog Timer
#pragma config PWRTE = ON    // Power-up Timer
#pragma config MCLRE = OFF   // MCLR Pin Function Select bit->MCLR pin function is digital input, MCLR internally tied to VDD
#pragma config CP = OFF      // Code Protection 
#pragma config LVP = OFF     // Low-Voltage Programming 
#pragma config LPBOR = ON    // Brown-out Reset Selection bits
#pragma config BORV = LO    // Brown-out Reset Voltage Selection
#pragma config WRT = OFF    // Flash Memory Self-Write Protection

//Used to calculate the delay time - Change depending on processor Speed
#define _XTAL_FREQ 8000000  //8Mhz

#define DEBOUNCE_TIME 50

void setup(void);
uint8_t input_debounce_A2(void);
void alternate_leds(void);

void main(void)
        uint8_t switch_status = 0;
        switch_status = input_debounce_A2();
        //The above is more long hand, it can be simplied down to.. 
        //if (input_debounce_A2()) - instead of creating a variable, reading the results into it - and compairing that variable, 
        if (switch_status == 1)
            LATAbits.LATA0 = 0; 
            LATAbits.LATA1 = 0;

void setup(void)
    //Disable analog for A0, set as Output, Set to Low
    ANSELAbits.ANSA0 = 0;
    TRISAbits.TRISA0 = 0; //A0 Output
    LATAbits.LATA0 = 0;  //Put A0 low
    //Disable analog for A1, set as Output, Set to Low
    ANSELAbits.ANSA1 = 0;
    TRISAbits.TRISA1 = 0; //A1 Output
    LATAbits.LATA1 = 0;  //Put A1 low
    //Setup A2 as input with WPU
    ANSELAbits.ANSA2 = 0;
    TRISAbits.TRISA2 = 1;
    WPUAbits.WPUA2 = 1; //Enable Weakpull up on A2   
    OPTION_REGbits.nWPUEN = 0; //Requires being enabled in option reg as well   

/*reads the input on A2 to see if it is low - we have a pullup here
 *so a low input would indicate that the switch that is tied to ground is being pressed
 *If low - we wait 50ms and read the status again
 *If low after 50ms, we return 1 to be true the switch is being pressed, otherwise 0 is returned. 
 *You also can adjust the value of the delay and do something like 1 second, which would require
 *the switch to be held for 1 second before something happens. */
uint8_t input_debounce_A2(void)
    if (PORTAbits.RA2 == 0)
        if (PORTAbits.RA2 == 0)
            return 1;
    return 0;  
    return 0;    

void alternate_leds(void)
    LATAbits.LATA0 = 0; //A0 off
    LATAbits.LATA1 = 1; //A1 on
    LATAbits.LATA1 = 0; //A1 off
    LATAbits.LATA0 = 1; //A0 on

Explanation of the Code

  1. Including the XC8 Header File:
    • The line #include includes the necessary definitions for the PIC10F322.
  2. Defining the Processor Frequency:
    • The macro _XTAL_FREQ defines the frequency of the internal oscillator, crucial for accurate timing.
  3. Defining Debounce Time:
    • The define DEBOUNCE_TIME sets the debounce delay in milliseconds.
  4. Configuration Bits:
    • These lines set up various hardware configurations like the oscillator type, watchdog timer, and power-up timer. Adjust these settings as needed for your specific setup.
  5. Setup Function:
    • Configures PortA pins, enabling weak pull-ups and setting input/output directions.
  6. Debounce Function:
    • The input_debounce_A3() function checks the state of RA3, waits for the debounce time, and checks the state again. If both readings are logic 0, it returns 1 (TRUE).
  7. Main Function:
    • Continuously checks the debounced state of RA3. If a button press is confirmed, it alternates the LEDs on RA0 and RA1. When the button is released, the LEDs turn off.


This guide provides a comprehensive explanation and code for creating a trigger-activated dual alternating flasher with the PIC10F322 microcontroller. By following the circuit setup and using the provided code, you can enhance your model train layout with realistic level crossing lights that respond to an external trigger. Customize the debounce time and blink intervals as needed to fit your specific requirements.

Have a Project or Idea!?

Seeking Bespoke Technology Solutions?


Pin It on Pinterest

Share This