/*! \file pulse.c \brief Pulse/frequency generation function library. */
//*****************************************************************************
//
// File Name : 'pulse.c'
// Title : Pulse/frequency generation function library
// Author : Pascal Stang - Copyright (C) 2000-2002
// Created : 2002-08-19
// Revised : 2003-05-29
// Version : 0.7
// Target MCU : Atmel AVR Series
// Editor Tabs : 4
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "global.h"
#include "timer.h"
#include "pulse.h"
// Global variables
// pulse generation registers
volatile static unsigned char PulseT1AMode;
volatile static unsigned short PulseT1ACount;
volatile static unsigned short PulseT1APeriodTics;
volatile static unsigned char PulseT1BMode;
volatile static unsigned short PulseT1BCount;
volatile static unsigned short PulseT1BPeriodTics;
// pulse mode bit definitions
// PULSE_MODE_COUNTED
// if true, the requested number of pulses are output, then output is turned off
// if false, pulses are output continuously
#define PULSE_MODE_CONTINUOUS 0x00
#define PULSE_MODE_COUNTED 0x01
// functions
void pulseInit(void)
{
// initialize timer1 for pulse operation
pulseT1Init();
}
void pulseT1Init(void)
{
// try to make sure that timer1 is in "normal" mode
// most importantly, turn off PWM mode
timer1PWMOff();
// set some reasonable initial values
// in case the user forgets to
PulseT1AMode = 0;
PulseT1BMode = 0;
PulseT1ACount = 0;
PulseT1BCount = 0;
PulseT1APeriodTics = 0x8000;
PulseT1BPeriodTics = 0x8000;
// attach the pulse service routines to
// the timer 1 output compare A and B interrupts
timerAttach(TIMER1OUTCOMPAREA_INT,pulseT1AService);
timerAttach(TIMER1OUTCOMPAREB_INT,pulseT1BService);
}
void pulseT1Off(void)
{
// turns pulse outputs off immediately
// set pulse counters to zero (finished)
PulseT1ACount = 0;
PulseT1BCount = 0;
// disconnect OutputCompare action from OC1A pin
cbi(TCCR1A,COM1A1);
cbi(TCCR1A,COM1A0);
// disconnect OutputCompare action from OC1B pin
cbi(TCCR1A,COM1B1);
cbi(TCCR1A,COM1B0);
// detach the pulse service routines
timerDetach(TIMER1OUTCOMPAREA_INT);
timerDetach(TIMER1OUTCOMPAREB_INT);
}
void pulseT1ASetFreq(u16 freqHz)
{
// set the frequency of the pulse output
// we need to find the requested period/2 (in timer tics)
// from the frequency (in hertz)
// calculate how many tics in period/2
// this is the (timer tic rate)/(2*requested freq)
PulseT1APeriodTics = ((u32)F_CPU/((u32)timer1GetPrescaler()*2*freqHz));
}
void pulseT1BSetFreq(u16 freqHz)
{
// set the frequency of the pulse output
// we need to find the requested period/2 (in timer tics)
// from the frequency (in hertz)
// calculate how many tics in period/2
// this is the (timer tic rate)/(2*requested freq)
PulseT1BPeriodTics = ((u32)F_CPU/((u32)timer1GetPrescaler()*2*freqHz));
}
void pulseT1ARun(u16 nPulses)
{
// set the number of pulses we want and the mode
if(nPulses)
{
// if the nPulses is non-zero, use "counted" mode
PulseT1AMode |= PULSE_MODE_COUNTED;
PulseT1ACount = nPulses<<1;
}
else
{
// if nPulses is zero, run forever
PulseT1AMode &= ~PULSE_MODE_COUNTED;
PulseT1ACount = 1<<1;
}
// set OutputCompare action to toggle OC1A pin
cbi(TCCR1A,COM1A1);
sbi(TCCR1A,COM1A0);
// now the "enabling" stuff
// set the output compare one pulse cycle ahead of current timer position
// to make sure we don't have to wait until the timer overflows and comes
// back to the current value
// set future output compare time to TCNT1 + PulseT1APeriodTics
//outw(OCR1A, inw(TCNT1) + PulseT1APeriodTics);
OCR1A += PulseT1APeriodTics;
// enable OutputCompare interrupt
sbi(TIMSK, OCIE1A);
}
void pulseT1BRun(u16 nPulses)
{
// set the number of pulses we want and the mode
if(nPulses)
{
// if the nPulses is non-zero, use "counted" mode
PulseT1BMode |= PULSE_MODE_COUNTED;
PulseT1BCount = nPulses<<1;
}
else
{
// if nPulses is zero, run forever
PulseT1BMode &= ~PULSE_MODE_COUNTED;
PulseT1BCount = 1<<1;
}
// set OutputCompare action to toggle OC1B pin
// (note: with all the A's and B's flying around, TCCR1A is not a bug)
cbi(TCCR1A,COM1B1);
sbi(TCCR1A,COM1B0);
// now the "enabling" stuff
// set the output compare one pulse cycle ahead of current timer position
// to make sure we don't have to wait until the timer overflows and comes
// back to the current value
// set future output compare time to TCNT1 + PulseT1APeriodTics
//outw(OCR1B, inw(TCNT1) + PulseT1BPeriodTics);
OCR1B += PulseT1BPeriodTics;
// enable OutputCompare interrupt
sbi(TIMSK, OCIE1B);
}
void pulseT1AStop(void)
{
// stop output regardless of remaining pulses or mode
// go to "counted" mode
PulseT1AMode |= PULSE_MODE_COUNTED;
// set pulses to zero
PulseT1ACount = 0;
}
void pulseT1BStop(void)
{
// stop output regardless of remaining pulses or mode
// go to "counted" mode
PulseT1BMode |= PULSE_MODE_COUNTED;
// set pulses to zero
PulseT1BCount = 0;
}
u16 pulseT1ARemaining(void)
{
// return the number of pulses remaining for channel A
// add 1 to make sure we round up, >>1 equivalent to /2
return (PulseT1ACount+1)>>1;
}
u16 pulseT1BRemaining(void)
{
// return the number of pulses remaining for channel A
// add 1 to make sure we round up, >>1 equivalent to /2
return (PulseT1BCount+1)>>1;
}
void pulseT1AService(void)
{
// check if TimerPulseACount is non-zero
// (i.e. pulses are still requested)
if(PulseT1ACount)
{
//u16 OCValue;
// read in current value of output compare register OCR1A
//OCValue = inp(OCR1AL); // read low byte of OCR1A
//OCValue += inp(OCR1AH)<<8; // read high byte of OCR1A
// increment OCR1A value by PulseT1APeriodTics
//OCValue += PulseT1APeriodTics;
// set future output compare time to this new value
//outp((OCValue>>8), OCR1AH); // write high byte
//outp((OCValue & 0x00FF),OCR1AL); // write low byte
// the following line should be identical in operation
// to the lines above, but for the moment, I'm not convinced
// this method is bug-free. At least it's simpler!
//outw(OCR1A, inw(OCR1A) + PulseT1APeriodTics);
// change again
OCR1A += PulseT1APeriodTics;
// decrement the number of pulses executed
if(PulseT1AMode & PULSE_MODE_COUNTED)
PulseT1ACount--;
}
else
{
// pulse count has reached zero
// disable the output compare's action on OC1A pin
cbi(TCCR1A,COM1A1);
cbi(TCCR1A,COM1A0);
// and disable the output compare's interrupt to stop pulsing
cbi(TIMSK, OCIE1A);
}
}
void pulseT1BService(void)
{
// check if TimerPulseACount is non-zero
// (i.e. pulses are still requested)
if(PulseT1BCount)
{
//u16 OCValue;
// read in current value of output compare register OCR1B
//OCValue = inp(OCR1BL); // read low byte of OCR1B
//OCValue += inp(OCR1BH)<<8; // read high byte of OCR1B
// increment OCR1B value by PulseT1BPeriodTics
//OCValue += PulseT1BPeriodTics;
// set future output compare time to this new value
//outp((OCValue>>8), OCR1BH); // write high byte
//outp((OCValue & 0x00FF),OCR1BL); // write low byte
// the following line should be identical in operation
// to the lines above, but for the moment, I'm not convinced
// this method is bug-free. At least it's simpler!
//outw(OCR1B, inw(OCR1B) + PulseT1BPeriodTics);
// change again
OCR1B += PulseT1BPeriodTics;
// decrement the number of pulses executed
if(PulseT1BMode & PULSE_MODE_COUNTED)
PulseT1BCount--;
}
else
{
// pulse count has reached zero
// disable the output compare's action on OC1B pin
cbi(TCCR1A,COM1B1);
cbi(TCCR1A,COM1B0);
// and disable the output compare's interrupt to stop pulsing
cbi(TIMSK, OCIE1B);
}
}
|