// uses 1 variable; unsigned long bres
// gets here every TMR0 int (every 256 ticks)
bres += 256; // add 256 ticks to bresenham total
if(bres >= 1000000) // if reached 1 second!
{
bres -= 1000000; // subtract 1 second, retain error
do_1sec_event(); // update clock, etc
}
// uses 1 variable; unsigned long bres
// gets here every TMR0 int (every 256 ticks)
bres += 256; // add 256 ticks to bresenham total
if(bres >= 5000000) // if reached 1 second!
{
bres -= 5000000; // subtract 1 second, retain error
do_1sec_event(); // update clock, etc
}
// uses 1 variable; unsigned long bres
// gets here every TMR0 int (every 1024 xtal ticks)
bres += 1024; // add 1024 xtal ticks to bresenham total
if(bres >= 12391000) // if reached 1 second!
{
bres -= 12391000; // subtract 1 second, retain error
do_1sec_event(); // update clock, etc
}
// uses 1 variable; unsigned long bres
// gets here every TMR1 16bit int (every 65536 ticks)
bres += 65536; // add 65536 ticks to bresenham total
if(bres >= 1000000) // if reached 1 second!
{
bres -= 1000000; // subtract 1 second, retain error
do_1sec_event(); // update clock, etc
}
// uses 1 variable; unsigned int bres
// gets here every TMR0 int (every 256 ticks)
bres += 16; // add (256/16) ticks to bresenham total
if(bres >= 62500) // if reached (1000000/16) 1 second!
{
bres -= 62500; // subtract 1 second, retain error
do_1sec_event(); // update clock, etc
}
// uses 1 variable; unsigned int bres
// TMR1 runs at 32768Hz, from external osc
// gets here every TMR1 bit3 toggle, is 1/2048th second
bres += 100; // add 100 to bresenham total
if(bres >= 2048) // if reached 1/100th of a second!
{
bres -= 2048; // subtract 1/100th of a second, retain error
do_100th_event(); // update clock, etc
}
// uses 1 variable; unsigned char bres
// gets here every 120Hz, synced to US mains freq
bres += 100; // add 100 to bresenham total
if(bres >= 120) // if reached 1/100th of a second!
{
bres -= 120; // subtract 1/100th of a second, retain error
do_100th_event(); // update clock, etc
}
// uses 1 variable; unsigned long bres
// gets here every TMR0 int (every 256 ticks)
bres += 256000; // add (256*1000) ticks to bresenham total
if(bres >= 1000003360) // if reached 1 second!
{
bres -= 1000003360; // subtract 1 second, retain error
do_1sec_event(); // update clock, etc
}
// PIC code, 4MHz xtal, TMR1 at 1:8 prescale
// uses 2 variables;
// unsigned int bres
// unsigned char pulse
start:
// wait here for 120Hz pulse
while(!check_120Hz());
// now generate 10 "fake" pulses, each is "1200Hz"
// which is 833uS. PIC 1Mhz xtal, TMR1 1:8 prescale,
// we use TMR1L period of 104 = 832uS
// note! TMR1L loop is also another zero-error system
// that we keep subtracting 104 from while retaining
// its internal error.
pulse = 10; // make 10 "fake" pulses
TMR1L = (256-104); // prep first 1/1200th sec delay
while(pulse)
{
// wait here for fake 1200Hz pulse
while(TMR1L.F7);
// now fix TMR1L and do the main bres event
TMR1L -= 104; // subtract 1/1200th of a second
bres += 100;
if(bres >= 1200) // if 100th sec reached!
{
bres -= 1200;
do_100th_event(); // update clock, etc
}
// subract a pulse, see if 10 done yet
pulse--;
}
// gets here after 10 "fake" 1200Hz pulses,
// need to detect a real 120Hz pulse, then
// do it all again!
goto start;
/******************************************************************************
ZeroJitter.c Generates zero-error and zero jitter interrupt period.
Open-source - 21 Nov 2009 - www.RomanBlack.com/one_sec.htm
PIC 12F675, 4MHz xtal.
This is like my zero-error 1 second timing system, that uses a convenient
constant to set ANY period (with 1 timer tick resolution).
However this system has zero jitter!
Can be used to generate 1 second period, or 50Hz freq output etc.
******************************************************************************/
// PERIOD sets the pin toggle freq; toggle PERIOD = (xtal / 4 / freq / 2)
#define PERIOD 10000 // (xtal 4Mhz) TMR0 1MHz, 10000 = 100Hz toggle (50Hz output)
#define PER_COUNTS ((PERIOD / 100) - 1) // don't edit this!
#define PER_REMAINDER (PERIOD - (PER_COUNTS * 100)) // don't edit this!
unsigned int pcount; // used in interrupt to count PER_COUNTS
//-----------------------------------------------------------------------------
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void interrupt()
{
//-----------------------------------------------------
// this is the TMR0 overflow interrupt.
// Note! TMR0 has a 3 tick write latency, writes must be -3
//-----------------------------------------------------
// check if time to toggle the output pin
if(!pcount)
{
asm {
movlw 0x01 ; // mask for pin 0
xorwf GPIO,f ; // toggle PIC pin GPIO.0
}
pcount = (PER_COUNTS+1); // how many delays to make total
TMR0 -= (PER_REMAINDER-3); // first delay will be ==remainder
}
// else make a normal delay
else
{
TMR0 -= (100-3); // make another 100 tick delay
}
pcount--;
//-----------------------------------------------------
// clear the TMR0 overflow flag and exit
INTCON.T0IF = 0;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//=============================================================================
// MAIN
//=============================================================================
void main ()
{
//-----------------------------------------------------
// PIC 12F675 setup ports
ANSEL = 0; // ADC off
CMCON = 0x07; // comparators off
GPIO = 0b00000000; // clear GPIO
TRISIO = 0b00000000; // All outputs
WPU = 0b00000000; // pin pullups; 1 = pullup on (for button)
//-----------------------------------------------------
// timer setup etc
OPTION_REG = 0b00001000; // TMR0 on, 1:1 prescale
pcount = 0;
INTCON = 0b10100000; // GIE on, T0IE on (turn interrupt on)
//-----------------------------------------------------
// main run loop here
while(1)
{
continue; // loop and do nothing, just let the interrupt happen
}
}
//-----------------------------------------------------------------------------
PIC pins;GP0 - outputA, sequence; 01110000 GP1 - outputB, sequence; 00000111
/******************************************************************************
ZJ_Inverter.c xtal-locked push-pull Inverter driver
Open-source - 30 Nov 2009 - www.RomanBlack.com/one_sec.htm
PIC 12F675, xtal (see below).
This was based on my ZEZJ system for generating a frequency.
It will drive push-pull FETs to make a mains inverter;
10MHz xtal; 50Hz output (same code is used for both)
12MHz xtal; 60Hz output
GP0 - this is push-pull outputA (hi = FET on)
GP1 - this is push-pull outputB (hi = FET on)
_BODEN_OFF _MCLRE_OFF _PWRTE_ON _WDT_OFF _HSOSC
******************************************************************************/
// edit PERIOD to give the freq; PERIOD = (xtal / 4 / (freq*8))
// Note! we need 8 period events per mains cycle, so use (freq * 8)
#define PERIOD 6250 // 10Mhz xtal = 50Hz, 12MHz xtal = 60Hz.
#define PER_COUNTS ((PERIOD / 100) - 1) // don't edit this!
#define PER_REMAINDER (PERIOD - (PER_COUNTS * 100)) // don't edit this!
unsigned int pcount; // used in interrupt to count PER_COUNTS
unsigned char outputs; // shadow register used to drive output pins
unsigned char outcount; // used to sequence the outputs
//-----------------------------------------------------------------------------
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void interrupt()
{
//-----------------------------------------------------
// this is the TMR0 overflow interrupt.
// Note! TMR0 has a 3 tick write latency, writes must be -3
//-----------------------------------------------------
// check if period has occured
if(!pcount)
{
// first send the outputs with no latency
asm {
movf outputs,w ; // send this value out to PIC pins
movwf GPIO ;
}
pcount = (PER_COUNTS+1); // how many delays to make total
TMR0 -= (PER_REMAINDER-3); // first delay will be ==remainder
// now handle push-pull output sequencing;
// 01110000 GP0
// 00000111 GP1
outcount++;
if(outcount >= 8) outcount = 0; // 8 steps in sequence
if(outcount == 0) outputs = 0b00000000; // load outputs ready to be used
if(outcount == 1) outputs = 0b00000001;
if(outcount == 4) outputs = 0b00000000;
if(outcount == 5) outputs = 0b00000010;
}
// else make a normal delay
else
{
TMR0 -= (100-3); // make another 100 tick delay
}
pcount--;
//-----------------------------------------------------
// clear the TMR0 overflow flag and exit
INTCON.T0IF = 0;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//=============================================================================
// MAIN
//=============================================================================
void main ()
{
//-----------------------------------------------------
// PIC 12F675 setup ports
ANSEL = 0; // ADC off
CMCON = 0x07; // comparators off
GPIO = 0b00000000; // clear GPIO
TRISIO = 0b00000000; // All outputs
//-----------------------------------------------------
// timer setup etc
OPTION_REG = 0b00001000; // TMR0 on, 1:1 prescale
pcount = 0;
outputs = 0;
outcount = 0;
INTCON = 0b10100000; // GIE on, T0IE on (turn interrupt on)
//-----------------------------------------------------
// main run loop here
while(1)
{
continue; // loop and do nothing, just let the interrupt happen
}
}
//-----------------------------------------------------------------------------
/******************************************************************************
ZE_MainsConverter.c mains freq converter push-pull inverter driver
Open-source - 30 Nov 2009 - www.RomanBlack.com/one_sec.htm
PIC 12F675, 4MHx xtal
This was based on my zero-error reduced-jitter system that
generates accurate 50Hz mains locked to 60Hz mains input,
or vice versa. The output frequency will be exact, as it is
locked to the input frequency. Generally this code would be
used to make a small inverter to drive an antique 60Hz mains
clock from 50Hz mains etc. The mains input can be as simple
as a diode and a voltage divider pot from 12v AC to PIC pin GP2,
so it gives half-cycle mains freq input of 0v-5v at the PIC pin.
GP2 - mains freq input (see above)
GP0 - this is push-pull outputA (hi = FET on)
GP1 - this is push-pull outputB (hi = FET on)
_BODEN_OFF _MCLRE_OFF _PWRTE_ON _WDT_OFF _HSOSC
******************************************************************************/
#define PERIOD 1667 // note! PERIOD is 1667 for either conversion!
#define PERHI (PERIOD / 256) // don't edit this!
#define PERLO (PERIOD - (PERHI*256)) // don't edit this!
// now select one of these 2 options;
#define OUTPULSES 12 // 50Hz in, 60Hz out
//#define OUTPULSES 10 // 60Hz in, 50Hz out
unsigned char pulse; // to generate output pulses
unsigned char outputs; // shadow register used to drive output pins
unsigned char outcount; // used to sequence the outputs
//-----------------------------------------------------------------------------
//=============================================================================
// MAKE OUTPUT this generates the output frequency
//=============================================================================
void make_output(void)
{
//-----------------------------------------------------
// first send the outputs to PIC pins (no latency)
GPIO = outputs;
// then sequence the PIC pins
#if OUTPULSES == 12 // if 60Hz output
{
// 0011100000 GP0 (sequence of 10 pulses = one 60Hz cycle)
// 0000000111 GP1
outcount++;
if(outcount >= 10) outcount = 0; // 10 steps in sequence
if(outcount == 0) outputs = 0b00000000; // load outputs ready to be used
if(outcount == 2) outputs = 0b00000001;
if(outcount == 5) outputs = 0b00000000;
if(outcount == 7) outputs = 0b00000010;
}
#else // else is 50Hz output
{
// 001111000000 GP0 (sequence of 12 pulses = one 50Hz cycle)
// 000000001111 GP1
outcount++;
if(outcount >= 12) outcount = 0; // 12 steps in sequence
if(outcount == 0) outputs = 0b00000000; // load outputs ready to be used
if(outcount == 2) outputs = 0b00000001;
if(outcount == 6) outputs = 0b00000000;
if(outcount == 8) outputs = 0b00000010;
}
#endif
}
//-----------------------------------------------------------------------------
//=============================================================================
// MAIN
//=============================================================================
void main ()
{
//-----------------------------------------------------
// PIC 12F675 setup ports
ANSEL = 0; // ADC off
CMCON = 0x07; // comparators off
GPIO = 0b00000000; // clear GPIO
TRISIO = 0b00000100; // GP2 is mains input, rest outputs
T1CON = 0b00000001; // TMR1 on, 1:1 prescale (1MHz)
pulse = 0; // and setup vars
outputs = 0;
outcount = 0;
//-----------------------------------------------------
// main run loop here
while(1)
{
while(!GPIO.F2); // wait for mains / edge to occur
pulse = OUTPULSES; // set number of outputpulses to make
TMR1L = 7; // rig TMR1 like a pulse just happened
TMR1H = 0;
goto do_pulse; // make first pulse straight away!
// now repeat this section until all pulses are made
while(pulse)
{
while(!PIR1.TMR1IF); // wait for TMR1 to roll (1 period)
do_pulse:
TMR1L -= PERLO; // fix TMR1, retain error for next period
TMR1H -= (PERHI + 1);
PIR1.TMR1IF = 0;
make_output(); // make the output pulse
pulse--;
}
}
}
//-----------------------------------------------------------------------------
/********************************************************************
* *
* Project: Sine 50Hz to 60Hz converter *
* Source: HybridSine_50-60.c *
* Author: Mike McLaren, K8LH (main code and sine routines) *
* Author2:RomanBlack (24:20 routine for 50Hz to 60Hz convert) *
* Date: 07-Dec-09 *
* Revised: 07-Dec-09 *
* *
* 12F683 60 Hz PWM sinewave output to drive small inverter. *
* PIC is freq locked to 50Hz mains frequency input, and converts *
* 50Hz input to a 60Hz sine output. (not yet hardware tested) *
* NOTE! TO drive push-pull outputs this requires a hardware *
* inverter to make the 2nd output, a transistor will do. *
* *
* IDE: MPLAB 8.14 (tabs = 4) *
* Lang: SourceBoost BoostC v6.95, Lite/Free version *
* *
* GP2 - CCP1 - PWM sinewave output *
* GP3 - 50Hz freq in (must be filtered 0v-5v) *
* *
********************************************************************/
#include <system.h>
#pragma DATA _CONFIG, _FCMEN_OFF&_IESO_OFF&_MCLRE_OFF&_WDT_OFF&_HS_OSC
#define PERIOD 2083 // for 50Hz input and 10MHz xtal (50Hz / 24)
#define PERHI (PERIOD / 256) // don't edit this!
#define PERLO (PERIOD - (PERHI*256)) // don't edit this!
// 60Hz output; sine table needs 20 entries, 0-99 range as PWM is 100 cycles
const rom char sine[] = { 49,64,78,89,96,99,96,89,78,64,
49,34,20,9,2,0,2,9,20,34};
unsigned char n = 0; // sine table index, 0..19
unsigned char pulse; // for pulse sequencing
void interrupt() //
{
pir1.TMR2IF = 0; // clear TMR2 interrupt flag
// nothing else needed in int now!
}
void main() //
{ //
cmcon0 = 0x07; // comparator not used!
ansel = 0; // a2d module off, digital I/O
trisio = 0b00001010; // GP3 is freq in, GP1 is Cin
gpio = 0; // set all output latches to '0'
pir1 = 0; // clear peripheral interrupt flags
pie1.TMR2IE = 1; // set Timer 2 interrupt enable bit
tmr2 = 0; // clear Timer 2 register
ccp1con = 0b00001100; // '00------' unimplemented bits
// '--00----' DC1B1:DC1B0 duty cycle bits
// '----1100' active hi PWM mode
t1con = 0b00000001; // TMR1 on, at 1:1 prescale
t2con = 0b00000101; // '0-------' unimplemented bit
// '-0000---' TOUTPS<3:0>, postscale 1
// '-----1--' TMR2ON, turn Timer 2 on
// '------00' T2CKPS<1:0>, prescale 1:1
pr2 = 100-1; // 100 x prescale 1:1 = 100 instruct cycles
intcon = 0b11000000; // '1-------' GIE, enable global ints
// '-1------' PEIE, enable peripheral ints
// '--0-----' T0IE, TMR0 ints disabled
// '---0----' INTE, off
// '----0---' GPIE, IOC disabled
// '-----000' T0IF/INTF/GPIF flags
while(1) // main program loop
{
// loop here and do the freq conversion; 24:20 (50Hz in, 60Hz out)
while(!GPIO.F3); // wait for 50Hz mains / edge to occur
pulse = 24; // set number of pulses per 50Hz cycle
TMR1L = 0; // rig TMR1 like a pulse just happened
TMR1H = 0;
goto do_pulse; // make first pulse straight away!
// now repeat this section until all pulses are made
while(pulse)
{
while(!PIR1.TMR1IF); // wait for TMR1 to roll (1 period)
do_pulse:
TMR1L -= PERLO; // fix TMR1, retain error for next period
TMR1H -= (PERHI + 1);
PIR1.TMR1IF = 0;
// make the output pulse
ccpr1l = sine[n]; // setup next sine duty cycle
n++; // 20 sine steps = 60Hz cycle
if(n >= 20) n = 0;
pulse--; // one more input pulse processed
}
}
}
void interrupt()
{
// TMR1 interrupt. This is a very simple zero-error TMR1 correction.
// It generates an interrupt every 2500 PIC instructions.
// (this code was hardware tested to exact cycle accuracy)
#define PERIOD 2500 // 10Mhz xtal = 50Hz, 12MHz xtal 60Hz. (don't edit)
#define PERHI (PERIOD / 256) // don't edit this!
#define PERLO (PERIOD - (PERHI*256)) // don't edit this!
TMR1L -= (PERLO - 2); // fix TMR1, retain error for next period
TMR1H -= (PERHI + 1);
step++;
PIR1.TMR1IF = 0;
}
//=============================================================================
void interrupt()
{
//-----------------------------------------------------
// This is a TMR1 overflow interrupt for 23 of 24 cases.
// on 1 of 24 cases it acts as a GP2 interrupt on \ edge.
// This is used to generate exactly 24 virtual pulses,
// synchronised to the 50Hz mains input (GP2 \ edge).
//-----------------------------------------------------
if(!vpulse) // if it is last vpulse0
{
// just leave TMR1 free running, this allows easy first sync!
vpulse = 23;
}
else // else is vpulse1-23
{
TMR1L = (256 - (PERLO - 3 - 16)); // load TMR1 for 1 vpulse period
TMR1H = (256 - (PERHI + 1));
vpulse--; // sequence the next virtual pulse
INTCON.INTF = 0; // clear GP2 \ edge int flag
}
step++; // sequence the next 60Hz PWM step
PIR1.TMR1IF = 0;
}
//=============================================================================