460 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			460 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <LPC17xx.h>
 | ||
| #include <stdbool.h>
 | ||
| 
 | ||
| /* ===================================================
 | ||
|  * SMART CALCULATOR - LPC1768 (Final v2)
 | ||
|  * Supports BIN, BASE4, OCT, DEC, and HEX
 | ||
|  * with 4x4 keypad, LCD, and 7-seg status display.
 | ||
|  *
 | ||
|  * Features:
 | ||
|  *  - Base-aware arithmetic and I/O
 | ||
|  *  - HEX: double MODE button press within 2s = operator mode
 | ||
|  *  - Hardware 1ms timing via SysTick
 | ||
|  * ===================================================
 | ||
|  */
 | ||
| 
 | ||
| /* 7-Segment patterns for 0–F */
 | ||
| const unsigned char seven_seg[16] = {
 | ||
|     0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
 | ||
|     0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71
 | ||
| };
 | ||
| 
 | ||
| /* ===== Pin & Mode Definitions ===== */
 | ||
| #define COL_BASE 15
 | ||
| #define ROW_BASE 19
 | ||
| #define COL_MASK (0x0F << COL_BASE)
 | ||
| #define ROW_MASK (0x0F << ROW_BASE)
 | ||
| 
 | ||
| #define SEG_SHIFT 4
 | ||
| #define DIGIT_EN (1 << 23)
 | ||
| 
 | ||
| #define LCD_DATA_SHIFT 23
 | ||
| #define LCD_DATA_MASK (0x0F << LCD_DATA_SHIFT)
 | ||
| #define LCD_RS (1 << 27)
 | ||
| #define LCD_EN (1 << 28)
 | ||
| 
 | ||
| #define MODE_BUTTON (1 << 12)
 | ||
| 
 | ||
| /* Supported Bases */
 | ||
| #define MODE_BIN 2
 | ||
| #define MODE_BASE4 4
 | ||
| #define MODE_OCT 8
 | ||
| #define MODE_DEC 10
 | ||
| #define MODE_HEX 16
 | ||
| 
 | ||
| /* Operators */
 | ||
| #define OP_NONE 0
 | ||
| #define OP_ADD 1
 | ||
| #define OP_SUB 2
 | ||
| #define OP_MUL 3
 | ||
| #define OP_CLR 4
 | ||
| #define OP_RES 5
 | ||
| 
 | ||
| /* ===== Global Variables ===== */
 | ||
| unsigned int current_base;
 | ||
| unsigned int input_num;
 | ||
| unsigned int stored_num;
 | ||
| unsigned int result;
 | ||
| unsigned int operation;
 | ||
| unsigned char result_displayed;
 | ||
| 
 | ||
| unsigned int key, last_key, stable;
 | ||
| unsigned int button_state, last_button_state, button_stable;
 | ||
| 
 | ||
| unsigned char hex_operator_mode;
 | ||
| unsigned long last_mode_press_time;
 | ||
| 
 | ||
| /* Millisecond counter via SysTick */
 | ||
| volatile unsigned long sys_millis = 0;
 | ||
| 
 | ||
| /* ===== Function Declarations ===== */
 | ||
| void delay(volatile unsigned int d);
 | ||
| void lcd_delay(unsigned long r);
 | ||
| void lcd_write_nibble(unsigned char nibble, unsigned char is_data);
 | ||
| void lcd_cmd(unsigned char cmd);
 | ||
| void lcd_data(unsigned char data);
 | ||
| void lcd_init(void);
 | ||
| void lcd_print_str(const char *str);
 | ||
| void lcd_print_num(unsigned int num, unsigned int base);
 | ||
| void display_mode(void);
 | ||
| void display_input(void);
 | ||
| void display_result(void);
 | ||
| unsigned int scan_keypad(void);
 | ||
| unsigned int scan_mode_button(void);
 | ||
| unsigned int is_valid_digit(unsigned int key);
 | ||
| void change_mode(void);
 | ||
| void handle_mode_button(void);
 | ||
| void operate(unsigned int op);
 | ||
| void perform_operation(void);
 | ||
| unsigned char is_operator_active(unsigned int key);
 | ||
| unsigned int base_arith(unsigned int a, unsigned int b,
 | ||
|                         unsigned int op, unsigned int base);
 | ||
| unsigned long millis(void);
 | ||
| 
 | ||
| /* ===== Utility ===== */
 | ||
| void delay(volatile unsigned int d) {
 | ||
|     while (d--) __NOP();
 | ||
| }
 | ||
| 
 | ||
| void lcd_delay(unsigned long r) {
 | ||
|     volatile unsigned long i;
 | ||
|     for (i = 0; i < r; i++)
 | ||
|         ;
 | ||
| }
 | ||
| 
 | ||
| /* ===== SysTick Millisecond Tracker ===== */
 | ||
| void SysTick_Handler(void) {
 | ||
|     sys_millis++;
 | ||
| }
 | ||
| 
 | ||
| unsigned long millis(void) {
 | ||
|     return sys_millis;
 | ||
| }
 | ||
| 
 | ||
| /* ===== LCD Control ===== */
 | ||
| void lcd_write_nibble(unsigned char nibble, unsigned char is_data) {
 | ||
|     unsigned long temp;
 | ||
|     temp = (nibble & 0x0F) << LCD_DATA_SHIFT;
 | ||
|     LPC_GPIO0->FIOPIN = (LPC_GPIO0->FIOPIN & ~LCD_DATA_MASK) | temp;
 | ||
| 
 | ||
|     if (is_data)
 | ||
|         LPC_GPIO0->FIOSET = LCD_RS;
 | ||
|     else
 | ||
|         LPC_GPIO0->FIOCLR = LCD_RS;
 | ||
| 
 | ||
|     LPC_GPIO0->FIOSET = LCD_EN;
 | ||
|     lcd_delay(100);
 | ||
|     LPC_GPIO0->FIOCLR = LCD_EN;
 | ||
|     lcd_delay(250000);
 | ||
| }
 | ||
| 
 | ||
| void lcd_cmd(unsigned char cmd) {
 | ||
|     lcd_write_nibble(cmd >> 4, 0);
 | ||
|     lcd_write_nibble(cmd, 0);
 | ||
| }
 | ||
| 
 | ||
| void lcd_data(unsigned char data) {
 | ||
|     lcd_write_nibble(data >> 4, 1);
 | ||
|     lcd_write_nibble(data, 1);
 | ||
| }
 | ||
| 
 | ||
| void lcd_init(void) {
 | ||
|     lcd_delay(5000000);
 | ||
|     lcd_write_nibble(0x03, 0);
 | ||
|     lcd_delay(500000);
 | ||
|     lcd_write_nibble(0x03, 0);
 | ||
|     lcd_delay(500000);
 | ||
|     lcd_write_nibble(0x03, 0);
 | ||
|     lcd_delay(500000);
 | ||
|     lcd_write_nibble(0x02, 0);
 | ||
|     lcd_delay(500000);
 | ||
| 
 | ||
|     lcd_cmd(0x28);
 | ||
|     lcd_cmd(0x0C);
 | ||
|     lcd_cmd(0x01);
 | ||
|     lcd_delay(500000);
 | ||
|     lcd_cmd(0x06);
 | ||
| }
 | ||
| 
 | ||
| void lcd_print_str(const char *str) {
 | ||
|     while (*str)
 | ||
|         lcd_data(*str++);
 | ||
| }
 | ||
| 
 | ||
| void lcd_print_num(int num, unsigned int base) {
 | ||
|     char buffer[17];
 | ||
|     int i = 0;
 | ||
|     unsigned int unum;
 | ||
| 
 | ||
|     if (base == MODE_DEC) {
 | ||
|         if (num < 0) {
 | ||
|             lcd_data('-');
 | ||
|             num = -num;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     unum = (unsigned int)num;
 | ||
| 
 | ||
|     if (unum == 0) {
 | ||
|         lcd_data('0');
 | ||
|         return;
 | ||
|     }
 | ||
| 
 | ||
|     while (unum > 0 && i < 16) {
 | ||
|         unsigned int d = unum % base;
 | ||
|         buffer[i++] = (d < 10) ? ('0' + d) : ('A' + d - 10);
 | ||
|         unum /= base;
 | ||
|     }
 | ||
| 
 | ||
|     while (i > 0)
 | ||
|         lcd_data(buffer[--i]);
 | ||
| }
 | ||
| 
 | ||
| /* ===== Display Helpers ===== */
 | ||
| void display_mode(void) {
 | ||
|     lcd_cmd(0x80);
 | ||
|     lcd_print_str("Mode: ");
 | ||
|     if (current_base == MODE_BIN)
 | ||
|         lcd_print_str("BIN   ");
 | ||
|     else if (current_base == MODE_BASE4)
 | ||
|         lcd_print_str("BASE4 ");
 | ||
|     else if (current_base == MODE_OCT)
 | ||
|         lcd_print_str("OCT   ");
 | ||
|     else if (current_base == MODE_DEC)
 | ||
|         lcd_print_str("DEC   ");
 | ||
|     else {
 | ||
|         lcd_print_str("HEX");
 | ||
|         if (hex_operator_mode)
 | ||
|             lcd_print_str("[OPS]");
 | ||
|         else
 | ||
|             lcd_print_str("    ");
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| void display_input(void) {
 | ||
|     lcd_cmd(0xC0);
 | ||
|     lcd_print_str("Input: ");
 | ||
|     lcd_print_num(input_num, current_base);
 | ||
|     lcd_print_str("       ");
 | ||
| }
 | ||
| 
 | ||
| void display_result(void) {
 | ||
|     lcd_cmd(0xC0);
 | ||
|     lcd_print_str("Result: ");
 | ||
|     lcd_print_num(result, current_base);
 | ||
|     lcd_print_str("       ");
 | ||
| }
 | ||
| 
 | ||
| /* ===== Keypad and Button ===== */
 | ||
| unsigned int scan_keypad(void) {
 | ||
|     unsigned int col, row, row_bits;
 | ||
|     for (col = 0; col < 4; col++) {
 | ||
|         LPC_GPIO0->FIOSET = COL_MASK;
 | ||
|         delay(50);
 | ||
|         LPC_GPIO0->FIOCLR = (1 << (COL_BASE + col));
 | ||
|         delay(200);
 | ||
|         row_bits = (LPC_GPIO0->FIOPIN & ROW_MASK) >> ROW_BASE;
 | ||
| 
 | ||
|         if (row_bits != 0x0F) {
 | ||
|             for (row = 0; row < 4; row++) {
 | ||
|                 if (!(row_bits & (1 << row))) {
 | ||
|                     LPC_GPIO0->FIOSET = COL_MASK;
 | ||
|                     return col * 4 + row;
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
|     LPC_GPIO0->FIOSET = COL_MASK;
 | ||
|     return 0xFF;
 | ||
| }
 | ||
| 
 | ||
| unsigned int scan_mode_button(void) {
 | ||
|     return ((LPC_GPIO2->FIOPIN & MODE_BUTTON) == 0) ? 1 : 0;
 | ||
| }
 | ||
| 
 | ||
| /* ===== Mode and Button ===== */
 | ||
| void change_mode(void) {
 | ||
|     if (current_base == MODE_DEC)
 | ||
|         current_base = MODE_BIN;
 | ||
|     else if (current_base == MODE_BIN)
 | ||
|         current_base = MODE_BASE4;
 | ||
|     else if (current_base == MODE_BASE4)
 | ||
|         current_base = MODE_OCT;
 | ||
|     else if (current_base == MODE_OCT)
 | ||
|         current_base = MODE_HEX;
 | ||
|     else
 | ||
|         current_base = MODE_DEC;
 | ||
| 
 | ||
|     display_mode();
 | ||
|     input_num = stored_num = result = 0;
 | ||
|     operation = OP_NONE;
 | ||
|     result_displayed = 0;
 | ||
|     display_input();
 | ||
| }
 | ||
| 
 | ||
| void handle_mode_button(void) {
 | ||
|     unsigned long now = millis();
 | ||
| 
 | ||
|     if (button_stable == 3 && button_state == 1) {
 | ||
|         if (current_base == MODE_HEX) {
 | ||
|             if ((now - last_mode_press_time) < 2000) {
 | ||
|                 hex_operator_mode = !hex_operator_mode;
 | ||
|                 display_mode();
 | ||
|                 last_mode_press_time = 0;
 | ||
|             } else {
 | ||
|                 last_mode_press_time = now;
 | ||
|             }
 | ||
|         } else {
 | ||
|             change_mode();
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* ===== Validation ===== */
 | ||
| unsigned int is_valid_digit(unsigned int key) {
 | ||
|     if (key >= 16)
 | ||
|         return 0;
 | ||
|     if (current_base == MODE_BIN && key >= 2)
 | ||
|         return 0;
 | ||
|     if (current_base == MODE_BASE4 && key >= 4)
 | ||
|         return 0;
 | ||
|     if (current_base == MODE_OCT && key >= 8)
 | ||
|         return 0;
 | ||
|     if (current_base == MODE_DEC && key >= 10)
 | ||
|         return 0;
 | ||
|     return 1;
 | ||
| }
 | ||
| 
 | ||
| /* ===== Arithmetic per base (optimized) ===== */
 | ||
| int base_arith(int a, int b, unsigned int op, unsigned int base) {
 | ||
|     int r = 0;
 | ||
| 
 | ||
|     switch (op) {
 | ||
|         case OP_ADD:
 | ||
|             r = a + b;
 | ||
|             break;
 | ||
| 
 | ||
|         case OP_SUB:
 | ||
|             r = a - b;
 | ||
|             break;
 | ||
| 
 | ||
|         case OP_MUL:
 | ||
|             r = a * b;
 | ||
|             break;
 | ||
| 
 | ||
|         default:
 | ||
|             r = a;
 | ||
|             break;
 | ||
|     }
 | ||
| 
 | ||
|     // For non-decimal modes, keep everything positive and base-limited
 | ||
|     if (base != MODE_DEC) {
 | ||
|         if (r < 0)
 | ||
|             r = ((r % base) + base) % base;
 | ||
|         else
 | ||
|             r = r % (base * base * base * base * base); // safe wraparound for multi-digit bases
 | ||
|     }
 | ||
| 
 | ||
|     return r;
 | ||
| }
 | ||
| 
 | ||
| /* ===== Operation/Arithmetic Handling ===== */
 | ||
| void operate(unsigned int op) {
 | ||
|     stored_num = input_num;
 | ||
|     input_num = 0;
 | ||
|     operation = op;
 | ||
|     result_displayed = 0;
 | ||
| }
 | ||
| 
 | ||
| void perform_operation(void) {
 | ||
|     result = base_arith(stored_num, input_num, operation, current_base);
 | ||
|     display_result();
 | ||
|     input_num = result;
 | ||
|     operation = OP_NONE;
 | ||
|     result_displayed = 1;
 | ||
| }
 | ||
| 
 | ||
| /* ===== Key Classifier ===== */
 | ||
| unsigned char is_operator_active(unsigned int key) {
 | ||
|     if (key >= 11 && key <= 15) {
 | ||
|         if (current_base == MODE_HEX)
 | ||
|             return hex_operator_mode;
 | ||
|         else
 | ||
|             return 1;
 | ||
|     }
 | ||
|     return 0;
 | ||
| }
 | ||
| 
 | ||
| /* ===== MAIN ===== */
 | ||
| int main(void) {
 | ||
|     current_base = MODE_DEC;
 | ||
|     input_num = stored_num = result = 0;
 | ||
|     operation = OP_NONE;
 | ||
|     result_displayed = 0;
 | ||
|     key = last_key = 0xFF;
 | ||
|     stable = 0;
 | ||
|     button_stable = 0;
 | ||
|     button_state = last_button_state = 0;
 | ||
|     hex_operator_mode = 0;
 | ||
|     last_mode_press_time = 0;
 | ||
| 
 | ||
|     /* ===== SystemClock & SysTick Setup ===== */
 | ||
|     SystemCoreClockUpdate();
 | ||
|     SysTick_Config(SystemCoreClock / 1000);  // 1ms tick
 | ||
| 
 | ||
|     /* ===== Peripheral Init ===== */
 | ||
|     LPC_PINCON->PINSEL0 = 0;
 | ||
|     LPC_PINCON->PINSEL1 = 0;
 | ||
|     LPC_PINCON->PINSEL3 = 0;
 | ||
|     LPC_PINCON->PINSEL4 = 0;
 | ||
| 
 | ||
|     LPC_GPIO0->FIODIR |= COL_MASK;
 | ||
|     LPC_GPIO0->FIODIR &= ~ROW_MASK;
 | ||
|     LPC_GPIO0->FIOSET = COL_MASK;
 | ||
| 
 | ||
|     LPC_GPIO2->FIODIR &= ~MODE_BUTTON;
 | ||
| 
 | ||
|     LPC_GPIO0->FIODIR |= (0xFF << SEG_SHIFT);
 | ||
|     LPC_GPIO1->FIODIR |= DIGIT_EN;
 | ||
|     LPC_GPIO0->FIODIR |= LCD_DATA_MASK | LCD_RS | LCD_EN;
 | ||
| 
 | ||
|     lcd_init();
 | ||
|     display_mode();
 | ||
|     display_input();
 | ||
| 
 | ||
|     /* ===== Main Loop ===== */
 | ||
|     while (1) {
 | ||
|         key = scan_keypad();
 | ||
|         button_state = scan_mode_button();
 | ||
| 
 | ||
|         // Debounce keypad
 | ||
|         if (key == last_key)
 | ||
|             stable = (stable < 5) ? stable + 1 : stable;
 | ||
|         else {
 | ||
|             last_key = key;
 | ||
|             stable = 0;
 | ||
|         }
 | ||
| 
 | ||
|         // Debounce button
 | ||
|         if (button_state == last_button_state)
 | ||
|             button_stable = (button_stable < 5) ? button_stable + 1 : button_stable;
 | ||
|         else {
 | ||
|             last_button_state = button_state;
 | ||
|             button_stable = 0;
 | ||
|         }
 | ||
| 
 | ||
|         handle_mode_button();
 | ||
| 
 | ||
|         if (stable == 3 && key != 0xFF) {
 | ||
|             if (is_operator_active(key)) {
 | ||
|                 switch (key) {
 | ||
|                     case 11: operate(OP_ADD); display_input(); break;
 | ||
|                     case 12: input_num = stored_num = result = 0;
 | ||
|                              operation = OP_NONE; display_input(); break;
 | ||
|                     case 13: operate(OP_SUB); display_input(); break;
 | ||
|                     case 14: operate(OP_MUL); display_input(); break;
 | ||
|                     case 15: perform_operation(); break;
 | ||
|                 }
 | ||
|             } else if (is_valid_digit(key)) {
 | ||
|                 result_displayed = 0;
 | ||
|                 input_num = input_num * current_base + key;
 | ||
|                 display_input();
 | ||
|             }
 | ||
|             stable = 5;
 | ||
|         }
 | ||
| 
 | ||
|         // Simple 7-seg base display
 | ||
|         LPC_GPIO0->FIOCLR = (0xFF << SEG_SHIFT);
 | ||
|         unsigned int segval = 0x3F;  // default '0'
 | ||
|         if (current_base == MODE_BIN) segval = seven_seg[2];
 | ||
|         else if (current_base == MODE_BASE4) segval = seven_seg[4];
 | ||
|         else if (current_base == MODE_OCT) segval = seven_seg[8];
 | ||
|         else if (current_base == MODE_DEC) segval = seven_seg[0];
 | ||
|         else if (current_base == MODE_HEX) segval = seven_seg[0xA];  // 'A' for HEX
 | ||
| 
 | ||
|         LPC_GPIO0->FIOSET = (segval << SEG_SHIFT);
 | ||
|         LPC_GPIO1->FIOSET = DIGIT_EN;
 | ||
| 
 | ||
|         delay(3000);
 | ||
|     }
 | ||
| }
 |