제목 : 실험 #1 결과보고서 GPIO LED 제어 실험일 : 2013. 03. 12. (2 주차 ) 실험내용 - 예비과제 : ATmega126의 8개의핀에연결되어있는 LED 점멸하는프로그램 - 실험과제 : ATmega126의 8개의 LED를순차적으로켜고끄는프로그램 실험결과 - 예비과제 - 해결방법 : 점멸되는시간 (Delay) 를구현하기위해임의의변수 i를적당한지연시간이생길정도의크기만큼증가시킨후, 해당크기가되면 LED로의출력값을반전시켜점멸효과구현. : 어셈블리코드에서는각레지스터는 1바이트, 0~255까지만을카운트할수있기때문에디지털시스템실험과목에서배운분주기를응용하여레지스터세개를사용해최대 3바이트크기를카운트할수있는코드를작성했다. : 단, 시뮬레이션에서는 PortB와모든 LED가연결되었음을가정하고작성하였지만, 실제실험킷에서는각포트와 LED가 PortB(7~4:7~4), D(3~2:5~4), E(1~0:3~2) 로나뉘어연결되어있어 LED 출력부의소스코드를수정해야했다. 또, LED회로의전류방향이예제와는반대로연결되어있어출력값역시반대로변경했다. 즉, 기존예제의 1=ON, 0=OFF가아닌 1=OFF, 0=ON이된다. : 또한, PortE3는킷의다른회로와연결되어있어제대로동작하지않았는데, 점퍼 (JP13) 설정을바꿔해당회로와의연결을끊어주었다. 또 PortE2는 Piezo 소자와연결되어점멸시비프음이발생하였기에역시점퍼 (JP11) 연결을끊어주었다. - PE3 과 JP13 을통해연결된소자 - - PE2 와 JP11 을통해 Piezo 와연결됨 -
- 결과 : 약 1초마다모든 LED가동시에켜졌다가동시에꺼지는동작을확인함. - 소스코드 C 언어소스코드 #include <avr/io.h> int main(void) { unsigned char LED_ON = 0x00; unsigned long i = 0; DDRB = 0B11110000; DDRD = 0B00110000; DDRE = 0B00001100; while(1) { if(i > 55000) { LED_ON ^= 0XFF; i = 0; PORTB = LED_ON & 0b11110000; PORTD = (LED_ON & 0b00001100) << 2; PORTE = (LED_ON & 0b00000011) << 2; i++; 어셈블리어소스코드.include "m128def.inc" LED 의출력값. ON 으로초기화한다. 딜레이카운트를위한변수. PortB 의 7~4 를출력으로사용하도록한다. PortD 의 5~4 를출력으로사용하도록한다. PortE 의 3~2 를출력으로사용하도록한다. PortB 를초기화한다. PortD 를초기화한다. PortE 를초기화한다. 프로그램종료없이계속반복실행한다. i 가적당히증가하여시간이지연되면이전 LED 출력값을반전시킨후 i 를 0 으로초기화해다시카운트한다. 각포트에맞는값을뽑아내출력한다. PortD 와 E 는출력할핀이출력값보다 2 자리앞에존재하기때문에왼쪽으로두번시프트한다. 그후, 딜레이계산을위해 i 를증가시킨다..def AH=R17.def AL=R16.def CNTA=R20.def CNTB=R19.def CNTC=R18.def CNTH=R21.def LED_ON=R22.cseg.org 0x000 LDI AH, high(ramend) LDI AL, low(ramend) OUT SPH, AH OUT SPL, AL LDI AL, 0B11110000 OUT DDRB, AL LDI AL, 0B00110000 OUT DDRD, AL LDI AL, 0B00001100 R17 레지스터를 AH 로매핑한다. R16 레지스터를 AL 로매핑한다. 딜레이카운트 (2~0 중 2) 를위한레지스터. 딜레이카운트 (2~0 중 1) 를위한레지스터. 딜레이카운트 (2~0 중 0) 를위한레지스터. 딜레이카운트의비교값을저장하기위한레지스터. LED 출력값을저장하기위한레지스터. 프로그램의시작점프로그램메모리의 0x0 번지에서부터코드를시작한다. 현재프로그램의스택을설정한다. LED 와연결된각포트의출력위치를설정한다.
OUT DDRE, AL LOOP: LDI CNTH, 0B00000011 LDI AL, 0B11111111 LDI AH, 0B00000001 LDI LED_ON, 0XFF ANDI R23, 0B11110000 OUT PORTB, R23 ANDI R23, 0B00001100 OUT PORTD, R23 ANDI R23, 0B00000011 OUT PORTE, R23 ADD CNTC, AH ADD CNTB, AH INC CNTA CP CNTA, CNTH BRNE LOOP EOR LED_ON, AL JMP LOOP 딜레이카운터를초기화한다. 딜레이는 255 * 255 * 3, 즉 3 비트가필요하기때문에세개의레지스터를사용한다. 카운터의비교값을설정한다. (0B00000011 = 3) 이후 AL 은출력반전을위한피연산자로이용된다. 이후 AH 는 1 증가 ADD 연산의피연산자로이용된다. LED 의초기출력값을 OFF 로설정한다. 반복문을위한레이블현재 LED 출력값을임시레지스터에복사한다. 해당포트에출력할값만을선택한다. 해당포트에출력한다. PortD 와 E 는각각출력할포트와출력값이두자리만큼차이나기때문에왼쪽으로두번시프트한뒤출력한다. 딜레이카운터를증가시킨다. 레지스터의최대값 (255) 를넘을경우캐리가발생하기때문에 LOOP 로돌아가지않고다음코드를실행하여다음레지스터를증가시킨다. 마찬가지로두번째레지스터도캐리가발생할경우카운터의최상위값을저장한레지스터를증가시키고, 그값이 CNTH 와같을때까지반복 ( 현재설정은 3 회 ) 한다. CNTA 와 CNTH 가같을경우 LED 출력값을반전시키고카운터를초기화한다. LOOP 로돌아가반복실행한다. - 실험과제 - 해결방법 : 동작을켜질때 8단계, 꺼질때 8단계, 총 16단계로구분하는변수를두고, 켜는동작은단순히왼쪽시프트연산의결과로최하위부터 0이채워지는원리를이용해켜도록만들고, 끄는동작시엔오른쪽시프트로밀려나생긴최상위비트의자리에 OR연산을통해 1을채우는코드를작성하여하나씩끄도록만들었다. - 결과 : 약 0.5초마다 LED가하나씩켜졌다가모두다켜진후다시꺼지는동작을확인함.
- 소스코드 C 언어소스코드 #include <avr/io.h> int main(void) { unsigned char LED_ON = 0xFF; unsigned int i = 0, j = 0; DDRB = 0B11110000; DDRD = 0B00110000; DDRE = 0B00001100; PORTD = 0XFF; PORTE = 0XFF; while(1) { if(i > 55000) { j = j % 16; if (j < 8) LED_ON = LED_ON << 0x1; else LED_ON = (LED_ON >> 0x1) 0b10000000; i = 0; j++; PORTB = LED_ON & 0b11110000; PORTD = (LED_ON & 0b00001100) << 0x2; PORTE = (LED_ON & 0b00000011) << 0x2; i++; 어셈블리어소스코드.include "m128def.inc" LED 의출력값. OFF 로초기화한다. i = 딜레이카운트를위한변수. j = LED 동작구분을위한변수. PortB 의 7~4 를출력으로사용하도록한다. PortD 의 5~4 를출력으로사용하도록한다. PortE 의 3~2 를출력으로사용하도록한다. PortB 를초기화한다. PortD 를초기화한다. PortE 를초기화한다. 프로그램종료없이계속반복실행한다. i 가증가하여시간이지연되면다음코드실행 j 를 0~15 사이의값으로고정 j 가 0~7 사이의값이라면 LED 를하나씩켠다. ( 왼쪽시프트시최하위는 0 으로채워지기때문 ) j 가 8~15 사이의값이라면 LED 를하나씩끈다. 이전값을오른쪽시프트로밀어내고최상위비트를 1 로채운다. 그후, 카운터를초기화하고켜지거나꺼진횟수 (j) 를증가시킨다. 각포트에맞는값을뽑아내출력한다. PortD 와 E 는출력할핀이출력값보다 2 자리앞에존재하기때문에왼쪽으로두번시프트한다. 그후, 딜레이계산을위해 i 를증가시킨다..def AH=R17.def AL=R16.def CNTA=R20.def CNTB=R19.def CNTC=R18.def CNTH=R21.def LED_ON=R22.def ON_OFF=R23.cseg.org 0x000 LDI AH, high(ramend) LDI AL, low(ramend) R17 레지스터를 AH 로매핑한다. R16 레지스터를 AL 로매핑한다. 딜레이카운트 (2~0 중 2) 를위한레지스터. 딜레이카운트 (2~0 중 1) 를위한레지스터. 딜레이카운트 (2~0 중 0) 를위한레지스터. 딜레이카운트의비교값을저장하기위한레지스터. LED 출력값을저장하기위한레지스터. 현재동작을구분하기위한레지스터 (1: 켜기, 0: 끄기 ) 프로그램의시작점프로그램메모리의 0x0 번지에서부터코드를시작한다. 현재프로그램의스택을설정한다. OUT SPH, AH OUT SPL, AL LDI AL, 0B11110000 LED 와연결된각포트의출력위치를설정한다.
OUT DDRB, AL LDI AL, 0B00110000 OUT DDRD, AL LDI AL, 0B00001100 OUT DDRE, AL LOOP: LDI CNTH, 0X07 LDI AH, 0X1 LDI LED_ON, 0XFF MOV ON_OFF, AH ADD CNTC, AH ADD CNTB, AH INC CNTA CP CNTA, CNTH BRNE LOOP CP ON_OFF, AH BRNE LEDOFF 딜레이카운터를초기화한다. 딜레이는 255 * 255 * 3, 즉 3 비트가필요하기때문에세개의레지스터를사용한다. 카운터의비교값을설정한다. (0X07 = 7) 이후 AH 는 1 증가 ADD 연산의피연산자로이용된다. LED 의초기출력값을 OFF 로설정한다. 회로의초기동작을 LED 켜기로설정한다. 반복문을위한레이블딜레이카운터를증가시킨다. 레지스터의최대값 (255) 를넘을경우캐리가발생하기때문에 LOOP 로돌아가지않고다음코드를실행하여다음레지스터를증가시킨다. 마찬가지로두번째레지스터도캐리가발생할경우카운터의최상위값을저장한레지스터를증가시키고, 그값이 CNTH 와같을때까지반복 ( 현재설정은 3 회 ) 한다. CNTA 와 CNTH 가같을경우다음코드를실행한다. 현재동작이 ON 인지 OFF 인지에따라해당동작을처리하는코드로분기한다. LEDON: MOV ON_OFF, AH LSL LED_ON BRCS LEDCTR DEC ON_OFF LEDOFF: LSR LED_ON ORI LED_ON, 0B10000000 BRCS LEDON LEDCTR: MOV AL, LED_ON ANDI AL, 0B11110000 OUT PORTB, LED_ON MOV AL, LED_ON LSL AL LSL AL MOV R24, AL ANDI R24, 0B00110000 OUT PORTD, R24 ANDI AL, 0B00001100 OUT PORTE, AL JMP LOOP LED 를하나씩켜는코드현재동작을 ON 으로설정한다. LED 출력값을왼쪽으로시프트한다. 꺼진상태에서는시프트시 1 이캐리로옮겨지지만모두켜지면최상위가 0 이므로캐리가발생하지않는다. LED 가모두다켜진게아니라면해당값을출력하는코드로분기한다. 모두켜졌을시동작을 OFF(0) 으로설정한다. LED 를하나씩끄는코드 LED 출력값을오른쪽으로시프트한다. 최상위비트를 1 로채운다. (LED 를끈다 ) 켜진상태에서는오른쪽시프트시 0 이캐리로옮겨지지만모두꺼지면최하위가 1 이므로캐리가발생한다. LED 가모두꺼졌다면다시 LED 를켜는코드로분기한다. LED 출력을처리하는코드카운터를초기화한다. 출력값을임시로 AL 에복사한다. 해당포트에출력할값만을선택한다. 해당포트에출력한다. PortD 와 E 는각각출력할포트와출력값이두자리만큼차이나기때문에왼쪽으로두번시프트한다. AL 을유지하기위해임시로 24 번레지스터에복사한뒤출력을처리한다. LOOP 로돌아가반복실행한다. 실험후느낀점 - 이번학기엔 ToastProg를사용해 AVR에프로그램을다운로드하는것으로알았지만, ToastProg와 ISP케이블의호환문제인지제대로동작하지않아한참헤메게되었다. 하지만예비조사과정에서알게된 AVRStudio에도다운로드프로그램이내장되어있다는걸기억해내고, 무사히실험을진행할수있어다행이었다. 또한예비리포트의중요성을깨닳게되었다. - LED를조작하는단순한동작을위해, 실험킷과 AVR의회로도를뒤져가며핀매핑과점퍼세팅, LED연결정보등찾아보는과정에서, 킷의전체회로가아니라킷내의각소자들의연결점만나와있어약간의어려움을겪었다. 하지만이번실험으로조금익숙해져다음실험에서는크게문제되지않을것이다.