라인트레이서강좌 4. 프로그래밍 2005년 8월 1일류대우 (davidryu@newtc.co.kr) 1. 라인트레이서란? 라인트레이서는정해진주행선을따라움직이는자율이동로봇이다. 현재공장자동화부분에서이용되고있는무인반송차가라인트레이서이다. 라인트레이서의기본적인원리는주어진주행선을센서로검출하여이것에따라목적위치까지이동하는것이다. 라인트레이서는크게 3부분 - 컨트롤러부, 센서부, 모터구동부로구성된다. 우선센서부의센서를잘배치해야길을잘인식하고턴마크인식할수있다. 모터구동부분은잡음이많이생기기때문에잘만들지않으면잡음으로인해다른부분이정상적으로동작하지못하는경우가생길수있다. 컨트롤러부는기본적인라인트레이서를제작할때는상대적으로비중이낮으나지능적으로만들려면많은작업이필요로한다. 본강좌에서는라인트레이서를공부하여만들고자하는초보자를위한강좌이므로라인트레이서로서의동작에목표를두고최대한간단하게동작시킬수있도록한다. 2. 라인트레이서의기본동작알고리즘라인트레이서기본동작센서 2쌍을라인의두께보다약간크게위치하여하드웨어를제작할경우센서가라인외곽을따라주행하도록한다. 더복잡하게할수도있지만여기서는위와같이가장간단한알고리즘으로알아보겠다.
1 번상황에서센서는라인을감지하지못하였으므로직진한다. 2 번상황에서오른쪽센서가인식되었으므로왼쪽바퀴의속도를증가시켜라인트레이서를오른쪽으로향하게한다. 3 번상황에서왼쪽센서가인식되었으므로오른쪽바퀴의속도를증가시켜라인트레이서를왼쪽으로향하게한다. 위의동작을반복하면라인트레이서는라인을따라진행하게된다. 3. 프로그래밍하기기본적인 C 언어프로그래밍과인터럽트에관한공부를먼저해야한다. C언어와인터럽트에관련해서는관련서적에서자세히나와있으므로이강좌에서는다루지않는다. 1 타이머인터럽트를이용하여스테핑모터구동하기타이머인터럽트를학습하여타이머인터럽트를이용하여스테핑모터구동해본다. 라인트레이서는스테핑모터를 2개구동해야하기때문에타이머인터럽트를 2개사용하여타이머인터럽트각각바퀴 1개씩을구동하도록한다. 2 센서입력받기센서의입력을받아흰색과검정색을구분할수있도록한다. 시리얼통신을이용하여값을확인하면쉽게할수있다. 3 메인함수와인터럽트루틴간의데이터공유메인함수에서는속도를변경하면타이머인터럽트에서변경된속도값을타이머에적용하여스테핑모터의속도를변경할수있도록한다. 그렇게하기위해서는속도값을전역변수로선언하여메인함수와인터럽트루틴이공유할수있도록해야한다. 4 센서입력에따른스테핑모터속도변경왼쪽센서입력이있을경우왼쪽스테핑모터의속도를감소시키고오른쪽센서입력이있을경우오른쪽스테핑모터의속도를감소시키도록프로그램을작성한다.
4. 예제프로그램 1 타이머인터럽트를이용하여스테핑모터구동하기아래소스는타이머 2 인터럽트의초기화루틴이다. void timer2_init(void) TCCR2 = 0x00; //stop TCNT2 = 0xF1; //setup OCR2 = 0x0F; TCCR2 = 0x04; //start 아래소스는타이머 2 인터럽트의인터럽트서비스루틴이다. void timer2_ovf_isr(void) unsigned char temp; CLI(); //disable all interrupts temp = PORTD & 0x0f; TCNT2 = l_speed; //reload counter value if(l_speed == 0) temp = temp; else temp = motoroneclock(temp, 1); PORTD = temp (PORTD & 0xf0); SEI(); //re-enable interrupts
2 센서입력받기아래소스는 AD Convert 의구동소스이다. startconvertion 함수는해당채널의컨버팅을시작하라는함수이고 readconvertdata 함수는컨버팅한채널의데이터를읽어오라는함수이다. void startconvertion(unsigned char ch) ADMUX = 0x20 (ch & 0x0f); ADCSRA = 0xc7; unsigned char readconvertdata(void) volatile unsigned char temp; while((adcsra & 0x10)==0); ADCSRA = ADCSRA 0x10; temp = ADCH; temp = ADCL; temp = ADCH; return temp; 3 메인함수루틴다음소스는메인함수에서수행되는무한루프로센서가연결되어있는 6번과 7번센서를컨버팅하여변수에저장시키고이것을임계값과비교하여왼쪽바퀴와오른쪽바퀴의속도를결정하도록하였다. void main(void) volatile unsigned char temp, l_sensor, r_sensor; volatile unsigned char step=0; while(1) l_sensor = 0; r_sensor = 0;
startconvertion(6); // Left sensor Converting l_sensor = readconvertdata(); startconvertion(7); // Right sensor Converting r_sensor = readconvertdata(); PORTF = 0x03; if(l_sensor > THRESHOLD && r_sensor < THRESHOLD) l_speed = SLOW_SPEED; r_speed = FAST_SPEED; PORTF = 0x01; else if(l_sensor < THRESHOLD && r_sensor > THRESHOLD) l_speed = FAST_SPEED; r_speed = SLOW_SPEED; PORTF = 0x02; else l_speed = FAST_SPEED; r_speed = FAST_SPEED; printf("ch6 :%d ch7 : %d\n\r", l_sensor, r_sensor); 5. 소스코드 // LineTracer Application // Target : M128 // Crystal: 11.059Mhz // Programed by Minsuk Kim #include <iom128v.h> #include <macros.h> #define FAST_SPEED 0x80 // 센서가감지되었을때빠른바퀴의센서값 #define SLOW_SPEED 0x10 // 센서가감지되었을때느린바퀴의센서값
#define THRESHOLD 17 // 센서의라인감지임계값 unsigned char l_speed=0, r_speed=0; // 모터를구동위한타이머값 void port_init(void) PORTA = 0x00; DDRA = 0x00; PORTB = 0x00; DDRB = 0x00; PORTC = 0x00; DDRC = 0x00; PORTD = 0x00; DDRD = 0xff; PORTE = 0x00; DDRE = 0x00; PORTF = 0x00; DDRF = 0x03; PORTG = 0x00; DDRG = 0x00; // Stepping Motor Port Left, Right // LED Port PF1, PF2 //UART0 initialize // desired baud rate: 9600 // actual: baud rate:9600 (0.0%) // char size: 8 bit // parity: Disabled void uart0_init(void) UCSR0B = 0x00; //disable while setting baud rate UCSR0A = 0x00; UCSR0C = 0x06; UBRR0L = 0x47; //set baud rate lo UBRR0H = 0x00; //set baud rate hi UCSR0B = 0x08;
//ADC initialize // Conversion time: 75uS void adc_init(void) ADCSRA = 0x00; //disable adc ADMUX = 0x00; //select adc input 0 ACSR = 0x80; ADCSRA = 0xC6; //TIMER2 initialize - prescale:64 // WGM: Normal // desired value: 1KHz // actual value: 1.008KHz (0.8%) void timer2_init(void) TCCR2 = 0x00; //stop TCNT2 = 0xF1; //setup OCR2 = 0x0F; TCCR2 = 0x04; //start #pragma interrupt_handler timer2_ovf_isr:11 void timer2_ovf_isr(void) unsigned char temp; CLI(); //disable all interrupts temp = PORTD & 0x0f; TCNT2 = l_speed; //reload counter value if(l_speed == 0) temp = temp; else
temp = motoroneclock(temp, 1); PORTD = temp (PORTD & 0xf0); SEI(); //re-enable interrupts //TIMER3 initialize - prescale:64 // WGM: 0) Normal, TOP=0xFFFF // desired value: 1KHz // actual value: 1.008KHz (0.8%) void timer3_init(void) TCCR3B = 0x00; //stop TCNT3H = 0xFF; //setup TCNT3L = 0xF1; OCR3AH = 0x00; OCR3AL = 0x0F; OCR3BH = 0x00; OCR3BL = 0x0F; OCR3CH = 0x00; OCR3CL = 0x0F; ICR3H = 0x00; ICR3L = 0x0F; TCCR3A = 0x00; TCCR3B = 0x04; //start Timer #pragma interrupt_handler timer3_ovf_isr:30 void timer3_ovf_isr(void) unsigned char temp; CLI(); //disable all interrupts //TIMER3 has overflowed TCNT3H = 0xFF; //reload counter high value
temp = (PORTD & 0xf0)>>4; TCNT3L = r_speed; //reload counter low value if(r_speed == 0) temp = temp; else temp = motoroneclock(temp, 0); PORTD = (temp<<4) (PORTD & 0x0f); SEI(); //re-enable interrupts //call this routine to initialize all peripherals void init_devices(void) //stop errant interrupts until set up CLI(); //disable all interrupts XDIV = 0x00; //xtal divider XMCRA = 0x00; //external memory port_init(); uart0_init(); adc_init(); timer2_init(); timer3_init(); MCUCR = 0x00; EICRA = 0x00; //extended ext ints EICRB = 0x00; //extended ext ints EIMSK = 0x00; TIMSK = 0x40; //timer interrupt sources ETIMSK = 0x04; //extended timer interrupt sources SEI(); //re-enable interrupts //all peripherals are now initialized
/* Analog Converter--------------------------------*/ void startconvertion(unsigned char ch) ADMUX = 0x20 (ch & 0x0f); ADCSRA = 0xc7; unsigned char readconvertdata(void) volatile unsigned char temp; while((adcsra & 0x10)==0); ADCSRA = ADCSRA 0x10; temp = ADCH; temp = ADCL; temp = ADCH; return temp; /*-------------------------------------------------*/ void delay(int n) volatile int i,j; for(i=1;i<n;i++) for(j=1;j<600;j++); /* Stepping Motor derive---------------------------*/ unsigned char motoroneclock(unsigned char step, char dir) unsigned char test; step = step & 0x0f; if(dir) switch(step)
case 0x03 : step=0x06; break; case 0x06 : step=0x0c; break; case 0x0c : step=0x09; break; case 0x09 : step=0x03; break; default : step=0x03; break; else switch(step) case 0x03 : step=0x09; break; case 0x06 : step=0x03; break; case 0x0c : step=0x06; break; case 0x09 : step=0x0c; break; default : step=0x03; break; return step; /* main function-----------------------------------*/ void main(void) volatile unsigned char temp, l_sensor, r_sensor; volatile unsigned char step=0; init_devices(); //insert your functional code here... printf(" \n\n\rtest program...\n\r"); PORTF = 0x03; delay(1000); PORTF = 0x00; delay(1000); PORTF = 0x03;
while(1) l_sensor = 0; r_sensor = 0; startconvertion(6); // Left sensor Converting l_sensor = readconvertdata(); startconvertion(7); // Right sensor Converting r_sensor = readconvertdata(); PORTF = 0x03; if(l_sensor > THRESHOLD && r_sensor < THRESHOLD) l_speed = SLOW_SPEED; r_speed = FAST_SPEED; PORTF = 0x01; else if(l_sensor < THRESHOLD && r_sensor > THRESHOLD) l_speed = FAST_SPEED; r_speed = SLOW_SPEED; PORTF = 0x02; else l_speed = FAST_SPEED; r_speed = FAST_SPEED; printf("ch6 :%d ch7 : %d\n\r", l_sensor, r_sensor); 6. Epilogue 하드웨어를공부하는데있어서펌웨어프로그래밍은매우중요한부분이다. 본인스스로프로그램을작성하여멋진라인트레이서를만들어보기바란다.