UV timer create a accurate time for the PCB expose box. Without buying a ready to use timer, we can make it by a simple programming on an 8-bit embedded controller.
The picture of the completed project |
PIC microcontroller-based projects with schematics and printed circuits
UV timer create a accurate time for the PCB expose box. Without buying a ready to use timer, we can make it by a simple programming on an 8-bit embedded controller.
The picture of the completed project |
A series of ds18S20 and ds18B20 of digital temperature sensor uses 1-wire bi-directional protocol to request and receive temperature data. DS18S20 has 9-bit resolution while ds18B20 has 12-bit resolution.
In this project I create a system that follow these operations:
The design is controlled by a PIC16F628A/PIC16F84A CPU. The inputs are: ds18X20 and input tactile switches. The outputs are a SPDT relay and a four-digit MUX SSD that I posses. I use an extra 4511 BCD to SSD decoder that I left. The system is powered by a +12V maximum AC/DC input to a regulated +5V/100mA 78L05 voltage stabilizer.
The firmware is written using MikroC pro for 8-bit PIC. The schematic capture and simulation is made using Proteus VSM 8.
I made my own PCB with in-house components. For more high quality PCB, choosing a PCB supply service could be a good option.
Project galleries:
Project archive:
https://github.com/BongPeav/MikroC-8-bit-PIC/raw/master/ds18S20_SSD_CON.rar
MikroC source code for this project:
#define INC PORTA.RA0
#define DEC PORTA.RA1
#define RLY PORTA.RA3
unsigned char T0Cnt,setTemp,setRead;
// Set TEMP_RESOLUTION to the corresponding resolution of used DS18x20 sensor:
// 18S20: 9 (default setting; can be 9,10,11,or 12)
// 18B20: 12
const unsigned short TEMP_RESOLUTION = 12;
unsigned Cnt1,Cnt2,Cnt3,Cnt4;
bit minus,ssdOn;
void interrupt(void){
asm{
CLRWDT
}
if(INTCON.T0IF==1){
T0Cnt++;
INTCON.T0IF=0;
}
}
void ioInit(void);
void ssdOut(unsigned int temp);
void tempRead(void);
unsigned int gettemp();
void tempSet(void);
void dataRead(void);
void main() {
unsigned tempData,oldData;
ioInit();
dataRead();
tempData=getTemp();
tempData=0;
delay_ms(20);
oldData=0;
while(1){
if(ssdOn==1){
if(Cnt1>=75){
tempData=getTemp();
tempData=getTemp();
Cnt1=0;
}
ssdOut(tempData);
}
else
ssdOut(setTemp);
tempSet();
if(Cnt4>=50){
if(tempData>=setTemp) {
RLY=1;
Cnt4=0;
}
else {
RLY=0;
Cnt4=0;
}
}
}
}
void ssdOut(unsigned int temp){
char ssd[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
char d1,d2,d3;
d1=temp/100;
d2=(temp%100)/10;
d3=temp%10;
switch(T0Cnt){
case 2 :
PORTB=0x00;
PORTB=d1&0x0F;
PORTB|=0x20;
break;
case 3 :
PORTB=0x00;
PORTB=d2&0x0F;
PORTB|=0x40;
break;
case 4 :
PORTB=0x00;
PORTB=d3&0x0F;
PORTB|=0x80;
break;
case 5 :
Cnt1++;
Cnt2++;
CNt3++;
Cnt4++;
T0Cnt=0;
break;
}
}
void ioInit(void){
PCON.OSCF=1;
TRISB=0x00;
TRISA=0x03;
CMCON=0x07;
PORTA=0x00;
OPTION_REG=0x03; // 1:16
INTCON.GIE=1;
INTCON.T0IE=1;
INTCON.T0IF=0;
PORTB=0x00;
T0Cnt=0;
Cnt1=0;
Cnt2=0;
Cnt3=0;
Cnt4=0;
minus=0;
ssdOn=1;
}
unsigned int gettemp(){
unsigned int temp;
const unsigned short RES_SHIFT = TEMP_RESOLUTION - 8;
Ow_Reset(&PORTA, 4); // Onewire reset signal
Ow_Write(&PORTA, 4, 0xCC); // Issue command SKIP_ROM
Ow_Write(&PORTA, 4, 0x44); // Issue command CONVERT_T
Delay_us(120);
Ow_Reset(&PORTA, 4);
Ow_Write(&PORTA, 4, 0xCC); // Issue command SKIP_ROM
Ow_Write(&PORTA, 4, 0xBE); // Issue command READ_SCRATCHPAD
temp = Ow_Read(&PORTA, 4);
temp = (Ow_Read(&PORTA, 4) << 8) + temp;
if(temp&0x8000){
minus=1;
temp=~temp;
}
else
minus=0;
temp = temp >> RES_SHIFT ;
return temp;
}
void tempSet(void){
if(DEC==0&&Cnt2>=10){
ssdOn=0;
if(setTemp<120) setTemp++;
EEPROM_Write(0x00,setTemp);
Cnt3=0;
Cnt2=0;
}
if(INC==0&&Cnt2>=10){
ssdOn=0;
if(setTemp<120) setTemp--;
EEPROM_Write(0x00,setTemp);
Cnt3=0;
Cnt2=0;
}
setRead=setTemp;
if(Cnt3>=150) {
ssdOn=1;
Cnt3=0;
}
}
void dataRead(void){
setTemp=EEPROM_Read(0x00);
}
For any custom design relates to this project, you can contact the author.
I made a seven segments clock display with ds1307 RTC. It feature an 8-digit hh-mm-ss with adjustment and alarm setting. Due to a design and PCB fabrication difficulties, I decided to divide two distinct board, one’s for the main controller board and another one is a display board that stack on the main controller board. The schematic below is the main controller board powered by PIC16F876A.
A 7-segments display board stack above the main board.
Making the PCB for this project by hands could spend a lot times etching, drilling and soldering. The user could select a PCB service from a PCB supplier.
PIC16F876A is an 8-bit PIC microcontroller with 28-pin package. its package is also come with a DIP version suitable for hobbyist projects and experiment.
This device is fairly fit to this project referring to the numbers of I/O pins requirement, RAM and ROM space.
An I2C peripheral is also require because it needs to read/write timing data between the DS1307 real time clock chip.
The CPU uses its timer0 timer ticks to multiplex an 8-digits 7-segments display. The display made from eight individual 0.8 inch hi-read common cathode display.
I put some buttons to adjust the HH:MM:SS and also the alarm setting. An output active buzzer beeps any time the alarm time is reached. Buttons and buzzer active time is implemented using the timer0 timer ticks method.
MPLABX XC8 is a compiler of choice for this project. Using this free compiler, the coder can access all the registers definition of the SFR just like one’s the the assembly language programming.
Source code and completed PCB project could be get from GitHub.
https://github.com/BongPeav/xc8/raw/master/SSD_CLOCK.X.rar
MPLABX XC8 source code lists below:
#include <xc.h>
#include "config.h"
#include "defination.h"
#include "i2c.h"
unsigned char T0Cnt;
unsigned char T1Cnt;
unsigned char ssdCnt;
unsigned char _time[3];
unsigned char _timeAlarm[3];
unsigned char modeSel;
unsigned char buzTimer;
unsigned char _PORTB;
unsigned int ssdBlink;
bit blink;
bit swOn;
void T0Init(void);
void timersTick(void);
void T1Init(void);
void ioInit(void);
void varInit(void);
void clockSetup(void);
void clockAdj(void);
void alarmSet(void);
void alarmCheck(void);
void SSDout(unsigned char hrs,unsigned char mns,unsigned char scs);
unsigned char getDate(unsigned char add);
unsigned char bcd2dec(unsigned char bcd);
unsigned char dec2bcd(unsigned char dec);
void rtcInit(void);
unsigned char eeRead(unsigned char add);
void eeCommand(void);
void eeWrite(unsigned char add,unsigned char dat);
void alarmLoad(void);
__EEPROM_DATA(12,30,10,0,0,0,0,0);
void interrupt ISR(void){
if(TMR0IF&&TMR0IE){
T0Cnt++;
TMR0=-78;
TMR0IF=0;
}
if(TMR1IF&&TMR1IE){
T1Cnt++;
TMR1IF=0;
}
}
void main(){
ioInit();
T0Init();
i2cMasterInit(100000);
varInit();
alarmLoad();
rtcInit();
T1Init();
while(1){
timersTick();
SSDout(_time[2],_time[1],_time[0]);
if(modeSel==0) clockAdj();
clockSetup();
alarmCheck();
}
}
void T0Init(void){
T0CS=0;
PSA=0;
OPTION_REGbits.PS=0x05; // 1:64
GIE=1;
TMR0IE=1;
TMR0IF=0;
}
void timersTick(void){
if(T0Cnt>=2){
ssdCnt++;
ssdBlink++;
T0Cnt=0;
}
if(ssdBlink>=blinkRate){
blink^=1;
if(modeSel==0)
for(int i=0;i<3;i++){
_time[i]=getDate(i);
_time[i]=bcd2dec(_time[i]);
}
buzTimer++;
ssdBlink=0;
}
if(T1Cnt>=7){
swOn^=1;
T1Cnt=0;
}
}
void ioInit(void){
TRISA=0xFC;
TRISB=0x00;
TRISC=0x00;
PORTB=0;
ADCON1bits.PCFG=0x06; // all digital
CMCONbits.CM=0x07; // comparator off
}
void varInit(void){
T0Cnt=0;
ssdCnt=0;
ssdBlink=0;
modeSel=0;
T1Cnt=0;
buzTimer=0;
}
void SSDout(unsigned char hrs,unsigned char mns,unsigned char scs){
// COMMON CATHODE
const unsigned char SEG[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char hrs10,hrs1,mns10,mns1,scs10,scs1;
/*
hrs10=hrs>>4;
hrs1=hrs&0x0F;
mns10=mns>>4;
mns1=mns&0x0F;
scs10=scs>>4;
scs1=scs&0x0F;
*/
hrs10=hrs/10;
hrs1=hrs%10;
mns10=mns/10;
mns1=mns%10;
scs10=scs/10;
scs1=scs%10;
switch(ssdCnt){
case 1 :
PORTA=0x00;
PORTC=0x00;
PORTB=SEG[hrs10]|_PORTB;
D1=1;
break;
case 2 :
PORTA=0x00;
PORTC=0x00;
PORTB=SEG[hrs1]|_PORTB;
D2=1;
break;
case 3 :
PORTA=0x00;
PORTC=0x00;
if(blink){
PORTB=64|_PORTB;
D3=1;
}
break;
case 4 :
PORTA=0x00;
PORTC=0x00;
PORTB=SEG[mns10]|_PORTB;
D4=1;
break;
case 5 :
PORTA=0x00;
PORTC=0x00;
PORTB=SEG[mns1]|_PORTB;
D5=1;
break;
case 6 :
PORTA=0x00;
PORTC=0x00;
if(blink){
PORTB=64|_PORTB;
D6=1;
}
break;
case 7 :
PORTA=0x00;
PORTC=0x00;
PORTB=SEG[scs10]|_PORTB;
D7=1;
break;
case 8 :
PORTA=0x00;
PORTC=0x00;
PORTB=SEG[scs1]|_PORTB;
D8=1;
break;
case 9 :
PORTA=0x00;
PORTC=0x00;
ssdCnt=0;
break;
}
}
unsigned char getDate(unsigned char add){
unsigned char _time;
i2cMasterStart();
i2cMasterWrite(ds1307_W);
i2cMasterWrite(add);
i2cMasterRepeatedStart();
i2cMasterWrite(ds1307_R);
_time=i2cMasterRead(0);
i2cMasterStop();
return _time;
}
unsigned char bcd2dec(unsigned char bcd){
unsigned char dec;
dec=10*(bcd>>4)+(bcd&0x0F);
return dec;
}
unsigned char dec2bcd(unsigned char dec){
unsigned char bcd,bcd10,bcd1;
bcd10=dec/10;
bcd1=dec%10;
bcd=(bcd10<<4)+bcd1;
return bcd;
}
void clockSetup(void){
if(mSel==0&&swOn==1){
modeSel++;
swOn=0;
}
switch(modeSel){
case 1 :
alarmSet();
break;
case 2 :
modeSel=0;
break;
}
}
void rtcInit(void){
i2cMasterStart();
i2cMasterWrite(ds1307_W);
i2cMasterWrite(0x07); // Select Control Register
i2cMasterWrite(0x10); // 1Hz
i2cMasterStop();
}
void clockAdj(void){
unsigned char temp;
if(sAdj==0&&swOn==1){
_time[0]++;
if(_time[0]==60) _time[0]=0;
temp=dec2bcd(_time[0]);
i2cMasterStart();
i2cMasterWrite(ds1307_W);
i2cMasterWrite(0x00);
i2cMasterWrite(temp);
i2cMasterStop();
swOn=0;
}
if(mAdj==0&&swOn==1){
_time[1]++;
if(_time[1]==60) _time[1]=0;
temp=dec2bcd(_time[1]);
i2cMasterStart();
i2cMasterWrite(ds1307_W);
i2cMasterWrite(0x01);
i2cMasterWrite(temp);
i2cMasterStop();
swOn=0;
}
if(hAdj==0&&swOn==1){
_time[2]++;
if(_time[2]==24) _time[2]=0;
temp=dec2bcd(_time[2]);
i2cMasterStart();
i2cMasterWrite(ds1307_W);
i2cMasterWrite(0x02);
i2cMasterWrite(temp);
i2cMasterStop();
swOn=0;
}
}
void eeWrite(unsigned char add,unsigned char dat){
// write address and data to eeprom
while(EECON1bits.WR);
EEADR=add;
EEDATA=dat;
EECON1bits.EEPGD=0; // point to data memory
EECON1bits.WREN=1; // enable write
}
unsigned char eeRead(unsigned char add){
unsigned char edata;
EEADR=add;
EECON1bits.EEPGD=0; // select data space
EECON1bits.RD=1; // read
edata=EEDATA;
return edata;
}
void eeCommand(void){
// Special Command
GIE=0;
EECON2=0x55;
EECON2=0xAA;
EECON1bits.WR=1;
GIE=1;
TMR0IE=1;
TMR0IF=0;
EECON1bits.WREN=0;
}
void alarmLoad(void){
for(int i=0;i<3;i++){
_timeAlarm[i]=eeRead(i);
}
}
void alarmSet(void){
_PORTB=0x00;
PORTB|=_PORTB;
for(int i=0;i<3;i++){
_time[i]=_timeAlarm[i];
}
if(sAdj==0&&swOn==1){
_timeAlarm[0]++;
if(_timeAlarm[0]==60) _timeAlarm[0]=0;
eeWrite(sAlarm,_timeAlarm[0]);
eeCommand();
swOn=0;
}
if(mAdj==0&&swOn==1){
_timeAlarm[1]++;
if(_timeAlarm[1]==60) _timeAlarm[1]=0;
eeWrite(mAlarm,_timeAlarm[1]);
eeCommand();
swOn=0;
}
if(hAdj==0&&swOn==1){
_timeAlarm[2]++;
if(_timeAlarm[2]==24) _timeAlarm[2]=0;
eeWrite(hAlarm,_timeAlarm[2]);
eeCommand();
swOn=0;
}
alarmLoad();
}
void T1Init(void){
T1CONbits.T1CKPS=0x02; // 1:4
T1CONbits.T1OSCEN=0;
T1CONbits.TMR1CS=0; //FOSC/4
T1CONbits.TMR1ON=1;
TMR1H=0;
TMR1L=0;
INTCONbits.PEIE=1;
PIE1bits.TMR1IE=1;
PIR1bits.TMR1IF=0;
}
void alarmCheck(void){
unsigned short long alarmSet;
unsigned short long timeRun;
alarmSet=(3600*_timeAlarm[2])+(60*_timeAlarm[1])+_timeAlarm[0];
timeRun=(3600*_time[2])+(60*_time[1])+_time[0];
if((timeRun<=alarmSet+10)&&(timeRun>=alarmSet)){
_PORTB=128;
}
else
_PORTB=0;
}
Bill of Material:
Category References Value Stock Code Unit Cost Capacitors C1 10p Maplin WX44X Capacitors C2 10p Maplin WX44X Capacitors C3 1000U Digikey P5142-ND Capacitors C4 100n Maplin BX03D Resistors R1 220R M220R Resistors R2 220R M220R Resistors R3 220R M220R Resistors R4 220R M220R Resistors R5 220R M220R Resistors R6 220R M220R Resistors R7 220R M220R Resistors R8 220R M220R Resistors R9 10k M10K Resistors R10 10k M10K Resistors R11 1k M1k Resistors R12 10k M10K Resistors R13 10k M10K Resistors R14 10k M10K Resistors R15 10k M10K Resistors R16 10k M10K Resistors R17 470R M220R Integrated Circuits U1 CONN-SIL8 Integrated Circuits U2 DD7SEG08 Integrated Circuits U3 7404 Integrated Circuits U4 7404 Integrated Circuits U5 ULN2804 Transistors Q1 2N3904 Diodes D1 CONN-SIL7 Diodes D2 LED Miscellaneous BAT1 3V Miscellaneous BUZ1 BUZZER03 Miscellaneous BUZ2 BUZZER03 Miscellaneous DP1 DD7SEG08 Miscellaneous DP2 DD7SEG08 Miscellaneous DP3 DD7SEG08 Miscellaneous DP4 DD7SEG08 Miscellaneous J1 DD7SEG08 Miscellaneous J2 CONN-SIL8 Miscellaneous J3 CONN-SIL2 Miscellaneous J4 ICSP Miscellaneous J5 CONN-SIL7 Miscellaneous J6 CONN-SIL8 Miscellaneous SW1 Miscellaneous SW2 Miscellaneous SW3 Miscellaneous SW4 Miscellaneous SW5 Miscellaneous X1 DD7SEG08 Miscellaneous X2 CRYSTAL
Click here to download full PCB project file.
Digital Volt Meter (DVM) could be built from scratch using any ADC IC such as ICL7107 or ICL7135. However those IC have a large pin counts requiring us more time to wire.
Using a microcontroller we can reduce the number of pin count for the display. A common way is using multiplexed display.
In this project I use PIC16F818 with a 10-bit built-in ADC to read analog voltage range from 0 to +40V DC.
ADC work only with +5V. So I use the voltage divider rule to reduce the scale of input voltage. As show in the schematic below, the resistors R3 and R2 are use for voltage divider. From voltage divider rule I get the scale factor of 0.13.
Download this source from GitHub:
https://github.com/BongPeav/CCS-PICC/raw/master/16F818_timer0_ADC_DVM_0_35V.rar
CCS PICC source code for this example:
// PIC16F818 DVM
#include <16f818.h>
#DEVICE ADC=10
#fuses INTRC_IO,NOWDT,NOLVP,NOPUT,NOBROWNOUT
#use delay(clock=4M)
#define LED_DELAY delay_ms(5)
unsigned int16 reader;
unsigned int8 counter;
unsigned int8 number[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
float voltage;
#int_timer0
void timer0isr()
{
counter++;
}
///////////////////////////////////
void displaydata(float raw){
unsigned int16 dig4,dig3,dig2,dig1,data,temp1,temp2;
float mydat;
mydat=raw*100;
data=mydat;
dig4=data/1000;
temp1=data%1000;
dig3=temp1/100;
temp2=data%100;
dig2=temp2/10;
dig1=data%10;
//////////////////////////////////////////
output_B(number[dig4]);
output_high(PIN_A1);
LED_Delay;
output_low(PIN_A1);
///////////////////////
output_B(number[dig3]|0x80);
output_high(PIN_A2);
LED_DELAY;
output_low(PIN_A2);
////////////////////////////////
output_B(number[dig2]);
output_high(PIN_A3);
LED_DELAY;
output_low(PIN_A3);
///////////////////////////
output_B(number[dig1]);
output_high(PIN_A4);
LED_DELAY;
output_low(PIN_A4);
}
void main()
{
SET_TRIS_B(0x00);
SET_TRIS_A(0x01);
SETUP_ADC(ADC_CLOCK_DIV_2);
SETUP_ADC_PORTS(ALL_ANALOG);
SET_ADC_CHANNEL(0);
DELAY_US(30);
SETUP_TIMER_0(RTCC_DIV_256);
ENABLE_INTERRUPTS(GLOBAL);
ENABLE_INTERRUPTS(INT_TIMER0);
CLEAR_INTERRUPT(INT_TIMER0);
while(TRUE)
{
//TODO: User Code
// if(counter==5){
// counter=0;
reader=read_adc();
while(!adc_done());
voltage=(reader*(5.0/1024))/0.130;
// }
displaydata(voltage);
}
}
Any suggestion relates to this post could be sent to the author using the contact form below.
Frequency meter can be made using a simple method, but this method could measure a low frequency range. It’s suitable for any application at the system that process with a low frequency input.
I use a C-loop to measure the input frequency. The loop repeats at 55800 times making a one-second period. during this period if the input state is High that will increase the counter.
code archive:
https://github.com/BongPeav/MikroC-8-bit-PIC/raw/master/PIC16F876A_LOWSPEED_FREQ.rar
MikroC source code for PIC:
// LCD module connections
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;
// End LCD module connections
#define FREQ_IN PORTC.RC0 // USE RC0 AS FREQUENCY SOURCE
void main() {
unsigned long freq=0;
unsigned long i;
unsigned char freq_data[12]; // used to store string
TRISC=0xFF; // PORTC AS INPUT
PORTC=0x00; // CLEAR PORTC
Lcd_Init();
Lcd_Cmd(_LCD_CURSOR_OFF); // TURN OFF CURSOR
Lcd_Out(1,4,"PIC16F876A");
Lcd_Out(2,2,"FREQUENCY METER");
delay_ms(1000);
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1,"FREQUENCY (Hz)");
while(1){
for(i=0;i<55800;i++){
if(FREQ_IN==1){ while(FREQ_IN==1); freq++;}
}
IntToStr(freq,freq_data);
Lcd_Out(2,1,freq_data);
freq=0;
}
}
Any suggestion relates to this post could be sent to the author using the contact form below.