[AVR] 계산기

2021. 11. 9. 22:22코딩/AVR

main.c

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdio.h>
#include <avr/interrupt.h>   // interrupt 관련 lib가 들어 있다.

#include "uart0.h"
#include "queue.h"
#include "I2C_LCD.h"
#include "I2C.h"

char buff[40];
extern void UART0_transmit(char data);
// printf를 동작 시키위한 mapping작업
FILE OUTPUT = FDEV_SETUP_STREAM(UART0_transmit, NULL, _FDEV_SETUP_WRITE);

volatile uint32_t keypad_timer=0;

ISR(TIMER0_COMP_vect)  
{
	volatile uint8_t keydata=0;
	
	keypad_timer++;
	if (keypad_timer >= 60)  // 60ms reached 
	{
		keypad_timer=0;

		if ((keydata=keypad_scan()))
		{
			insert_queue(keydata);
		}	
	}
}

void timer0_init()
{
	TCCR0 |= (1 << CS02) | (0 << CS01) | (0 << CS00);   // 64분주
	TCCR0 |= (1 << WGM01) | (0 << WGM00); // CTC mode
	TIMSK |= ( 1<< OCIE0);   // 비교 일치
	OCR0 = 250;  // 1ms 간격으로  INT 발생
}

int index=0;
char buff[40];

int main(void)
{
	uint8_t key_value;
	
	timer0_init();
	
	stdout = &OUTPUT;
	UART0_init();
	keypad_int();
	I2C_LCD_init();			//lcd 모듈 초기화
	I2C_LCD_clear();
	
	sei();				 // grobal int enb
	
	printf("main start !!!!\n");
	

	
	while (1)
	{
		I2C_LCD_write_string_XY(0,0,buff);
		if (queue_empty() != TRUE)	//queue_empty 가 TRUE면 LCD 클리어
		{
			
			I2C_LCD_clear();
			
			key_value=read_queue();
			
			buff[index]=key_value;	//buff[index]에 값을 저장
			index++;				// 1번index ++로 다음 2번 index에 저장하는식
			if (key_value == '=')	// 만약 =를 넣을시
			{
				buff[index]='\0';	//문자열을 마무리하고  //문자열은 항상 null,\0로 끝나야한다. 없을시 끝이 어딘지 몰라서 계속 프린트를 실행한다.
				calculator(buff);	//calculator식을 불러와서 계산
				
			}
			if (key_value == ' ')
			{
				clear_buff();		// ' '일시 buff를 비운다.
			}
			
			printf("key_value:%c\n", key_value);
		}
		
	}
	
	
}

// 111+123=
void calculator(char *buff)			//배열의 주소는 자기자신 그니까 이 밑에 숫자 1~9,연산자를 따로 할당하지않고 위 배열에 할당된 값을 그대로 이용
{
	char num1buff[10];				// buff 포인터로 불러왔기 때문에 &buff인데 &가 안보이는 상태이다
	char num2buff[10];				// 각각 숫자1,2번을 출력하는 buff10칸 
	
	int i,j;
	int num1, num2, result;
	char calculator_mode=' ';
	
	
	
	for (i=0; buff[i] >= '0' &&  buff[i] <= '9'; i++ )	//ex)123 + 456 일때 buff[0]에 1을넣고 i++로인해 buff[1]넘어가서 2를넣고
	num1buff[i]=buff[i];								//buff[2]에 3을넣고 연산자는 아스키코드로 1~9 사이의 조건에 안맞기 때문에 for문을 빠져나온다
	
	num1buff[i]='\0';									// 문자열에 null를 표기 안해주면 끝을 모르기때문에 위 예시같은경우 buff[3]칸에 \0가 들어간 것이다.
	calculator_mode=buff[i];
	
	i++;												// 연산자를 끝낸후 num2에 값(456) 을 넣어주기위한 과정 
	for (j=0; buff[i] >= '0' &&  buff[i] <= '9'; i++,j++ )	//위와 메커니즘은 같으나 num2buff[j]에 j대신 i를 넣을시 기존 num1buff[i] 와 충돌이날수있다.
	num2buff[j]=buff[i];
	
	num2buff[j]='\0';   // 123\0
	
	num1=atoi(num1buff);								//atoi = ascii to interger 문자열을 정수형으로 받겠다는의미
	num2=atoi(num2buff);
	
	switch(calculator_mode)	{
		case '+':
		result = num1 + num2;
		break;
		
		
		case '-':
		result = num1 - num2;
		break;
		
		
		case '*':
		result = num1 * num2;
		break;
		
		
		case '/':
		result = num1 / num2;
		break;
		
		default:
		break;
		
	}
	
	sprintf(buff, "%d%c%d=%d", num1,calculator_mode,num2,result);
	printf("%s\n", buff);

	
}



void clear_buff()
{	
	for (int i=0; i < 40; i++)
	buff[i]='\0';
	
	index=0;
}

keypad.c

#include "keypad.h"

#define KEYPAD_DDR DDRA
#define KEYPAD_PIN PINA
#define KEYPAD_PORT PORTA

void keypad_init()
{
	KEYPAD_DDR = 0x0f;	// row(PA4 ~ 7) : input	 col(PA0 ~ 3) : output
	KEYPAD_PORT = 0xff;
}

uint8_t keypad_scan()
{
	uint8_t data = 0;	// key 값 저장 변수
	for (int row = 0; row < 4; row++)
	{
		for (int col = 0; col < 4; col++)
		{
			data = get_button(row,col);
			if (data)	//if (data >= 1)
				return data;
		}
	}
}

uint8_t get_button(int row, int col)
{
	uint8_t keypad_char[4][4] = {
		{'-', '*', '/', '+'},
		{'9', '6', '3', '='},
		{'8', '5', '2', '0'},
		{'7', '4', '1', ' '},
		};
	
	static uint8_t prev_state[4][4]= 
	{
		{1, 1, 1, 1},
		{1, 1, 1, 1},
		{1, 1, 1, 1},
		{1, 1, 1, 1},
	};
	
	uint8_t current_state = 1;
	
	KEYPAD_PORT = 0xff;
	KEYPAD_PORT &= ~(1 << col);	// 해당 col에 전류를 흘린다.
	
	for(int delay = 0; delay < 20; delay++)	// 키패드 체크 delay를 위한 코드
		;
	
	current_state = (KEYPAD_PIN & (1 << (row + 4))) >> (row + 4);
	// 예) row 0 : PA4에 연결 row:0 +4
	// 76543210
	// 11101111 : KEYPAD_PIN & ( 1<< (row+4))
	// 00001110 : >> (row+4)
	
	
	if ((current_state == 0) && (prev_state[col][row] == 1))	
	{		//처음 버튼이 눌렸을 때(현재 버튼이 눌리고 이전에 버튼이 안눌렸다) 
		prev_state[col][row] = current_state;
		return 0;
	}
	else if ((current_state == 1) && (prev_state[col][row] == 0))
	{		// 이전에 버튼이 눌러졌고 현재는 버튼을 뗀 상태이면
		prev_state[col][row] = current_state;	// key status table을 release 상태로 바꾼다.
printf("key: %c\n", keypad_char[col][row]);
		return(keypad_char[col][row]);
	}
	
	return 0;
}

계산기 동작 동영상