Appendix A: Code Listing

      digi-lev.c

/**********************************************
 * "Digi-Lev" (TM) Digital Level              *
 *                                            *
 *  C source for CodeVisionAVR                *
 *  MCU: Atmel Mega32 AVR 8-bit RISC          *
 *                                            *
 *  Matthew DeLio        - mld32@cornell.edu  *
 *  Oren Benjamin Yeshua -  oby2@cornell.edu  *
 *                                            *
 *  Cornell University                        *
 *  ECE476 - Final Project                    *
 *                                            *
 **********************************************/

#include <mega32.h>
#include <math.h>
#include <stdio.h>
#include <delay.h>

// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x15
#endasm
#include <lcd.h>

//-- Global variables --// // Task counters // Time-base 1ms #define TIME_BUT 30 #define TIME_ACC 10 #define TIME_LCD 100 unsigned char but_cnt=TIME_BUT; unsigned char acc_cnt=TIME_ACC; unsigned char lcd_cnt=TIME_LCD; // Acceleration values char measure_position = 0; // 0: horizontal, 1: vertical unsigned char channels[2][2] = {{0,1},{2,3}}; unsigned int num_avg = 15; unsigned int sensitivity = 15; int accel[2] = {0,0}; unsigned int avg_cnt = 0; float pitch_avg = 0.0f; float roll_avg = 0.0f; int old_pitch_avg = 0; int old_roll_avg = 0; int old_plum_avg = 0; unsigned int num_avg_msk = 0; unsigned int num_avg_hlf = 0; int p_1 = 0; int r_1 = 0; int pitch_avg = 0; int pitch_avg_2 = 0; int roll_avg = 0; int roll_avg_2 = 0; int plum_avg_1 = 0; char p_level = 1; char r_level = 1; //LCD output char old_bubble=0; char new_bubble=0; // Calibration values #define CAL_AVG 1000 eeprom int mid_cal_store[2][2] = {{766, 732}, {694, 725}}; unsigned int mid_cal[2][2] = {{766, 732}, {694, 725}};
/* * Function Prototypes */ unsigned int read_adc(unsigned char); void tsk_poll_accelerometers(void); void tsk_buttons(void); void tsk_lcd_update(void); void calibrate_midpoint(char); void calibrate_gravity(char);
/* * ADC interrupt service routine (ISR) * Loads conversion result into 'adc_data' */ unsigned int adc_data; #define ADC_VREF_TYPE 0x00 #pragma savereg- interrupt [ADC_INT] void adc_isr(void) { #asm push r30 push r31 #endasm // Read the AD conversion result adc_data=ADCW; #asm pop r31 pop r30 #endasm } #pragma savereg+
/* * Timer 1 ISR * Provides 1ms timebase for task execution * (decrements task counters at 1000Hz) */ interrupt [TIM1_COMPA] void timer1_compa_isr(void) { //only decrement if positive if (but_cnt > 0) but_cnt--; if (acc_cnt > 0) acc_cnt--; if (lcd_cnt > 0) lcd_cnt--; }
/* * ADC conversion * -Enter ADC sleep mode (noise reduction) * -Start conversion * -Returns: conversion result */ unsigned int read_adc(unsigned char adc_input) { ADMUX=adc_input|ADC_VREF_TYPE; #asm in r30,mcucr cbr r30,__sm_mask sbr r30,__se_bit out mcucr,r30 sleep cbr r30,__se_bit out mcucr,r30 #endasm return adc_data; }
/* * Task: Poll Accelerometers * - calculates average accelerometer values * - compares with calibration values for leveling * - bit level accuracy * - displays level info on LED panel * - automatically detects and transfers * from horizontal to vertical measurement mode */ void tsk_poll_accelerometers(void) { char to_output = 0; //Bit blasting for LEDs unsigned char c_0, c_1; acc_cnt = TIME_ACC; //reset counter //---------------// //Read Accelerometers c_0 = channels[measure_position][0]; c_1 = channels[measure_position][1]; accel[0] = (read_adc(c_0)-mid_cal[measure_position][0]); // x1 accel[1] = (read_adc(c_1)-mid_cal[measure_position][1]); // y1 //Keep track of averages avg_cnt++; avg_cnt%=num_avg; pitch_avg += accel[0]; roll_avg += accel[1]; if(!avg_cnt) // finished sampling set, take average { //-- Pitch --// p_1 = pitch_avg; p_1 = abs(p_1); if((p_1>>sensitivity) < 1) // are we within one bit of level? { p_1 &= num_avg_msk; // if so... get remainder if(p_1 >= num_avg_hlf) // is remainder less than 1/2? p_level = 0; // if no, then not level else p_level = 1; // if yes, then level } else p_level = 0; // if not, then not level //-- Roll --// r_1 = roll_avg; r_1 = abs(r_1); if((r_1>>sensitivity) < 1) { r_1 &= num_avg_msk; if(r_1 >= num_avg_hlf) r_level = 0; else r_level = 1; } else r_level = 0; //-- Output to LEDs --// if(!p_level) to_output |= (pitch_avg < 0) ? 0b10000000 : 0b00010000; if(!r_level) to_output |= (roll_avg < 0) ? 0b00100000 : 0b01000000; if(to_output == 0) to_output = 0b00001000; to_output = ~to_output | 0b00000111; PORTB = to_output; //output on top 5 bits, input on bottom 3 (w/pull-up) //-- Update position --// if(abs(pitch_avg>>sensitivity) > 60) measure_position = abs(measure_position-1); //-- Save values for LCD display --// old_pitch_avg = pitch_avg; old_roll_avg = roll_avg; //-- Reset values for next average --// pitch_avg = 0; roll_avg = 0; } }
/* * Task: Poll Buttons * - Left Button : Toggles LCD "bubble" display * between Pitch -P- and Roll -R- * - Middle Button: Calibrates level to current position * - Right Button : Stores calibration values in EEPROM */ char lcd_select=0; char prev_press = 0; char eep_press_1 = 0; char eep_press_2 = 0; void tsk_buttons(void) { but_cnt = TIME_BUT; // reset counter //---------------// if(!PINB.0) // toggle P/R { if (!prev_press) { lcd_select++; lcd_select%=2; prev_press = 1; } } else prev_press = 0; if (!PINB.1) // calibrate midpoint { calibrate_midpoint(0); calibrate_midpoint(1); } if (!PINB.2) // store calibration { // Blink LEDs PORTB = 0b00000111; // Store calibration values in eeprom mid_cal_store[0][0] = mid_cal[0][0]; mid_cal_store[0][1] = mid_cal[0][1]; mid_cal_store[1][0] = mid_cal[1][0]; mid_cal_store[1][1] = mid_cal[1][1]; delay_ms(2000); } // A user adjustable dial controlls sensitivity // dial input 3<->7 (num_avg = 8<->128) sensitivity = ((read_adc(5) & 0xfffc)>>6)+3; num_avg_hlf = ((unsigned int)1) << (sensitivity); num_avg_msk = (num_avg_hlf << 1) - 1; num_avg = ((unsigned int)1)<<sensitivity; }
/* * Task: Update the LCD display * - draw "bubble" * - display bubble measurement * Pitch -P- or Roll -R- * - display measure position * Horizontal -H- or Vertical -V- */ void tsk_lcd_update(void) { lcd_cnt = TIME_LCD; //reset counter //---------------// switch(lcd_select) { case 0: lcd_gotoxy(14,1); lcd_putchar('R'); new_bubble = (old_roll_avg>>sensitivity)/6+7; break; case 1: lcd_gotoxy(14,1); lcd_putchar('P'); new_bubble = (old_pitch_avg>>sensitivity)/6+7; break; } // Output Sensitivity Level lcd_gotoxy(10,1); lcd_putchar(' '); lcd_gotoxy(10,1); lcd_putchar(sensitivity+46); // Output measure position (horizontal or vertical) lcd_gotoxy(1,1); if(measure_position) lcd_putchar('V'); else lcd_putchar('H'); // Erase old bubble lcd_gotoxy(old_bubble,0); if (old_bubble == 6 || old_bubble == 8) lcd_putchar('|'); else lcd_putchar(' '); // Draw new bubble if(new_bubble < 0) { old_bubble = 0; lcd_gotoxy(0,0); lcd_putchar('o'); } else if(new_bubble > 15) { old_bubble = 15; lcd_gotoxy(15,0); lcd_putchar('o'); } else { lcd_gotoxy(new_bubble, 0); lcd_putchar('o'); //Remember what the old one is old_bubble = new_bubble; } }
/* * Calibrate level * -Input: accelerometer to calibrate (0:x or 1:y) */ void calibrate_midpoint(char channel) { unsigned int i; //counter float mid = 0; //accumulator // Average over CAL_AVG reading for (i=0; i<CAL_AVG; i++) mid += read_adc(channels[measure_position][channel]); // Divide and round mid_cal[measure_position][channel] = floor(0.5f + mid/((float)CAL_AVG)); // Output calibration values to terminal printf("Midpoint Cal: %d: %u\r", channel, mid_cal[measure_position][channel]); }
/* * Main - program entry point and initialization */ void main(void) { // Port B initialization PORTB=0xFF; DDRB=0xF8; // Port D initialization PORTD=0xFF; DDRD=0x00; // Timer 1 initialization // Clock source: System Clock [16MHz] // Prescale: 64 TCCR1A=0x00; TCCR1B=0b00001011; OCR1A=250; // Time-base: 1 ms [1000Hz] OCR1B=0x0000; // Enable Timer 1 ISR TIMSK=0x10; // USART initialization // Communication Parameters: 8 Data, 1 Stop, No Parity // USART Baud rate: 9600 UCSRA=0x00; UCSRB=0x18; UCSRC=0x06; UBRRH=0x00; UBRRL=0x67; // Analog Comparator initialization // Analog Comparator: Off // Analog Comparator Input Capture by Timer/Counter 1: Off // Analog Comparator Output: Off ACSR=0x80; SFIOR=0x00; // ADC initialization // ADC Clock frequency: 125.000 kHz // ADC Voltage Reference: AREF pin ADMUX=ADC_VREF_TYPE; ADCSR=0x8F; // LCD module initialization lcd_init(16); lcd_clear(); //Put crosshairs in for bubble lcd_gotoxy(6,0); lcd_putsf("| |"); //Display Indicator: -H- for horizontal, -V- for vertical lcd_gotoxy(0,1); lcd_putsf("- - sense:"); //Display Indicator: -R- for roll, -P- for pitch lcd_gotoxy(13,1); lcd_putsf("- -"); // Load calibration values from eeprom mid_cal[0][0] = mid_cal_store[0][0]; mid_cal[0][1] = mid_cal_store[0][1]; mid_cal[1][0] = mid_cal_store[1][0]; mid_cal[1][1] = mid_cal_store[1][1]; // Global enable interrupts #asm("sei") while (1) { if(acc_cnt == 0) tsk_poll_accelerometers(); if(but_cnt == 0) tsk_buttons(); if(lcd_cnt == 0) tsk_lcd_update(); }; }