|
|
|
/*
|
|
|
|
* level - electronic level using the ADXL202
|
|
|
|
*
|
|
|
|
* This program consists of an interrupt routine that measures the pulse
|
|
|
|
* width from the ADXL202 and a main routine that updates the dot matrix
|
|
|
|
* display on the DISPLAY board.
|
|
|
|
*
|
|
|
|
* Timer1 and the two Capture/Compare registers are used to measure the
|
|
|
|
* pulse width.
|
|
|
|
*
|
|
|
|
* Use PICCLITE to compile this program for the PIC16F877.
|
|
|
|
*
|
|
|
|
* Status: Sept 25, 2003
|
|
|
|
* Working. The code is pretty brute force and the resolution isn't
|
|
|
|
* what I expected. Need to review calculations for angle vs. accel
|
|
|
|
* (gravity). I determined the zero offsets empirically and hard coded
|
|
|
|
* them. For coding simplicity I am discarding any measurements where
|
|
|
|
* the counter overflows during the measurement.
|
|
|
|
*
|
|
|
|
* Improvements should include: a method to zero the offsets at
|
|
|
|
* runtime and increased gain (which probably means going to 16 bit
|
|
|
|
* values). Also consider filtering the measured values to gain stability.
|
|
|
|
* Add code to handle counter overflow.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <pic.h>
|
|
|
|
#include "test.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* bit macros suggested by the PICCLITE manual
|
|
|
|
*/
|
|
|
|
#define BITSET(var, bitno) ((var) |= 1 << (bitno))
|
|
|
|
#define BITCLR(var, bitno) ((var) &= ~(1 << (bitno)))
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pulse with variables
|
|
|
|
*
|
|
|
|
* These are set up the measurePW() ISR and read by the main loop
|
|
|
|
*/
|
|
|
|
unsigned char PeriodStartX;
|
|
|
|
unsigned char PeriodEndX;
|
|
|
|
unsigned char PulseEndX;
|
|
|
|
unsigned char PulseEndTempX;
|
|
|
|
unsigned char PWSyncX;
|
|
|
|
|
|
|
|
unsigned char PeriodStartY;
|
|
|
|
unsigned char PeriodEndY;
|
|
|
|
unsigned char PulseEndY;
|
|
|
|
unsigned char PulseEndTempY;
|
|
|
|
unsigned char PWSyncY;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* interrupt service routine to measure pulse width
|
|
|
|
*
|
|
|
|
* This routine uses the capture facility of the 16F877 to measure the
|
|
|
|
* period and the pulse width of a signal. This routine has two states
|
|
|
|
* the first looks for the rising edge of the signal which indicates the
|
|
|
|
* end of one period and the beginning of the next. The second state
|
|
|
|
* looks for the falling edge of the signal to determine the pulse
|
|
|
|
* width.
|
|
|
|
*
|
|
|
|
* Only the high 8 bits of the captured time are record. At 20MHz this
|
|
|
|
* yields a time resolution of approximately 51us.
|
|
|
|
*
|
|
|
|
* RC0 and RC1 indicate ISR over-run (in other words the main loop did
|
|
|
|
* not handle the measured values quickly enough).
|
|
|
|
*/
|
|
|
|
void interrupt
|
|
|
|
measurePW(void)
|
|
|
|
{
|
|
|
|
/* determined which capture register requires service */
|
|
|
|
if (CCP1IF == 1)
|
|
|
|
{
|
|
|
|
/* CCP1 */
|
|
|
|
if ((CCP1CON & 0b00000111) == 0b00000101)
|
|
|
|
{
|
|
|
|
/* rising edge-
|
|
|
|
* record last rise time (period_end -> period_start)
|
|
|
|
* record last falling time (pulse_end_temp -> pulse_end)
|
|
|
|
* record this rise time (capture -> period_end)
|
|
|
|
* set synchronization flag
|
|
|
|
* change mode to look for falling edge
|
|
|
|
*/
|
|
|
|
PeriodStartX = PeriodEndX;
|
|
|
|
PulseEndX = PulseEndTempX;
|
|
|
|
PeriodEndX = CCPR1H;
|
|
|
|
PWSyncX++;
|
|
|
|
CCP1CON = 0b00000100;
|
|
|
|
|
|
|
|
/* Indicated an ISR over-run if the sync flag was
|
|
|
|
* not cleared by the main loop
|
|
|
|
*/
|
|
|
|
if (PWSyncX > 1)
|
|
|
|
{
|
|
|
|
RC0 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* assume falling edge */
|
|
|
|
{
|
|
|
|
/* falling-
|
|
|
|
* save this falling time for later
|
|
|
|
* change mode to look for rising edge
|
|
|
|
*/
|
|
|
|
PulseEndTempX = CCPR1H;
|
|
|
|
CCP1CON = 0b00000101;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* clear interrupt flags and return
|
|
|
|
*/
|
|
|
|
CCP1IF = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* CCP2 */
|
|
|
|
if ((CCP2CON & 0b00000111) == 0b00000101)
|
|
|
|
{
|
|
|
|
/* rising edge-
|
|
|
|
* record last rise time (period_end -> period_start)
|
|
|
|
* record last falling time (pulse_end_temp -> pulse_end)
|
|
|
|
* record this rise time (capture -> period_end)
|
|
|
|
* set synchronization flag
|
|
|
|
* change mode to look for falling edge
|
|
|
|
*/
|
|
|
|
PeriodStartY = PeriodEndY;
|
|
|
|
PulseEndY = PulseEndTempY;
|
|
|
|
PeriodEndY = CCPR2H;
|
|
|
|
PWSyncY++;
|
|
|
|
CCP2CON = 0b00000100;
|
|
|
|
|
|
|
|
/* Indicated an ISR over-run if the sync flag was
|
|
|
|
* not cleared by the main loop
|
|
|
|
*/
|
|
|
|
if (PWSyncY > 1)
|
|
|
|
{
|
|
|
|
RC3 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* assume falling edge */
|
|
|
|
{
|
|
|
|
/* falling-
|
|
|
|
* save this falling time for later
|
|
|
|
* change mode to look for rising edge
|
|
|
|
*/
|
|
|
|
PulseEndTempY = CCPR2H;
|
|
|
|
CCP2CON = 0b00000101;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* clear interrupt flags and return
|
|
|
|
*/
|
|
|
|
CCP2IF = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* measure tilt and update dot matrix display
|
|
|
|
*
|
|
|
|
* This program uses the times recorded by the measurePW() ISR to
|
|
|
|
* calculate pulse width. The pulse width is then translated into a tilt
|
|
|
|
* measurement and finally displayed on the dot matrix display.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
main(void)
|
|
|
|
{
|
|
|
|
unsigned char periodX = 0;
|
|
|
|
unsigned char pulseX = 0;
|
|
|
|
unsigned char periodY = 0;
|
|
|
|
unsigned char pulseY = 0;
|
|
|
|
int tiltX;
|
|
|
|
int tiltY;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup I/O ports
|
|
|
|
* RC0 and RC3 outputs, the rest are inputs.
|
|
|
|
* RA5 output (power to ADXL202)
|
|
|
|
* Set port C outputs high to turn off the LEDs
|
|
|
|
*/
|
|
|
|
TRISC = 0b11110110;
|
|
|
|
RA5 = 1;
|
|
|
|
PORTC = 0xff;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup the I/O ports that control the dot matrix display
|
|
|
|
*/
|
|
|
|
TRISA = 0b11000000;
|
|
|
|
TRISB = 0b11100000;
|
|
|
|
TRISD = 0b10000000;
|
|
|
|
ADCON1 = 0x06; /* disable ADC so that port A is digital I/O */
|
|
|
|
PORTD = 0; /* turn off all rows */
|
|
|
|
PORTB = 0xff; /* turn off all columns */
|
|
|
|
PORTA = 0xff; /* turn off all columns */
|
|
|
|
|
|
|
|
/* configure timer1
|
|
|
|
* 1:1 prescale
|
|
|
|
* Internal clock (Fosc/4)
|
|
|
|
* Enabled
|
|
|
|
*/
|
|
|
|
T1CON = 0b00000001;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* configure capture registers
|
|
|
|
* capture on rising edge
|
|
|
|
*/
|
|
|
|
CCP1CON = 0b00000101;
|
|
|
|
CCP2CON = 0b00000101;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* clear sync flag
|
|
|
|
*/
|
|
|
|
PWSyncX = 0;
|
|
|
|
PWSyncY = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* enable interrupts
|
|
|
|
*/
|
|
|
|
PEIE = 1;
|
|
|
|
CCP1IE = 1;
|
|
|
|
CCP2IE = 1;
|
|
|
|
ei();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* main loop
|
|
|
|
*/
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* kick watchdog
|
|
|
|
*/
|
|
|
|
CLRWDT();
|
|
|
|
|
|
|
|
if (PWSyncX > 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Test for easy calculations
|
|
|
|
* i.e., no counter roll over during the measurment
|
|
|
|
*/
|
|
|
|
if (PeriodEndX > PeriodStartX)
|
|
|
|
{
|
|
|
|
if (PulseEndX > PeriodStartX)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* no roll over, so proceed
|
|
|
|
*/
|
|
|
|
periodX = PeriodEndX - PeriodStartX;
|
|
|
|
pulseX = PulseEndX - PeriodStartX;
|
|
|
|
|
|
|
|
periodX = (periodX / 2) + 9; /* offset = 9 */
|
|
|
|
tiltX = (periodX) - pulseX;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* turn off all rows and then figure out which row to turn
|
|
|
|
* on based on the tilt
|
|
|
|
*/
|
|
|
|
PORTD = 0;
|
|
|
|
if (tiltX < -5)
|
|
|
|
{
|
|
|
|
RD6 = 1;
|
|
|
|
}
|
|
|
|
else if (tiltX < -4)
|
|
|
|
{
|
|
|
|
RD5 = 1;
|
|
|
|
}
|
|
|
|
else if (tiltX < -2)
|
|
|
|
{
|
|
|
|
RD4 = 1;
|
|
|
|
}
|
|
|
|
else if (tiltX > 5)
|
|
|
|
{
|
|
|
|
RD0 = 1;
|
|
|
|
}
|
|
|
|
else if (tiltX > 4)
|
|
|
|
{
|
|
|
|
RD1 = 1;
|
|
|
|
}
|
|
|
|
else if (tiltX > 2)
|
|
|
|
{
|
|
|
|
RD2 = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RD3 = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PWSyncX = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PWSyncY > 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Test for easy calculations
|
|
|
|
* i.e., no counter roll over during the measurment
|
|
|
|
*/
|
|
|
|
if (PeriodEndY > PeriodStartY)
|
|
|
|
{
|
|
|
|
if (PulseEndY > PeriodStartY)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* no roll over, so proceed
|
|
|
|
*/
|
|
|
|
periodY = PeriodEndY - PeriodStartY;
|
|
|
|
pulseY = PulseEndY - PeriodStartY;
|
|
|
|
|
|
|
|
periodY = (periodY / 2) - 1; /* offset = -1 */
|
|
|
|
tiltY = (periodY) - pulseY;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* turn off all columns then figure out which column
|
|
|
|
* to turn on based on the tilt
|
|
|
|
*/
|
|
|
|
PORTA = PORTA | 0b00111111;
|
|
|
|
PORTB = PORTB | 0b00011111;
|
|
|
|
if (tiltY < -5)
|
|
|
|
{
|
|
|
|
RB1 = 0;
|
|
|
|
}
|
|
|
|
else if (tiltY < -4)
|
|
|
|
{
|
|
|
|
RB0 = 0;
|
|
|
|
}
|
|
|
|
else if (tiltY < -2)
|
|
|
|
{
|
|
|
|
RA4 = 0;
|
|
|
|
}
|
|
|
|
else if (tiltY > 5)
|
|
|
|
{
|
|
|
|
RA0 = 0;
|
|
|
|
}
|
|
|
|
else if (tiltY > 4)
|
|
|
|
{
|
|
|
|
RA1 = 0;
|
|
|
|
}
|
|
|
|
else if (tiltY > 2)
|
|
|
|
{
|
|
|
|
RA2 = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RA3 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PWSyncY = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|