@

Source Code - plotter.c


#include <Mega32.h>
#include <Stdio.h>
#include <Stdlib.h>
#include <delay.h>
#include <math.h>
#asm
.equ __lcd_port=0x15 ;LCD = PORT C
#endasm
#include <lcd.h>

#define begin {
#define end }
#define T_BUT 30 //poll keypad every 30 milliseconds
#define T_500 500
#define steps_per_cm 48.0/1.9
#define D 22.5 // length D in cm
#define base_x D/2 //start at initial x and y that will form an
#define base_y (0.866*D) //equliateral triangle for calibration
#define MAXSTEPS 400 //max steps motor can take
#define MINSTEPS 0 //min steps motor can take
#define MOTOR_TIMEBASE 20.0 //timebase for motor runtime
#define DRAWRES 0.3 // freedraw resolution in cm
#define T_ABC 1000 // hold time when entering characters for string

//function prototypes
void state_table();

// Main Menu Functions
void enter_main(void);
void mainMenu(void);
void LCDscrUPmain(void);
void LCDscrDOWNmain(void);

// Plot Coor Functions
void enter_plotCoor();
void getAngle(void);
void enter_getdist(void);
void getDist(void);
void enter_plotLine();


// Freedraw Functions
void enter_freedraw(void);
void freedraw(void);
void adjust_xy();

// Plot Text Functions
void enter_getText(void);
void getText(void);
void enter_plotText();
void plotText(void);

// Plot Programmed Objects Functions
void enter_progMenu();
void progMenu();
void enter_plotProg();
void plotProg(void);
void enter_getLength();
void getLength();
void LCDscrUPprog(void);
void LCDscrDOWNprog(void);

// Motor Functions
void runMotorR();
void runMotorL();

void makeStepL(void);
void makeStepR(void);
void tickBL(void);
void tickFL(void);
void tickBR(void);
void tickFR(void);

// da/db calculation function
void Update(void);
void calculate_da(void);
void calculate_db(void);

// Key acquisition functions
void key_press(void);
char getKeyChar(char k);

// Lift Pen (Solinoid ctrl) Functions
void liftpen();
void pls_wait();

//state variables
unsigned char CurrState;
unsigned char mainState, penState;
//pushButton states
enum{nopush, maybepush, pushed, maybenopush}; //assign each state a number.
//software mode states
enum{main_menu, free_draw, get_angle, plot_line, get_dist, plot_text, prog_menu, plot_prog, get_text, get_length};
//program plot selection states
enum{sqre_select, trig_select, house_select};

unsigned char currMenuSelect;

unsigned char main_selection[4][13] = {
{'F','R','E','E','D','R','A','W',' ',' ','-','1', 0},
{'P','L','O','T',' ','C','O','O','R',' ','-','2', 0},
{'P','L','O','T',' ','T','E','X','T',' ','-','3', 0},
{'P','L','O','T',' ','P','R','O','G',' ','-','4', 0}
};

unsigned char prog_selection[3][13] = {
{'S','Q','U','A','R','E',' ',' ',' ',' ','-','1', 0},
{'T','R','I','A','N','G','L','E',' ',' ','-','2', 0},
{'H','O','U','S','E',' ',' ',' ',' ',' ','-','3', 0}
};

unsigned char keypad_char[10][6] = {
{'0', 0 , 0 , 0 , 0 , 0 },
{'.','-',' ','1', 0 , 0 },
{'A','B','C','2', 0 , 0 },
{'D','E','F','3', 0 , 0 },
{'G','H','I','4', 0 , 0 },
{'J','K','L','5', 0 , 0 },
{'M','N','O','6', 0 , 0 },
{'P','Q','R','S','7', 0 },
{'T','U','V','8', 0 , 0 },
{'W','X','Y','Z','9', 0 }
};

// character map - data arrays with each of them storing
// the following info - penlift info, angle of line, and distance.
flash unsigned char char_penlift_map[39][10] = {
{2,1,2,0,0,0,0,0,0,0}, //.
{2,1,2,2,0,0,0,0,0,0}, //-
{2,0,0,0,0,0,0,0,0,0}, //' '
// numbers
{1,1,1,1,2,0,0,0,0,0}, //0
{2,1,2,2,0,0,0,0,0,0}, //1
{2,1,1,1,1,1,2,0,0,0}, //2
{1,1,1,2,1,1,2,2,0,0}, //3
{2,1,2,1,1,2,2,0,0,0}, //4
{1,1,1,1,1,2,2,0,0,0}, //5
{2,1,1,1,1,1,1,2,2,0}, //6
{2,1,1,2,0,0,0,0,0,0}, //7
{1,1,1,1,1,1,2,1,2,0}, //8
{1,1,1,1,1,2,2,0,0,0}, //9


//ABC
{1,1,2,1,2,2,0,0,0,0}, //A
{1,1,1,1,1,1,2,1,2,0}, //B
{1,1,2,1,2,0,0,0,0,0}, //C
{1,1,1,1,2,0,0,0,0,0}, //D
{1,2,1,1,2,1,1,2,2,0}, //E
{1,1,2,1,1,2,2,0,0,0}, //F
{1,1,1,2,2,1,1,2,0,0}, //G
{1,2,1,1,2,1,2,0,0,0}, //H
{1,2,1,2,1,2,0,0,0,0}, //I
{2,1,1,1,2,1,2,2,0,0}, //J
{1,2,1,1,2,0,0,0,0,0}, //K
{1,2,1,2,2,0,0,0,0,0}, //L
{1,1,1,1,2,0,0,0,0,0}, //M
{1,1,1,2,2,0,0,0,0,0}, //N
{1,1,1,1,2,0,0,0,0,0}, //O
{1,1,1,1,2,2,0,0,0,0}, //P
{1,1,1,1,2,2,1,2,2,0}, //Q
{1,1,1,1,1,2,0,0,0,0}, //R
{1,1,1,1,1,2,2,0,0,0}, //S
{2,1,2,1,2,2,0,0,0,0}, //T
{1,1,2,1,2,0,0,0,0,0}, //U
{2,1,1,2,2,0,0,0,0,0}, //V
{1,1,2,1,1,2,1,2,0,0}, //W
{1,2,1,2,0,0,0,0,0,0}, //X
{2,1,1,2,1,2,2,0,0,0}, //Y
{2,1,1,1,2,0,0,0,0,0} //Z
};


flash unsigned int char_angle_map[39][10] = {
{90,90,90,0,0,0,0,0,0,0}, //.
{360,90,90,180,0,0,0,0,0,0}, //-
{90,0,0,0,0,0,0,0,0,0}, //' '
// numbers

{360,90,180,270,90,0,0,0,0,0}, //0
{90,360,90,180,0,0,0,0,0,0}, //1
{360,90,180,270,180,90,90,0,0,0}, //2
{90,360,270,90,360,270,90,180,0,0}, //3
{90,360,270,180,90,180,90,0,0,0}, //4
{90,360,270,0,90,180,90,0,0,0}, //5
{360,180,90,360,270,0,90,180,90,0}, //6
{360,90,225,90,0,0,0,0,0,0}, //7
{90,360,270,360,90,180,270,180,90,0}, //8
{90,360,270,180,90,180,90,0,0,0}, //9


//ABC
{26.5,153,360,270,180,90,0,0,0,0}, //A
{90,360,270,360,90,180,270,180,90,0}, //B
{360,90,180,270,90,0,0,0,0,0}, //C
{360,90,180,270,90,0,0,0,0,0}, //D
{90,270,360,90,270,360,90,180,90,0}, //E
{360,90,270,360,90,90,180,0,0,0}, //F
{90,360,270,90,360,270,180,90,0,0}, //G
{360,90,180,270,90,180,90,0,0,0}, //H
{90,360,270,90,180,90,0,0,0,0}, //I
{360,180,90,360,270,90,90,180,0,0}, //J
{360,90,225,135,90,0,0,0,0,0}, //K
{90,270,360,90,180,0,0,0,0,0}, //L
{360,135,45,180,90,0,0,0,0,0}, //M
{360,135,360,90,180,0,0,0,0,0}, //N
{360,90,180,270,90,0,0,0,0,0}, //O
{360,90,180,270,90,180,0,0,0,0}, //P
{360,90,180,270,90,360,180,360,90,0}, //Q
{360,90,180,270,135,90,0,0,0,0}, //R
{90,360,270,360,90,180,90,0,0,0}, //S
{90,360,270,90,90,180,0,0,0,0}, //T
{90,360,270,180,90,0,0,0,0,0}, //U
{360,153.5,26.5,180,90,0,0,0,0,0}, //V
{90,360,180,90,360,270,180,90,0,0}, //W
{45,270,135,90,0,0,0,0,0,0}, //X
{90,360,315,90,225,90,180,0,0,0}, //Y
{360,90,225,90,90,0,0,0,0,0} //Z
};


flash float char_dist_map[39][10] = {
{0.45,0.1,0.1,0,0,0,0,0,0,0}, //.
{0.5,1.0,0.1,0.5,0,0,0,0,0,0}, //-
{1.1,0,0,0,0,0,0,0,0,0}, //' '

// numbers
{1.0,1.0,1.0,1.0,1.1,0,0,0,0,0}, //0
{0.5,1.0,0.6,1.0,0,0,0,0,0,0}, //1
{1.0,1.0,0.5,1.0,0.5,1.0,0.1,0,0,0}, //2
{1.0,0.5,0.7,0.7,0.5,1.0,1.1,1.0,0,0}, //3
{1.0,1.0,1.0,0.5,1.0,0.5,0.1,0,0,0}, //4
{1.0,0.5,1.0,0.5,1.0,1.0,0.1,0,0,0}, //5
{0.5,0.5,1.0,0.5,1.0,0.5,1.0,1.0,0.1,0},//6
{1.0,1.0,1.4,1.1,0,0,0,0,0,0}, //7
{1.0,0.5,1.0,0.5,1.0,0.5,1.0,0.5,1.1,0},//8
{1.0,1.0,1.0,0.5,1.0,0.5,0.1,0,0,0}, //9


//ABC
{1.1,1.1,0.5,1.0,0.5,1.1,0,0,0,0}, //A
{1.0,0.5,1.0,0.5,1.0,0.5,1.0,0.5,1.1,0},//B
{1.0,1.0,1.0,1.0,1.1,0,0,0,0,0}, //C
{1.0,1.0,1.0,1.0,1.0,0,0,0,0,0}, //D
{1.0,1.0,0.5,0.7,0.7,0.5,1.0,1.0,0.1,0},//E
{0.5,0.7,0.7,0.5,1.0,0.1,1.0,0,0,0}, //F
{1.0,0.5,0.3,0.3,0.5,1.0,1.0,1.1,0,0}, //G
{1.0,1.0,0.5,1.0,1.0,0.5,0.1,0,0,0}, //H
{1.0,1.0,1.0,0.5,1.0,0.6,0,0,0,0}, //I
{0.5,0.5,0.5,1.0,0.5,1.0,0.1,1.0,0,0}, //J
{1.0,1.0,0.7,0.7,0.1,0,0,0,0,0}, //K
{1.0,1.0,1.0,1.1,1.0,0,0,0,0,0}, //L
{1.0,0.7,0.7,1.0,0.1,0,0,0,0,0}, //M
{1.0,1.4,1.0,0.1,1.0,0,0,0,0,0}, //N
{1.0,1.0,1.0,1.0,1.1,0,0,0,0,0}, //O
{1.0,1.0,0.5,1.0,1.1,0.5,0,0,0,0}, //P
{1.0,1.0,1.0,1.0,0.7,0.3,0.6,0.3,0.4,0},//Q
{1.0,1.0,0.5,1.0,0.7,0.1,0,0,0,0}, //R
{1.0,0.5,1.0,0.5,1.0,1.0,0.1,0,0,0}, //S
{0.5,1.0,0.5,1.0,0.1,1.0,0,0,0,0}, //T
{1.0,1.0,1.0,1.0,1.1,0,0,0,0,0}, //U
{1.0,1.1,1.1,1.0,0.1,0,0,0,0,0}, //V
{0.5,0.5,0.5,0.5,1.0,1.0,1.0,1.1,0,0}, //W
{1.4,1.0,1.4,0.1,0,0,0,0,0,0}, //X
{0.5,0.5,0.7,1.0,0.7,0.6,0.5,0,0,0}, //Y
{1.0,1.0,1.4,1.0,0.1,0,0,0,0,0} //Z
};

//House Coordinates
flash unsigned char house_penlift_map[18] = {1,1,1,2,1,1,1,2,1,1,1,1,2,1,1,1,2,0};
flash float house_angle_map[18] = {60.0,120.0,270.0,90.0,180.0,90.0,360.0,232.43,180.0,90.0,360.0,270.0,206.57,360.0,270.0,180.0,270.0,0};
flash float house_dist_map[18] = {1.5,1.5,2.6,0.25,1.5,2.1,1.5,0.82,0.5,0.5,0.5,0.5,1.12,1.0,0.5,1.0,2.3,0};

//Triangle Coordinates
flash unsigned char trig_penlift_map[4] = {1,1,1,0};
flash float trig_angle_map[4] = {30.0,150.0,270.0,0};
flash float trig_dist_map[4] = {1.0,1.0,1.0,0};

//Square Coordinates
flash unsigned char sqre_penlift_map[5] = {1,1,1,1,0};
flash float sqre_angle_map[5] = {360.0,90.0,180.0,270.0,0};
flash float sqre_dist_map[5] = {1.0,1.0,1.0,1.0,0};


//position variables
float current_x, current_y, current_a, current_b;
float da, db, dx, dy;
float partial_da, partial_db, partial_dx, partial_dy; //used to calculate partially travelled distance

//stepper motor variables
int distStepR, distStepL; //steps needed to be taken by each motor
unsigned int tempR, tempL; // abs values of distStepR, distStepL
unsigned char stepR, stepL; // current step state in stepper motor
unsigned char Rsteps_left, Lsteps_left; // true if there are steps left to take

unsigned int MSCR, MSCL; //Master Step Counter R and L
unsigned char movableR, movableL; // true if plot is within valid area of each stepper motor
unsigned int stepcountR, stepcountL; //stores # of steps taken for each plot
int runtimeR, runtimeL, timebaseR, timebaseL; // stores stepping time frame for each motor
unsigned char flag_MLdone, flag_MRdone; //high if made enough steps

//counter/flag variables
int keytime, kt, // time counter for state machine
unsigned char liftpentime; // time counter for pen lift function

unsigned char flag_freedraw;
unsigned char flag_plotcoor,flag_gotangle,flag_gotdist,flag_plotprog,flag_donechar;
unsigned char flag_plottext;
unsigned char prog_select;
unsigned char i; //temp value for loops

// LCD display buffer
char lcd_buffer[15];


// keypad variables
unsigned char key, keychar;
unsigned char keypad_i1, keypad_i2;

unsigned char plotString[16], plotChar, plotStrPos;
unsigned int map_i1, map_i2; //indices for character map arrays

float plotAngle, plotDist; // coordinate set for line plot


//**************** INTERRUPTS *******************************
interrupt [TIM0_COMP] void timer0_overflow(void)
begin
if (keytime>0) --keytime;
if (runtimeL>0) --runtimeL;
if (runtimeR>0) --runtimeR;
if (ABCcounter>0) --ABCcounter;
if (liftpentime>0) --liftpentime;
end

//----------------- INITIALIZATION --------------------------
void init()
begin

DDRC=0xFF; //make port C output used for LCD
PORTC = 0;

DDRB=0xFF; //make port B output LED's on STK board
PORTB = 0x00;

DDRA = 0xFF; //port A trivially set to output, will be used by keypad
PORTA = 0x00;

DDRD = 0xFF; //port D is output used for stepper motors
PORTD = 0;


//set up timer 0
TIMSK=2; //turn on timer 0 cmp match ISR
OCR0 = 250; //set the compare re to 250 time ticks
//prescalar to 64 and turn on clear-on-match
TCCR0=0b00001011;

// init UART (for debugging)
UCSRB = 0b00011000 ; //hex 0x18
UBRRL = 103 ; //using a 16 MHz crystal (9600 baud)

//init the task timer
keytime=T_500;

//init stepper counters
stepR =0;
stepL =0;
stepcountR =0;
stepcountL =0;

//init position counters
current_a = D;
current_b = D;
current_y = base_y;
current_x = base_x;


//initialize the LCD for 16 char wide
lcd_init(16);
lcd_clear();

// reset runtime and step counter for each motor
runtimeL = MOTOR_TIMEBASE;
runtimeR = MOTOR_TIMEBASE;
MSCL = 0;
MSCR = 0;

//wind to center of board
PORTD.7 = 1;
for(i=0;i<150;i++)
begin
tickFR(); tickFL();
delay_ms(10);
end
PORTD.7 = 0;

enter_main(); //start at main menu

//crank up the ISRs
#asm
sei
#endasm
end

//----------------- MAIN ------------------------------------
void main()
begin
init();
while(1)
begin
// lift pen button detection
if(!liftpentime) liftpen();

//enter state table to execute current function
if (!keytime) state_table();

//if ready to plot line......
if (mainState == plot_line)
begin
if (!runtimeL) runMotorL();
if (!runtimeR) runMotorR();

if(flag_MLdone && flag_MRdone) Update();
end
end
end

//-*************** OS state handler ***************************
void state_table(void)
begin
keytime = kt;

switch(mainState)
begin
case main_menu: mainMenu(); break;
case free_draw: freedraw(); break;
case get_angle: getAngle(); break;
case get_dist: getDist(); break;
case plot_text: plotText(); break;
case prog_menu: progMenu(); break;
case plot_prog: plotProg(); break;
case get_text: getText(); break;
case get_length: getLength(); break;
end

end


//******************* OS MAIN MENU ******************************
// main menu parameters setup function
void enter_main(void)
begin
pls_wait();

lcd_clear();
lcd_gotoxy(0,0);
lcd_puts(main_selection[0]);

lcd_gotoxy(0,1);
lcd_puts(main_selection[1]);
currMenuSelect = 0;

kt = 500;

CurrState = nopush;
mainState = main_menu;
end
//--------- main menu------------------------
void mainMenu(void)
begin
key_press(); //get key press from keypad
keychar = getKeyChar(key);

switch(keychar)
begin
case '1': // freedraw
pls_wait();
CurrState = nopush;
enter_freedraw();
break;

case '2': //plot coordinates
pls_wait();
enter_plotCoor();
break;

case '3': // plot text
pls_wait();
enter_getText();
break;

case '4': //plot programmed obj
pls_wait();
enter_progMenu();
break;
case 'A': //scroll up menu
LCDscrUPmain();
break;
case 'B': //scroll down menu
LCDscrDOWNmain();
break;
end

end

//--------- Menu Scroll Functions
void LCDscrUPmain(void)
begin
if(currMenuSelect > 0)
begin
lcd_clear();
lcd_gotoxy(0,1);
lcd_puts(main_selection[currMenuSelect--]);

lcd_gotoxy(0,0);
lcd_puts(main_selection[currMenuSelect]);
end
end

void LCDscrDOWNmain(void)
begin
if(currMenuSelect < 3)
begin
lcd_clear();
lcd_gotoxy(0,0);
lcd_puts(main_selection[++currMenuSelect]);

lcd_gotoxy(0,1);
lcd_puts(main_selection[currMenuSelect+1]);
end
end

//------------- plot coorindates function ---------------------
void enter_plotCoor()
begin
flag_plotcoor = 1;
flag_gotangle = 0;
flag_gotdist = 0;

kt = 30;
i = 0;

sprintf(lcd_buffer," ");

mainState = get_angle;
CurrState = nopush;

lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("ANGLE?");
end
//------------------ GET ANGLE ---------------------------------
// getAngle() - get angle value from user for line plotting
void getAngle()
begin
key_press();
keychar = getKeyChar(key);

// debounce State Machine
switch(CurrState)
begin
case nopush:
if(key != 0xff) CurrState = maybepush;
break;

case maybepush:
if(key !=0xff)
begin
CurrState = pushed;
switch(keychar)
begin
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
lcd_buffer[i++] = keychar;
lcd_gotoxy(7,0);
lcd_puts(lcd_buffer);
break;
case 'C': //confirm
plotAngle = ((float)(atoi(lcd_buffer) % 360))/180.0*PI;
enter_getdist();
break;
case 'D': //delete
if(i>0) lcd_buffer[--i] = ' ';
lcd_gotoxy(7,0);
lcd_puts(lcd_buffer);
break;
case '*': //decline
flag_plotcoor = 0;
enter_main();
break;
end
end
else CurrState = nopush;
break;
case pushed:
if(key==0xff) CurrState = maybenopush;
break;
case maybenopush:
if(key!=0xff) CurrState = pushed;
else CurrState = nopush;
break;
end
end

//-------------------- GET DIST -------------------------------
void enter_getdist()
begin
pls_wait();

kt = 30;
i = 0;

sprintf(lcd_buffer," ");
mainState = get_dist;
CurrState = nopush;

lcd_clear(); lcd_gotoxy(0,0);
lcd_putsf("DIST?");
end

// getDist() - get distance value from user for line plotting
void getDist()
begin
key_press();
keychar = getKeyChar(key);

// debounce State Machine
switch(CurrState)
begin
case nopush:
if(key != 0xff) CurrState = maybepush;
break;

case maybepush:
if(key !=0xff)
begin
CurrState = pushed;
switch(keychar)
begin
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
lcd_buffer[i++] = keychar;
lcd_gotoxy(6,0);
lcd_puts(lcd_buffer);
break;
case 'C': //confirm
plotDist = atof(lcd_buffer);
if((plotDist > 10) || (plotDist < 0))
begin
lcd_gotoxy(0,0);
lcd_putsf("INVALID DIST");
delay_ms(1000);
enter_getdist();
end
else
begin
pls_wait();
enter_plotLine();
end
break;
case 'D': //delete
if(i>0) lcd_buffer[--i] = ' ';
lcd_gotoxy(6,0);
lcd_puts(lcd_buffer);
break;
case '*': //decline
flag_plotcoor = 0;
enter_main();
break;
end
end
else CurrState = nopush;
break;

case pushed:
if(key==0xff) CurrState = maybenopush;
break;

case maybenopush:
if(key!=0xff) CurrState = pushed;
else CurrState = nopush;
break;
end
end

//***************** FREEDRAW*******************************
void enter_freedraw(void)
begin
flag_freedraw = 1;

kt = 20;

mainState = free_draw;

lcd_clear(); lcd_gotoxy(0,0);
lcd_putsf("FREEDRAW MODE");
end

//freedraw() - detects key press from user and determine plot direction, distance predetermined
// continues calling plotLine() until button is released
void freedraw(void)
begin

key_press();
keychar = getKeyChar(key);

// debounce State Machine
if(flag_freedraw)
begin
switch(CurrState)
begin
case nopush:
if(key != 0xff) CurrState = maybepush;
break;

case maybepush:
if(key !=0xff)
begin
CurrState = pushed;
plotDist = DRAWRES;
switch(keychar)
begin
case '1': plotAngle = 1.75*PI; break; // MOVE NW
case '2': plotAngle = 0; break; // MOVE N
case '3': plotAngle = 0.25*PI; break; // MOVE NE
case '4': plotAngle = 1.50*PI; break; // MOVE W
case '6': plotAngle = 0.50*PI; break; // MOVE E
case '7': plotAngle = 1.25*PI; break; // MOVE SW
case '8': plotAngle = PI; break; // MOVE S
case '9': plotAngle = 0.75*PI; break; // MOVE SE
case '*': flag_freedraw = 0;
plotDist = 0; break; //exit from freedraw
default: plotDist = 0; break;
end
end
else CurrState = nopush;
break;

case pushed:
if(key!=0xff) enter_plotLine();
else CurrState = maybenopush;
break;

case maybenopush:
if(key!=0xff) CurrState = pushed;
else
begin
CurrState = nopush;
adjust_xy(); // adjust X & Y after sudden release
end
break;
end//switch
end
else enter_main();
end

// adjust_xy() handles any distance adjustments in case the user disables
// plotter movement in freedraw mode before it reaches its preset resolution.
void adjust_xy()
begin
if(db<0) partial_db = -((float)stepcountR)/steps_per_cm;
else partial_db = ((float)stepcountR)/steps_per_cm;

if(da<0) partial_da = -((float)stepcountL)/steps_per_cm;
else partial_da = ((float)stepcountL)/steps_per_cm;

partial_dx = (current_a/D*partial_da) - (current_b/D*partial_db);
partial_dy = (current_x*current_b/(D*current_y))*partial_db + current_a/current_y*(1.0-current_x/D)*partial_da;

current_y = current_y + partial_dy;
current_x = current_x + partial_dx;
current_a = current_a + partial_da;
current_b = current_b + partial_db;
end


//-------------- PLOT LINE FUNC ------------------------------
void enter_plotLine()
begin
// reset step counts for new plot
stepcountL = 0;
stepcountR = 0;

// calculate dx, dy, da, db for new plot
dx = plotDist*sin(plotAngle);
dy = (-1.0)*plotDist*cos(plotAngle);
calculate_da();
calculate_db();

//calculate step need to be taken for new plot
distStepR = (int)(steps_per_cm*db);
distStepL = (int)(steps_per_cm*da);

tempR = abs(distStepR);
tempL = abs(distStepL);

// true if movement can be made with # of steps already taken
movableR = ((MSCR - distStepR) <= MAXSTEPS) && ((MSCR - distStepR) >= MINSTEPS);
movableL = ((MSCL - distStepL) <= MAXSTEPS) && ((MSCL - distStepL) >= MINSTEPS);

// if plot can be made......
if(movableL && movableR)
begin
//adjust time base for each motors
// one with higher # steps is set with base timeframe
if(tempR > tempL)
begin
timebaseR = (int)MOTOR_TIMEBASE;
runtimeR = timebaseR;
timebaseL = abs((int)(db/da*MOTOR_TIMEBASE));
runtimeL = timebaseL;
end
else
begin
timebaseL = (int)MOTOR_TIMEBASE;
runtimeL = timebaseL;
timebaseR = abs((int)(da/db*MOTOR_TIMEBASE));
runtimeR = timebaseR;
end

// reset motor flags
flag_MLdone = 0;
flag_MRdone = 0;

mainState = plot_line;
end
else //if invalid entry
begin
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("OUT OF BOUNDS");
delay_ms(2000);

if(flag_plotcoor) enter_plotCoor();
else if(flag_freedraw) enter_freedraw();
else enter_main();
end
end

// UPDATE() - once plot is completed, it updates all coordinate values
// then return to respective modes after completion
void Update(void)
begin
current_y = current_y + dy;
current_x = current_x + dx;
current_a = current_a + da;
current_b = current_b + db;

if(flag_plotcoor) enter_plotCoor();
else if(flag_freedraw) enter_freedraw();
else if(flag_plottext) plotText();
else if(flag_plotprog) plotProg();
else enter_main();
end

//------------- plot text function -----------------------------
void enter_getText()
begin
pls_wait();

lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("ENTER:");

kt = 30;
i = 0;

flag_plottext = 1;

sprintf(lcd_buffer," ");
mainState = get_text;
CurrState = nopush;
end

// getText() - stores characters entered sequentially by user
// sends string to plotText() when user completes string
void getText()
begin
key_press();
keychar = getKeyChar(key);

// debounce State Machine
switch(CurrState)
begin
case nopush:
keypad_i1 = 0;
keypad_i2 = 0;
if(key != 0xff) CurrState = maybepush;
break;

case maybepush:
if(key !=0xff)
begin
CurrState = pushed;

switch(keychar)
begin
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
ABCcounter = T_ABC;
keypad_i1 = keychar - 48;
keypad_i2 = 0;

lcd_buffer[i] = keypad_char[keypad_i1][keypad_i2];
lcd_gotoxy(0,1); lcd_puts(lcd_buffer);
break;
case 'C': //confirm
lcd_buffer[i] = 0;
sprintf(plotString,"%s",lcd_buffer);
printf("string = %s\r",plotString);
plotStrPos = 0;
flag_donechar = 1;
enter_plotText();
break;
case 'D': //delete
if(i>0)lcd_buffer[i--] = ' ';
lcd_gotoxy(0,1); lcd_puts(lcd_buffer);
break;
case '*': //decline
flag_plottext = 0;
enter_main();
break;
end
end
else CurrState = nopush;
break;

case pushed:
if(key!=0xff)
begin
if(!ABCcounter)
begin
ABCcounter = T_ABC;
if(keypad_char[keypad_i1][++keypad_i2] == 0) keypad_i2 = 0;
lcd_buffer[i] = keypad_char[keypad_i1][keypad_i2];
lcd_gotoxy(0,1); lcd_puts(lcd_buffer);
end
end
else CurrState = maybenopush;
break;
case maybenopush:
if(key!=0xff) CurrState = pushed;
else
begin
CurrState = nopush;
i++;
end
break;
end
end

void enter_plotText()
begin
// if string not yet completed and character completed,
// retrieve new one
if((plotChar = plotString[plotStrPos++]) && flag_donechar)
begin

// goes thru data arrays and retrieve appropriate index
flag_donechar = 0;
if(plotChar == 46) map_i1 = 0;
else if(plotChar == 45) map_i1 = 1;
else if(plotChar == 32) map_i1 = 2;
else if(plotChar >= 48 && plotChar <= 57) map_i1 = plotChar-45;
else if(plotChar >= 65 && plotChar <= 90) map_i1 = plotChar-52;
else map_i1 = 0;

map_i2 = 0;

plotText();
end
else enter_getText();
end
// plotText() - goes thru each character and plots their lines one by one
// after completing the line it increments the second index, after completing
// the character it recalls enter_plotText() for next one.
void plotText()
begin
//if still plotting same character
if(char_penlift_map[map_i1][map_i2])
begin
// set parameters to appropriate values
PORTD.7 = char_penlift_map[map_i1][map_i2]-1;
delay_ms(20);
plotAngle = ((float)char_angle_map[map_i1][map_i2])/180.0*PI;
plotDist = char_dist_map[map_i1][map_i2++];

enter_plotLine(); // plot the line
end
else // after completing plotting of character
begin
flag_donechar = 1;
enter_plotText();
end
end

//------------- plot prog function -----------------------------
void enter_progMenu(void)
begin
lcd_clear();
lcd_gotoxy(0,0);
lcd_puts(prog_selection[0]);

lcd_gotoxy(0,1);
lcd_puts(prog_selection[1]);
currMenuSelect = 0;

kt = 500;

CurrState = nopush;
mainState = prog_menu;
end
//--------- program menu------------------------
void progMenu(void)
begin
key_press(); //get key press from keypad
keychar = getKeyChar(key);

switch(keychar)
begin
case '1': // square
prog_select = sqre_select;
enter_getLength();
break;
case '2': // triangle
prog_select = trig_select;
enter_getLength();
break;

case '3': //house
prog_select = house_select;
pls_wait();
enter_plotProg();
// make house selection
break;

case 'A': //scroll up
LCDscrUPprog();
break;
case 'B': //scroll down
LCDscrDOWNprog();
break;
case '*': //decline
flag_plotprog = 0;
enter_main();
break;
end
end
//--------- Menu Scroll Functions
void LCDscrUPprog(void)
begin
if(currMenuSelect > 0)
begin
lcd_clear();
lcd_gotoxy(0,1);
lcd_puts(prog_selection[currMenuSelect--]);

lcd_gotoxy(0,0);
lcd_puts(prog_selection[currMenuSelect]);
end
end

void LCDscrDOWNprog(void)
begin
if(currMenuSelect < 3)
begin
lcd_clear();
lcd_gotoxy(0,0);
lcd_puts(prog_selection[++currMenuSelect]);

lcd_gotoxy(0,1);
lcd_puts(prog_selection[currMenuSelect+1]);
end
end
//-------------------- GET LENGTH -------------------------------
void enter_getLength()
begin
pls_wait();

kt = 30;
i = 0;

sprintf(lcd_buffer," ");
mainState = get_length;
CurrState = nopush;

lcd_clear(); lcd_gotoxy(0,0);
lcd_putsf("LENGTH?");

end

// getLength() - prompts user for object length
void getLength()
begin
key_press();
keychar = getKeyChar(key);

// debounce State Machine
switch(CurrState)
begin
case nopush:
if(key != 0xff) CurrState = maybepush;
break;

case maybepush:
if(key !=0xff)
begin
CurrState = pushed;
switch(keychar)
begin
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
lcd_buffer[i++] = keychar;
lcd_gotoxy(8,0);
lcd_puts(lcd_buffer);
break;
case 'C': //confirm
plotDist = atof(lcd_buffer);
if((plotDist > 10) || (plotDist < 0))
begin
lcd_gotoxy(0,0);
lcd_putsf("INVALID LENGTH");
delay_ms(1000);
enter_getLength();
end
else
begin
pls_wait();
enter_plotProg();
end
break;
case 'D': //delete
if(i>0) lcd_buffer[--i] = ' ';
lcd_gotoxy(6,0);
lcd_puts(lcd_buffer);
break;
case '*': //decline
flag_plotprog = 0;
enter_main();
break;
end
end
else CurrState = nopush;
break;

case pushed:
if(key==0xff) CurrState = maybenopush;
break;

case maybenopush:
if(key!=0xff) CurrState = pushed;
else CurrState = nopush;
break;
end
end

void enter_plotProg()
begin
kt = 30;
i = 0;

mainState = plot_prog;
flag_plotprog = 1;

map_i1 = 0;
map_i2 = 0;

lcd_clear(); lcd_gotoxy(0,0);
lcd_putsf("PLOTTING");
end

// plotProg() - based on user selection runs plotLine()
// repeatedly to draw selected objects
void plotProg(void)
begin
//square
if(prog_select == sqre_select)
begin
if(sqre_penlift_map[map_i1])
begin
PORTD.7 = sqre_penlift_map[map_i1]-1;
plotAngle = sqre_angle_map[map_i1]/180.0*PI;
plotDist = sqre_dist_map[map_i1++]*plotDist;

enter_plotLine();
end
else enter_progMenu();
end
if(prog_select == trig_select)
begin
if(trig_penlift_map[map_i1])
begin
PORTD.7 = trig_penlift_map[map_i1]-1;
plotAngle = trig_angle_map[map_i1]/180.0*PI;
plotDist = trig_dist_map[map_i1++]*plotDist;

enter_plotLine();
end
else enter_progMenu();
end

if(prog_select == house_select)
begin
if(house_penlift_map[map_i1])
begin
PORTD.7 = house_penlift_map[map_i1]-1;
delay_ms(20);
plotAngle = house_angle_map[map_i1]/180.0*PI;
plotDist = house_dist_map[map_i1++];

printf("PORTD.7 = %i Angle = %i Dist = %i\r",PORTD.7,(int)house_angle_map[map_i1-1],(int)plotDist);
enter_plotLine();
end
else enter_progMenu();
end
end

//********************IMPORTANT PLOT FUNCTIONS*********************************
//CALCULATE dA - calculate the change in da as outlined in "High Level Design"
void calculate_da(void)
begin
da = ((current_x / current_a) * dx) + ((current_y / current_a) * dy);
end

//CALCULATE dB - calculate the change in db as outlined in "High Level Design"
void calculate_db(void)
begin
db = (((current_x - D)/ current_b) * dx) + ((current_y / current_b) * dy);
end
//-------------- RUN MOTOR FUNC -------------------------------
void runMotorR(void)
begin
runtimeR=timebaseR;
Rsteps_left = stepcountR < tempR;

if(Rsteps_left)
begin
if(db<0) tickFR();
else tickBR();
stepcountR++;
end
else flag_MRdone = 1;
end

void runMotorL(void)
begin
runtimeL=timebaseL;
Lsteps_left = stepcountL < tempL;

if(Lsteps_left)
begin
if(da<0) tickFL();
else tickBL();
stepcountL++;
end
else flag_MLdone = 1;
end


//STEP TEST LEFT - maps the four possible "states" of the LEFT motor
//onto appropriate outputs for PORTA. Logic is include to ensure
//output to one half of the Port does not disrupt the other half.
void makeStepL(void)
begin
if (stepL == 0) PORTA = 16+32+(PORTA&0x0F);
if (stepL == 1) PORTA = 32+64+(PORTA&0x0F);
if (stepL == 2) PORTA = 64+128+(PORTA&0x0F);
if (stepL == 3) PORTA = 128+16+(PORTA&0x0F);
end

//STEP TEST RIGHT - same procedure, this time for the RIGHT motor
void makeStepR(void)
begin
if (stepR == 0) PORTA = 1+2+(PORTA&0xF0);
if (stepR == 1) PORTA = 2+4+(PORTA&0xF0);
if (stepR == 2) PORTA = 4+8+(PORTA&0xF0);
if (stepR == 3) PORTA = 8+1+(PORTA&0xF0);
end

//LEFT BACK TICK - function to make the LEFT motor tick BACKWARDS once
void tickBL(void)
begin
if(MSCL - 1 >= MINSTEPS)
begin
if (stepL-- == 0) stepL = 3;
makeStepL();
MSCL--;
end
end

//LEFT FORWARD TICK - function to make the LEFT motor tick FORWARDS once
void tickFL(void)
begin
if(MSCL + 1 <= MAXSTEPS)
begin
if (stepL++ == 3) stepL = 0;
MSCL++;
makeStepL();
end
end

//RIGHT BACK TICK - function to make the RIGHT motor tick BACKWARDS once
void tickBR(void)
begin
if(MSCR - 1 >= MINSTEPS)
begin
if (stepR++ == 3) stepR = 0;
makeStepR();
MSCR--;
end
end


//RIGHT FORWARD TICK - function to make the RIGHT motor tick FORWARD once
void tickFR(void)
begin
if(MSCR+1<=MAXSTEPS)
begin
if (stepR-- == 0) stepR = 3;
makeStepR();
MSCR++;
end
end

//KEY PRESS - semi-standard keypad scanning routine. Scans one half,
//then the other. then looks up the mapping in a flash table and assigns
//the mapping a number. if it's invalid, then assign it the designated
//invalid number.
void key_press(void)
begin
//get lower nibble
DDRB = 0x0f;
PORTB = 0xf0;
delay_us(5);
key = PINB;

//get upper nibble
DDRB = 0xf0;
PORTB = 0x0f;
delay_us(5);
key = key | PINB;
end

char getKeyChar(char k)
begin
switch(k)
begin
case 0xd7: return '0';
case 0xee: return '1';
case 0xde: return '2';
case 0xbe: return '3';
case 0xed: return '4';
case 0xdd: return '5';
case 0xbd: return '6';
case 0xeb: return '7';
case 0xdb: return '8';
case 0xbb: return '9';
case 0xe7: return '*';
case 0xb7: return '#';
case 0x7e: return 'A';
case 0x7d: return 'B';
case 0x7b: return 'C';
case 0x77: return 'D';
end
end

// liftpen() - uses solenoid to lift the pen
void liftpen()
begin
liftpentime = 30;
key_press();

switch(penState)
begin
case nopush:
if(key!=0xff) penState=maybepush;
break;

case maybepush:
if(key!=0xff)
begin
if(getKeyChar(key) == '#') PORTD.7 ^= 1;
penState=pushed;
end
else penState=nopush;
break;

case pushed:
if(key==0xff) penState=maybenopush;
break;

case maybenopush:
if(key!=0xff) penState=pushed;
else penState=nopush;
break;
end
end

// delay function
void pls_wait()
begin
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("PLEASE WAIT...");
delay_ms(1000);
end

@