이번 글에서는 직렬통신과 병렬통신, 동기와 비동기, 통신 방식, ATmega의 UART 통신, 그리고 UART 레지스터의 개념 공부를 한 것에 대해서 정리해보려 한다.
개념 공부한 것과 더불어 ATmega128의 UART 통신 기능을 이용하여 ADC부분을 공부할 때 LCD에 표현하였던 것을 블루투스를 이용하여 휴대폰에 전송시켰었는데, 이것 역시 인터럽트를 사용하여 구현하였다.
직렬통신 : 하나의 와이어를 통해 하나씩 전송하는 방식
병렬통신 : 각 비트 별 와이어를 통해 동시에 전송하는 방식
직렬통신 | 병렬통신 | |
---|---|---|
전송속도 | 느림 | 상대적으로 빠름 |
통신선로의 개수 | 한 개 | 여러 개 |
클럭 당 전송되는 비트 수 | 한 비트 | 통신선로의 개수 만큼 |
전송 거리 | 장거리 | 단거리 |
병렬통신의 문제점으로는 타이밍 틀어짐 또는 클럭 틀어짐 현상이 있다.
이 현상은 병렬 통신 방식을 사용하는 송신기기가 여러 개의 비트 정보를 동시에 전송하였을 때, 전송 된 신호들이 수신 기기에 도달하는 시점이 제각각 되는 현상을 말하는데, 이로 인해 완전히 다른 데이터가 전송될 수 있다.
직렬 데이터 전송 방식으로도 고속의 데이터 전송이 가능해져 점점 더 많은 영역에서 병렬통신 방식보다 직렬통신 방식이 사용되고 있다.
- 데이터 흐름이 한 방향으로만 한정되어 있는 통신 방식(송신측, 수신측 고정)
- 일방적인 송신 또는 수신만 가능
- 예 : 텔레비전 방송, 라디오
- 통신 채널에 접속 된 두 대의 단말기 중 어느 한 쪽이 데이터를 송신하면 상대편은 수신만 가능한 통신방식
- 송신측과 수신측이 정해져있지 않으며, 양 단말기의 상호 협력에 의해 송수신 방향이 바뀐다.
- 예 : 무전기
- 접속 된 두 대의 단말기들 간에 동시에 데이터를 송수신하는 통신
- 두 개의 통신 채널을 이용하여 양방향으로 동시에 송수신
- 전송 효율이 높고, 데이터의 양이 많을 때 사용한다.
- 예 : 휴대전화
- 요청과 결과가 동시에 일어난다.
- 요청을 하면 시간이 얼마나 걸리던지 요청한 자리에서 결과가 주어져야 한다.
- 장점 : 순서에 맞춰 진행되며, 설계가 매우 간단하고 직관적이다.
- 단점 : 여러가지 요청들을 동시에 처리할 수 없다.
- 예 : 전화교환망, ATM, 데이터 통신망
- 요청과 결과가 동시에 일어나지 않는다.
- 하나의 요청에 따른 응답을 즉시 처리하지 않아도, 대기시간동안 또 다른 요청에 대해 처리가 가능하다.
- 장점 : 여러개의 요청을 동시에 처리할 수 있으므로 자원을 효율적으로 사용할 수 있다.
- 단점 : 동기 방식보다 속도가 떨어질 수 있다.
- 예 : RS-232
AVR
에서 나오는 신호는 TTL
신호 레벨을 가진다.
이 떄, TTL
이란 Transistor-Transistor Logic의 약자로, 노이즈에 약하고 통신거리에 제약이 있는데, 이 TTL 신호를 입력받아서 멀리 갈 수 있도록 해주는 인터페이스 IC가 바로 RS-232
이다.
RS-232는 PC에서 지원하며, TTL보다 높은 전압으로 먼거리 통신이 가능하다. 보통 +12V는 0, -12V는 1을 사용한다.
내가 이해한대로 정리하자면, PC의 경우 시리얼 통신을 위해 RS-232 규격을 사용하고, -12V~+12V의 전압을 갖는 반면에 AVR의 경우 TTL(UART) 규격을 사용하고, 0V~5V의 전압을 갖는다.
전압과 신호가 다르기 때문에 MAX232라는 레벨 시프터를 사용하여 변환해주어야 한다.
MAX232에 대해서 조금 더 공부해보았다.
MAX232의 핀 맵은 위와 같다.
11번(T1IN), 10번(T2IN) : TTL 신호 입력 -> 14번(T1OUT), 7번(T2OUT) : RS232 신호로 출력
13번(R1IN), 8번(R2IN) : RS232 신호 입력 -> 12번(R1OUT), 9번(R2OUT) : TTL 신호로 출력
또한, High로 인식하기 위한 최소 전압은 2V이며, Low로 인식하기 위한 최대 전압은 0.8V임을 알 수 있다.
바이패스 커패시터 1uF이 필요한데, 보통 AC 성분을 제거하고, DC 성분을 통과시키기 위한 역할로 많이 사용되며, 전원단에 위치하여 Voltage Spike에서 발생할 수 있는 전하들을 흡수하여 방전하는 역할을 하고 노이즈를 최소화 시킨다.
UART 통신을 위해 사용하는 핀은 위의 사진에 표시된 핀들이다.
UART란 Universal Synchronous Asynchronous Receiver Transmitter의 약자로 한국어로는 범용 동기 비동기 통신으로 불린다.
다른 통신에 비해 속도가 느리고, 멀리 데이터를 보낼 수는 없지만, 근거리에서 소량의 데이터를 보낼 때 유용하다.
유선 방식을 사용하고, 전이중 방식을 사용해 직렬 전송을 하며, 비동기 방식을 이용하여 송수신간 신호를 맞춘다.
통신을 위해 3개의 신호선을 사용하는데, RXD는 데이터 수신, TXD는 데이터 송신, GND를 사용한다.
또한, ATmega UART 통신의 방식으로는 Polling 방식과 Interrupt 방식이 존재한다.
main문 내에서 직접 데이터 수신을 위한 함수를 실행시켜 데이터를 저장하는 방식
인터럽트 서비스 루틴을 통해서 데이터를 저장하는 방식
나 같은 경우, Interrupt 방식을 이용하여 코드를 짰다.
ATmega128에 UART 관련 레지스터가 몇 개 있는데, 각 레지스터의 비트들의 역할 같은 경우 datasheet에 아주 자세히 나와있기 때문에 세미나 하면서 정리해놨던 것들을 사진으로 간단히 남기고 넘어가도록 하겠다.
- 데이터 버퍼 기능을 수행하는 8bit 레지스터
- 송신 할 데이터를 쓰면 송신 데이터 버퍼 TXBn에 저장
- 수신 될 데이터를 쓰면 수신 데이터 버퍼 RXBn에 읽힘
- UART 관련 제어, 상태 레지스터
- Baud Rate를 설정하는 레지스터
블루투스를 이용하기 위해서 HC-06
을 사용했다.
- 전원 공급 장치 : 3.3V ~ 5V
- 블루투스 사양 : V4.0 BLE(Bluetooth Low Energy; 저전력 블루투스)
- 최대 50mA 필요
- 활성 상태일 때 약 9mA 사용
HC-06
의 장점으로는 근거리 무선통신을 할 때 유용하고, 쉽게 인터페이스하고 통신이 가능하다는 점과, 적은 전력을 사용한다는 점, 그리고 UART 인터페이스를 사용하기 때문에 거의 모든 컨트롤러 떠는 프로세서와 인터페이스가 가능하다는 점이 있다.
#define F_CPU 16000000UL //16MHz
#include <stdio.h> //sprintf 사용 위한 헤더파일
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "lcd.h"
#include "adc.h"
void UART_init_reg(void);
void UART_send_fn(char str);
volatile unsigned int adc_result; //ADC 결과 값(디지털 값)
volatile float v_in; //아날로그 값
char *digital_str = NULL; //디지털 값 저장 할 문자형 포인터 선언
char *analog_str = NULL; //아날로그 값 저장 할 문자형 포인터 선언
void UART_init_reg(void)
{
UBRR0H = 0;
UBRR0L = 103; //baud rate 9600으로 설정
UCSR0B = (1 << TXEN0); //UART용 송신 단자 허용
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); //전송 문자 데이터 비트 수 8비트
}
void UART_send_fn(char str)
{
while(!(UCSR0A & (1 << UDRE0))); //송신 가능 대기
UDR0 = str; //문자열 전송
}
baud rate의 경우 위의 사진에 보이는 공식을 이용해주었다.
ISR(ADC_vect) //ADC 인터럽트 서비스 루틴
{
adc_result = ADC; //ADC 결과 값 저장
v_in = (float)adc_result * 5.0 / 1024.0; //아날로그 값 저장
}
ISR(USART0_UDRE_vect)
{
while(*analog_str != '\0') //null값이 아닌 동안
{
UART_send_fn(*analog_str++);
}
UART_send_fn('\t');
while(*digital_str != '\0') //null값이 아닌 동안
{
UART_send_fn(*digital_str++);
}
UART_send_fn('\n');
_delay_ms(100);
UCSR0B &= ~(1 << UDRIE0); //송신 준비완료 인터럽트 인에이블 비트 clear
}
int main(void)
{
char adcResult[16]; //ADC결과 값(디지털 값) 저장 위한 문자형 변수
char voltage[16]; //ADC이전 값(아날로그 값) 저장 위한 문자형 변수
int level; //PORTA 비트 이동을 위한 변수(디지털 변환했을 때 단계)
init_port(); //port 초기 설정 함수
init_reg(); //레지스터 초기 설정 함수
lcd_init(); //lcd화면 초기화
cursor_off(); //커서 안 보이게
UART_init_reg();
sei(); //SREG 7번비트 1로 세트
while (1)
{
ADCSRA |= (1 << ADSC); //ADC 변환 시작
while(ADCSRA & (1 << ADIF) == 0); //ADIF 비트가 1로 세트되었는지 안되었는지 확인 가능
sprintf(voltage, "%.2fV", v_in); //아날로그 전압 값 voltage 변수에 할당
sprintf(adcResult, "%d", adc_result); //변환 된 디지털 값 adcResult 변수에 할당
analog_str = &voltage;
digital_str = &adcResult;
//lcd 출력
lcd_gotoxy(0,1); lcd_puts(voltage);
lcd_gotoxy(0,0); lcd_puts(adcResult);
_delay_ms(500);
lcd_clear();
UCSR0B |= (1 << UDRIE0); //송신 데이터 레지스터 준비완료 인터럽트 허용
level = adc_result/128; //8단계로 나누어서 출력하므로 디지털 값/128
PORTA = 0xff << level; //나온 값만큼 비트 이동을 시켜주어 LED 출력
}
}
블루투스를 이용해서 휴대폰으로 확인하기 전에 PC와 연결하여 디지털 값과 아날로그 값을 출력시켜보았다.
약간의 delay가 있어 값들이 완전 정확하지는 않았지만 블루투스를 이용하여 휴대폰으로 값을 확인하였을 때도 대체적으로 잘 나옴을 확인할 수 있다.