Friday, August 21, 2020

A DIY PCB UV Expose Timer Using PIC16F876A Using MPLABX XC8

Requirements For PCB Making Using Dry Film Technique

Creating a printed circuit board (PCB) with dry film method, could produce a high quality PCB. It's because of good copper track coating. Comparing to a simple toner transfer method, it's excellent in output but it costs more dollars and more time consuming.

A PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
A sample a pre-fabricated PCB made by a Gerber viewer 

00:00 / 00:00
The dry film is made from polymer. it is photo sensitive to UV light. In industrial PCB making, dry film is choice of PCB copper tracking protecting. In the market it cost around 10 US Dollars for 30 cm by 5 meters.

Making a PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
My dry film in use. It is 5 meters long. I bought it from E bay for 8 US Dollars.

However using dry film for PCB making requires UV lamp with a precise timer to make the process more accurate. A low lost timer made by some Chinese manufacturer cost around 3 US Dollars.

The source of UV light beside the sun light are a UV light bulb or a popular UV LED available on line.



Making a PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
A low cost Chinese light bulb works at 220V AC with the output power of 40 W. It's suitable for
personal use. I use this device for my electronic workshop at home.




Making a PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
A sample PCB I made using dry film method with solder mask coating in 2008.


Making A DIY UV Time Exposes Unit Using PIC16F876A

Design Requirements

For a normal control device, it require some user inputs, a display output and a driving output. 
User input has no other words than a push button in common. 
A display device, could be a simple seven-segment display because it has a big display value and very simple to program.
An output driving, commonly drive an AC load. In this project, it drive an output relay to switch the AC UV light bulb on and off.

To make an embedded controller project, we must thing about all controller's resource requirement that fit the project. For this simple project, an 8-bit PIC device is a good choice. Most 8-bit PIC has a rich of digital input output, analog inputs and PWM output driving.

I posses a lot of PIC16F876A I bought for my working project to some companies. I have left a dozen of it from the finished projects.

Making a PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
A reference image of this device taken from RS components. Some components
I possess are very old and used that is not suitable to post them here.



The Completed Project

In this design, I use the following stuffs to make a timer work well for my personal use:
  1. A variable resistor used for adjust time
  2. An ENTER button used for inputting the time value
  3. A LOAD button used for reading the saved timing from previous setting
  4. An 8-digit multiplexed displays for display the time
  5. An output relay driver to switch the lamp on and off
  6. A buzzer alert the beginning and the end of the running timer
Making a PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
The picture of the completed project

The display digit are 0.36 inches diagonal. Conventionally, the multiplexing method could work well for only 8 digits.



Making a PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
Schematic Diagram I designed in Proteus 8



Making a PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
A sample PCB design the red line indicates the design rule error, but
it doesn't matter. 


Making a PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
A computer software rendered of this design

I took the photo of my finished work as below.






MPLABX XC8 Source Code

The overall program is written using XC8 from the device vendor.

#include <xc.h>
#include "config.h"
#define _XTAL_FREQ 20000000
#define oneSec 300
#define SW1 PORTAbits.RA1
#define SW2 PORTAbits.RA2
#define BUZ PORTAbits.RA4
#define RLY PORTAbits.RA5
#define H_ADD 0x00
#define M_ADD 0x01
#define S_ADD 0x02
__EEPROM_DATA(0,0,50,0,0,0,0,0);
void variableInit(void);
void systemTicks(void);
void loadSetting(void);
void timeSet(void);
void SSD(unsigned short long secs);
void systemInit(void);
void timerInit(void);
void timerRun(void);
void timeSave(unsigned short long secs);
unsigned short long timeLoad(void);
unsigned char eeRead(unsigned char add);
void eeCommand(void);
void eeWrite(unsigned char add,unsigned char dat);
unsigned short timeRead(void);
bit hrs_on(void);
bit mns_on(void);
unsigned short long secSet;
unsigned short cnt0,cnt1,cnt2,cnt3;
unsigned char swCnt;
unsigned char sysTick;
bit _L,load,_timerRun;
void interrupt T0_ISR(void){
if(INTCONbits.TMR0IF){
sysTick++;
}
INTCONbits.TMR0IF=0;
}
void main(){
__delay_ms(100);
systemInit();
variableInit();
while(1){
systemTicks();
if(_timerRun==1)
timerRun();
else
timerInit();
SSD(secSet);
}
}
void timerInit(void){
if(SW2==0){
if(cnt2>=150){
load^=1;
swCnt=2;
cnt2=0;
}
}
if(load==0) timeSet();
else loadSetting();
}
void timerRun(void){
unsigned short long temp;
static unsigned long buzTimer=0;
temp=secSet;
if(cnt3>=oneSec) {
secSet--;
RLY=1;
buzTimer++;
if(buzTimer<=3) BUZ=0;
else BUZ=1;
if(secSet<=3) BUZ=0;
cnt3=0;
}
if(secSet==0){
RLY=0;
_timerRun=0;
secSet=temp;
BUZ=1;
swCnt=2;
}
}
void SSD(unsigned short long secs){
// Common Cathode
const char SEG[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F} ;
unsigned short long hrs,mns,scs;
if(cnt1>=oneSec){
_L^=1;
cnt1=0;
}
hrs=secs/3600;
mns=(secs%3600)/60;
scs=secs%60;
switch(cnt0){
case 1 :
PORTC=0x00;
PORTB=SEG[hrs/10];
if(hrs_on()==1)
PORTC=0x01;
else PORTC=0;
break;
case 2 :
PORTC=0x00;
PORTB=SEG[hrs%10];
if(hrs_on()==1)
PORTC=0x02;
else PORTC=0;
break;
case 3 :
PORTC=0x00;
if(_L==1){
PORTB=0x40;
if(hrs_on()==1)
PORTC=0x04;
else PORTC=0x00;
}
break;
case 4 :
PORTC=0x00;
PORTB=SEG[mns/10];
if(mns_on()==1)
PORTC=0x08;
else PORTC=0x00;
break;
case 5 :
PORTC=0x00;
PORTB=SEG[mns%10];
if(mns_on()==1)
PORTC=0x10;
else PORTC=0x00;
break;
case 6 :
PORTC=0x00;
if(_L==1){
PORTB=0x40;
if(mns_on()==1)
PORTC=0x20;
else PORTC=0x00;
}
break;
case 7 :
PORTC=0x00;
PORTB=SEG[scs/10];
PORTC=0x40;
break;
case 8 :
PORTC=0x00;
PORTB=SEG[scs%10];
PORTC=0x80;
break;
case 9 :
PORTC=0x00;
cnt0=0;
break;
}
}
void systemInit(){
TRISA=0x0F;
TRISB=0x00;
TRISC=0x00;
// Analog Config
ADCON1bits.PCFG=14; // RA0 Analog
ADCON1bits.ADFM=1; // right justified
ADCON1bits.ADCS2=0; // adc osc
ADCON0bits.ADCS=0x03; // int RC
ADCON0bits.CHS=0x00; // AN0
ADCON0bits.ADON=1; // ADC ON
// Timer Config
OPTION_REGbits.T0CS=0; // SELECT FOSC
OPTION_REGbits.PSA=0; // TMR0 Prescaler
OPTION_REGbits.PS=0x04; // 1:32
// Interrupt Config
INTCONbits.GIE=1;
INTCONbits.TMR0IE=1;
INTCONbits.TMR0IF=0;
}
void variableInit(void){
// Variable Initialize
cnt0=0;
cnt1=0;
cnt2=0;
cnt3=0;
load=0;
_L=0;
_timerRun=0;
swCnt=0;
sysTick=0;
BUZ=1;
RLY=0;
}
unsigned short timeRead(void){
unsigned int RL,RH;
unsigned int adResult;
unsigned short timeVal;
ADCON0bits.GO_nDONE=1;
while(ADCON0bits.GO_nDONE);
RL=ADRESL;
RH=ADRESH;
adResult=(RH*256)+RL;
timeVal=(adResult*59)/1023;
return timeVal;
}
void loadSetting(void){
secSet=timeLoad();
if(SW1==0&&cnt2>100){
_L=0;
_timerRun=1;
cnt2=0;
}
}
void timeSet(void){
unsigned short long timeVal;
static unsigned short long hms[3]={0,0,0};
if(sysTick==1)
timeVal=timeRead();
hms[0]=timeVal;
if(swCnt==3){
if(secSet!=0) {
_timerRun=1;
timeSave(secSet);
}
GIE=1;
TMR0IE=1;
TMR0IF=0;
swCnt=0;
}
if((SW1==0)&&(cnt2>100)){
swCnt++;
if(swCnt=2){
cnt0++;
cnt1++;
cnt2++;
cnt3++;
sysTick=0;
}
}
bit hrs_on(void){
char state;
if(load==0&&_timerRun==0)
if(swCnt>1)
state=1;
else state=0;
return state;
}
bit mns_on(void){
char state;
if(load==0&&_timerRun==0)
if(swCnt>0)
state=1;
else state=0;
return state;
}
The config.h file lists below.

/*
* File: config.h
* Author: BongTha
*
* Created on September 21, 2018, 8:35 PM
*/
#ifndef CONFIG_H
#define CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_H */
// PIC16F876A Configuration Bit Settings
// CONFIG
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit
#pragma config WRT = OFF // Flash Program Memory Write Enable bits
#pragma config CP = OFF // Flash Program Memory Code Protection bit
The completed project archive could be downloaded here.

I shared this project on the PCB fab service website. 

PCB from=From pcbway

The pictures below show a sample pre-production PCB created by the Gerber viewer.

A PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
Components Side



A PCB UV Expose Timer Using PIC16F876A With MPLABX XC8
Copper Soldering Side


No comments:

Post a Comment

Labels

ADC (10) Analog (14) Arduino (12) Atmega16 (19) Audio (2) AVR (20) Charger (1) Cortex-M0 (1) Counter (10) CPLD (25) Digital I/O (22) Display (34) EEPROM (2) Environment Sensor (1) esp8266 (2) Experiment Board (10) I2C (4) Interrupt (7) LCD (1) LDmicro (29) measurement and instrumentation (7) Microchip Studio (3) MikroC (1) One-Shot (3) OpAmp (1) PCB (31) PIC16 Microcontrollers (16) PIC16F877A (2) PIC16F887 MikroC (22) PLC (35) PWM (11) Regulator (1) RTC (2) Sensor (8) Shift Registers (5) SPI (5) Timer (34) UART (2) ultra-sonic sensor (1) USB (1) VHDL (21) xc8 (1) XC95108 (9) XC9536 (15) XC9572 (1) Xilinx (23) Xilinx ISE (22)