mirror of
https://github.com/arduino/Arduino.git
synced 2024-11-30 11:24:12 +01:00
287 lines
8.1 KiB
C
Executable File
287 lines
8.1 KiB
C
Executable File
/*! \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
|
|
//
|
|
//*****************************************************************************
|
|
|
|
#ifndef WIN32
|
|
#include <avr/io.h>
|
|
#include <avr/signal.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/pgmspace.h>
|
|
#endif
|
|
|
|
#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);
|
|
}
|
|
}
|