제목 : 실험 #2 결과보고서 외부 LED & Dip 스위치제어 실험일 : 2013. 03. 19. (3 주차 ) 실험내용 - 예비과제 : 메모리맵드 IO를통해 Dip 스위치의값을읽고, On 상태의스위치가하나이상있다면외부 LED를점멸하는프로그램 - 실험과제 : 메모리맵드 IO를통해 Dip 스위치의값을읽고, Dip 스위치의조작을통한사칙연산결과를 LED를통해출력하는프로그램. 연산자는 0~3까지를덧셈, 뺄셈, 곱셈, 나눗셈으로정한다. 음수결과는 LED의부호위치를켜고, 결과의절대값으로표시한다. 피연산자1 연산자 피연산자2 - Dip 스위치설정 - 연산자부호연산결과 ( 곱셈시부호까지확장 ) - LED 설정 1 ( 나눗셈이외의연산 ) - 연산자 몫 나머지 - LED 설정2 ( 나눗셈연산 ) - 실험결과 - 예비과제 - 해결방법 : 실험키트의 Dip 스위치의값이 On 일때 0, Off 일때 1로, 예시와는반대로동작하기때문에 Dip 스위치가하나라도 On인지확인하기위해모두 Off 상태인 0xFF(=0b11111111) 과비교하도록작성하였다. 이번실험에서조작할외부 LED 역시이전실험에서쓰인 LED와는반대로설계되어 0일때불이꺼지고, 1일때불이켜진다. - 결과 : Dip 스위치가하나이상 On일때, 0.5초주기로모든 LED 켜졌다가꺼지는동작을확인함. - 소스코드 C 언어소스코드 #include <avr/io.h> #include <avr/delay.h> #define LED_CS (*(volatile unsigned char *)0x4800) #define DIP_SW_CS (*(volatile unsigned char *)0x8000) 딜레이함수를사용하기위한헤더파일선언
int main(void){ MCUCR = 0x80 ; while(1){ if (DIP_SW_CS < 0xFF) { LED_CS = 0xFF; _delay_ms(500); LED_CS = 0x00; _delay_ms(500); else LED_CS = 0x00; 어셈블리어소스코드.include "m128def.inc".def AH = R16.def AL = R17.def CNT0 = R18.def CNT1 = R19.def CNT2 = R20.def LED_ON = R21.equ LED_CS = 0x4800.equ DIP_SW_CS = 0x8000.cseg.org 0x0000 LDI AH, HIGH(RAMEND) LDI AL, LOW(RAMEND) OUT SPH, AH OUT SPL, AL LDI AH, 0x80 OUT MCUCR, AH 외부메모리에접근하기위해 MCUCR 레지스터의 SRE 비트를 1 로설정한다. Dip 스위치가모두 Off 인상태가아니라면 LED 매핑주소에 0xFF 를내보내 LED 를모두켜도록한뒤 500ms 의지연시간을가진다. 그후 0x00 을내보내모든 LED 를끄도록하여점멸효과를구현한다. Dip 스위치가모두 Off 라면 LED 를모두끈다. R17 레지스터를 AH 로매핑한다. R16 레지스터를 AL 로매핑한다. 딜레이카운트 (2~0 중 2) 를위한레지스터. 딜레이카운트 (2~0 중 1) 를위한레지스터. 딜레이카운트 (2~0 중 0) 를위한레지스터. LED 출력값을저장하기위한레지스터. 프로그램의시작점프로그램메모리의 0x0 번지에서부터코드를시작한다. 현재프로그램의스택을설정한다. 외부메모리에접근하기위해 MCUCR 레지스터의 SRE 비트를 1 로설정한다. LOOP: BLINK: CLR CNT0 CLR CNT1 CLR CNT2 CLR LED_ON LDI AL, 0x01 LDS AH, DIP_SW_CS CPI AH, 0xFF BRNE BLINK EOR AH, AH STS LED_CS, AH RJMP LOOP ADD CNT0, AL BRCC LOOP ADD CNT1, AL BRCC LOOP ADD CNT2, AL CPI CNT2, 0x3 BRNE LOOP SER CNT2 EOR LED_ON, CNT2 STS LED_CS, LED_ON CLR CNT0 CLR CNT1 CLR CNT2 RJMP LOOP 딜레이카운터와 LED 값을초기화한다. 딜레이는 255 * 255 * 3, 즉 3 비트가필요하기때문에세개의레지스터를사용한다. 이후 AL 은 1 증가 ADD 연산의피연산자로이용된다. (INC 는 Carry 가발생하지않기때문에 ADD 사용 ) 반복문을위한레이블 Dip 스위치값을읽어온다. 모두 Off 인지확인후, 하나이상 On 이라면 BLINK 레이블로이동한다. 모두 Off 라면 LED 역시모두끄도록, LED 와매핑된주소에 0 을내보낸다. 반복문의처음으로돌아간다. LED 점멸을위한레이블딜레이카운터를증가시킨다. 레지스터의최대값 (255) 를넘을경우캐리가발생하기때문에 LOOP 로돌아가지않고다음코드를실행하여다음레지스터를증가시킨다. 마찬가지로두번째레지스터도캐리가발생할경우카운터의최상위값을저장한레지스터를증가시키고, 그값이 3 과같을때까지반복하며딜레이를발생시킨다. 카운트후필요없어진카운터레지스터를 LED 값반전을위해 0xFF 로만들고 XOR 연산을통해 LED 값을반전시킨다. LED 와매핑된주소에 LED 값을내보내점멸효과를구현한다. 딜레이카운터를초기화한다. LOOP 로돌아가반복실행한다.
- 실험과제 - 해결방법 : Dip 스위치에서읽어온값을비트연산을통해피연산자1, 2와연산자로분리한뒤, 연산자에따라각각의연산을수행뒤결과를저장하고, 최종적으로다시비트연산을통해 8비트공간에순서에맞도록배열한뒤 LED로출력하도록작성하였다. - 결과 : 각각의연산결과에따라설정한 LED 값이올바르게출력됨을확인하였다. < 4 + 6 = 10 > < 4 6 = -2 > < 6 * 4 = 24 > < 6 / 4 = 1...2 > - 소스코드 C 언어소스코드 #include <avr/io.h> #define LED_CS (*(volatile unsigned char *)0x4800) #define DIP_SW_CS (*(volatile unsigned char *)0x8000) int main() { unsigned char dipsw = 0x00; unsigned char A = 0, B = 0, OP = 0; char RST = 0; MCUCR = 0x80; while(1) { dipsw = DIP_SW_CS ^ 0xFF; A = dipsw >> 5; B = dipsw & 0x07; OP = (dipsw & 0b00011000) >> 3; switch (OP) { case 0x00: // + RST = A + B; case 0x01: // - RST = A B; Dip 스위치값을저장할변수피연산자들의값을저장할변수연산결과를저장할변수연산자, 부호, 나머지값을저장할변수외부메모리에접근하기위해 MCUCR 레지스터의 SRE 비트를 1 로설정한다. 실험킷은 On 일때 0 이입력되기때문에, 코드작성의편의를위해 Dip 스위치값을반전한다. 상위 3 비트를피연산자 1 에저장한다. 하위 3 비트를피연산자 2 에저장한다. 가운데 2 비트를연산자에저장한다. 연산자에따라 switch 하여해당연산을수행한다. 뺄셈결과가음수일경우
if (RST < 0) RST = (RST * -1) 0x20; case 0x02: // * RST = A * B; case 0x03: // / RST = (A / B) << 3 (A % B); LED_CS = (OP << 6) RST; 어셈블리어소스코드.include "m128def.inc".def AH = R16.def AL = R17.def OPR1 = R18.def OPR2 = R19.def OP = R20.equ LED_CS = 0x4800.equ DIP_SW_CS = 0x8000.cseg.org 0x0000 LDI AH, HIGH(RAMEND) LDI AL, LOW(RAMEND) OUT SPH, AH OUT SPL, AL LDI AH, 0x80 OUT MCUCR, AH GET_DIPSW: LDS AH, DIP_SW_CS SER AL EOR AH, AL MOV OPR1, AH SWAP OPR1 R1 ANDI OPR1, 0x07 MOV OPR2, AH ANDI OPR2, 0x07 MOV OP, AH ANDI OP, 0x03 CLR AH OP_SUM: CPI OP, 0x00 BRNE OP_SUB ADD OPR1, OPR2 OP_SUB: CPI OP, 0x01 BRNE OP_MUL SUB OPR1, OPR2 SBRS OPR1, 7 결과를양수로만들고부호를 1 로설정한다. 나눗셈시, 몫의값을 3 비트올려몫의저장위치로옮기고, 하위에나머지값을넣어준다. 연산자를최상위로옮기고, 연산결과를더해 LED 에출력한다. R17 레지스터를 AH 로매핑한다. R16 레지스터를 AL 로매핑한다. 피연산자 1 을저장하기위한레지스터피연산자 2 를저장하기위한레지스터연산자를저장하기위한레지스터 프로그램의시작점프로그램메모리의 0x0 번지에서부터코드를시작한다. 현재프로그램의스택을설정한다. 외부메모리에접근하기위해 MCUCR 레지스터의 SRE 비트를 1 로설정한다. 반복동작을위한레이블. Dip 스위치값을읽어오는루틴 Dip 스위치의값을읽어온다. XOR 연산을통한값반전을위해 AL 을 0xFF 로설정한다. 실험킷은 On 일때 0 이입력되기때문에, 코드작성의편의를위해 Dip 스위치값을반전한다. 쉬프트횟수를줄이기위해상하위니블을교환한다. 나머지 1 비트를쉬프트하여위치를맞춘다. 레지스터에피연산자 1 의값만을남긴다. 하위 3 비트를피연산자 2 에저장한다. 가운데 2 비트를연산자로사용하기위해오른쪽으로 3 비트쉬프트하여값의위치를맞춘다. 레지스터에연산자값만을남긴다. 연산에사용하기위해 AH 를 0 으로초기화한다. 덧셈연산을위한레이블 현재수행할연산이덧셈이아니라면뺄셈연산으로이동 덧셈연산수행 뺄셈연산을위한레이블 현재수행할연산이뺄셈이아니라면곱셈연산으로이동 뺄셈연산수행결과가음수가아닐경우바로
SER AH EOR OPR1, AH INC OPR1 ORI OPR1, 0x20 OP_MUL: CPI OP, 0x02 BRNE OP_DIV OP_DIV: MUL OPR1, OPR2 MOV OPR1, R0 CP OPR1, OPR2 BRLO SET_LED_DIV SUB OPR1, OPR2 INC AH RJMP OP_DIV SET_LED_DIV: SWAP AH LSR AH OR OPR1, AH SET_LED: SWAP OP LSL OP LSL OP OR OPR1, OP STS LED_CS, OPR1 RJMP GET_DIPSW 결과가음수일경우 2 의보수연산을위해 AH 를 0xFF 로설정연산결과를반전반전한값에 1 을더해양수값얻음부호출력위치에 1 을설정해음수결과임을표시 곱셈연산을위한레이블 현재수행할연산이곱셈이아니라면나눗셈연산으로이동 곱셈연산수행곱셈결과는 (R1:R0) 에저장되기때문에 OPR1 로결과값복사. 나눗셈연산을위한레이블. 나눗셈명령이없어서직접구현피연산자 2 가피연산자 1 보다커서뺄수없다면 (= 나눌수없다면 ) 출력값을만들기위해 SET_LED_DIV 로이동피연산자 1 이크다면피연산자 2 를빼고몫 (AH 에저장 ) 을증가시킨다. 나눌수없을때까지반복 나눗셈결과값을만들기위한레이블 5~3 비트에몫의자리를맞추기위해, 또쉬프트횟수를줄이기위해니블교환후나머지 1 비트를오른쪽으로쉬프트. 하위에나머지값을추가하여결과값완성. LED 출력을위한레이블연산자를최상위자리로맞출때, 쉬프트횟수를줄이기위해니블교환후나머지 2 비트를왼쪽으로쉬프트. 출력할결과값에연산자값추가 최종적으로완성된출력값을 LED 에출력 Dip 스위치를읽는루틴으로돌아가전체반복실행 실험후느낀점 - Dip 스위치등의동작이디지털시스템, 임베디드시스템실험에서썼던장비들과는반대로동작하도록회로가구성되어있어초반에약간의혼동을겪었다. 회로도를자세히봤더라면진작에알수있었을텐데하고후회되었지만실험을무사히완수할수있어다행이다. - 실험과제코드작성중, 계산결과를저장하는변수를습관적으로 unsigned로선언해음수결과가되는뺄셈연산에서잘못된결과를출력하였다. 다행히오류를수정하였지만, 앞으로는조심해야하겠다. - 다른조원이도움을요청해그조의코드를살펴봤는데언뜻별다른이상이없는듯보였다. 하지만출력만을위한 LED_CS에서값을읽어 OR연산을수행하도록되어있는것을알아채고해당코드를수정하니역시정상적인결과를보였다. 포트매핑과는달리메모리매핑을통한 IO에서는입출력방향이잘와닫지않는듯하다. 조심해야할부분이다.