반응형

[8051 강좌] 시리얼통신 이해해 보기...

 

시리얼통신

/*
타이머0번 사용 + 시리얼통신(타이머1번 사용) 하는 소스
: 이야기화면에 글을 쓰면서 LED는 제 시간동안(1sec) 깜박이는 프로그램소스

 

프로그램 실행시 TY_ISP 보드를 통해 프로그램을 전송하고 TY_RS232 보드를 통해 시리얼통신을 하게 됩니다. TY52 보드와 ISP, RS232 는 항상 연결되어 있어도 됩니다. ^^
이야기 등의 통신프로그램에서 9600bps, 패리티비트 None, 데이타비트 8, 정지비트 1, 흐름제어 None 으로 맞춰주세요 ^^
아래에 시리얼통신 질문에 대한 답을 적다가 아예 소스를 짜서 올립니다. 시리얼통신은 자체적으로 타이머를 하나 사용합니다. 그때 사용하는 타이머와 별도로 구동되는 타이머가 같은 모드를 사용하면서 약간의 충돌이 일어나는 것 같더군요. 그리고 설정된 것들도 조금씩 다 이상해서.. ^^; 그냥 제가 하나 만들었습니다.

이 소스를 보시고 더 추가하셔서 공부하시면서 사용하시면 될것 같습니다.
SDCC 에서 컴파일하고 실행까지 검사완료되었습니다. ^^


*/

 

#include <AT89X52.H>

#define HIGH10MS    0xB1
#define LOW10MS     0xDF
#define TH19600BPS  0xF3

typedef unsigned char u08;
typedef          char s08;
typedef unsigned int  u16;
typedef          int  s16;

volatile unsigned int ui_cnt;
volatile unsigned int ui_cnt2;
unsigned char uc_cnt;

void Put_Byte(u08 bydata);
void Init_Serial(void);
void delay(int number);
void t0init(void);


void t0init(void)
{
    // Timer0 초기설정
    // Mode1 사용 (16비트 타이머)
    //  EA=0;
    TMOD &= 0xF0;           // TMOD 값을 조절해서 Timer0 을 16비트로 사용하도록
    TMOD |= 0x01;           // 합니다. TMOD 의 앞4개 비트는 타이머1에 관한 것
                            // 이므로 이를 변경하지 않고, 하위비트 4개만
                            // 조정합니다. 그래서 ???? 0001 로 만들어 줍니다.

    TH0 = HIGH10MS;         // 만들어진 타이머0를 작동시키기 전 우선
    TL0 = LOW10MS;          // 넣어줍니다. 이 값은 타이머가 정확히 0.01초마다
                            // 작동하도록 해 줍니다.

    ET0=1;                  // Timer0 인터럽트를 가능하게 합니다.
    EA=1;                   // 인터럽트가 가능하게 합니다.
    TR0=1;                  // Timer0 를 시작한다
    //  IP = 0x00;
    //  PT0 = 1;
}

void it_timer0(void) interrupt 1
{
    ui_cnt++;
    TH0 = HIGH10MS;         // 만들어진 타이머0를 0.01초로 작동시키기 위해
    TL0 = LOW10MS;          // 필요한 초기값
}

void Put_Byte(u08 bydata)
{
    while(!TI);             // TI 는 SCON 레지스터안에 있습니다.
                            // 시리얼통신에 관한 자세한 강좌는 다음번에
                            // 하도록 하겠습니다. 아직 보드가 없어서 발을
                            // 동동 구르시면서 기다리는 분들이 계셔서요 ^^
    SBUF = bydata;
    TI = 0;
}

void Init_Serial(void)
{
    EA=0;                   // 이후로는 다음번 강좌를 기다리세요 ^^
    TMOD &= 0x0F;           // 필요하신 분은 소스를 직접 보시면서 이해하셔도
    TMOD |= 0x20;           // 되겠습니다. ^^
    PCON = 0x80;
    TH1 = TH19600BPS;       // 소스는 시리얼통신의 간단한 예제와
    TR1=1;                  // 타이머0 번을 사용한 LED 깜박이기 입니다 ^^
    SCON = 0x52;
    SBUF = 0x00;
    RI = 0;
    TI = 0;
        IP = 0x00;
    PS = 1;

//    ET1=1;
    EA=1;

//  TH1 = 0xF3;
//  TR1 = 1;
}

void delay(int number)
{
    unsigned int i;
    for(i=0;i<number*1000;i++);
}

void main(void) {

    u08 i;

    t0init();
    ui_cnt = 0;

    // LED TEST ROUTINE //
    for(i=0;i<5;i++) {
        delay(100);
        P1_2 = 1;
        P1_3 = !P1_2;

        delay(100);
        P1_2 = 0;
        P1_3 = 1;
    }

    // SerialCommunication Test Routine //
    Init_Serial();

    for(i=0x30;i<0x3b;i++) {
        Put_Byte(i);
    }

//  t0init();

    while(1)
    {
        if(ui_cnt>100) { ui_cnt-=100; P1_3=!P1_3; }
        for(i=0x30;i<0x39;i++) Put_Byte(i);     // Display 0 .. 9
        for(i=0x41;i<0x5A;i++) Put_Byte(i);     // Display A .. Z
        for(i=0x61;i<0x7A;i++) Put_Byte(i);     // Display a .. z
        Put_Byte(' ');                          // Display Hello World!
        Put_Byte('H');
        Put_Byte('e');
        Put_Byte('l');
        Put_Byte('l');
        Put_Byte('o');
        Put_Byte(' ');
        Put_Byte('W');
        Put_Byte('o');
        Put_Byte('r');
        Put_Byte('l');
        Put_Byte('d');
        Put_Byte('!');
        Put_Byte(' ');
//      Put_Byte('n');
//      Put_Byte('r');
    }
}


---------------------------------------

시리얼통신을 해보자. 인터럽트에 관해서 간단하게 알아보았고, 타이머라는 것도 사용해 보았다. 여기서는 시리얼통신을 사용해 보기로 한다.

시리얼통신은 요즘 보편적으로 많이 사용되는 TCP/IP 같은 것에 비하면 속도도 느리고 보안도 문제가 있다. 하지만 8051 같이 간단한 프로세서를 사용하면서 거창한 프로토콜을 주장할 필요는 없다. 단순하게 정보를 전달해 주고받을 수 있는 가장 단순한 통신방식이 RS232 시리얼통신이다. 우선 이런 저런 복잡한 말보다는 간단하게 프로그램을 짜서 어떻게 움직이는 보는 것이 실용적이겠다.

우선 통신을 하기 위해서는 컴퓨터에 8051이 서로간에 대화할 수 있는(통신할 수 있는) 프로그램이 있어야 한다.
이야기 또는 세롬데이타맨을 공개 자료실에서 얻어 설치한다. 필자는 세롬보다는 이야기가 좋다. 심파일 자료실에 가면 이야기가 있다. 워낙 오래된 프로그램이긴하지만 여전히 시리얼통신이 필요할 때는 그걸 가져다 쓴다. 아니면 그냥 윈도우에 있는 하이퍼터미날을 사용해도 아무 문제 없다.

그리고 시리얼통신이 되어야 하므로 컴퓨터에서 시리얼모뎀을 설치한다. 하드웨어 설치에서,

[설정] - [제어판] - [시스템] - [하드웨어] - [장치관리자] - [모뎀] - [두 컴퓨터간의 통신케이블]

이 있는지 확인한 후 없으면 - 아마 없을 것이다 - 설치한다. (순서는 XP, WIN2000, 98등에 따라 조금씩 다를 것이다. 나름대로 찾아주기 바란다 ^^)

[설정] - [제어판] - [새하드웨어추가] - [목록에서 직접] -  ... - [모뎀설치] - [검색하지 않고 목록에서] - [표준모뎀] : [두 컴퓨터간의 통신케이블]

이렇게 선택을 하면 된다. 모뎀을 설치하는데 하드웨어적으로 어떤 모뎀이 있는 것이 아니라 COM1 포트를 예전에 널모뎀이라고 불렀던 방식으로 시리얼 케이블을 서로 연결하고 통신을 하는 방식을 말한다. 8051과도 시리얼통신을 하는 것이기 때문에 COM1 혹은 COM2 포트에 모뎀이 있는 것처럼 하고, 그 모뎀은 [두 컴퓨터간의 통신케이블] 혹은 [널모뎀] 이라고 하는 것을 선택하면 된다.

그 다음 시리얼모뎀으로 연결하고, 이야기에서 통신설정을 한다. 9600-N81 로 설정한다. 9600은 9600 bps, 초당 9600 개의 비트를 전송한다는 의미이다. N은 패리티와 흐름제어를 None로 하는 것이고, 데이터비트를 8, 정지비트를 1로 한다는 의미이다.

이렇게 설정이 되었으면 PC쪽에서 할 일을 다 한 셈이다. TY52보드와 PC를 RS232 보드로 연결시켜두면 다 되었다.

지난번 레지스터라는 것을 설명하면서 밥통의 스위치로 비교한 적이 있다. 밥통에 쌀을 넣고 밥을 하기 위해서는 [취사]라는 버트을 눌러야 한다. 마찬가지로 8051에서 시리얼 통신을 하기 위해서는 PC와 선을 연결 한 후, 8051 내부에 있는 레지스터를 잘 조작해서 시리얼통신을 한다는 것을 8051에게 알려줘야 한다.

여기에 사용되는 레지스터는 다음과 같다.

 

 

 


EA : 인터럽트의 중지, 시작을 결정한다. 0을 주면 인터럽트가 발생해도 무시하고 아무것도 안하겠다는 것이고, 1이 되면 인터럽트 발생시 미리 만들어 둔 ISR 을 실행시키겠다는 뜻이다.

TMOD : 타이머 설정에 필요한 레지스터. 타이머1과 0 사용에 관한 설정을 하는 곳이다. 앞쪽 4개 비트를 0010 으로 만들고 뒤쪽 4개 비트는 그대로 둔다. 즉, 타이머1만 8비트 자동 재설정 모드로 만들어두고 타이머0에 관한 설정값은 건드리지 않는다.

TMOD &= 0x0F; 
// TMOD 가 ???? ???? 값이면 ???? ???? & 0000 1111 => 0000 ???? 이 된다.
// 여기서 ? 는 0 인지 1 인지 확실치 않으므로 원래의 값이라고 생각하면 된다.

TMOD |= 0x20;
// TMOD 는 0000 ???? 이 되었다. 여기에 | 연산을 하면,
// 0000 ???? | 0010 0000 으로 0010 ???? 이 된다.
// 최종적으로 TMOD 값은 앞 4개 비트만 0010 으로 바뀌고 나머지는 예전값을 그대로 간직하게 된다.

PCON : 파워 제어 레지스터로 맨 앞의 하나의 비트값이 SMOD 라고 불리는데 여기서는 그 값만 유의해서 보면 된다.

PCON = 0x80;

즉, 1000 0000 이라는 값을 PCON 에 넣어주면 된다. 아니면 그냥 SMOD = 1; 이렇게 해줘도 된다. SMOD 는 PCON 의 최상위 비트의 이름이다. 즉 PCON이 1000 0000 이라는 값을 가질때 1 의 값을 가지는 비트의 다른 이름이 SMOD 이다.

SMOD 가 1이 되면 보레이트 설정시에 사용된다.

보레이트라는 것은 무엇인가? baus rate 를 그대로 쓴 것이다. 보레이트의 단위는 bps 가 된다. bit per second 로 초당 몇개의 비트가 전송이 되는지에 대한 속도를 나타낸다.
위에서 타이머1을 자동 재설정모드로 두었는데, 자동 재설정 모드는 TL1 의 값이 1씩 증가하다가 오버플로우가 발생하면(8비트니까 1111 1111(2) 이 가장 큰 수가 된다. 여기서 1이 더해지면 0000 0000(2)이 되는데 이것을 오버플로우, 즉 담을 수 있는 한계를 넘어서 한바퀴 돌아 0가 되어 버린상태를 말한다), TH1 의 값을 TL1에 복사해서 넣어주고 미리 만들어 둔 ISR(인터럽트 서비스 루틴)을 실행한다.
그래서 자동 재설정 모드로 타이머를 사용할때는 TH1 의 값이 중요하다. 여기서 TH1의 값에 따른 보레이트의 값을 계산하는 식을 잠시 본다.

보레이트 = 2^(SMOD) / 32 * 발진주파수(크리스탈의 값) / 12(256-TH1)

SMOD 는 1을 넣는다고 했다. 이 식을 다시 TH1 에 대해 정리하면 다음과 같다. (크리스탈은 24000000)

TH1 = 256 - (125000/보레이트)

보레이트가 9600 이면,

TH1 = 256 - 125000/9600 = 242.979 = 243 = 0xF3

TH1 에 0xF3 을 넣어주면 9600 bps 의 보레이트를 생성하게 된다. 이런식으로 자신이 쓰고자 하는 값을 만들어주면 되는 것이다. 보통 보레이트는 57600, 28800, 19200, 14400, 9600, 4800, 2400, 1200, 600, 300 등의 값이 사용된다. 위의 식에 이 값들을 넣어 그에 맞는 TH1 의 값을 찾아서 그 값으로 설정해주면 시리얼통신을 위한 준비가 되는 것이다.

여기서 참고할 것은 현재 24MHz 짜리 크리스탈을 사용하기 때문에 TH1 의 값이 정확한 정수로 떨어지지 않았다. 소숫점이 나오는 숫자가 되었고, 이것을 적당한 선에서 잘라서 사용했다. 실제로 사용해보면 이 정도의 오차는 아무런 문제가 되지 않는다. 전혀 에러가 나지 않고 통신은 잘 된다. 하지만 이 소숫점 이하의 숫자를 없이하기 위해서 많이 사용되는 방법이 24MHz 나 12MHz 같은 크리스탈이 아닌 11.0592MHz 나 22.1184MHz 같은 크리스탈을 사용하는 것이다. 이 값을 넣어서 계산해보면 신기할 정도로 정수로 딱 맞아 떨어진다. 정수로 맞아 떨어지도록 미리 준비한 값이 11.0592 라는 값이다. 그래서 8051 보드를 보면 11.0592 나 22.1184 같은 값이 보인다. 이 값을 보면 시리얼통신시 발생할 수 있는 에러의 가능성을 최소화하기 위한 값이라고 여기면 된다.

TR1 = 1;

이것은 타이머 1을 시작한다는 뜻이다. 타이머 1을 모드는 8비트 자동 재설정이고, 이것은 시리얼통신을 하기 위해 보레이트를 만들게 된다. 타이머 1이 시작된다는 것은 이제 시리얼통신을 하기 위한 일정한 간격을 클락이 만들어졌고, 이것이 시작되었다는 것을 말한다.

SCON = 0x52;

0x52 = 0101 0010(2) 는 각각 비트의 값은 다음과 같다.

SM0, SM1은 각각 0과 1로 설정한다. 이것은 모드 1이다. 보통 우리가 사용하는 것은 모드 1만을 사용한다. 여기에 모드 0, 2, 3이 있는데, 이것들은 일반적으로 사용되지 않는 방식이다. SM2 역시 항상 0으로 설정해 둔다.

참고로 현재의 통신 방식에 시리얼통신은 그렇게 큰 비중을 두지는 않는다. CAN 이나 USB, 기타 인터넷 프로토콜을 사용하는 통신방식이 많이 사용되고 있다. 하지만 8051 로 복잡한 통신프로토콜을 구현하는 것은 그렇게 추천할 만한 일은 아니다. 물론 원한다면 다른 복잡한 프로토콜을 사용할 수 있다. 하지만 굳이 그러기보다는 간단한 시리얼통신만으로도 충분히 만족할 만한 결과를 얻을 수 있다. 다른 빠른 통신방식들이 많이 나와있기에 시리얼통신을 하면서 다양한 방식에 관심을 가질 필요까지는 없어 보인다. 기본적으로 널리 쓰이는 통신방식을 사용하고, 더 빠른 통신 방식이 필요하면 USB 나 CAN 등의 다른 통신을 사용하면 된다.

최근에 이더넷 기능을 가진 8051 칩이나 USB 통신이 가능한 8051 칩도 등장하고 있다. 필요하면 그런 칩을 사용해 보는 것도 좋을 것이다. 하지만 여기서는 가장 간단한 시리얼통신만을 사용하고 있다. ^^

SCON 에서 REN 과 TI  비트를 1로 설정해 두었다. REN 은 수신 가능을 설정하는 비트로 1로 설정하면 수신이 가능해진다. 만약 REN 이 0이면 수신하지 못한다. TI 를 1로 설정해둔다. 나중에 새로운 값을 전송하기 전에는 0으로 만들어주어야 한다. 전송이 완료되면 저절로 1이 된다. 역시 다른 값을 또 전송하기 위해서는 1의 값을 0으로 소프트웨어적으로 고쳐주어야 한다.

SBUF = 0x00; 는 시리얼데이타 버퍼로 8개의 비트의 값으로 구성되다. SBUF 는 이중구조로 되어 있는데, 이것이 무슨 뜻인고하니 두개의 별도의 물리적 공간에 따로 존재한다는 뜻이다. (이것은 또 무슨 소린고? ^^;) 그러니까 사실은 8비트짜리 레지스터가 두개 있다. 두개는 다른 물리적공간을 차지하고 있는 놈이다. 그런데 두개의 이름이 같다. ^^
그러면 이름이 같은 두 놈을 어떻게 구별해서 사용할까? 사용법은 간단하다. SBUF 에 어떤 값을 넣으면 개똥이가 그 값을 받아서 시리얼포트[TXD핀]를 통해 외부로 보내주게 되고, SBUF 에 어떤 값이 있는지 읽으면 소똥이가 시리얼포트[RXD핀]를 통해 들어오는 값을 읽어오는 것이다.

말은 이렇게 했지만 쓸때는 별 생각없이 그냥 읽거나 쓰면 된다. 대신 보내기 위해 SBUF에 저장된 값을 다시 체크하기 위해 읽어들일수는 없다. ^^ 위에서 한번 언급했듯이 다시 읽어들이면 내가 SBUF에 쓴 값을 읽는 것이 아니라 외부 TXD 핀에 들어오는 신호를 읽게 된다.

RI=0; TI=0; 이제 시리얼통신을 시작한다는 것으로 플래그값을 깨끗이 정리해 주는 것이다. 항상 쓰기 전에 TI=0; 으로 해주고, 읽은 후에 RI=0; 으로 해 주어야 다시 다른 값을 쓰거나 읽을 수 있다.

그 다음 IP=0x00; 을 해주고 PS=1; 을 해주었다.
이것은 IP, 즉 인터럽트 우선순위를 정해주는 레지스터를 0으로 해서 다른 인터럽트가 우선순위를 가지지 못하게 해준 다음 PS=1; 을 통해 시리얼통신 인터럽트를 우선순위를 가지게 해 주었다.

시리얼통신을 사용할 때는 보통 시리얼통신에 우선순위를 둔다. 우선순위를 둔다는 것은 다른 인터럽트가 동시에 걸렸을 때 우선순위가 높은 녀석을 먼저 처리한다는 뜻이다.

그 다음 EA=1; 을 통해 인터럽트가 전체적으로 시작되도록 했다.

여기까지가 Init_Serial 소스의 내용을 간략히 정리한 것이다.

타이머는 예전 강좌를 보면 설명이 되어 있으니 어려움은 없을 것이고, Put_Byte() 라는 함수를 보도록 한다. 이 함수가 8051 에서 PC 쪽으로 데이타를 보내는 함수다.

while(!TI); 라는 구문을 보면 TI 가 0이 될 때까지 뒤로 넘어가지 않고 기다린다. TI 는 어떤 값을 전송하면 1이 되고, 그 값을 다시 0으로 만들어주지 않으면 다음 값을 전송해 주지 못한다. 쉽게 생각해서 물동이를 나르고 있다고 생각해 보자. 죽 ~ 줄을 서서 물동이를 옆사람에게 건네준다. 그런데 옆사람의 손을 보니 아직 손에 물동이가 들려 있다. 동작이 굼떠서 느린 녀석인게다. ^^ 이럴때는 아무리 물동이를 넘겨주고 싶어도 잠시 기다려야 한다. 옆사람 손이 빌 때까지 참고 기다려야 한다.
마찬가지로 데이타를 PC쪽으로 넘겨주고 싶은데 PC가 아직 예전에 보낸 데이타를 받지 못했다. 그런데 그걸 무시하고 계속 데이타를 날려주면 데이타들이 중간에 사라지거나 문제가 발생하게 된다. 데이타를 다 받았는지 확인을 하고 그 다음에 다음번 데이타를 넘겨주어야 하는 것이다.

쉽게 말해서 [Hello, TY52] 라는 데이타를 날리고 싶을때는 한글자씩 넘겨주어야 한다. 그리고 한 글자가 다 넘어갔다는 확인이 되면 그 다음 글자를 넘겨주게 된다.

즉, H 자를 넘겨준다. 이 글자를 넘겨주면서 TI 값이 1이 된다. 즉, 이제부터는 다른 글자를 받지 않는다. H 자가 완전히 넘어갔으면 소프트웨어적으로 TI 를 0으로 클리어 해준다. 그러면 다음 값을 받을 준비가 되는 것이다.

설명이 조금 복잡하게 들렸어도 잘 이해해 주기를 바란다. Put_Byte() 를 아주 빠르게 여러번 실행시켰어도 첫번째 실행한 후에

while(!TI);
SBUF = bydata;
TI = 0;

여기까지 마무리가 되서 TI가 0이 되어야만 다음번 Put_Byte() 를 실행시킬 수 있는 것이다.

설명이 조금 부족하면 반복해서 읽어보고 그래도 이해가 안되면 질문을 해주기 바란다. ^^

참고로 기말기간이라 무척 바쁘다. 여기까지 글을 쓰고 잠시 한 열흘정도 바쁘게 지내야 할 것 같다. 방학이라고 생각하고, 지금까지 했던 내용을 점검해보고 했던 것을 응용해서 나름대로 재미있는 공작을 해보는 시간을 가져보는게 어떨까? ^^

TY52는 실용성을 목적으로 만든 것이다. 불필요하게 큰 메모리를 붙이는 것 보다는 8kbyte 라는 충분히 실용적인 메모리를 살려서 직접 뭔가를 할수 있도록 구상한 보드이다. ^^ 한번 시계도 좋고, 다른 무엇도 좋다. 복잡한 프로그램이 중요한 것이 아니다. 수백, 수천줄을 코드가 중요한 것이 아니다. 짧아도 좋다. 다만 무엇인가 내가 흥미를 가질 수 있고, 무엇이든 재미있는 것이면 좋지 않을까? ^^

며칠전에 우연히 광섬유를 이용한 작은 크리스마스 장식품을 하나 샀다. 가격은 5천원 정도 였다. 아래부분에 LED 를 반짝이면서 광섬유(그냥 비닐로 된 낚시줄 같다 ^^;)의 끝부분에 불빛이 비치는 것이다. 이걸 뜯었다. ^^ 아래부분에 TY52 에 고휘도 LED를 붙여서 다시 만들어보려고 한다. 지금은 조금 바빠서 두주후쯤 만들 예정이다. ^^

그때 한번 업그레이드 된 크리스마스 장식품을 한번 만들어볼 생각이다. ^^


          ------------------------------------------
           Timy의 전자카페
           기분좋은 하루되세요. ^^
           블로그 : http://www.electoy.net
           스터디카페 : http://cafe.daum.net/timy8051
          ------------------------------------------ 


반응형

+ Recent posts