Power Your Raspberry Pi with an ATX Supply Using A PIC16F628A

🛑 Important Safety Disclaimer

Warning: This project involves working directly with mains-powered equipment (an ATX power supply).

  • Risk of Electric Shock: Even when disconnected, a PSU’s capacitors can hold a dangerous charge. Never open the sealed metal case of the ATX power supply.
  • Risk of Fire/Damage: Incorrect wiring can cause short circuits, permanently damaging your components (including the PSU, Raspberry Pi, and microcontroller) and creating a fire hazard.
  • No Warranty: This guide is for educational purposes only. You follow these instructions entirely at your own risk. The author and any associated parties accept no liability for any damage to equipment or injury to yourself or others. If you are not confident working with electronics and mains voltage, do not attempt this project.

 

Introduction: ATX Soft-Power Control for Raspberry Pi

This guide details a robust method for using a standard ATX computer power supply to power a Raspberry Pi, complete with “soft-power” control. This means a single push-button will intelligently turn the Pi on and, via a signal from the Pi itself, turn it off again once the Pi has safely shut down.

We will use a PIC16F628A microcontroller as the “brains” of the operation. It will be powered by the ATX supply’s +5V standby rail, allowing it to “listen” for a button press even when the main power is off. It will then control the ATX PS_ON# line to boot the main rails and monitor a GPIO pin from the Pi to manage a safe shutdown.


 

🔧 Component Selection

This design requires a few specific components beyond just the Pi and PSU.

  • ATX Power Supply: Any standard 20-pin or 24-pin ATX PSU. The critical signal is the +5V Standby (5VSB) rail, typically the purple wire. This rail is always on as long as the PSU is plugged in and switched on at the back.
  • Microcontroller: PIC16F628A. This 18-pin microcontroller is ideal because it’s inexpensive, widely available, has an internal oscillator (simplifying the circuit), and has enough I/O pins.
  • Transistor: BC547 (or 2N2222) NPN Bipolar Junction Transistor (BJT). This is essential. A PIC’s I/O pin cannot safely sink the current required by the PS_ON# line. The transistor acts as a switch, controlled by the PIC.
  • Resistors:
    • 1 x 10k Ohm: Pull-up resistor for the power button.
    • 1 x 1k Ohm: Base-current limiting resistor for the NPN transistor.
    • 2 x 330 Ohm: Current-limiting resistors for the status LEDs.
  • LEDs: 1 x Yellow (or Red) for ‘Standby’, 1 x Green for ‘Power On’.
  • Switch: 1 x Momentary push-button (push-to-make).
  • Connectors/Board: Perfboard or a custom PCB, plus wires and optionally a 24-pin ATX breakout board.

 

🔌 Circuit Design and Wiring

The circuit is designed around the key ATX control signals:

  • GND (Black): The common ground reference for all voltages.
  • +5VSB (Purple): Provides +5V standby power. This will power our PIC16F628A continuously.
  • PS_ON# (Green): The “Power Supply On” signal. This line is held at ~+5V by the PSU’s internal pull-up. To turn the PSU on, this line must be pulled low (to GND). This is what our transistor will do.
  • +5V (Red): The main +5V rail. This is what will power the Raspberry Pi. Do not power the Pi from the +5VSB rail, which cannot supply enough current.
  • PWR_OK (Grey): A signal from the PSU to the circuit, indicating that the +5V, +3.3V, and +12V rails are stable and ready. We can monitor this for a more robust design, but for simplicity, we will omit it in this version.

 

Pin Connections

1. PIC Power:

  • PIC Pin 14 (VDD) -> Purple (+5VSB)
  • PIC Pin 5 (VSS) -> Black (GND)

2. Status LEDs:

  • PIC Pin 6 (RB0) -> 330 Ohm Resistor -> Anode of Standby LED (Yellow) -> Cathode -> Black (GND)
  • PIC Pin 7 (RB1) -> 330 Ohm Resistor -> Anode of Power On LED (Green) -> Cathode -> Black (GND)

3. Power Button:

  • PIC Pin 12 (RB6) -> One side of the push-button
  • Other side of the push-button -> Black (GND)
  • PIC Pin 12 (RB6) -> 10k Ohm Resistor -> Purple (+5VSB) (This is our external pull-up resistor)

4. PS_ON# Control (The Transistor Switch):

  • PIC Pin 8 (RB2) -> 1k Ohm Resistor -> Base of BC547 Transistor
  • Emitter of BC547 -> Black (GND)
  • Collector of BC547 -> Green (PS_ON#)

5. Raspberry Pi Handshake:

  • PIC Pin 13 (RB7) -> A GPIO pin on the Raspberry Pi (e.g., GPIO 26 / Pin 37)
  • Crucially: You must also connect a GND pin from the Raspberry Pi to the common Black (GND) of the ATX PSU to ensure a shared ground reference.

6. Raspberry Pi Power:

  • Raspberry Pi +5V Pin (e.g., Pin 2 or 4) -> Red (+5V) from the ATX PSU.
  • Raspberry Pi GND Pin (e.g., Pin 6) -> Black (GND) from the ATX PSU.

 

🖥️ Raspberry Pi Configuration (The “Soft” Part)

For the Pi to tell the PIC it has finished shutting down, you need to add an overlay to your /boot/config.txt file.

  1. On your Pi, edit the file: sudo nano /boot/config.txt
  2. Add the following line to the end of the file:
    dtoverlay=gpio-shutdown,gpio_pin=26,active_low=1,gpio_pull=up
    

Explanation:

  • gpio_pin=26: This tells the Pi to use GPIO 26 (physical pin 37) as the shutdown signal pin. This must match the pin you connected to the PIC’s RB7.
  • active_low=1: When you run sudo shutdown -h now, the Pi will pull this pin low after it has halted.
  • gpio_pull=up: This keeps the pin HIGH during normal operation.

Our PIC code will monitor this pin. When it sees it go LOW, it knows the Pi is safe to power off.


 

🔬 PIC16F628A C Code (XC8 Compiler)

This code implements a simple state machine to manage the power states. It must be compiled using MPLAB X IDE and the XC8 compiler and then flashed to your PIC using a programmer (like a PICkit).

C

/*
 * ATX Soft Power Controller for Raspberry Pi
 * PIC16F628A
 *
 * This code is provided as-is, with no warranty.
 * Use at your own risk.
 */

// CONFIGURATION BITS
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = ON       // Brown-out Detect Enable bit (BOD enabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#include <xc.h>

#define _XTAL_FREQ 4000000 // 4MHz Internal Oscillator

// --- Pin Definitions ---
// PORTB
#define LED_STANDBY     LATBbits.LATB0
#define LED_POWER_ON    LATBbits.LATB1
#define PS_ON_CONTROL   LATBbits.LATB2
// (RB3, RB4, RB5 are unused in this simple version)
#define SWITCH_IN       PORTBbits.RB6
#define PI_SHUTDOWN_SIG PORTBbits.RB7

// --- State Machine ---
typedef enum {
    STATE_OFF,
    STATE_BOOTING,
    STATE_ON,
    STATE_SHUTTING_DOWN
} PowerState;

// --- Debounce Helper ---
// Basic debounce check for the switch
unsigned char is_button_pressed() {
    if (SWITCH_IN == 0) { // Button is active low
        __delay_ms(20); // Wait for debounce
        if (SWITCH_IN == 0) { // Check again
            while (SWITCH_IN == 0); // Wait for button release
            return 1; // Button was pressed
        }
    }
    return 0; // Not pressed
}

void main(void) {
    // --- System Initialisation ---
    CMCON = 0x07;       // Disable comparators
    TRISA = 0xFF;       // Port A all inputs
    TRISB = 0b11000000; // RB0, RB1, RB2 are outputs (LEDs, PS_ON)
                        // RB6, RB7 are inputs (Switch, Pi Signal)
    
    // Set initial output states
    PORTB = 0x00;       // All outputs low
    LED_STANDBY = 1;    // Start with Standby LED on
    PS_ON_CONTROL = 0;  // Keep PSU off (0 = NPN transistor off)
    
    PowerState currentState = STATE_OFF;
    
    // --- Main Loop ---
    while (1) {
        
        switch (currentState) {
            
            case STATE_OFF:
                LED_STANDBY = 1;
                LED_POWER_ON = 0;
                PS_ON_CONTROL = 0; // Transistor OFF (PSU OFF)
                
                // Wait for button press to turn on
                if (is_button_pressed()) {
                    currentState = STATE_BOOTING;
                }
                break;
                
            case STATE_BOOTING:
                // Turn on the PSU
                LED_STANDBY = 0;
                LED_POWER_ON = 1; // Or flash this LED
                PS_ON_CONTROL = 1; // Transistor ON (pulls PS_ON# LOW)
                
                // Note: The Pi's /boot/config.txt overlay holds PI_SHUTDOWN_SIG HIGH.
                // We wait for the Pi to boot and pull this signal HIGH.
                // We'll just assume it's booted after a delay for simplicity.
                // A better design monitors PWR_OK signal from PSU.
                
                __delay_ms(5000); // Wait 5 seconds for Pi to boot
                currentState = STATE_ON;
                break;

            case STATE_ON:
                LED_STANDBY = 0;
                LED_POWER_ON = 1;
                PS_ON_CONTROL = 1; // Keep PSU ON
                
                // Check for Pi shutdown signal OR a button press
                // The 'gpio-shutdown' overlay also monitors the *same pin*
                // for a button press to *initiate* shutdown.
                // But our button is on RB6. Let's just monitor the Pi's signal.
                
                // If the Pi shutdown overlay pulls the pin LOW, we power off.
                if (PI_SHUTDOWN_SIG == 0) { 
                    currentState = STATE_SHUTTING_DOWN;
                }
                
                // Optional: Implement a "force-off" if button is held for >5 sec
                if (is_button_pressed()) {
                    // A short press here could *signal* the Pi to shut down
                    // but our simple example relies on the Pi's overlay.
                    // For now, a button press does nothing while ON.
                }
                break;
                
            case STATE_SHUTTING_DOWN:
                // Pi has halted and pulled the signal low.
                // We wait a moment, then cut power.
                LED_POWER_ON = 0;
                LED_STANDBY = 1; // Or flash a "shutting down" light
                
                __delay_ms(2000); // Give Pi 2 seconds
                
                PS_ON_CONTROL = 0; // Transistor OFF (PSU OFF)
                
                // Wait for the shutdown signal pin to go high again
                // (it will float high when Pi powers off, or be pulled up by config.txt)
                // This prevents a boot-loop.
                while(PI_SHUTDOWN_SIG == 0); 
                
                currentState = STATE_OFF;
                break;
        }
        
        __delay_ms(50); // Small delay to prevent loop spinning
    }
}

 

🛠️ Final Assembly and Testing

  1. Test the PIC Circuit First: Before connecting the Pi or the ATX +5V rail, power your PIC circuit only from the +5VSB (Purple) and GND (Black) wires. The yellow ‘Standby’ LED should light up.
  2. Test the Switch: Press the push-button. The ‘Standby’ LED should turn off, the ‘Power On’ LED should light up, and the NPN transistor should turn on. You should hear the ATX PSU fan spin up, and you can measure +5V on the Red wires. If you release the switch, it should stay on (as it’s in STATE_ON).
  3. Test the Shutdown: Manually ground the PI_SHUTDOWN_SIG pin (the one connected to RB7). After a few seconds, the PIC should enter the STATE_SHUTTING_DOWN and then turn off the PSU (fan stops, ‘Power On’ LED goes off, ‘Standby’ LED comes on).
  4. Connect the Pi: If all tests pass, you can confidently connect the +5V (Red) and GND (Black) lines to your Raspberry Pi’s power pins, and connect the GPIO shutdown signal wire.

You now have a robust, intelligent power supply for your Raspberry Pi project.