제목 : 실험 #3 예비보고서 Keypad & 7-Segment 제어 실험목적 - Atmega128을이용하여 Easy Processor Kit의 Keypad 및 7-Segment를제어한다. - Keypad의키스캔방식을통한제어와, Digit 선택을이용한다수의 7-Segment 제어방법을이해한다. 실험장비 - ATmega128(AVR Chip), 7-Segment(FND), Push button 실험이론 - Keypad 키패드는복수개의푸시버튼으로구성되어있다. 버튼을각각 AVR의한포트씩직접연결되도록한다면프로그램구현은쉽게될수있지만, 포트를낭비하게된다. 예를들어총 16개의버튼으로이루어진 Keypad의버튼들이각각한포트에직접연결되어있다면 16개의포트가필요하게된다. 따라서, 일반적으로위회로와같이, 다수의푸시버튼을각행또는열을병렬로연결, 각행또는열에스캔신호를보내그에따른열또는행의버튼값을얻도록구성되어있다. 이렇게함으로써위와같은 4x4, 총 16개버튼을절반인 8개의포트로제어할수있게된다. - 실험킷의 Keypad 제어 - 실험킷의 Keypad 구성회로 -
푸시버튼들이 4x4로배치되어있으며, 앞뒤로현재값을읽어올행값을저장하고, 지속적으로스캔신호를내보내줄 D-FF(74HC574PW) 과해당행에서눌려진버튼값을 CPLD에서의신호와동기화해내보내기위한버퍼 (74HC541PW) 가연결되어있다. 각버튼은 D-FF(74HC574PW)-> 버튼-> 버퍼 (74HC541PW) 방향으로전류가흐르는데, 위회로를보면버퍼와버튼사이에풀업저항이열결되어있다. 따라서, 버튼을누르지않은상태에서는 VDD에서버퍼로전류가흐르기때문에 1, 버튼을눌렀을때엔 0으로판별될것이다. 마찬가지로 D-FF에서는키스캔시 0(GND) 을출력해야버튼을눌렀을때전류가제대로흐를것이므로키스캔값역시스캔할행을 0으로, 그외행을 1인값으로표현해야할것이다. - 분석과제 1 - 시뮬레이션회로 - - 시뮬레이션상황 - 실험킷회로의시뮬레이션을위해위와같이회로를구성하였다. 동작분석 첫행의키스캔을위해첫행에만 0 을출력한다. 1 번버튼을누르면 0b1110 이출력된다. 키스캔 =0b0111 & 키 =0bxxx0 인조건에의해 1 번버튼이눌렸음을처리한다.
두번째행의키스캔을위해두번째행에만 0 을출력한다. 7, 8 번버튼이눌려있기때문에 0b0011 이출력된다. 키스캔 =0b1011 & 키 =0b0xxx 인조건에의해 8 번버튼이눌렸음을처리한다. C 예제코드에서는 8, 7 번순으로처리되지만, ASM 예제코드에서는한버튼을처리하면해당행의다른버튼은처리되지않기때문에 7 번은처리되지않는다. 세번째행의키스캔을위해세번재행에만 0 을출력한다. 하지만눌려진키가없어 0b1111 이출력되고, 만족하는조건이없기때문에다음행의키스캔을실행한다. 네번째행의키스캔을위해네번째행에만 0 을출력한다. 13 번버튼이눌려있기때문에 0b1110 이출력된다. 키스캔 =0b1110 & 키 =0bxxx0 인조건에의해 13 번버튼이눌렸음을처리한다.
- 7-Segment(FND) - 7-Segment - - 복수의 7-Segment로구성된소자 - FND는 Flexible Numeric Display의약어로, 숫자를표시할수있는소자를말하는지칭한다. 이것을표시할수있는최소한의구성으로만들어진소자가 7-Segment이다. 실질적으로는 a~g외에점을표현하는 dp를포함하여 8개세그먼트로구성된다. 7-Segment는공통단자인 Common 단자가 Anode(+) 인가 Cathode(-) 인가에따라두가지형태가있다. Common-Cathode는각 LED가 GROUND와연결되어있기때문에 LED를켜려면해당핀에 1을입력해야한다. 반면에 Common-Anode는 VCC와연결되어있어 0을입력해야해당핀에불이들어온다. - 복수의 Cathode 7-Segment 소자의회로도 - 복수개의 7-Segment로구성된소자를제어할때, 각 7-Segment마다직접연결된포트를쓴다면 8*N개의포트가필요하게된다. 이것은심각한낭비이므로, 이런회로를구성할경우각세그먼트의 a~dp입력은공통으로쓰되각 7-Segment의 Common단자만을따로조작하여불이켜지도록만든다. 실제로는 Common단자에값 (CC: 0, CA: 1) 이설정된 7-Segment만불이켜지는것이지만, 사람의눈이인식하지못하는아주짧은시간안에각 7-Segment의 Common단자를조작하여모두한번에켜진것같은착시효과를이용하는것이다. 이경우, 각 Segment를조작할 8개의핀과 7-Segment의갯수 (=Common 핀의갯수 ) 만큼, 예를들어 4개의 7-Segment 소자라면 8 + 4 = 12개의핀으로조작가능하게된다.
- 실험킷의 7-Segment 제어 ( 분석과제 2) - 실험킷에구성된 7-Segment 소자의회로도 - 각세그먼트를조작하기위한 FND_A~FND_DP까지 8개, Common단자조작을위해 FND_COM단자가 4 개인 7-Segment가두개장착되어있음을확인할수있다. 위회로를살펴보면, CPLD에서 FND_DATA 신호가도착하면함께도착한세그먼트데이터를기억하기위해 D-FF(74HC574PW) 이부착되어있고, 그뒤 7-Segment의 LED 소자가과부하로타버리지않도록저항을통과한뒤마지막으로 7-Segment에연결되어있다. Common단자조작을위한회로역시 CPLD에서 FND_CS 신호에의해동작하는것외에구성은같다. 회로도에는이 Cathode인지 Anode인지나와있지않지만, 예시소스분석결과 Anode라고판단된다. ATmega128에서내보낸신호들에의해 CPLD에서 FND_DATA신호를내보내면이미도착해있던세그먼트값이 D-FF(U17) 에기억된다. 그후저항을지나 FND의 FND_A~DP 핀을통해 FND에도착한다. 두 FND의 FND_A~DP 신호는 8개의 7-Segment 모두동일한신호를입력받는다. 따라서 9개의 7-Segment 모두동일한세그먼트값을가지게된다. ATmega128에서내보낸신호들에의해이번엔 CPLD에서 FND_CS신호를내보내고, 이미도착해있던 COM값이 D-FF(U18) 에기억된다. 그후저항을지나첫번째 FND의 FND_COM0~3과두번째 FND의 FND_COM4~7에도착한다. 이때 COM값이 1인 7-Segment에만세그먼트값에맞게불이켜지게된다. - 소스분석 1: 7-Segment C 소스코드 #include <avr/io.h> #define DIG_SELECT (*(volatile unsigned char *)0x7000) #define FND_DATA (*(volatile unsigned char *)0x6C00) void ExtFndSet(int sel) { switch(sel) { case 0 : FND_DATA = ~0x3F; break; case 1 : FND_DATA = ~0x06; break; case 2 : FND_DATA = ~0x5B; break; case 3 : FND_DATA = ~0x4F; break; case 4 : FND_DATA = ~0x66; break; case 5 : FND_DATA = ~0x6D; break; case 6 : FND_DATA = ~0x7C; break; case 7 : FND_DATA = ~0x07; break; case 8 : FND_DATA = ~0x7F; break; case 9 : FND_DATA = ~0x6F; break; case 10 : FND_DATA = ~0x77; break; case 11 : FND_DATA = ~0x7C; break; case 12 : FND_DATA = ~0x39; break; case 13 : FND_DATA = ~0x5E; break; FND 의 COM 핀의메모리매핑주소 FND 의 A~DP 핀의메모리매핑주소 sel 값을 7-Segment 에맞는값으로출력해주는함수. sel 값에따라 FND_DATA 값을만들어준다. 실험킷은 Anode 타입이기때문에 Cathode 타입의세그먼트값의보수 (~) 를출력한다. ex) 0 = 0b11111100 (Cathode) ~0b11111100 = 0b00000011 (Anode)
case 14 : FND_DATA = ~0x79; break; case 15 : FND_DATA = ~0x71; break; case 16 : FND_DATA = ~0x76; break; case 17 : FND_DATA = ~0x40; break; case 18 : FND_DATA = ~0x80; break; case 19 : FND_DATA = ~0x00; break; void delay_us(unsigned char time_us) { register unsigned char i; for(i = 0; i < time_us; i++) { asm volatile("push R0"); asm volatile("pop R0"); 딜레이함수. 이전보고서참조. void delay_ms(unsigned int time_ms) { register unsigned int i; for(i=0;i<time_ms;i++) { delay_us(250); delay_us(250); delay_us(250); delay_us(250); int main(void) { int i; MCUCR = 0x80 ; while(1) { for(i=1;i<20;i++) { DIG_SELECT = 0xFF; ExtFndSet(i); delay_ms(200); ASM 소스코드.equ SPH = 0x3E.equ SPL = 0x3D.equ MCUCR = 0x35.equ FND_DATA = 0x6C00.equ DIG_SELECT = 0x7000.def AH = R16.def AL = R17.cseg.org 0x0000 LDI AH, high(0x10ff) LDI AL, low(0x10ff) OUT SPH, AH OUT SPL, AL IN R16, MCUCR ORI R16, 0x80 OUT MCUCR, R16 LOOP: LDI R16, 0xFF STS DIG_SELECT,R16 LDI R16, 0xFF STS FND_DATA, R16 메인프로그램 외부메모리에접근하기위해 MCUCR 레지스터의 SRE 비트를 1 로설정한다. 1~19 까지의값을출력하기위한반복문모든 7-Segment 에동일한값을표시하도록한다. 숫자에맞는세그먼트값을내보내도록함수호출. 숫자의변화를확인하기쉽도록출력사이에딜레이를준다. 1~15 는 16 진수, 16~19 는 H, -,., 공백을출력하도록설정되어있다. (ExtFndSet 함수 ) 표준 IO 레지스터를사용하기편하게키워드로매핑 7-Segment 의메모리맵주소를사용하기편하게키워드로매핑 범용레지스터를사용하기편하게키워드로매핑 프로그램코드의시작이후의코드를메모리주소 0x00 부터시작프로그램의스택설정 외부메모리에접근하기위해 MCUCR 레지스터의 SRE 비트를 1 로설정한다. 프로그램반복을위한레이블 실험킷의모든 7-Segment 를선택해동일한값을출력. 세그먼트값초기화 ( 모두 Off)
LDI R17, 0x00; LOOP1: MOV R16, R17 CALL BIN2LED STS FND_DATA, R16 CALL D50MS INC R17 CPI R17, 0x10 BRNE LOOP1 LDI R16, 'H' CALL BIN2LED STS FND_DATA, R16 CALL D50MS LDI R16, '-' CALL BIN2LED STS FND_DATA, R16 CALL D50MS R17 = 7-Segment 에출력할숫자 (0~15). 0 으로초기화. 숫자를증가시켜출력하기위한레이블 BIN2LED 함수는인자로 R16 을사용하기때문에 R17 을 R16 에복사한뒤호출. (BIN2LED: 숫자를세그먼트값으로변환 ) 변환된세그먼트값 (R16) 을 7-Segment 에출력 50ms 의딜레이출력할숫자를증가시킨다. 0~15 까지의숫자 (16 진수 ) 를출력할때까지위코드반복 H 를표현하기위해 R16 에 H 를저장한뒤 BIN2LED 함수호출. 7-Segment 에출력. 50ms 의딜레이 이후 - 과. 출력을위해위작업반복 LDI R16, '.' CALL BIN2LED STS FND_DATA, R16 CALL D50MS LDI R16, ''' CALL BIN2LED STS FND_DATA, R16 CALL D50MS JMP LOOP.include "Bin2LED.asm".include "Delay.asm" 모든 7-Segment 를꺼진상태로만드는루틴 BIN2LED 에모든 7-Segment 를꺼진상태인값을 로정의해놓음. 변환된세그먼트값을 7-Segment 로출력. 프로그램의처음으로돌아가숫자 0 부터반복 어셈블링시 BIN2LED 함수가정의되어있는 ASM 파일포함. 어셈블링시딜레이관련함수가정의되어있는 ASM 파일포함. - 소스분석 2: Keypad C 소스코드 #include <avr/io.h> #define LED_CS (*(volatile unsigned char *)0x4800) #define KEY_DATA (*(volatile unsigned char *)0x5400) #define KEY_SCAN (*(volatile unsigned char *)0x5800) void Platform_Init(void){ MCUCR = 0x80 ; void LED_ON(uint8_t data) { LED_CS = data; void Write_KeyPadData(uint8_t data) { KEY_DATA = data; uint8_t Read_KeyPadData(void) { return KEY_SCAN; void KeyPadScan() { uint8_t data; Write_KeyPadData(0x0E); data = Read_KeyPadData(); if(!(data&0x01))led_on(0x04); if(!(data&0x02))led_on(0x03); if(!(data&0x04))led_on(0x02); 외부 LED 의메모리매핑주소키스캔할행값의메모리매핑주소키스캔결과의메모리매핑주소 프로그램초기화함수외부메모리에접근하기위해 MCUCR 레지스터의 SRE 비트를 1 로설정한다. data 값을 LED 에출력하는함수별다른처리없이바로외부 LED 에출력한다. 키패드에키스캔값을출력하는함수 data 값을키스캔값으로바로출력한다. 키스캔중인행의키값을읽어오는함수키스캔결과를그대로리턴한다. 키패드처리함수. 실질적인메인코드이다. 키패드에서읽어온키값을저장할변수 첫번째행 (0b1110) 을스캔한다. 첫번째행의스캔결과를 data 변수에저장한다. data 변수의 LSB 가 0 이면 4 번버튼을누른것이기때문에 LED 로 4 를표현한다. 마찬가지로 1~4 가 data 변수하위 nibble 의각비
if(!(data&0x08))led_on(0x01); Write_KeyPadData(0x0D); data = Read_KeyPadData(); if(!(data&0x01))led_on(0x08); if(!(data&0x02))led_on(0x07); if(!(data&0x04))led_on(0x06); if(!(data&0x08))led_on(0x05); Write_KeyPadData(0x0B); data = Read_KeyPadData(); if(!(data&0x01))led_on(0x0c); if(!(data&0x02))led_on(0x0b); if(!(data&0x04))led_on(0x0a); if(!(data&0x08))led_on(0x09); 트와매핑되고그결과를 LED 로표현한다. 이코드는한행의키값을처리후바로다음키스캔을실행하는코드가아니기때문에한행에동시입력발생시큰값부터차례대로 LED 에표시된다. 하지만너무빠른시간내에처리되기때문에육안으로구분되지는않을것이다. 이후두번재행 (0b1101), 세번째행 (0b1011), 네번째행 (0b0111) 에대해같은작업을반복하여처리한다. Write_KeyPadData(0x07); data = Read_KeyPadData(); if(!(data&0x01))led_on(0x10); if(!(data&0x02))led_on(0x0f); if(!(data&0x04))led_on(0x0e); if(!(data&0x08))led_on(0x0d); int main(void) { Platform_Init(); while(1) { KeyPadScan(); ASM 소스코드.equ SPH = 0x3E.equ SPL = 0x3D.equ MCUCR = 0x35.equ LED_CS = 0x4800.equ KEY_DATA = 0x5400.equ KEY_SCAN = 0x5800.def AH = R16.def AL = R17.def LED_COL = R18.def LED_ROW = R19.def DELAY_COUNT = R25.cseg.org 0x0000 LDI AH,high(0x10FF) LDI AL,low(0x10FF) OUT SPH,AH OUT SPL,AL IN R16, MCUCR ORI R16, 0x80 OUT MCUCR, R16 LOOP: CALL KEYSCAN_ROW1 CALL KEYSCAN_ROW2 CALL KEYSCAN_ROW3 CALL KEYSCAN_ROW4 JMP LOOP KEYSCAN_ROW1: LDI LED_ROW, 0 LDI R16, 0x0E STS KEY_DATA, R16 LDS R16, KEY_SCAN 메인프로그램프로그램에필요한초기화작업을해주는함수호출 반복실행하여프로그램이종료되지않게한다. 실질적인메인코드. 키패드를처리하는함수호출. 표준 IO 레지스터를사용하기편하게키워드로매핑 외부 LED 의메모리맵주소를사용하기편하게키워드로매핑 Keypad 의메모리맵주소를사용하기편하게키워드로매핑 범용레지스터를사용하기편하게키워드로매핑 프로그램코드의시작이후의코드를메모리주소 0x00 부터시작프로그램의스택설정 외부메모리에접근하기위해 MCUCR 레지스터의 SRE 비트를 1 로설정한다. 프로그램반복을위한레이블키패드각행의키스캔및눌려진키를처리하는루틴호출 프로그램반복실행 키패드의첫번째줄을스캔하는루틴현재행의 ( 첫번째버튼번호 1) 값설정. 첫번째버튼 = 1 번으로설정스캔할행값을출력하기위해 R16 에 0xE(=0b1110) 저장키패드회로에스캔값출력첫번째행의키값을가져와 R16 에저장
CALL CHECK_COL KEYSCAN_ROW2: LDI LED_ROW, 4 LDI R16, 0x0D STS KEY_DATA, R16 LDS R16, KEY_SCAN CALL CHECK_COL 키패드의눌려진키를처리하기위한루틴호출 이후 KEYSCAN_ROW2~4 루틴은각행의첫번째버튼값을 5, 9, 13 으로설정하고해당행의스캔값을설정한뒤위함수와같은동작. KEYSCAN_ROW3: LDI LED_ROW, 8 LDI R16, 0x0B STS KEY_DATA, R16 LDS R16, KEY_SCAN CALL CHECK_COL KEYSCAN_ROW4: LDI LED_ROW, 12 LDI R16, 0x07 STS KEY_DATA, R16 LDS R16, KEY_SCAN CALL CHECK_COL CHECK_COL: PUSH R16 LDI LED_COL, 4 ANDI R16, 0x01 CPI R16, 0x00 BREQ LED_ON POP R16 PUSH R16 LDI LED_COL, 3 ANDI R16, 0x02 CPI R16, 0x00 BREQ LED_ON POP R16 입력받은키패드의키값에서눌려진키를처리하기위한루틴인수인 R16 을보존하기위해스택에저장이번행의네번째버튼임을처리하기위해 LED_COL 에 4 를저장 MSB 가첫번째, LSB 가네번째버튼이므로 0b0001 과 AND 연산을통해필터링후버튼이눌려졌는지 (=0) 비교. 버튼이눌려졌다면 LED 를켜는루틴으로이동눌려지지않았다면스택에백업했던원래키값복원 이후첫번째 ~ 세번째키를같은방법으로처리 PUSH R16 LDI LED_COL, 2 ANDI R16, 0x04 CPI R16, 0x00 BREQ LED_ON POP R16 PUSH R16 LDI LED_COL, 1 ANDI R16, 0x08 CPI R16, 0x00 BREQ LED_ON POP R16 LDI LED_ROW, 0 LDI LED_COL, 0 LED_ON: POP R16 ADD LED_ROW, LED_COL STS LED_CS, LED_ROW.include "Delay.asm" 아무키도눌려지지않았다면버튼번호계산을위한레지스터인 LED_ROW 와 LED_COL 을 0 으로초기화. 눌려진버튼에따라 LED 를켜기위한루틴 의복귀주소복원을위해 CHECK_COL 에서백업했던키값을 POP 행의첫번째버튼번호에눌려진버튼의열번호를더해버튼번호를구한뒤, 외부 LED 에해당값을출력한다. 어셈블링시딜레이관련함수가정의되어있는 ASM 파일포함.
- Bin2LED.asm 소스분석 ( 분석과제 3) 이소스는위 7-Segment 소스의 ExtFndSet 함수에해당하는작업을하는것으로, ExtFndSet 함수와는달리직접 7-Segment 로값을출력하지는않고이함수를호출한함수측에, 수치가변환된세그먼트값을반환하기만하는함수이다..equ RAMPZ = 0x3b.def ZH = r31.def ZL = r30 BIN2LED: PUSH R17 PUSH ZH PUSH ZL CPI R16,'H' BRNE BIN2L1 LDI R16,0x10 RJMP BIN2L4 BIN2L1: CPI R16,'-' BRNE BIN2L2 LDI R16,0x11 RJMP BIN2L4 BIN2L2: CPI R16,'.' BRNE BIN2L3 LDI R16,0x12 RJMP BIN2L4 BIN2L3: CPI R16,0x27 BRNE BIN2L4 LDI R16,0x13 BIN2L4: LDI R17,byte3(FND_TABLE << 1) LDI ZH,high(FND_TABLE << 1) LDI ZL,low(FND_TABLE << 1) ADD ZL,R16 CLR R16 ADC ZH,R16 ADC R17,R16 OUT RAMPZ,R17 LPM R16,Z POP ZL POP ZH POP R17 FND_TABLE: ; 7-segment LED data table.db 0b11000000,0b11111001 ; 0,1.db 0b10100100,0b10110000 ; 2,3.db 0b10011001,0b10010010 ; 4,5.db 0b10000010,0b11011000 ; 6,7.db 0b10000000,0b10010000 ; 8,9.db 0b10001000,0b10000011 ; A,b.db 0b11000110,0b10100001 ; C,d.db 0b10000110,0b10001110 ; E,F.db 0b10001001,0b10111111 ; H,-.db 0b01111111,0b11111111 ;.,' RAMPZ 레지스터의주소 ( 아래에설명 ) Z 레지스터의주소 ( 상위바이트 ) Z 레지스터의주소 ( 하위바이트 ) 함수의메인코드호출을위한레이블이함수에서 R17, Z 레지스터를사용하며값이변경되기때문에스택에백업한다. 이함수의인수인변경할숫자는 R16 으로넘겨받는다. 넘겨받은인수가 H 가아니라면 - 비교루틴 (BIN2L1) 으로이동한다. H 라면 R16 에 16 을넣고세그먼트값으로변환하는루틴 (BIN2L4) 으로이동한다. 넘겨받은인수가 - 가아니라면. 비교루틴 (BIN2L2) 으로이동한다. - 라면 R16 에 17 을넣고세그먼트값으로변환하는루틴 (BIN2L4) 으로이동한다. 넘겨받은인수가. 이아니라면 비교루틴 (BIN2L3) 으로이동한다.. 라면 R16 에 18 을넣고세그먼트값으로변환하는루틴 (BIN2L4) 으로이동한다. 넘겨받은인수가 가아니라면바로세그먼트값으로변환하는루틴 (BIN2L4) 으로이동한다. 라면 R16 에 19 를넣는다. R16 값에따라세그먼트값으로변환하는루틴변환될세그먼트값이저장된주소인 FND_TABLE 에 2 를곱해확장된 3 바이트값을 R17, ZH, ZL 레지스터에저장한다. ( 표아래설명참조 ) 수치순으로메모리에저장되있기때문에 ZL 에넘겨받은인수를더한후, R16 값을유지할필요가없기때문에 0 으로초기화한다. ZL 과 R16 의합에올림수 (Carry) 가발생한경우 ZH 에더해주소값인 Z 레지스터의상위바이트를증가시킨다. 마찬가지이유로 ZH 에올림수발생시 R17 을증가시킨다. 메모리의페이지번호를설정하기위해 R17 을 RAMPZ 레지스터에출력한다. ( 코드상불필요. 표아래설명참조 ) 완성된세그먼트값주소에서값을읽어와 R16 에저장한다. 원래코드로복귀하기위한준비로, 스택에백업했던이전 Z, R17 레지스터값을복원한다. 이함수를호출했던원래코드로복귀한다. 변환될세그먼트값의주소를얻기위한레이블각값은 1 바이트값으로, 0~19 까지 20 개의바이트배열로생각하면된다. 0~15 까지는 16 진수 0~F 로출력되고 16 은 H 17 은 18 은. 19 는공백 ( 모든세그먼트 Off) 으로설정되어있다.
FND_TABLE 주소에 1번의 LSL을수행하는이유 : 프로그램코드에작성된상수등은프로그램메모리에저장된다. 평상시프로그램메모리는 16bit-wide로동작하여메모리주소가 1 증가하면실제메모리공간에서의주소는 16비트가증가하게된다. 하지만런타임중프로그램코드상에서프로그램메모리에접근하는명령들은 8bit-wide로동작하는데이때, 두배의주소공간이더필요하다는문제가발생하기때문에 64K단위의페이지로나눠페이지0의 0~64K, 페이지1의 0~64K 주소로접근하는방법을통해모든메모리공간에접근할수있게하였다. 여기서접근할페이지번호를설정하는레지스터가 RAMPZ 레지스터이다. : 본론인 1번의 LSL을수행하는이유는, FND_TABLE로지정된메모리주소의상수값은 16bit-wide시의주소값이기때문이다. LPM명령수행시 8bit-wide로동작하기때문에 FND_TABLE의두배값을사용해야하고, 그렇기에연산결과가원래값의 2의배수가되는 LSL을 1회실행하는것이다. RAMPZ 레지스터 (RAM Page Z Select Register) : 다른비트들은나중을위해예약되어있어 0번비트만사용된다. ( 페이지0, 1 구분 ) : ATmega128의 128KB 메모리공간은 64K * 16bit로서 2바이트의메모리주소로모든메모리에접근가능하지만 8bit로동작시 128K * 8bit로 128K, 즉두배의주소공간이더필요하게된다. 그래서 64K단위의페이지로나눠페이지0의 0~64K(= 하위 64K, $0000~$7FFF), 페이지1의 0~64K(= 상위 64K, $8000~$FFFF) 주소로접근하는방법을통해모든메모리공간에접근할수있게되는것이다. : 하지만이레지스터는 ELPM/SPM 명령어가사용될때에만적용된다. 분석과제 3의코드에서쓰인 LPM 명령에는적용되지않기때문에코드분석의주석에서코드상필요없는명령이라고한것이다. 실험방법 - AVR Studio 4를이용하여프로그램소스코드작성후빌드하여실행파일 (.hex) 을생성한다. - AVR Studio 4의 AVR Programmer 를이용, AVR칩에프로그램을다운로드하여실험킷에서의동작을확인한다.
예비과제시뮬레이션결과 - 예비과제시뮬레이션회로구성 - - 키패드는 PA7~PA4를통해키스캔값을출력하고, PA3~PA0로키값을입력받는다. 또 PB를통해 FND의 a~dp의세그먼트값을출력하고, PC3~0을 FNC의 COM과연결하여회로를구성하였다. - 위화면은 14번버튼이눌러진뒤의화면이다. 프로그램이제대로동작하여 7-Segment에 14가표시됨을확인할수있다. 예비과제코드분석.include "m128def.inc".def AH = R16.def AL = R17.def KP_SCAN = R18.def KP_DATA = R19.def LASTNUM = R20.cseg.org 0x0000 LDI AH, high(ramend) LDI AL, low(ramend) OUT SPH, AH OUT SPL, AL LDI R16, 0xF0 OUT DDRA, R16 LDI R16, 0x0F OUT DDRC, R16 LDI R16, 0xFF OUT DDRB, R16 OUT PORTA, R16 OUT PORTB, R16 OUT PORTC, R16 키스캔값을저장할레지스터. ( 첫행부터 0~3) 키스캔에대한현재키값을저장할레지스터. 가장최근에입력된키값을저장할레지스터. 프로그램스택을설정한다. PortA 의 7~4 를키스캔출력, 3~0 을키값입력으로사용하도록한다. PortC 의 3~0 을 FND_COM 출력으로사용하도록한다. PortB 를 FND_DATA 출력으로사용하도록한다. 각포트를초기화한다. LOOP: RCALL SET_SCAN RCALL GET_DATA 반복실행을위한레이블현재키스캔값을 Keypad 에출력한다. Keypad 에서키스캔에대한키값을가져온다.
SBRC KP_DATA, 3 RCALL KP_DATA8 SBRC KP_DATA, 2 RCALL KP_DATA4 SBRC KP_DATA, 1 RCALL KP_DATA2 SBRC KP_DATA, 0 RCALL KP_DATA1 RCALL SEG_ON INC KP_SCAN SBRC KP_SCAN, 2 CLR KP_SCAN RJMP LOOP KP_DATA8: MOV AH, KP_SCAN INC AH MOV LASTNUM, AH 현재키값의 3 번비트 (0b1xxx) 가 1 이라면 KP_DATA8 루틴을호출하여 LASTNUM 값을갱신한다. 현재키값의 2 번비트 (0bx1xx) 가 1 이라면 KP_DATA4 루틴을호출하여 LASTNUM 값을갱신한다. 현재키값의 1 번비트 (0bxx1x) 가 1 이라면 KP_DATA2 루틴을호출하여 LASTNUM 값을갱신한다. 현재키값의 0 번비트 (0bxxx1) 가 1 이라면 KP_DATA1 루틴을호출하여 LASTNUM 값을갱신한다. LASTNUM 값을 7-Segment 에출력한다. 키스캔값을증가시킨다. 키스캔값이 4 가되었다면키스캔값을 0 으로다시초기화한다. 처음으로돌아가반복실행한다. KP_DATAn 루틴은키스캔값 ( 행 ) * 4 + 입력된키값의비트자리 ( 열 ) 를통해눌려진버튼의번호를계산하고, 그값을 LASTNUM 레지스터에업데이트하는함수이다. * 4 연산을위해두번의좌측쉬프트를사용한다. KP_DATA4: MOV AH, KP_SCAN LDI AL, 0x02 ADD AH, AL MOV LASTNUM, AH KP_DATA2: MOV AH, KP_SCAN LDI AL, 0x03 ADD AH, AL MOV LASTNUM, AH KP_DATA1: MOV AH, KP_SCAN LDI AL, 0x04 ADD AH, AL MOV LASTNUM, AH SET_SCAN: MOV AH, KP_SCAN LDI AL, 0x01 LSL_START: DEC AH BRMI LSL_END LSL AL RJMP LSL_START LSL_END: SWAP AL COM AL OUT PORTA, AL Keypad 에키스캔값을출력하는함수현재키스캔값을유지하기위해 AH 에복사한다. AL 에 1 을저장한다. ( 키스캔할행 ) 현재키스캔값만큼 LSL 을수행하는반복문키스캔값을감소한다. 감소된값이음수이면반복문을종료한다. 음수가아니라면 LSL 연산을수행한다. 반복문의처음으로돌아가반복수행한다. 키스캔은 PortA 의 7~4 를사용하기때문에상하위 Nibble 을교환한다. 값이 0 인행을키스캔하기때문에값을반전시킨다. 완성된키스캔값을 PortA 로출력한다.
GET_DATA: IN KP_DATA, PINA COM KP_DATA SEG_ON: MOV AH, LASTNUM RCALL OPDIV LDI R21, 0x01 OUT PORTC, R21 RCALL Bin2LED OUT PORTB, AH CALL D1MS LDI R21, 0x02 OUT PORTC, R21 MOV AH, AL RCALL Bin2LED OUT PORTB, AH CALL D1MS LDI R21, 0x0C OUT PORTC, R21 CLR AH RCALL Bin2LED OUT PORTB, AH CALL D1MS 키스캔에대한키값을 KP_DATA 에가져오는루틴 KP_DATA 에현재키값을가져온다. 버튼이눌려졌을때 0 이입력되기때문에코드의편의상값을반전시킨다. 7-Segment 에눌려진버튼값을출력하는루틴 LASTNUM 값을유지하기위해 AH 에복사한다. AH 값을나눗셈연산을통해자리수를분리한다. 10 의자리 : AL, 1 의자리 : AH PortC 를통해 0b0001 을출력하여가장오른쪽 ( 네번째 ) FND 를선택한다. 1 의자리값을세그먼트값으로변환한다. ( 분석과제코드사용 ) 변환된세그먼트값을 PortB 를통해 7-Segment 에출력한다. 7-Segment 에값이적절히 Setup 되도록약간의딜레이를준다. PortC 를통해 0b0010 을출력하여가장세번째 FND 를선택한다. 10 의자리값을세그먼트값으로변환하기위해 AH(R16) 에복사한뒤 Bin2LED 루틴을호출한다. 변환된세그먼트값을 PortB 를통해 7-Segment 에출력한다. 7-Segment 에값이적절히 Setup 되도록약간의딜레이를준다. PortC 를통해 0b1100 을출력하여가장첫번째와두번째 FND 를동시에선택한다. 16 개버튼은세자리이상의자리수는필요없으니 0 을출력하기위해 AH 를 0 으로초기화한뒤 Bin2LED 루틴을호출한다. 변환된세그먼트값을 PortB 를통해 7-Segment 에출력한다. 7-Segment 에값이적절히 Setup 되도록약간의딜레이를준다. OPDIV: CLR AL DIV_START: CPI AH, 0x0A BRLO DIV_END SUBI AH, 0x0A INC AL RJMP DIV_START DIV_END: 자릿수분리를위한나눗셈루틴몫을저장할 AL 을 0 으로초기화한다. 나눗셈을위한반복문나눌수 ( 피제수 ) 가 10 보다작다면나눗셈을종료한다. 그렇지않다면피제수에서 10 을뺀다. 그리고몫을증가시킨다. 반복문의처음으로돌아가반복수행한다. 결과로몫은 AL 에, 나머지는 AH 에저장되어 10 의자리, 1 의자리로분리된다..include "Bin2LED.asm".include "Delay.asm" 예비과제소스에서쓰인 Bin2LED 와 Delay 함수를사용하기위한 include 문. 참고문헌 - Keypad 이론 : Keypad interfacing with Microcontrollers Tutorial ( http://www.8051projects.net/keypad-interfacing/introduction.php ) - 7-Segment(FND) 이론자료1: FND(Flexible Numeric Display) ( http://blog.naver.com/joke1977/110043017535 ) - 7-Segment(FND) 이론자료2: FND(7-segment) 기초지식 ( http://blog.naver.com/dpsjwl82/80119732265 ) - 분석과제 3 자료1: RAMPZ ; RAM Page Z Select Register ( http://www.cyworld.com/uryoni_home2/3718177 ) - 분석과제 3 자료2: 실험참고자료 C언어를이용한 AVR 설계및응용.pdf p.13(p.20)