Pin connections pIC16F57
PIC16F57 24-pin DIP package.
Click for larger image. Wiring schematic Visit Hobby Page

🖧 PIC16F57 LCD Display – Binary and Decimal Counter

by Lewis Loflin

This page cover using and setting up the PIC16F57. It is very basic, inexpensive, and great for testing hardware. Hardware is the focus here.

The next project in the link below uses the PIC16F57 microcontroller to count from 0 to 255, displaying both the binary and decimal values on an HD44780-compatible LCD. The counter updates every 500 milliseconds using TMR0 driven by an external 60 Hz timebase. It’s a straightforward hardware test designed to verify the PIC16F57's function, explore its quirks (such as the 2-level call stack), and demonstrate practical embedded development — with no simulation software involved.

Every aspect of this circuit is tested on real hardware. No guesses, no virtual models — just measurable, observable behavior. This is not a sandbox — it’s a lab.

See 8-Bit LCD Interface with PIC16F57: Binary and Decimal Counter in C (XC8)

The PIC16F57 is perfect for testing eight-bit hardware and that is the focus with these projects.


⚠️ Why I Don't Use Simulation Software

Many visitors to this site attempt to simulate these circuits using SPICE-based tools or drag-and-drop simulators — and then wonder why they don’t work. Here's the reality:

If you're serious about learning how electronics actually work — build it, measure it, break it, and fix it. That’s what I do here.

This isn’t a sandbox. It’s a lab.


⚠️ Real Hardware Matters: The 2-Level Stack Problem

A perfect example of why simulation isn’t enough: the PIC16F57 has a fixed two-level return stack. That means it can only remember the return addresses for two nested subroutine calls.

See PIC16F57 Serial LCD and 4x4 Keypad Demo

This isn’t just a technical detail — it can cause program crashes, missed returns, or corrupt behavior if you nest more than two calls. And the worst part?

I would never have spotted this unless I actually ran a program and debugged it. The simulator didn’t show it — the hardware did.

If you're using chips like the 16F57, you must test on real hardware to find these hidden limitations. Simulators won't show stack overflows, pin conflicts, or timing glitches — only live debugging will.

Test it. Watch it fail. Learn why — and fix it.


Comparison chart of PIC16F57, PIC16F84A, PIC16F628A, PIC12F683, and PIC16F690.
Characteristics of Microchip PIC16F57, PIC16F84A, PIC16F628A, PIC12F683, PIC16F690.
Click image for larger view.

What works on the PIC16F57


📘 PIC16F57 Register File Map

File Address Register
00h INDF(1)
01h TMR0
02h PCL
03h STATUS
04h FSR
05h PORTA
06h PORTB
07h PORTC
08h – 1Fh General Purpose Registers

(1)INDF is not a physical register; it is used with FSR for indirect addressing.

Note 1: INDF is not a physical register. See Section 3.7, "Indirect Data Addressing; INDF and FSR Registers."

📌 Special Instruction Notes:

🛠️ Best Practice: Clear All PORTs Before Use

In all of my assembly programs, I make it a rule to clear PORTA, PORTB, and PORTC during initialization — even if they are not used.

This avoids potential problems caused by unpredictable startup states or leftover values from resets. Uninitialized ports may float, latch, or output spurious logic levels, which can interfere with proper operation or downstream hardware.

CLRF PORTA
CLRF PORTB
CLRF PORTC
  

Note: This is especially important on older devices like the PIC16F54 or PIC16F57, where all ports are digital and have no analog fallback or default behavior.


8 LEDs displaying binary count driven by PIC16F57 microcontroller.
View Full Size | Visit Hobby Page

PIC16F57 Intro Project: 8-bit Binary Counter

by Lewis Loflin

See PIC16F57 wiring connections.

This project uses the PIC16F57 to generate a binary count (0–255) on PORTC, updating every 500 milliseconds. This is a basic test to confirm that the microcontroller, crystal oscillator, and PORTC outputs are functioning properly.

PIC16F57 Characteristics and Observations

See Zero-Crossing Detectors Circuits and Applications

See Improved AC Zero Crossing Detectors for Arduino

T0CLKI (pin 1) is the TMR0 external clock input. It has a Schmitt trigger input. Must be tied to VSS or VDD, if not in use, to reduce current consumption.

T0CKI (pin 1) is Schmitt Trigger clock input to Timer0. Must be tied to VSS or VDD, if not in use, to reduce current consumption.

Two level hardware stack, operating voltage 2V - 5.5V.

Hardware Setup

Test Code

// 8 LEDs on PORTC
// PIC16F57 C Version: Count 0–255 (binary) on PORTC with 500ms delay
// Assumes external 4 MHz crystal
// 51 bytes program 12 bytes SRAM with MPLAB X and C8.

#include <xc.h>
// Configuration bits
#pragma config WDT = OFF   // Watchdog Timer disabled
#pragma config CP = OFF     // Code protection off
#pragma config OSC = XT    // XT Oscillator (use 4 MHz crystal)

#define _XTAL_FREQ 4000000UL

void INIT57(void)   {
    // TRIS settings
    // TRISA = 0x0F; // Lower 4 bits as input, upper bits output
    // TRISB = 0xFF; // All input
    TRISC = 0x00; // All output 
    
    // Clear all ports
    PORTA = 0x00; 
    PORTB = 0x00;   
    PORTC = 0x00;    
}

void main(void) {
    INIT57();
    unsigned char counter = 0;
    while (1) {
        PORTC = counter;
         __delay_ms(500);
        counter++;
    }
}

This example uses only 8 output pins and very little flash or SRAM. It’s ideal as a first test when evaluating a PIC16F57 for 8-bit digital output applications.



Pin connection outline for PIC16F84A.
Click for schematic | Visit Hobby Page

🔄 Comparing PORTB Pull-Ups and Interrupts: PIC16F84A vs. PIC16F628A

✅ Common Features

Differences

Feature PIC16F84A PIC16F628A
Pull-Up Enable Bit OPTION_REG, bit 7 = RBPU
0 = pull-ups enabled
Same: OPTION_REG, bit 7 = nRBPU (name inverted but function identical)
RB0 External Interrupt Yes, on edge Yes, same functionality
RB4–RB7 Interrupt-on-Change Yes Yes, identical
IO Direction Control TRISB TRISB (same)

🧠 Additional Notes



Electronics and Science

Six Parts:


The following has information on ferrite materials, SCRs, Neon sign colors.


Common register control using C-style bitwise logic:

Operation Purpose Example
|= Set bit n to 1 (make high) PORTB |= (1 << 2);
&= ~(1 << n) Clear bit n to 0 (make low) TRISB &= ~(1 << 3);
^= Toggle bit n (flip state) PORTB ^= (1 << 0);

These are especially useful when working with individual I/O pins without affecting other bits in a register.

#pragma config Overview

#pragma is used to send special instructions to the compiler. In XC8, it's mainly used to set configuration bits (fuses) directly from your C source code.

Directive Meaning
#pragma config FOSC = XT Use external crystal oscillator
#pragma config WDTE = OFF Disable watchdog timer
#pragma config MCLRE = OFF Disable MCLR (RA5 becomes I/O)
#pragma config LVP = OFF Disable low-voltage programming

These lines are essential for ensuring your microcontroller starts up with the correct hardware settings. Without them, you'll rely on manual IDE configuration — and that can lead to mismatched builds.

Bit Manipulation Macros:

#define bitset(var, bitno)   ((var) |=  (1 << (bitno)))   // Set bit
#define bitclr(var, bitno)   ((var) &= ~(1 << (bitno)))   // Clear bit
#define bitflip(var, bitno)  ((var) ^=  (1 << (bitno)))   // Toggle bit
#define bitcheck(var, bitno) (((var) &   (1 << (bitno))) != 0)  // Check bit
  

🕒 Using _XTAL_FREQ and Delay Macros in XC8

To use __delay_ms() and __delay_us() in MPLAB XC8 projects, you must define the system clock frequency using _XTAL_FREQ as an unsigned long.

✅ Example Setup

#include <xc.h>
  
// Configuration bits
#pragma config WDT = OFF   // Watchdog Timer disabled
#pragma config CP = OFF     // Code protection off
#pragma config OSC = XT    // XT Oscillator (use 4 MHz crystal)

#define _XTAL_FREQ 4000000UL  // Required: 4 MHz crystal, note the UL!

void main(void) {
    TRISB = 0x00;      // PORTB output
    PORTB = 0x00;

    while (1) {
        PORTB ^= 0x01;  // Toggle RB0
        __delay_ms(500);  // 500ms delay
    }
}

📌 Key Notes

🚫 Common Pitfalls


🔧 Flash Usage Comparison – Toggling RB1 on PIC16F57

Compiled with XC8 for PIC16F57, these code snippets toggle LED on RB1. Measured flash usage illustrates macro efficiency:

Code Flash Used Notes
PORTBbits.RB1 ^= 1; 67 bytes Standard bit toggle, safe and readable
PORTBbits.RB1 = PORTBbits.RB1 ^ 1; 66 bytes Explicit XOR form; minor gain
LED = LED ^ 1; 66 bytes Macro hides bit expression
bitflip(PORTB, 1); 58 bytes ✅ Most efficient — direct XOR via macro

Macro used:

#define bitflip(var, bitno) ((var) ^= (1 && (bitno)))

Conclusion: For size-critical code, direct macros like bitflip() save valuable flash on baseline PICs such as the 16F57.


🔍 Understanding int8_t and int16_t in Embedded C

The types int8_t and int16_t are part of the <stdint.h> header and define fixed-width signed integers. These are essential in embedded systems where memory and data width must be tightly controlled.

Type Size Range Description
int8_t 8 bits -128 to +127 Signed 8-bit integer (1 byte)
uint8_t 8 bits 0 to 255 Unsigned 8-bit integer
int16_t 16 bits -32,768 to +32,767 Signed 16-bit integer (2 bytes)
uint16_t 16 bits 0 to 65,535 Unsigned 16-bit integer

📘 Why Use These Types?

🌡️ Example: DS18B20 Temperature Reading

The DS18B20 sensor returns a 16-bit signed value that must be interpreted correctly. This is why we use:

int16_t temp;     // Holds raw 16-bit signed temperature from DS18B20
int8_t whole;     // Stores the integer (whole °C) part of the temperature
uint8_t frac;     // Stores the fractional part (in tenths of a degree)

For example, if the sensor returns 0x00A2, that equals 162 / 16 = 10.125°C. The code separates this into:

Using these types ensures accurate, portable temperature display on an LCD or serial output.