먼저 Keypad의 구동방식입니다.
일반적으로 사용자의 입력을 받기위한 방식으로 정적 구동방식이 있습니다. 정적구동방식은 스위치당 I/O포트를 이용해서 신호를 받는 방법이다. 모든 스위치를 개별적으로 동시에 받을 수 있다는 점이 있긴 하지만, I/O포트의 낭비가 심합니다. 16개의 자판이 있는 키패드의 경우 16개의 I/O포트가 필요하기 때문에 정적 구동방식이 아닌 동적 구동방식으로 키패드를 인식할 수 있다. 간단히 생각해서 열과 행을 나누어 생각한 다음, 열을 차례로 스캔한 후에 행 데이터 입력을 이용해서 어느 스위치가 눌려졌는지 알아낼 수 있는 방법이다. 이때 스캔주기는 30ms~50ms정도가 적절하다.
아래와 같은 키패드가 있다고 생각해 보면 우선 PC0-PC3포트를 행을 나타내고, PC4-PC7포트로 열을 나타냈다고 생각해보자. PC0-PC3번 포트를 ‘1110,1101,1011,0111’ 순서로 내려가면서 스캔하고 이때 눌린 스위치의 행만 생각한 다음에 둘을 조합하면 어느 곳의 스위치가 눌려졌는지 알 수 있다. 예를 들어 2번포트를 스캔할 때 5번 포트에 입력신호가 들어왔다면 10번스위치를 나타내고 있다는 사실을 알 수 있다.
이때 스위치의 값을 수식으로 나타내고 싶다면 각 x,y축으로 잡고 위 표와같이 설정한다면 pc1을 스캔할 때 pc6번이 눌러진 경우 즉 y=2, x=3일 때 11번스위치가 눌렸다는걸 표로도 알 수 있지만 (4 * y ) + x 식으로 이끌어 낼 수도 있다.
조금 헷갈리는 개념일 수 있으나, 코드를 동작시키면서 읽으면 이해할 수 있습니다.
다음은 FND 동작 방식입니다.
FND(7-segment)는 세그먼트 방식의 숫자 표시 소자로 일반적으로 7개의 세그먼트로 숫자를 표시하는 방식을 뜻한다. type으로 cathode type과 anode 타입 2개가 존재한다.
각 자리마다 해당되는 알파벳이 생기고, 빛을 점등하여 글자를 표시하게 된다. 즉, 아래 그림처럼 표시되게 된다.
여러개의 FND가 들어올 경우, FND마다 포트를 할당하게 되면 많은 수의 포트가 필요하기 때문에, 점등을 1 > 2 > 3 > 4 빠르게 점등하여 마치 사람눈에는 한번에 출력되는것처럼 보이게 하는 방식으로 구동한다.
buzzer는 atmega내장되어 있는 buzzer를 사용할 것이기에 Hz를 넣어 해당하는 음을 낸다고 생각하면 쉬울것 같습니다. 아래 프로그램은 KeyPad를 사용하여 숫자를 입력하고 숫자를 fnd에 띄우며, 그 수를 각도로 생각하여 서보모터를 구동해보는 예제 프로그램입니다.
#include <mega128.h>
#include <delay.h>
#define Do 1908 // 262Hz (3817us) 1908us
#define Re 1700 // 294Hz (3401us) 1701us
#define Mi 1515 // 330Hz (3030us) 1515us
#define Fa 1432 // 349Hz (2865us) 1433us
#define Sol 1275 // 370Hz (2703us) 1351us
#define La 1136 // 440Hz (2273us) 1136us
#define Si 1012 // 494Hz (2024us) 1012us
char zero_flag=0; // 0을 눌렀을때만 FND가 바뀌도록 도와주는 flag
unsigned char Port_char[] ={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,
0x80,0x90 ,0x88,0x83,0xc4, 0xa1,0x84,0x8e};
unsigned int Port_fnd[] ={0x1f,0x2f,0x4f,0x8f}; //FND 값 설정
void PORT_Init(void)
{
DDRE=0xF0; //FND 출력을 위한 설정
DDRF=0xFF; //FND 출력을 위한 설정
DDRC=0x0F; //키패드를 위한 설정
DDRG=0x10; //부저 출력을 위한 설정
DDRB=0x20; //서브모터 출력을 위한 설정
PORTC=0xFF;
}
void Init_Timer1(void)
{
TCCR1A = (1<<COM1A1) | (1<<WGM11);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
TCNT1 = 0x00;
ICR1 = 36864-1; // TOP 값 : 36864-> 20ms(0.542us X 36864), 0~36863
OCR1A =2765 ; // 초기 시작 위치 0도
TIMSK |= (1<<OCIE1A); // Output Compare Match Interrupt 허가
}
interrupt [TIM1_COMPA] void compare(void)
{
#asm("nop"); //비교일치 인터럽트 구문
}
void myDelay_us(unsigned int delay)
{
unsigned int loop;
for(loop=0; loop<delay; loop++)
delay_us(1);
}
void Buzzer_play(unsigned int delay)
{
unsigned int loop;
unsigned char Play_Tim=0;
Play_Tim = 10000/delay;
for(loop=0; loop<Play_Tim; loop++)
{
PORTG |= 1<<4; //buzzer off, PORTG의 4번 핀 on(out 1)
myDelay_us(delay);
PORTG &= ~(1<<4); //buzzer on, PORTG의 4번 핀 off(out 0)
myDelay_us(delay);
}
}
unsigned char KeyScan(void) //키패드를 받는 함수
{
unsigned int key_scan_line = 0xFE;
unsigned char key_scan_loop =0, getPinData =0;
unsigned int key_num =0;
for(key_scan_loop =0; key_scan_loop <4;key_scan_loop++)
{
PORTC = key_scan_line;
delay_us(1);
getPinData = PINC & 0xF0;
if(getPinData != 0x00)
{
switch(getPinData)
{
case 0x10:
key_num = key_scan_loop * 4 +1 ;
break;
case 0x20:
key_num = key_scan_loop * 4 +2 ;
break;
case 0x40:
key_num = key_scan_loop * 4 +3 ;
break;
case 0x80:
key_num = key_scan_loop * 4 +4 ;
break;
}
return key_num;
}
key_scan_line = (key_scan_line <<1);
}
}
unsigned char Changenum(unsigned char num) //키패드로 받아들어온 숫자를 키패드 //상황에 맞게 변환
{
unsigned char return_num=0;
if(num ==0){
return_num =0;
}
else if (num%4 ==0){ // 1 2 3 13
return_num = 12 + num/4; // 4 5 6 14
// 7 8 9 15
} // 10 0 12 16
else if( num/4 ==0){ // 위처럼 인식되게 변환
return_num = (4*(num/4) +num%4) ;
}
else if( num/4 ==1){
return_num = (4*(num/4) +num%4) -1 ;
}
else if( num/4 ==2){
return_num = (4*(num/4) +num%4) -2 ;
}
else if( num/4 ==3){
return_num = (4*(num/4) +num%4) -3 ;
}
if (return_num ==11){
return_num =0;
zero_flag =1; // 아무것도 누르지 않을때도 0이 저장되기에
// 0을 누를때 zero_flag동작되게 설정
}
return return_num;
}
void OUTFND( unsigned int t)
{
unsigned char FND0, FND1, FND2, FND3;
// 숫자 t를 받아서 FND에 출력하는 함수
FND3 = t/1000;
FND2 = (t%1000)/100;
FND1 = (t%100)/10;
FND0 = t%10;
PORTE = Port_fnd[0];
PORTF = Port_char[FND0]; //1의자리
delay_ms(10);
PORTE = Port_fnd[1];
PORTF = Port_char[FND1]; //10의자리
delay_ms(10);
PORTE = Port_fnd[2];
PORTF = Port_char[FND2]; //100의 자리
delay_ms(10);
PORTE = Port_fnd[3]; //1000의 자리
PORTF = Port_char[FND3];
delay_ms(10);
}
void main (void)
{
int t=0; //키패드로 받은 숫자
int count =0; //count 변수
int finalnum=0; //FND에 출력으로 넣어줄 변수
int fnd[4]={0,0,0,0};
signed int angle=0; // 서브모터 각도로 넣을 변수
Init_Timer1(); // 타이머 초기설정
PORT_Init(); // 포트들 입출력 초기 설정
while(1)
{
t= Changenum(KeyScan());
if(t<10 & t>0 ) //숫자가 눌리면 새로운 값을 저장하도록 count값 설정
{
count++;
delay_ms(50);
}
else if(t==0 & zero_flag) //zero_flag가 실행된 경우에만 0으로 입력
{
count++;
zero_flag =0; //계속 0으로 입력된 상태가 안되게 zero_flag를 다시 0으로
delay_ms(50);
}
else if(t==13) // FND 출력숫자 리셋버튼 기능
{
fnd[0]=0;
fnd[1]=0;
fnd[2]=0;
fnd[3]=0;
}
else if (t ==14) //현재 FND의 숫자각도만큼 모터각도 변경
{
angle = finalnum%360;
if(angle >= 180){ //예제 모터는 90도까지만되나 180도 이상은
angle -= 360; //-각도로 생각
}
OCR1A = 2765 + 10.249*angle; //1도단위까지도 입력 가능
}
if((count%2) ==0){ //count가 짝수일때 들어온 t값을 저장하고
//다시 count를 홀수로 만듬
fnd[3] = fnd[2];
delay_ms(50);
fnd[2] = fnd[1];
delay_ms(50);
fnd[1] = fnd[0];
delay_ms(50);
fnd[0] = t;
count++;
delay_ms(50);
}
finalnum = 1000*fnd[3] + 100*fnd[2] + 10*fnd[1] + fnd[0];
OUTFND(finalnum);
switch(t) //각 키패드마다 나오는 음 설정
{
case 1:
Buzzer_play(Do);
break;
case 2:
Buzzer_play(Re);
break;
case 3:
Buzzer_play(Mi);
break;
case 4:
Buzzer_play(Fa);
break;
case 5:
Buzzer_play(Sol);
break;
case 6:
Buzzer_play(La);
break;
case 7:
Buzzer_play(Si);
break;
case 8:
Buzzer_play(Do/2);
break;
case 9:
Buzzer_play(Re/2);
break;
default:
break;
}
}
}
위 사진은 실제 프로테우스란 시뮬레이션 프로그램을 통하여 구현해본 예제파일입니다. 45도만큼 서브모터를 제어할 수 있습니다.
LED는 간단하여 적지 않을 예정이고, LCD는 제 글의 C - Atmega에 따로 서술할 것 같습니다.
지금까지 프로젝트 진행을 위한 센서들의 구동방식과 구현예제들을 적어보았고 이제 이 함수들을 멋있게 합쳐서 프로젝트를 구현해보겠습니다.
'프로젝트 > 스마트독서실 시스템' 카테고리의 다른 글
06_구현 2단계_퇴장 및 비밀번호 일치 (0) | 2021.12.06 |
---|---|
06_프로그램 구현 1단계 (0) | 2021.12.02 |
04_구동함수_Servo motor (0) | 2021.11.19 |
03_구동함수_ONE025 (0) | 2021.11.19 |
02_센서구동함수_SRF02 (0) | 2021.11.19 |