The concept is quite simple. The file types.h includes the ANSI-required file limits.h. It then explicitly tests each of the predefined data types for the smallest type that matches signed and unsigned 1-, 8-, 16-, and 32-bit variables. The result is that my data type UCHAR is guaranteed to be an 8-bit unsigned variable, INT is guaranteed to be a 16-bit signed variable, and so forth. In this manner, the following data types are defined: BOOLEAN, CHAR, UCHAR, INT, UINT, LONG, and ULONG.
소스코드는 AVR BIBLE (배성중/북두출판사) 를 참고하였으며
winavr 요즘 버전에 맞추고(ISR, outp 매크로, include 경로), 클럭이 맞지않아 변수들을 수정하여 타이밍을 조절하였다.
지루한 계산 16Mhz = 16,000,000 hz 이고
서보 모터는 20ms = 0.02sec 단위로 신호를 넣어준다.
그리고 PWM 신호는 0.5ms ~ 2ms 사이의 길이를 넣어주면 0 ~ 180도의 각도로 이동한다.
(책에는 1.5ms 에서 길거나 짧거나 라고 하는데 서보마다 다른듯.. 데이터 시트에도 없다 ㄱ-)
일단 8bit timer를 사용함으로 256 clock 마다 overflow를 발생시키며
16,000,000(clock/sec) / 256 = 62,500 times 1초에 62,500 번의 overflow가 발생하게 된다.
1/62500 = 0.000016 sec 이며 62500/50 = 1250 번이다.(20ms 는 1초에 50회)
즉, 256번씩의 overflow를 1250번 반복하게 되면 0.02sec = 20msec 간격을 잡을수 있다.
그리고 1msec는 62.5 인터럽트가 모이면 되고,
실험적으로 서보에서 사용하는 PWM의 width를 얻어내면 된다.
아무튼 위의 값은 정확한건 아니지만.. (ㄱ-) 대략적으로 맞아들어가며
0도와 180도의 하한/상한을 찾은뒤 평균내면 90도가 잡아진다.(레드썬!)
(위의 값으로는 180도 쪽이 약간 5도 정도 부족해 보이나,
끽끽대며 더이상 가지 못하는 문제가 있어 실질적으로 90도를 약간 좌측으로 수정해야 하지 않을까 싶다.)
대충의 계산방식이 들어있는 스프레드시트 파일.
클럭과 timer overflow 에 필요한 clock을 입력하면 된다.
일단 사용법을 몰라서. 구글 검색하다 나온 rcan 님의 블로그 내용을 일단 복사해서 붙여넣었다.
[링크 : http://rcan.net/560]
기본적인 내용은 printf() 사용하는 것들이고, F_CPU는 cpu 클럭에 관한 선언문으로
AVRStudio wizard 사용시 클럭을 넣어주면 생성되는 변수이다.
타이머 관련 내용은 다음과 같다.
ISR(TIMER0_OVF_vect) // 8bit Timer0 에 대한 인터럽트 루틴
TCCR0; // 타이머 프리스케일러
TCNT0; // 타이머/타운터용 초기값
TIMSK; // 타이머 오버플로우시 인터럽트 발생
일단 TCCR0를 보자면
타이머/카운터 제어용 레지스터로서,
Bit 7 – FOC0: Force Output Compare
Bit 6, 3 – WGM01:0: Waveform Generation Mode
Bit 5:4 – COM01:0: Compare Match Output Mode Bit 2:0 – CS02:0: Clock Select
에 대한 설정을 하게 된다.
TCCR0 = 0x04 에서 0은 WGM01:0=0 으로 아래의 테이블을 보면(엄밀하게는 0x48 값의 위치이다) Timer/Counter Mode of Operation 가 Normal로 되어있다.
Normal Mode
The simplest mode of operation is the normal mode (WGM01:0 = 0). In this mode the counting direction is always up (incrementing), and no counter clear is performed. The counter simply
overruns when it passes its maximum 8-bit value (TOP = 0xFF) and then restarts from the bot- tom (0x00). In normal operation the Timer/Counter overflow flag (TOV0) will be set in the same
timer clock cycle as the TCNT0 becomes zero. The TOV0 flag in this case behaves like a ninth
bit, except that it is only set, not cleared. However, combined with the timer overflow interrupt
that automatically clears the TOV0 flag, the timer resolution can be increased by software. There
are no special cases to consider in the normal mode, a new counter value can be written anytime.
The output compare unit can be used to generate interrupts at some given time. Using the output
compare to generate waveforms in normal mode is not recommended, since this will occupy
too much of the CPU time.
이 모드에서는 0에서 부터 255까지(8bit 타이머) 증가하며,
별도의 카운터 값 리셋은 하지 않으나 오버플로우 된상태로 계속 더하므로,
실질적으로 255다음에 0부터 계속 증가하게 된다. (TCNT0는 수정하는 즉시 그 값부터 증가하게 됨)
TCCR0 = 0x04 에서 4는 CS02=1로 아래의 테이블을 보면 clkT0S/64 (From prescaler) 라고 되어있다. 즉, 입력 클럭을 64로 나누어서 느긋하게 증가시킨다.
그리고 TCNT0는
카운트를 위한 변수이고, 8bit timer/counter 이므로 0x00 에서 0xFF 즉, 0 에서 255 값을 가지며
255가 되면 overflow interrupt를 발생시킨후 0부터 다시 숫자를 증가시킨다. (normal mode)
그런데 이 변수에 복잡한 수식으로 값을 넣는 이유는 정확한 시간을 발생하기 위해서이다.
클럭마다 다르겠지만, 일단 클럭을 위에서 1/64로 주므로 64 clock 마다 1씩 증가된다. 16Mhz 에서 64clock 마다 인터럽트를 생성하면(F_CPU / Prescaler) 1초에 250,000 번 발생하게 되고 이 오버플로우 갯수를 세어 1000번을 묶으면 (tic_time == 1000 그리고 F_CPU / TICKS_PER_SEC / Prescaler) 1초에 250번의 오버플로우가 발생하게 된다.
그런데 오버플로우 값은 255 까지(총 256) 이므로, 0부터 증가해서 255까지 timer를 증가시키면
1초가 맞지 않게 되므로, TCNT0의 값을 OVERFLOW - 250 으로 하여 초기값을 맞춰주게 된다.
결과적으로 TCNT0의 값은 6이 된다.
(음.. OVERFLOW가 255여야 하지 않을려나..)
그리고 TIMSK는 이름대로 타이머 인터럽트 마스크 레지스터로,
Bit 1 – OCIE0: Timer/Counter0 Output Compare Match Interrupt Enable
Bit 0 – TOIE0: Timer/Counter0 Overflow Interrupt Enable
에코 서버라고 하니 먼가 거창한데,
간단하게 입력하면 그걸 그대로 돌려줘서 화면에 나타나게 하는 프로그램이다.
시리얼로 전송하면, 받는쪽에서 그 값을 돌려주지 않으면 터미널에서 그 값이 출력되지 않는다.
가장 간단하고, 확실한 테스트 방법이라서 일단 echo 하도록 하는데 먼가 험난했다 ㄱ-
아무튼 개인적으로 선호하는 115200bps - N - 8 - 1 로 설정하고, UART0를 통해 UART echo server를 만들어보자
오늘 필요한 녀석은
#include <avr/interrupt.h>
ISR(USART0_RX_vect)
{
UDR0 = UDR0;
}
요렇게 두 부분이다.
위의 헤더는 ISR() 이라는 매크로를 사용하게 하는 것이고,
ISR은 Interrupt Serive Routine의 약자이다.
예전에는 SIGNAL(SIG_UART0_RECV) 로 사용했을 것이지만,
winAVR 버전이 올라가면서 ISR()로 바뀐 것으로 알고 있다. (물론 사용중인 버전이 20080610 버전으로 좀 오래됐다 ㅠ.ㅠ)
아무튼 USART0_RX_vect 라는 것은 iom128.h의 432 라인에 기술되어있다.
(iom128.h는 <avr/io.h>와 makefile의 cpu 선언에 의해서 자동으로 불려지는 파일이다)
시리얼포트 초기화시에는 당연히 UART RX 인터럽트를 사용하도록 설정을 해야하고,
전역 인터럽트를 사용하도록 해주어야 한다.
UART 인터럽트는 UCSR0A/B/C 레지스터로 조작을 해준다.
/* for UART */
/* "BaudRate" related setting */
UBRR0H = 0;
UBRR0L = 8; // 115k with U2X = 0
UCSR0A = 0x00; // U2X = 0;
/* "Interrupt" related setting */
UCSR0B = 0x98;
/* "Sync or Async mode - Parity - stop bits - data bits" related setting*/
UCSR0C = 0x06; //Asyncronous - no parity - 1bits(stop) - 8bits(data)
UCSR0A 레지스터는 U2X0 를 제외하면 전부 Status Flag 이므로 U2X0를 사용하지 않는다면 0x00으로 설정한다.
UBBR0H 와 UBBR0L 은 BaudRate와 관련된 것으로, 클럭과 원하는 BaudRate에 따라 변하지만,
귀찮으면, 데이터 시트에 나와있는 값으로 입력을 하면된다. (위의 값은 계산하지 않고 그냥 데이터시트 값을 사용한 것이다)
그리고 U2X는 UCSR0A의 비트로, 에러율을 낮추기 위해 2배 속도로 샘플링하는 것이다.
(디바이더(Divider)의 값을 반으로 줄여, 샘플링을 자주해서 값을 놓치지 않도록 한다)
• Bit 1 – U2Xn: Double the USART Transmission Speed
This bit only has effect for the asynchronous operation. Write this bit to zero when using synchronous operation.
Writing this bit to one will reduce the divisor of the baud rate divider from 16 to 8 effectively doubling the transfer rate for asynchronous communication.
UCSR0B 레지스터의 98 값은 인터럽트 관련 설정값이다.
일단 Bit 5를 set 하게 되면, 원하는대로 echo 되지 않으니
UCSR0B = 0xD8 이나
UCSR0B = 0x98 로 설정해주면 된다.
(Bit 7 에서 Bit 4까지 1001(2) 이므로 0x90 이고 (RX enable / Receiver Enable)
Bit 3 으로 인해 0x08, 합쳐서 0x98이 된다.)
UCSR0C 레지스터는 Asynchronous / Synchronous mode 를 고르고,
어떤 종류의 패리티를 쓸지, 스탑 비트는 몇 비트를 할지, 데이터는 몇 비트로 사용할지 결정한다.
친숙하게 보는 115200-N-8-1 이러한 설정값에 대한것 중 BaudRate를 제외한 거의 모든것을 결정한다.
그리고 sei() 라는 매크로를 통해, 전역 인터럽트를 사용가능하도록 설정해주어야 한다.
#define sei ()
#include <avr/interrupt.h>
Enables interrupts by setting the global interrupt mask.
This function actually compiles into a single line of assembly, so there is no function call overhead.
Enables interrupts by setting the global interrupt mask. This function actually compiles into a single line of assembly, so there is no function call overhead. */ #define sei() #else /* !DOXYGEN */ # define sei() __asm__ __volatile__ ("sei" ::) #endif /* !DOXYGEN */
Disables all interrupts by clearing the global interrupt mask. This function actually compiles into a single line of assembly, so there is no function call overhead. */ #define cli() #else /* !DOXYGEN */ # define cli() __asm__ __volatile__ ("cli" ::) #endif /* !DOXYGEN */
USART Baud Rate Register (UBRR) 은 일종의 divider의 설정값으로, 클럭을 분기하는데 사용하는 값이라고 생각이 된다.
아무튼 16.0MHz 같은녀석은, Error 율이 0% 인녀석이 일반적으로 사용하는 115k 이하에는 없으므로 그리 좋지 않다.
그리고 U2X는
Bit 1 – U2Xn: Double the USART Transmission Speed
This bit only has effect for the asynchronous operation. Write this bit to zero when using synchronous operation.
Writing this bit to one will reduce the divisor of the baud rate
divider from 16 to 8 effectively doubling the transfer rate for
asynchronous communication
라고 나와있 듯, asynchronous 모드에서만 작동이 되며, 클럭을 두배로 증가시켜 에러율을 줄이는 효과를 보인다.
아무튼, USART / UART를 설정하는데 사용되는 레지스터는
UCSR0A, UCSR0B, UCSR0C 세가지가 있으며
UCSR0A에는 U2X를 제외하면 실질적으로 전부 Status 레지스터이다.
• Bit 7 – RXCn: USART Receive Complete
• Bit 6 – TXCn: USART Transmit Complete
• Bit 5 – UDREn: USART Data Register Empty
• Bit 4 – FEn: Frame Error
• Bit 3 – DORn: Data OverRun
• Bit 2 – UPEn: Parity Error
• Bit 1 – U2Xn: Double the USART Transmission Speed
• Bit 0 – MPCMn: Multi-Processor Communication Mode
UCSR0B에는 인터럽트와 , TX / RX enable 를 설정하며
• Bit 7 – RXCIEn: RX Complete Interrupt Enable
• Bit 6 – TXCIE: TX Complete Interrupt Enable
• Bit 5 – UDRIEn: USART Data Register Empty Interrupt Enable
• Bit 4 – RXENn: Receiver Enable
• Bit 3 – TXENn: Transmitter Enable
• Bit 2 – UCSZn2: Character Size
• Bit 1 – RXB8n: Receive Data Bit 8
• Bit 0 – TXB8n: Transmit Data Bit 8
UCSR0C에는 눈에 익숙한 N-8-1 (No Parity - 8bit data - 1bit stop) 이러한 설정값을 조절한다.
• Bit 7 – Reserved Bit
• Bit 6 – UMSELn: USART Mode Select
• Bit 5:4 – UPMn1:0: Parity Mode
• Bit 3 – USBSn: Stop Bit Select
• Bit 2:1 – UCSZn1:0: Character Size
• Bit 0 – UCPOLn: Clock Polarity