반응형

일단 펌입니다..
출처는 맨밑에...

펌웨어로 시리얼 통신을 구현하는 중에
두 무선통신 노드사이에 시리얼을 bypass통신을 해야 하는 경우가 생겼다
사람이 시리얼로 내려보내는 속도와 무선으로 날라가는 속도가 많이 차이가 나기 때문에 (아스키 파일로 내려보내면 무지 빠르다...)

중간 버퍼의 필요성을 느끼고..
아무 생각없이 linked queue로 구현하고는 대략 낭패...
결과적으로 잘 동작 안하더라

그래서 인터넷 찾다가.. 원형큐에대한 정보를 얻게 된다.
사실 linked queue나 원형큐나 차이가 없다고 생각했었는데
밑에 원형 큐 그림들은 나를 새롭게 깨우쳐 주었다..

인터럽트로 들어오는 시리얼 데이터는 계속 큐에 쌓아두고
외부포인터만 증가시킨다.
큐에서 읽을 때에는 내부포인터로부터 읽어온다..
여기서 핵심은 비동기적으로 읽고 쓸때 포인터에 대한 접근이
겹치지 않는다는 것이다.. 큐에 넣을때에는 외부포인터만 쓰이며
큐에서 읽을 때에는 내부포인터만 쓰인다는것..

펌웨어 레벨에서 크리티컬 섹션도 쓰레드도.. 파이프도 없는 상황에서
굉장히 유용한 방법이라 할 수 있다..


위 파일은 어딘가에서 찾은 linked-queue소스에다가
간단하게 짜본 원형큐 소스가 추가되어있다.. (serialQ부분)

이 밑은 펌자료
/////////////////////////////////////////////////////
② QUEUE
큐는 순서를 가지고 있는 선형리스트 구조입니다.
요소의 삽입과 인출(삭제)가 다른 구조로 되어있으며 매표소에서 입장권를 사기 위해서 줄을 서서 기다리
는 사람들의 모습을 생각하시면 됩니다.(물론 새치기는 허용이 되지 않습니다.)
여기서는 먼저 들어간 요소가 먼저 인출이 되므로 선입선출,혹은 FIFO(First In First Out)이라고도 합니
다.
그리고 요소의 삽입이 일어나는 곳을 꼬리(Rear)라고 하며 인출이 일어나는 곳을 머리(Front)라고 합니다.

③ STACK
스택은 한쪽 끝이 막혀있는 구조로서 요소의 삽입과 인출이 한쪽끝에서만 일어나는 선형 리스트 구조입니
다.
밑이 막혀있는 통을 세워놓은 구조를 생각하시면 됩니다.
여기서는 나중에 들어간 요소가 먼저 인출이 되게 되어있는 구조이므로 후입선출,혹은 LIFO(Last In First
Out)이라고도 합니다.
요소의 삽입,인출이 일어나는 곳을 스택의 상위(Top)이라고 부르며 요소를 스택에 넣는 것을 푸쉬(Push)라
고 부르며 요소를 인출하는 것을 팝(Pop)이라고 합니다.
스택은 서브루틴 호출이나 인터럽트 처리시 돌아갈 주소를 저장하는 용도로 많이 쓰이기도 합니다.

④ CIRCLE QUEUE
비베에서 가지는 통신용 버퍼에는 두가지가 있습니다.
수신버퍼와 송신 버퍼입니다.
이 버퍼의 크기는 각각 MSComm의 속성중 InBufferSize와 OutBufferSize의해서 결정이 됩니다.
우리가 주로 프로그램에서 많은 고민을 하는버퍼는 수신버퍼입니다.
따라서 지금부터는 수신 버퍼를 기준으로 설명 드리도록 하겠습니다.
수신버퍼는 환형큐라고 부르는 구조로 되어있습니다.
이 환형큐는 기본 구조 및 동작은 일반큐와 같으나 머리와 꼬리가 붙어있는 환형으로 되어있는 것이 다릅니
다.
이 환형큐의 특징은 두개의 포인터를 가지고 있습니다.
하나의 포인터는 통신포트에 도착한 데이터를 수신버퍼에 갖다놓은 위치를 가리키고 있으며, 이 포인터를
외부포인터라고 부르겠습니다.
또 하나의 포인터는 응용프로그램에서 가져올수 있는 수신버퍼의 위치입니다.
이 포인터는 내부포인터라고 부르겠습니다.
다음의 그림을 참고 하시기 바랍니다.

사용자 삽입 이미지

                                    <<첨부그림 2>>

각각의 격자에는 수신되는 데이터가 바이트 단위로 적재가 됩니다.
위의 그림처럼 외부포인터와 내부포인터가 마주 보고있다면 더 이상 버퍼에 남은 데이터가 없는걸로 생각하
여 InBufferCount가 0이 됩니다.

먼저 데이터의 흐름을 생각해보시기 바랍니다.

사용자 삽입 이미지

                                    <<첨부 그림 1>>
외부에서 발생한 데이터가 통신 포트에 도착하면 CPU 및 통신드라이브는 그 데이터를 1바이트씩 묶어서 메
모리에 갖다 둡니다.
이때의 메모리는 수신버퍼로 생각하시면 됩니다.
그러면 우리가 만들거나 이미 만들어진 통신 프로그램에서는 수신버퍼에 들어와있는 데이터를 확인 하여 메
모리로부터 가져오게 됩니다.
즉,우리의 응용프로그램에서는 통신포트로부터 직접 데이터를 가져오는게 아니며 통신 드라이브가 데이터
를 바이트 단위로 묶어서 수신버퍼(메모리)에 갖다두면 그때 우리가 데이터를 얻을수 있는 구조로 되어있다
는걸 생각하시기 바랍니다.

자, 실제 예를 들어 보겠습니다.
지금 외부에서 보낸 데이터가 있습니다. ‘12345” 라는 문자가 들어왔다고 하겠습니다.
물론 Input명령은 아직 사용하지 않았습니다.
그러면 다음과 같은 그림이 그려집니다.
사용자 삽입 이미지

                                    <<첨부 그림 3>>

아직 데이터를 가져가지 않았기 때문에 두개의 포인터는 벗어나 있습니다.
이때 InBufferCount를 보면 두 포인터의 차이를 계산해서 5라는 값이 나옵니다.
이때 InputLen라는 속성을 2로 하고 Input을 해보면 다음의 그림과 같이 됩니다.

사용자 삽입 이미지

                                    <<첨부 그림 4>>

즉 두바이트를 인출 시켜서 응용프로그램에 돌려주고는 내부포인터의 위치가 변경이 됩니다.
그리고 다시 데이터가 들어왔다고 가정합시다.
이번에는 ‘ABCD” 가 들어 왔다면 다음처럼 데이터가 누적이 됩니다.

사용자 삽입 이미지

                                    <<첨부 그림 5>>

이번에는 InPutLen속성을 0으로 주고 다시 읽어봅니다.
물론 MSComm1.Input으로 읽겠지요?
그러면 버퍼에 남아있던 모든 데이터를 다 되돌려주고 두 포인터는 서로 마주 보게 됩니다.
다음의 그림처럼..

사용자 삽입 이미지

                                    <<첨부 그림 6>>

자, 여기서 정리를 해보겠습니다.
우리가 사용하는  MSComm의 수신버퍼는 환형큐로 되어있습니다.
이 환형큐보다 더 큰 데이타가 들어오거나 우리가 미처 수신 데이터를 가져가기전에 다른 데이터가 들어온
다면 버퍼내에서는 오버런이 발생합니다.
이때는 이전의 데이터는 없어지면서 그 위에 덮어쓰게 됩니다.
그리고 InputLen은 한번의 Input명령에 수신될 바이트를 지정한다는건 이해가 되셨을겁니다.
그리고 InBufferCount의 속성은 두개의 포인터의 차이를 되돌려주며 이는 수신 버퍼에 남아잇는 데이터 수
를 돌려줍니다.
그런데 이 InBufferCount 의 또다른 용도가 있습니다.
MSComm1.InBufferCount = 0 이라고 하면..수신버퍼를 클리어시키는 역할을 합니다.
수신 버퍼에 쓰레기 데이터가 있다고 가정할 때 수신버퍼를 비우는 용도로도 사용되지요.
저번의 강좌에서도 말씀드렸지만 9600보레이트로 통신을 한다면 통신포트에서부터 수신버퍼까지 데이터가
들어오는 시간이 1밀리초가 걸립니다.
따라서 수신버퍼에 우리가 눵하는 데이터가 다 수신이 되는데는 수신하려는 바이트수 * 1밀리초이상이 걸리
므로 이 시간을 잘 기다려 주셨다가 읽어야만 여러분들께서 원하는 데이터를 얻으실수 있다는걸 유념해 주
시기 바랍니다.

다음 강좌는 폴링과 인터럽트에 대해서 말씀드리겠읍니다.

출처: http://www.moohantec.com/neboard/show.asp?id=lecture&ref=3&step=1&page=1

반응형

+ Recent posts