AVR2012.02.04 11:23

[AVR Essay 11] 2 x 16 Character LCD

 

 

2 x 16 Character LCD는 학습용 키트에서 가장 많이 사용되는 LCD로 알고 있다.

 

내 경험 상, 이거 안 달린 키트를 못 봤고 이 내용을 다루지 않는 마이크로 프로세서 서적을 못 봤다.

 

그리고 내 방에 가장 많이 굴러다니고 있는 LCD이기도 하다.

 

요놈은 다음과 같이 생겼다.

 

 

이 놈은 제조 회사나 백라이트 유무에 따라 많은 모델이 있고

 

모듈 형태도 조금씩 다르다.

 

그리고 여기서는 2 x 16 크기만 다루지만 1 x 8 부터 4 x 40 크기까지 다양한 크기가 있다.

 

(여기서 2 x 16 이라는 것은 2줄짜리이고 한 라인에 16 글자 표현 가능하다는 의미이다.)

 

그런데, 내부 구조는 거의 동일 하다.

 

즉, 한가지 모델만 제대로 이해하면 다른 모델은 데이터 시트만 쓱 보면 잘 사용 할 수 있다는 것이다.

 

 

일반적인 Character LCD는 다음과 같은 특징을 가지고 있다.

 

1. 5V의 단일 전원으로 동작한다.

 

2. CG (Character Generator) ROM, CG RAM, DD (Display Data) RAM을 내장하고 있다

 

3. 인터페이스가 표준화 되어 있고, 8bit 데이터 버스, 4bit 데이터 버스 중 선택 해서 제어가 가능하다.

 

4. 내부 제어 명령이 거의 표준화되어 있어서 제조회사나 모델에 상관 없이 동일한 방법으로 구동 가능하다.

 

5. LCD 모듈은 일반적인 I/O 인터페이스 반도체 소자에 비해 액세스 시간이 상당히 길어 동작이 느리다.

 

6. 영문 ASCII 문자와 일본어를 표시 가능하며 8개의 사용자 정의 문자를 만들어 사용 할 수 있다.

 

7. 자동 커서 증가, 커서 이동, 커서 디스플레이 유무를 조작 할 수 있다.

 

8. 문자를 5 x 7 (혹은 5 x 10) 도트 매트릭스 방식으로 표시한다.

 

9. DD RAM, CG RAM에 데이터를 읽고 쓸 수 있다.

 

10. 백라이트 모델이 있어 어두운 곳에서도 화면을 볼 수 있고 백라이트 광원도 다양하다.

 

 

대충 이정도로 이론은 정리하고, 내부 구조 등을 상세히 알고 싶으면 데이터 시트를 참조 해라.

 

(데이터시트 구하기 생각보다 어려웠다. 그래서 이놈 데이터 시트는 이 문서에 첨부 했다.)

 

 

외부 핀은 다음과 같은 기능을 한다.

신호명

기능

1

Vss

 전원 GND

2

Vdd

 전원 +5V

3

Vo

 화면 밝기 제어 동작 전압 (4.2 < Vdd-Vo < 4.6)

4

Rs

 Register Select (1 = data, 2 = instruction)

5

R/W

 Read/Write ( 1 = CPU <- LCD, 0 = CPU -> LCD )

6

E

 Enable signal for read/write

7

DB0

 (LSB)

8

DB1

 

9

DB2

 

10

DB3

 데이터 버스

11

DB4

 (4bit 인터페이스 모드에서는

12

DB5

  DB7~DB4를 사용)

13

DB6

 

14

DB7

 (MSB)

 

모델에 따라 15, 16은 백라이트로 사용된다.

 

백라이트는 모델마다 사용법이 다를 수 있으므로 해당 모델 데이터 시트를 구해서 참조 하기를 바란다.

 

LCD 자체 전원은 다음과 같이 준다.

 

이때 VDD와 VSS의 방향이 바뀌면 LCD 뿐만 아니라 회로 전체가 손상 입을 수 있으므로 주의 해야한다.

 

(실제로 나는 전원 잘 못 줬는데 LCD는 멀쩡한데 MCU가 나가 버렸다.)

 

여기서 가변 저항은 Vo에 들어가는 전압을 조작하기 위한 용도이기 때문에

 

아무것이나 사용해도 상관 없지만 경험상 5k옴 정도가 적합한 것 같다.

 

가변 저항은 LCD에 표시되는 화면을 최적으로 조정하기 위해서 사용한다.

 

 

핀 4, 5, 6은 제어 포트이고 7에서 14는 데이터 I/O 포트이다.

 

이 내용은 아래 표에 설명 되어 있다.

 

기능

제어 신호

제어 명령

실행시간

(Max.)

RS

R/W

7

6

5

4

3

2

1

0

 Clear Display

0

0

0

0

0

0

0

0

0

1

1.52 ms

 Return Home

0

0

0

0

0

0

0

0

1

-

1.52 ms

 Entry mode set

0

0

0

0

0

0

0

1

I

S

37 us

 Display ON/OFF control

0

0

0

0

0

0

1

D

C

B

37 us

 Cursor or display shift

0

0

0

0

0

1

S

R

-

-

37 us

 Function set

0

0

0

0

1

D

N

F

-

-

37 us

 Set CG RAM address

0

0

0

1

CG RAM address

37 us

 Set DD RAM address

0

0

1

DD RAM address

37 us

 Read busy flag and address

0

0

B

Address Counter

0 us

Data write to CG RAM/ DD RAM

1

0

 

41 us

Data read from CG RAM/ DD RAM

1

1

 

41 us

 

① 화면을 모두 지우고 커서를 home으로 위치 시킨다.

 

② 커서만 home으로 위치 시킨다. (데이터 보존)

 

③ LCD를 read/write 할 때 DD RAM 어드레스 증가(I=1), 감소(I=0), 화면 시프트 할 것(S=1)인지 안 할 것(S= 0)인지 지정한다.

 

④ 화면 On/Off (D), 커서 On/Off (C), 커서를 깜박이게 할 것인지(B)를 지정한다.

 

⑤ 커서를 이동시킨다. (R = 1이면 오른쪽, R=0이면 왼쪽)

 

⑥ 데이터 비트 제어(8bit : D = 1, 4bit : D = 0), 화면 행 수(2행 : N = 1), 폰트 크기(F)를 지정한다.

 

⑦ CG RAM을 선택한다.

 

⑧ DD RAM을 선택한다.

 

⑨ LCD가 명령을 수행 중인지 확인한다.

 

 

제어 명령은 몰라도 대충 어떤 기능이 있는지는 알고 있어야 한다.

 

lcd 관련 라이브러리를 직접 만들 수도 있겠지만 범위가 방대하기 때문에

 

다른 사람이 제작한 라이브러리를 읽고 이해하는 정도면 충분 하다고 생각한다.

 

 

LCD를 사용하기 위해서는 간단한 초기화 과정이 필요 하다.

 

이는 사용 준비를 하기위한 절차이며

 

4 bit로 제어 할 것인지, 8 bit로 제어 할 것인지 설정 하기 위함이다.

 

 

8 bit 제어는 다음 절차를 거친다.

 

 

4 bit 제어 초기화는 다음과 같다.

 

만약 잘 안 보이면 첨부한 데이터시트를 참조 하도록 하라.

 

 

DD RAM은 표시할 문자들의 ASCII 코드가 저장되어 있는 내부 메모리이며 모두 80개의 번지가 있다.

 

그 중에서 2 x 16 CLCD는 32개만을 사용한다. 그리고 이 주소에 해당하는 위치가 실제 출력 위치이다.

 

 

문자 데이터는 데이터 시트에 잘 나와있고 그 중 일부를 발췌 했다.

 

아래와 같은 형태로 출력이 되고 데이터 값 자체는 ASCII 코드와 일치 하므로 편리하다.

 

 

이제 그러면 직접 ATmega 8535로 LCD를 구동해 보도록 하자.

 

다음과 같이 하드웨어를 구성한다.

 

(여기서 VDD, VSS, Vo는 생략 했다. 회로도에는 생략되었지만 반드시 구현해야한다.)


 

 

이 프로그램에 대한 소스는 다음과 같다. (delay.h와 delay.c는 생략 했다.)

 

형광색으로 칠한 주석 부분은 소스의 파일 명이다.

 

 

// main.c

#include <avr/io.h>
#include "delay.h"
#include "lcd.h"

int main(void)
{
   lcd_init() ;

   //                              "0123456789ABCDEF"
   lcd_display_string(0, 0, "          Hello!         ");
   lcd_display_string(0, 1, "ATmega8535 World");
 
   return 0;
}

 

 

// lcd.h

#ifndef _LCD_H_
#define _LCD_H_

 

#include <avr/io.h>
#include "delay.h"

 

// LCD 사용 포트를 지정한다.
#define LCD_DCTRL (DDRC)
#define LCD_DDATA (DDRD)
#define LCD_CTRL (PORTC)
#define LCD_DATA (PORTD)

#define LCD_RS   (PC5)
#define LCD_RW   (PC6)
#define LCD_E   (PC7)
#define LCD_DB0  (PD0)
#define LCD_DB1  (PD1)
#define LCD_DB2  (PD2)
#define LCD_DB3  (PD3)
#define LCD_DB4  (PD4)
#define LCD_DB5  (PD5)
#define LCD_DB6  (PD6)
#define LCD_DB7  (PD7)

 

void lcd_init(void); // LCD 초기화
void lcd_control(uint8_t data) ; // 기능 제어
void lcd_write_char(uint8_t data) ; // CG/DD RAM 데이터 쓰기
void lcd_position (uint8_t x, uint8_t y) ; // 커서 위치 지정
void lcd_display_string(uint8_t x, uint8_t y, char *string) ; // 문자열 출력

 

#endif

 

 

// lcd.c

#include "lcd.h"

void lcd_init(void) 

   LCD_DDATA = 0xFF;

   LCD_DCTRL = 0xFF;
   LCD_DATA = 0;
   LCD_CTRL = 0;

   delay_ms(1);

 

   lcd_control(0x38);  // Function Set (8bit, 2line, 5 x 7 dot 
   lcd_control(0x0C);  // Display ON, Cursor OFF
   lcd_control(0x06);  // Entry Mode Set (increment, not shift)
   lcd_control(0x01);  // Clear Display   
}

 

void lcd_control(uint8_t data) 
{
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_RW);
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_RS);
   LCD_DATA = data;
   LCD_CTRL = LCD_CTRL | _BV(LCD_E);
   delay_ms(2);
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_E);
}


void lcd_write_char(uint8_t data) 
{
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_RW);

   LCD_CTRL = LCD_CTRL | _BV(LCD_RS);
   LCD_DATA = data;
   LCD_CTRL = LCD_CTRL | _BV(LCD_E);
   delay_ms(2);
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_E);
}


void lcd_position(uint8_t x, uint8_t y) 
{
   uint8_t location=0;


   if(y > 0x01) 
      y = 0x01; 
   if(x > 0x0f)

      x = 0x0f;
 

   if(y == 0)

      location = x + 0x80;

   else

      location = x + 0xC0; 
   lcd_control(location); 
}


void lcd_display_string(uint8_t x, uint8_t y, char *string) 
{
   lcd_position(x,y);

   while(*string != '\0')
   {
      lcd_write_char(*string);
      string++;
   }
}


 

대충 어떤 것을 출력 할 것인지는 감이 올 것이다.

 

lcd.h에서 #define 된 부분에 괄호속에 값을 바꾸면 손쉽게 제어 포트를 바꿀 수 있다.

 

그리고 명령 중에 PC에서 프로그래밍 할 때 못 본 내용이 있다.

 

_BV() 인데.... 이는 sft_defs.h에

 

#define _BV(bit) (1 << (bit))

 

과 같이 정의 되어 있다.

 

나머지는 표준 C 언어로 되어 있기 때문에 생략 한다.

 

명령 코드 표과 참조해서 보면 쉽게 이해가 될 것이다.

 

또, LCD의 많은 기능 중에서 문자 출력만 구현 했는데,

 

더 원하는 기능이 있다면 함수를 추가해서 사용 할 수 있다.

 

그러나 당장 사용하지 않는 기능을 함수로 추가 하는 것은 별로 좋지 않다.

 

AVR은 성능이나 용량이 PC에 비해 형편없이 딸리기 때문에 어느 정도 최적화를 고려 해야 한다.

 

 

결과는 다음과 같다.

 

 

 

PS...

 

2007년 06월 26일 4bit 제어 라이브러리도 추가하였다.

 

Posted by 콩알은
AVR2012.02.04 11:22

[AVR Essay 10] 74LS47을 이용한 7-Segment 제어

 

 

앞에서 봤듯이 7-Segment를 제어하기 위해서는 8개의 포트가 필요하다.

 

즉, ATmega 8535에는 32개의 포트가 있기 때문에 총 4개의 7-Segment를 제어 할 수 있다.

 

그런데 이를 두배로 뻥 튀겨 주는게 있으면 어떨까?

 

이런 기능을 하는 것 중 대표적인 것이 74LS47이다.

 

유사한 칩으로는 74LS46, 74LS48, 74LS49, 74LS246, 74LS247, 74LS248, 74LS249가 있다.

 

일단 이들은 7-Segment가 어떤 타입이냐, 어떤 전압이 필요하냐에 따른 구분으로 기능은 동일하다.

 

이들 칩을 BCD to 7-Segment Decoder/Driver이라고 부른다.

 

 

기능은 BCD 값을 입력하면 그에 맞는 값을 7-Segment에 출력 한다.

 

여기서 BCD는 Binary-Coded Decimal의 약자로서 이진수와는 조금 다른 개념이다.

 

간단하게 말하면 십진수의 각 자리를 2진수로 표현 한 것이다.

 

예를 들어 보면 십진수로 261은

 

2는 2진수로 0010

 

6은 2진수로 0110

 

1은 2진수로 0001

 

이므로 0010 0110 0001 로 표현 한 것이 BCD이다.

 

잘 이해가 안 되면 검색해 보도록 한다.

 

 

 

7-Segment 하나를 제어하는데는 8개의 포트가 필요 했지만

 

BCD를 사용하면 0에서 9까지 표현하는데 있어서 4개의 포트만 있으면 된다.

 

(이때, 7-Segment의 DP는 제외다.)

 

 

핀 이름

기능

A0  A3

BCD 데이터 입력

RBI

Ripple Blanking Input (Active LOW)

LT

램프 테스트 입력 (Active LOW)

BI/RBO

Blanking Input (Active LOW) /

Ripple Blanking Output (Active LOW)

 f

7- Segment 출력 (Active LOW)

 

74LS47은 위와 같이 생겼다.

 

핀 A3, A2, A1, A0에는 BCD 코드를 입력으로 준다. 만약 1010이라는 BCD 코드를 입력 할려면

 

A3 : High

 

A2 : Low

 

A1 : High

 

A0 : Low

 

위와 같이 데이터를 주면 된다.

 

그러면 핀 a ~ g에서 출력이 나온다.

 

이는 7-Segment에 있는 핀 a ~ g와 일치하게 연결 하면 된다.

 

단 이때, 보통 출력이 5V이기 때문에 7-Segment와 74LS47사이에 330옴 저항을 달아 줘야 한다.

 

 

그리고 나머지 포트는 사실 잘 사용하지 않는다.

 

74LS47을 사용하는 대부분의 목적이 포트를 줄이는 것인데

 

나머지 포트를 사용하면 이 효과가 없기 때문이다.

 

그래도 알아둬서 나쁠 것은 없기 때문에 간단히 설명하겠다.

 

RBI 포트가 Low인 경우 0에 해당하는 BCD 코드가 입력되었을 때, 7-Segment의 모든 램프가 꺼진다.

 

만약 4개의 7-Segment를 제어 시 BCD 코드로 10을 출력 한다면 RBI를 사용하지 않는다면 출력이 “0010”이 되겠지만

 

1보다 위의 코드 경우 RBI를 Low로 set 한 후 출력하면 “  10”과 같이 불필요한 0을 제거 해 준다.

 

이때 RBO는 LOW가 된다. BI는 입력된 BCD 코드와 관계 없이 7-Segment의 모든 램프를 끈다.

 

데이터 조작 없이 램프를 점멸 하고자 할 때 사용한다.

 

 

 

입력한 값에 따라 아래와 같은 형태로 출력된다.

 

(위에 인덱스 값이 왜 BCD가 아닌 그냥 십진수인지는 모르겠다.)

 

그런데 10부터 15깨지는 A ~ F가 나와야 할 것 같지만 그렇지 않다.

 

나는 처음에 저 문자들이 뭔가 의미가 있은 것인 줄 알았는데...

 

사실 아무 의미가 없는 값 들이다.

 

74LS47은 0부터 9까지 출력하도록 설계 되었기 때문에 나머지는 신경을 쓰지 않았다.

 

데이터 시트를 보년 74LS47의 내부 구조가 그려져 있는데 이 회로에 따른 부산물일 뿐이다.

 

유식하게 don't care라고 부르기도 한다.

 

 

이제 직접 실험을 해 보도록 하자.

 

아래와 같이 회로를 꾸민다.

 

 

 

 

나는 여기서 74LS47과 7-Segment를 2개로 묶어서 모듈을 만들었다.

 

앞으로 디지털 시계 등을 제작할 것인데 이렇게 해 두면 상당히 편리하다. 

 

 

 


프로그램 소스는 다음과 같다.

 

 

#include <avr/io.h>
#include "delay.h"

 

int main(void)
{
   DDRA = 0xFF;
   int i = 0;

 

   while(1)
   {
      PORTA = ((i / 10) << 4) | (i % 10);
      delay_ms(200);
      i++;

      if( i == 100 ) i = 0;
   }
 
   return 0;
}

 

 

여기서 PORTA = ((i / 10) << 4) | (i % 10); 이 부분을 잘 이해 해야 한다.

 

숫자를 다룰때 자주 사용하는 기법이다.

 

잘 이해가 안 되면 연습장을 꺼내서 i값에 따라 어떤 데이터가 PORTA에 입력되는지

 

한번 손으로 계산해 보는 것도 좋은 방법이다.

 

 

실행 결과는 다음과 같다.

Posted by 콩알은
AVR2012.02.04 11:07

[AVR Essay 19] UART 통신 6 - UART 라이브러리

 

이번에는 내가 여기 저기서 주워온 소스를 적당히 수정해서 만든

 

ATmega8535용 UART 통신 라이브러리다.

 

말이 라이브러리지 내가 만들어서 그리 다양한 기능은 없다.

 

일단 이 문서에 포함되어 있는 파일의 압축을 풀면 다음과 같은 파일을 볼 수 있다.

 

 

uart.c, uart.h

 

buffer.c buffer.h

 

main.c

 

makefile

 

 

여기서 uart.c, uart.h는 실제 UART 장치 설정하는 라이브러리다. 이는 buffer에 수신된 데이터를 쌓아두기 때문에

 

별도의 버퍼가 필요한데 이 녀석이 buffer.c와 buffer.h이다.

 

즉, 인터럽트 방식과 폴링 방식의 장점을 적절히 모아 놓았다고 보면 된다.

 

소스 코드 자체가 그리 어렵지는 않고, 내가 할 수 있는한 자세하게 주석을 달아 두었으니

 

분석하고 싶다면 직접 소스코드를 열어서 분석을 해 보도록...

 

 

일단 여기서는 이 놈을 어떻게 사용하는지에 대해서만 언급하겠다.

 

간단하게 앞에서 사용했던 하드웨어를 그대로 사용한다.

 

앞에서 해서 알겠지만 먼저 makefile에 사용하고자 하는 라이브러리를 등록한다.

 

# List C source files here. (C dependencies are automatically generated.)
SRC = delay.c uart.c buffer.c

 

위 내용은 makefile의 80번째 줄 쯤에 있다.

 

 

그 다음으로는 실제 실습 소스를 토대로 이야기 해 보자.

 

실습 소스는 첨부한 파일에서 main.c 이고 프로그램에 쬐끔 크기 때문에 전체 소스는 여기에 뿌리지 않겠다.

 

참고로 이 프로그램은 세 문자를 PC에서 받아서

 

이에 해당하는 대문자는 소문자로, 소문자는 대문자로 변환해서 PC로 전송하는 프로그램이다.

 

결과를 보면 다음과 같다.

 

 

main.c를 열어 보면 일단 다음과 같은 #include를 볼 수 있다.

 

#include <stdio.h>       

#include <avr/io.h>
#include 
<avr/interrupt.h>

#include "delay.h"

#include "uart.h"

#include "buffer.h"

 

다른 녀석들은 쉽게 이해되지만 stdio.h는 새롭게 추가되었다.

 

요놈은 사실 PC상에서 C언어로 작업할 때 지겹도록 #include 해 주던 녀석이다.

 

본 라이브러리에서 stdio.h와 관련된 몇가지 기능이 필요해서 이 녀석을 #include 해 줘야 한다.

 

 

//UART 기초 설정
static FILE define_uart = FDEV_SETUP_STREAM(uart_transmit, NULL, _FDEV_SETUP_WRITE);

#define UART (FILE*)(&define_uart)

static BUFFER_STR_HANDLE uart_received_buffer;

static int8_t uart_rx_buffer[UART_SIZE_BUFFER];

 

그 다음으로 볼 수 있는게 위 문장들이다.

 

뭔가 많이 어려운 말들만 있다. 이해가 안되면 그냥 써라. 이해하고 싶다면 나름 주석은 달아 놓았으니 참고 하도록...

 

ATmega8535로 작업한다면 그냥 저 문장 그대로 쓰면 된다. 믿어라~~~

 

 

// 데이터 수신시 이 함수를 통해서 데이터를 버퍼에 넣는다.
int shell_uart( char c )
{
    buffer_putchar(&(uart_received_buffer), c);
    return c;
}

이 함수도 그냥 만들어 줘라. 인터럽트 발생시 데이터 수신 후 버퍼에 넣어주는 기능을 한다.

 

전체 소스에 이 놈 프로토타입도 있으니 나중에 필요하면 써 줘야 한다.

 

 

// UART 초기화
uart_init(9600, shell_uart);    
// UART 장치 초기화
uart_received_buffer.depth = UART_SIZE_BUFFER;    
// 버퍼 크기 지정
uart_received_buffer.data = uart_rx_buffer; 
// 실제 버퍼 저장 공간과 연결
buffer_init(&(uart_received_buffer));            
// 버퍼 초기화

sei();    
// 전체 인터럽트 허용

main() 함수에 위 구문만 넣어주면 UART 장치 쓸 준비가 다 되었다.

 

9600  대신 다른 숫자를 넣으면 해당 숫자로 통신 속도가 바뀐다.

 

마지막 sei(); 는 전체 인터럽트를 허용하기 때문에 안 해주면 데이터 수신이 전혀 이루어 지지 않으므로 주의하자.

 

 

이제 본격적으로 데이터를 전송해 보자.

 

fprintf(UART, "\r\nHello UART World!\r\n");

fprintf(UART, "\r\n-> %s\r\n", temp_buffer);    // 데이터를 전송한다.

위 두 문장을 보면 딱 느낌이 올 것이다. 바로 printf() 함수 이다. 이 녀석과 동일하게 써 주면 된다.

 

아마 PC 상에서 파일 입/출력을 해 봤다면 쉽게 이해가 될 것이다.

 

 

그렇다면 데이터 수신은 어떨까? 대략 다음과 같다.

 

// 버퍼에서 수신된 데이터를 확인한다.
// 인터럽트에 의해 수신된 데이터는 버퍼에 쌓인다.
while(buffer_count(&(uart_received_buffer)) != 0)
{
     buffer_getchar(&(uart_received_buffer), &temp_char);

     ...

     ...

}

 

우선 while()문에서 버퍼에 데이터가 있는지 확인한 후 데이터가 있는 동안 내부 구문을 일행한다.

buffer_gerchar() 명령으로 버퍼에 가장 마지막에 수신되어 남아있는 데이터를 temp_char라는 변수에 넣는다.

이는 자료구조에서 Queue와 같다고 생각하면 될 것이다.

그 후 수신한 데이터를 샤바샤바 처리해 주면 된다.

주로 패킷 단위로 데이터가 오고갈때 이를 이용하면 편리하게 처리 할 수 있다.

 

 

이 정도 했으면 아마 소스코드 보고 주석 적당히 참조하면 본 라이브러리를 쓰는데는 지장 없을 것이다.

 

참고로 이 라이브러리를 쓰면 4KB 정도 프로그램이 커진다.

 

ATmega8535가 8KB이기 때문에 상당히 많이 차지 하는 편이다.

 

가장 큰 원인은 stdio.h를 #include 한 것인데... 나도 모르겠다...ㅋㅋㅋ

 

(원래 이 라이브러리는 ATmega128에서 사용하던 것을 ATmega8535용으로 수정한 것이다.)

 

 

이로써 UART에 관해서는 마치도록 하겠다.

Posted by 콩알은