Last active
August 29, 2015 14:01
-
-
Save furkantektas/e1ed65ffd13d6d74bdaa to your computer and use it in GitHub Desktop.
HCS12 Garage Simulation Project
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <hidef.h> /* common defines and macros */ | |
| #include "derivative.h" /* derivative-specific definitions */ | |
| #include <stdio.h> /* derivative-specific definitions */ | |
| #include <stdlib.h> | |
| #include "lcd.h" | |
| #pragma CODE_SEG __NEAR_SEG NON_BANKED | |
| // ASM | |
| #define enable_intr() __asm(cli) | |
| #define disable_intr() __asm(sei) | |
| // INTERRUPTS | |
| #define INTERRUPT_RTI interrupt(((0x10000-Vrti)/2)-1) | |
| #define INTERRUPT_TIMOVF interrupt(((0x10000-Vtimovf)/2)-1) | |
| #define INTERRUPT_TIMCH1 interrupt (((0x10000 - Vtimch1)/2)-1) | |
| #define INTERRUPT_TIMCH2 interrupt (((0x10000 - Vtimch2)/2)-1) | |
| #define INTERRUPT_TIMCH4 interrupt (((0x10000 - Vtimch4)/2)-1) | |
| // #define INTERRUPT_BUZ interrupt (((0x10000 - Vtimch5)/2)-1) | |
| #define BIT0 0x01 | |
| #define BIT1 0x02 | |
| #define BIT2 0x04 | |
| #define BIT3 0x08 | |
| #define BIT4 0x10 | |
| #define BIT5 0x20 | |
| #define BIT6 0x40 | |
| #define BIT7 0x80 | |
| #define KEYA 10 | |
| #define KEYB 11 | |
| #define KEYC 12 | |
| #define KEYD 13 | |
| #define KEYASTERISK 14 | |
| #define KEYSHARP 15 | |
| // KEYPAD | |
| #define ROW_NUM 4 | |
| #define COL_NUM 4 | |
| #define TRUE 1 | |
| #define FALSE 0 | |
| #define MIN 1 | |
| #define SEC 0 | |
| #define NOTAKEY -1 | |
| #define BUZZERSEC 1 | |
| #define TIMOVFPERSEC 11 | |
| #define RTIOVFPERSEC 25 | |
| #define TIMERRESOLUTION 40 | |
| #define SENSORDISTANCE 143500 | |
| #define MAXSPEED 60 | |
| #define NOTINITIALIZED -1 | |
| #define MSGHIGHSPEED "HIGH SPEED!" | |
| #define MSGGARAGEFULLERROR "GARAGE FULL!" | |
| #define MSGGARAGEEMPTYERROR "GARAGE EMPTY!" | |
| #define GARAGECAPACITY 5 | |
| #define LCDWIDTH 16 | |
| #define MAINMENUCOUNT 2 | |
| volatile const char menu[MAINMENUCOUNT][LCDWIDTH] = { | |
| "GARAGE", | |
| "CLOCK & AVG" | |
| }; | |
| typedef enum direction_e{ | |
| IN, | |
| OUT, | |
| NODIRECTION | |
| } direction; | |
| typedef enum screenModes_e{ | |
| MODEGARAGE, | |
| MODECLOCK, | |
| MODEHIGHSPEED, | |
| MODEMAINMENU, | |
| MODEGARAGEFULL, | |
| MODEGARAGEEMPTY, | |
| } screenModes; | |
| const int MAXINT = (unsigned int) -1; | |
| // Global Variables | |
| // Time variables | |
| volatile unsigned int rtiCount = 0, | |
| timerCount = 0, | |
| timer1Val = NOTINITIALIZED, | |
| timer2Val = NOTINITIALIZED; | |
| volatile int remainingTime = -1; | |
| volatile short gMenuSelection = 0, | |
| gGaraceCount = 0, | |
| gTotalGaraceInCount = 0, | |
| gTotalGaraceOutCount = 0; | |
| short gLastSpeed = 0.0; | |
| direction gLastDirection = '-'; | |
| screenModes gMode = MODEMAINMENU, oldGMode = MODEMAINMENU; | |
| // boolean variables | |
| volatile short refreshLCD = TRUE, | |
| autoRefreshLCD = FALSE, | |
| wasHighSpeed = FALSE; | |
| direction getDirection(); | |
| char getLastDirectionChar(); | |
| // Speed Functions | |
| short getSpeed() { | |
| int timeInterval = abs(timer1Val-timer2Val); | |
| if(timer1Val == NOTINITIALIZED || timer2Val == NOTINITIALIZED) | |
| return NOTINITIALIZED; | |
| if(timeInterval < 0) | |
| timeInterval *= -1; | |
| else if(timeInterval == 0) | |
| return MAXINT; | |
| return (SENSORDISTANCE/(timeInterval* RTIOVFPERSEC * TIMERRESOLUTION)); | |
| } | |
| short isHighSpeed() { | |
| short speed = getSpeed(); | |
| if(speed == NOTINITIALIZED) | |
| return FALSE; | |
| if(speed == MAXINT) | |
| return TRUE; | |
| return (speed > MAXSPEED); | |
| } | |
| void resetTimers() { | |
| gLastSpeed = getSpeed(); | |
| wasHighSpeed = isHighSpeed(); | |
| gLastDirection = getLastDirectionChar(); | |
| timer1Val = NOTINITIALIZED; | |
| timer2Val = NOTINITIALIZED; | |
| } | |
| direction getDirection() { | |
| // if timer1 and timer2 does not initialized or equal, no direction is captured | |
| if(timer1Val == timer2Val || timer1Val == NOTINITIALIZED || timer2Val == NOTINITIALIZED) | |
| return NODIRECTION; | |
| return (timer1Val > timer2Val); | |
| } | |
| void PlaySound(void); | |
| // Keypad Functions | |
| int ReadKey(void); | |
| void disableTimerIntr(void) { | |
| TIOS = TIOS | BIT5; /* Configure PT5 as Output */ | |
| /* Compare */ | |
| TCTL2 = (TCTL2 | BIT2) & ~BIT3; /* Set up PT5 */ | |
| /* to toggle on compare */ | |
| TFLG1 = 0x00; /* Clear Channel 5 flag */ | |
| TSCR2 = TSCR2 | BIT7; | |
| } | |
| //Interrupts | |
| INTERRUPT_RTI void intrRti(void); | |
| INTERRUPT_TIMCH1 void intrTimCh1(void); | |
| INTERRUPT_TIMCH2 void intrTimCh2(void); | |
| INTERRUPT_TIMCH4 void intrTimCh4(void); | |
| INTERRUPT_TIMOVF void intrTimovf(void); | |
| // INTERRUPT_BUZ void SoundBuzzer(void); | |
| void init(void); | |
| void printMenu(void); | |
| void enableBuzzer(void) { | |
| PTT = PTT | BIT5; | |
| } | |
| void toggleBuzzer(void) { | |
| PTT = PTT ^ BIT5; | |
| } | |
| void disableBuzzer(void) { | |
| PTT = PTT & ~BIT5; | |
| } | |
| short isGarageFull() { | |
| return gGaraceCount >= GARAGECAPACITY; | |
| } | |
| short isGarageEmpty() { | |
| return gGaraceCount <= 0; | |
| } | |
| void main(void) { | |
| int key = NOTAKEY; | |
| direction d = NODIRECTION; | |
| // Initializing RTI, LCD and Keypad | |
| init(); | |
| for(;;) { | |
| do { | |
| d = getDirection(); | |
| if(d == IN) { | |
| if(!isGarageFull()) { | |
| gGaraceCount += 1; | |
| gTotalGaraceInCount += 1; | |
| } | |
| } | |
| else if(d == OUT) { | |
| if(!isGarageEmpty()) { | |
| gGaraceCount -= 1; | |
| gTotalGaraceOutCount += 1; | |
| } | |
| } | |
| if(wasHighSpeed) { | |
| oldGMode = (gMode == MODEHIGHSPEED) ? oldGMode : gMode; // backing up the current mode | |
| gMode = MODEHIGHSPEED; | |
| PlaySound(); | |
| refreshLCD = TRUE; | |
| } | |
| if(d == IN && isGarageFull()) { | |
| oldGMode = (gMode == MODEGARAGEFULL) ? oldGMode : gMode; // backing up the current mode | |
| gMode = MODEGARAGEFULL; | |
| PlaySound(); | |
| refreshLCD = TRUE; | |
| } | |
| if(d == OUT && isGarageEmpty()) { | |
| oldGMode = (gMode == MODEGARAGEEMPTY) ? oldGMode : gMode; // backing up the current mode | |
| gMode = MODEGARAGEEMPTY; | |
| PlaySound(); | |
| refreshLCD = TRUE; | |
| } | |
| if(d != NODIRECTION) { | |
| resetTimers(); | |
| refreshLCD = TRUE; | |
| } | |
| // if(!(rtiCount%RTIOVFPERSEC)) | |
| printMenu(); | |
| // if(!(rtiCount%5000)) | |
| // resetTimers(); | |
| //refreshLCD = TRUE; | |
| } while((key=ReadKey()) == NOTAKEY); | |
| switch(key) { | |
| case KEYA: // up | |
| gMenuSelection = (gMenuSelection-1+MAINMENUCOUNT)%MAINMENUCOUNT; | |
| break; | |
| case KEYB: // down | |
| gMenuSelection = (gMenuSelection+1+MAINMENUCOUNT)%MAINMENUCOUNT; | |
| break; | |
| case KEYD: // return | |
| gMode = ((gMenuSelection+MAINMENUCOUNT)%MAINMENUCOUNT); | |
| break; | |
| case KEYASTERISK: // escape | |
| gMode = MODEMAINMENU; | |
| break; | |
| } | |
| if(key == KEYA || key == KEYB || key == KEYD || key == KEYASTERISK) | |
| refreshLCD = TRUE; | |
| else | |
| refreshLCD = (!(rtiCount % RTIOVFPERSEC)); | |
| //refresh lcd at least on each second | |
| _FEED_COP(); | |
| } /* loop forever */ | |
| /* please make sure that you never leave main */ | |
| } | |
| /* Initializing hw for the program */ | |
| void init() { | |
| DisableInterrupts; | |
| /* Enabling keypad */ | |
| DDRA = 0x0F; /* PORTA[0-3] is out, PORTA[4-7] input */ | |
| DDRT = DDRT | BIT5; /* PORTT as output for PT5 buzzer */ | |
| DDRB = 0xFF; /* Set PortB as out */ | |
| DDRP = 0x00; /* Disabling 7segment */ | |
| PTP = 0x00; | |
| PORTB = 0x00; | |
| /* Enabling RTI */ | |
| RTICTL = 0x64; /* Set rate to 20.48 ms */ | |
| CRGINT = BIT7; /* Enable RTI interrupts */ | |
| CRGFLG = BIT7; /* Clear RTI Flag */ | |
| /* Setup for IC1 */ | |
| TIOS = TIOS & ~BIT1; /* Configure PT1 as IC */ | |
| TCTL4 = (TCTL4 | BIT3) & ~BIT2; /* Capture Rising Edge */ | |
| TIE |= BIT1; /* Enable IC1 Interrupt */ | |
| TFLG1 = BIT1; /* Clear IC1 Flag */ | |
| /* Setup for IC2 */ | |
| TIOS = TIOS & ~BIT2; /* Configure PT2 as IC */ | |
| TCTL4 = (TCTL4 | BIT5) & ~BIT4; /* Capture Rising Edge */ | |
| TIE |= BIT2; /* Enable IC2 Interrupt */ | |
| TFLG1 = BIT2; /* Clear IC2 Flag */ | |
| /* Setup for OC5 */ | |
| TIOS |= BIT4; /* Configure PT5 as OC */ | |
| TCTL1 = (TCTL1 | BIT1) & ~BIT2; /* Capture Rising Edge */ | |
| TIE |= BIT4; /* Enable oC5 Interrupt */ | |
| TFLG1 = BIT4; /* Clear OC5 Flag */ | |
| /* Turn on timer subsystem */ | |
| TSCR1 = BIT7; | |
| /* Set prescaler to 32 (87.38 ms), enable TOF interrupt */ | |
| TSCR2 = 0x05; | |
| TFLG2 = BIT7; /* Clearing overflow flag */ | |
| openlcd(); | |
| EnableInterrupts; | |
| } | |
| // Gets pressed key number from keypad | |
| // Waits for any key to pressed otherwise won't return. | |
| int ReadKey() { | |
| int row, | |
| column; | |
| const char row_mask[ROW_NUM] = { 0xFE,0xFD,0xFB,0xF7 }; | |
| const char col_mask[COL_NUM] = { BIT4,BIT5,BIT6,BIT7 }; | |
| const unsigned int keys[ROW_NUM][COL_NUM] = | |
| { 1,2,3,10, | |
| 4,5,6,11, | |
| 7,8,9,12, | |
| 14,0,15,13 }; | |
| for(row=0 ; row < ROW_NUM ; ++row) { | |
| PORTA = 0xFF;// Reset PortA | |
| for(column=0; column < COL_NUM ;++column) { | |
| PORTA = PORTA & row_mask[row]; // Set specific row to 0 | |
| if( (PORTA & col_mask[column]) == 0 ) { | |
| // Check if any key is pressed | |
| delay_1ms(10); | |
| // Wait 50ms and check again for make sure key is pressed. | |
| if( (PORTA & col_mask[column]) == 0 ) { | |
| refreshLCD = TRUE; | |
| return keys[row][column]; | |
| } | |
| } | |
| } | |
| } | |
| return NOTAKEY; | |
| } | |
| INTERRUPT_RTI void intrRti(void) { | |
| ++rtiCount; | |
| CRGFLG = BIT7; | |
| } | |
| // INTERRUPT_TIMOVF void intrTimovf(void) { | |
| // ++timerCount; | |
| // if(!(--remainingTime)) { | |
| // disableBuzzer(); | |
| // disableTimerIntr(); | |
| // } | |
| // TFLG2 = BIT7; | |
| // } | |
| INTERRUPT_TIMCH1 void intrTimCh1(void) { | |
| PORTB ^= BIT5; | |
| timer1Val = rtiCount; | |
| TFLG1 |= BIT1; | |
| refreshLCD = TRUE; | |
| } | |
| INTERRUPT_TIMCH2 void intrTimCh2(void) { | |
| PORTB ^= BIT3; | |
| timer2Val = rtiCount; | |
| TFLG1 |= BIT2; | |
| refreshLCD = TRUE; | |
| } | |
| INTERRUPT_TIMCH4 void intrTimCh4(void) { | |
| if(remainingTime >= 0) { | |
| --remainingTime; | |
| toggleBuzzer(); | |
| } | |
| // using different frequency for highspeed warning | |
| TC4 += (gMode == MODEHIGHSPEED) ? 750 : 1250; | |
| TFLG1 |= BIT4; | |
| } | |
| char direction2Char(direction d) { | |
| if(d == NODIRECTION) | |
| return '-'; | |
| return getDirection() ? '<' : '>'; | |
| } | |
| char getDirectionChar() { | |
| return direction2Char(getDirection()); | |
| } | |
| char getLastDirectionChar() { | |
| return direction2Char(gLastDirection); | |
| } | |
| void printLCD(char* txt) { | |
| clearDisplay(); | |
| while (*txt) { /* While character to send */ | |
| if(*txt == '\n') | |
| moveCursorToSecondLine(); | |
| else | |
| put2lcd(*txt,DATA); /* Write data to LCD */ | |
| ++txt; /* Go to next character */ | |
| } | |
| } | |
| /* Time functions | |
| Returns the time since program start */ | |
| int getSec() { | |
| return (rtiCount/RTIOVFPERSEC)%60; | |
| } | |
| int getMin() { | |
| return (rtiCount/(RTIOVFPERSEC*60))%60; | |
| } | |
| int getHour() { | |
| return (rtiCount/(RTIOVFPERSEC*60*60))%60; | |
| } | |
| int getTotalGarageInCount() { | |
| return gTotalGaraceInCount/(getMin()+getHour()*60); | |
| } | |
| int getTotalGarageOutCount() { | |
| return gTotalGaraceOutCount/(getMin()+getHour()*60); | |
| } | |
| void printClockMenu(char* txt) { | |
| autoRefreshLCD = TRUE; | |
| sprintf(txt," %02d:%02d:%02d\nAVG IN:%d OUT:%d",getHour(),getMin(),getSec(),getTotalGarageInCount(),getTotalGarageOutCount()); | |
| } | |
| void showMainMenu(char* txt) { | |
| autoRefreshLCD = FALSE; | |
| sprintf(txt,"%d",ReadKey()); | |
| } | |
| void printMainMenu(char* txt) { | |
| /* FIXING MODULO BUG */ | |
| int a = (gMenuSelection+MAINMENUCOUNT)%MAINMENUCOUNT, | |
| b = (gMenuSelection+1+MAINMENUCOUNT)%MAINMENUCOUNT; | |
| autoRefreshLCD = FALSE; | |
| sprintf(txt,">%s\n %s",menu[a],menu[b]); | |
| } | |
| void printGarageMenu(char* txt) { | |
| sprintf(txt,"IN=%d DIR:%c\nSPEED = %d",gGaraceCount,gLastDirection,gLastSpeed); | |
| } | |
| void printHighSpeedMenu(char* txt) { | |
| sprintf(txt,"%s\nrt=%d",MSGHIGHSPEED, remainingTime); | |
| autoRefreshLCD = TRUE; | |
| if(remainingTime <= 0) { | |
| gMode = ((oldGMode == MODEHIGHSPEED) ? MODEMAINMENU : oldGMode); | |
| refreshLCD = TRUE; | |
| } | |
| } | |
| void printError(char* txt, char *msg, screenModes s) { | |
| sprintf(txt,"%s\n",msg); | |
| autoRefreshLCD = TRUE; | |
| if(remainingTime <= 0) { | |
| gMode = ((oldGMode == s) ? MODEMAINMENU : oldGMode); | |
| refreshLCD = TRUE; | |
| } | |
| } | |
| void printMenu(void) { | |
| char txt[34]; //1 for \n | |
| if(refreshLCD) { | |
| switch(gMode) { | |
| case MODECLOCK: | |
| printClockMenu(txt); | |
| break; | |
| case MODEHIGHSPEED: | |
| //printHighSpeedMenu(txt); | |
| wasHighSpeed = wasHighSpeed ? FALSE : wasHighSpeed; | |
| printError(txt,MSGHIGHSPEED,MODEHIGHSPEED); | |
| break; | |
| case MODEGARAGE: | |
| printGarageMenu(txt); | |
| break; | |
| case MODEGARAGEFULL: | |
| printError(txt,MSGGARAGEFULLERROR,MODEGARAGEFULL); | |
| break; | |
| case MODEGARAGEEMPTY: | |
| printError(txt,MSGGARAGEEMPTYERROR,MODEGARAGEEMPTY); | |
| break; | |
| case MODEMAINMENU: | |
| default: | |
| printMainMenu(txt); | |
| break; | |
| } | |
| printLCD(txt); | |
| } | |
| // refreshing LCD twice per second if autoRefreshLCD is enabled | |
| refreshLCD = (autoRefreshLCD && !(rtiCount % (RTIOVFPERSEC/2))); | |
| } | |
| void PlaySound(void) { | |
| remainingTime = BUZZERSEC * TIMOVFPERSEC * TIMERRESOLUTION; | |
| autoRefreshLCD = TRUE; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef LCD_H_ | |
| #define LCD_H_ | |
| /* Dragon12 Development Board LCD */ | |
| #define LCD_DAT PORTK /* Port K drives LCD data pins, E, and RS */ | |
| #define LCD_DIR DDRK /* Direction of LCD port */ | |
| #define LCD_E 0x02 /* LCD E signal */ | |
| #define LCD_RS 0x01 /* LCD Register Select signal */ | |
| #define CMD 0 /* Command type for put2lcd */ | |
| #define DATA 1 /* Data type for put2lcd */ | |
| /* Prototypes for functions in lcd.c */ | |
| void openlcd(void); /* Initialize LCD display */ | |
| void put2lcd(char c, char type); /* Write command or data to LCD controller */ | |
| void puts2lcd (char *ptr); /* Write a string to the LCD display */ | |
| void delay_50us(int n); /* Delay n 50 microsecond intervals */ | |
| void delay_1ms(int n); /* Delay n 1 millisecond intervals */ | |
| void clearDisplay(void); | |
| void moveCursorToSecondLine(void); | |
| void openlcd(void) { | |
| LCD_DIR = 0xFF; /* configure LCD_DAT port for output */ | |
| delay_1ms(50); /* Wait for LCD to be ready */ | |
| put2lcd(0x28,CMD); /* set 4-bit data, 2-line display, 5x7 font */ | |
| put2lcd(0x0F,CMD); /* turn on display, cursor, blinking */ | |
| put2lcd(0x06,CMD); /* move cursor right */ | |
| put2lcd(0x01,CMD); /* clear screen, move cursor to home */ | |
| delay_1ms(1); /* wait until "clear display" command is complete */ | |
| } | |
| void puts2lcd (char *ptr) { | |
| while (*ptr) { /* While character to send */ | |
| put2lcd(*ptr,DATA); /* Write data to LCD */ | |
| delay_50us(1); /* Wait for data to be written */ | |
| ptr++; /* Go to next character */ | |
| } | |
| } | |
| void put2lcd(char c, char type) { | |
| char c_lo, c_hi; | |
| c_hi = (c & 0xF0) >> 2; /* Upper 4 bits of c */ | |
| c_lo = (c & 0x0F) << 2; /* Lower 4 bits of c */ | |
| if (type == DATA) LCD_DAT |= LCD_RS; /* select LCD data register */ | |
| else LCD_DAT &= (~LCD_RS); /* select LCD command register */ | |
| if (type == DATA) | |
| LCD_DAT = c_hi|LCD_E|LCD_RS; /* output upper 4 bits, E, RS high */ | |
| else | |
| LCD_DAT = c_hi|LCD_E; /* output upper 4 bits, E, RS low */ | |
| LCD_DAT |= LCD_E; /* pull E signal to high */ | |
| __asm(nop); /* Lengthen E */ | |
| __asm(nop); | |
| __asm(nop); | |
| LCD_DAT &= (~LCD_E); /* pull E to low */ | |
| if (type == DATA) | |
| LCD_DAT = c_lo|LCD_E|LCD_RS; /* output lower 4 bits, E, RS high */ | |
| else | |
| LCD_DAT = c_lo|LCD_E; /* output lower 4 bits, E, RS low */ | |
| LCD_DAT |= LCD_E; /* pull E to high */ | |
| __asm(nop); /* Lengthen E */ | |
| __asm(nop); | |
| __asm(nop); | |
| LCD_DAT &= (~LCD_E); /* pull E to low */ | |
| delay_50us(1); /* Wait for command to execute */ | |
| } | |
| #define D50US 133 /* Inner loop takes 9 cycles; need 50x24 = 1200 cycles */ | |
| void delay_50us(int n) { | |
| volatile int c; | |
| for (;n>0;n--) | |
| for (c=D50US;c>0;c--) ; | |
| } | |
| #define D10US 26 | |
| void delay_10us(int n) { | |
| volatile int c; | |
| for (;n>0;n--) | |
| for (c=D10US;c>0;c--) ; | |
| } | |
| void delay_1ms(int n) { | |
| for (;n>0;n--) delay_50us(200); | |
| } | |
| void delay_halfms(int n) { | |
| for (;n>0;n--) delay_50us(100); | |
| } | |
| void clearDisplay(void) { | |
| put2lcd(0x01,CMD); /* clear screen, move cursor to home */ | |
| delay_50us(40); /* wait until "clear display" command is complete */ | |
| } | |
| void moveCursorToSecondLine(void) { | |
| put2lcd(0xC0,CMD); | |
| delay_50us(40); /* wait until "clear display" command is complete */ | |
| } | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment