2015년 9월 3일 목요일

[Ada] GNAT Serial Communications을 활용한 시리얼 통신 튜토리얼













  Ada 언어에서의 시리얼 통신 방법을 소개합니다.

Ada 언어는 Windows 버전의 GPS (GNAT Programming Studio)에서 활용했습니다.







  우분투 리눅스에서도 Windows의 COM포트가 TTY로 바뀔 뿐 코드는 다르지 않습니다.

읽기, 쓰기를 확인할 상대역으로 Processing 언어로 동작하는 아두이노를 두었습니다.

실습환경은 아래 이미지와 같습니다.







  여기서 오른쪽의 USB-UART를 구동하는 언어가 Ada, 왼쪽의 아두이노를 구동하는

언어가 Processing이 됩니다. 언어는 다르지만 UART라는 공통된 프로토콜을 쓰기 때문에

통신이 이루어집니다.



  이제 소스코드를 부분적으로 보면서 설명하겠습니다. 주석처리된 소스코드를 함께

첨부했습니다.






C언어에서 "#include" 명령어로 헤더파일을 추가하듯이,

Ada에서도 기능 활성화를 위해 "with" 명령어로

패키지 포함을 선언합니다. "GNAT.Serial_Communications"는 시리얼 포트 세팅,

읽기, 쓰기 기능을 포함하고, "Ada.Streams"는 UART 통신에서 쓸 수 있는 스트림 데이터

타입을 쓸 수 있게 해줍니다. Ada 언어는 시리얼 통신에서

"Stream_Element_Array"라는 타입을 써야 합니다.


  Ada는 소켓통신에서도 데이터를 담을 때 이 타입을 써야하는데,

일반 자료형과 통신을 위한 자료형을 분리해 놓은 것은 안전성을 위해서라고 생각됩니다.

C언어에서 "byte" 변수를 선언해 놓으면 이 변수에 온/습도 데이터가 담기던, 시리얼통신

버퍼로 사용하던 지 컴파일 오류가 뜨지 않습니다. 구문적 오류이기 때문이죠.

하지만 Ada 언어에서는 데이터 통신에 쓰는 데이터를 다른 자료형으로 두기에

일반적으로 쓰던 Integer 데이터를 시리얼 통신에 곧바로 실어보내려면

컴파일 오류가 발생하게 됩니다.








  이제 procedure 내부에 변수 타입을 선언해줍니다. "port"는 시리얼 통신을 사용하기

위한 타입이며, 스트림 오프셋 "last"는 스트림 배열 "recv_Buffer"에 몇 바이트의

데이터가 담기는지 나타내는 타입입니다. "message"라는 Ada String 타입을

보시면 Ada 언어의 안정성 특징인 변수의 범위한정이 나옵니다. 이는 C언어에서의

"char message[3]"과는 성격이 조금 다릅니다. C는 message[0]으로써 0이 시작이지만,

Ada는 message(1)로써 1이 시작이 됩니다. 잠시 Ada 언어의 범위 특징을 보자면,


  만약 라면을 끓이는 Ada 로봇이 있고, 위와 같이 "type" 명령어를 통해

끓는물 범위를 100..110으로 잡아놓으면 저 범위 안의 온도에서만 면을 집어넣을 것입니다.

C언어에서라면,

  위와 같이 선언 후에 "Ramen_Boiling"이라는 정수 범위 검사를 별도로 해야겠지만,

Ada에서는 타입 선언부터 그 범위를 한정할 수 있습니다.







  다시 시리얼 통신으로 돌아와서, 위에 선언한 "port" 타입의 요소를 채워봅시다.

여기서 정의하는 특징은 "Open"과 "Set"이라는 Procedure입니다.

보시는 바와 같이, C언어에서 UART 프로토콜을 정의하는 것과 그 성질이 같습니다.

포트번호는 "제어판 -> 장치 관리자 -> 포트(COM & LPT)" 항목에서 확인하실 수 있습니다.


  저의 경우 USB-UART가 인식된 COM포트는 16번이었습니다. Baud rate는 9600,

8비트 단위, Stop bit는 하나, 패리티 비트는 두지 않았습니다. Block은 송수신에서

타임아웃과 연관된 타입으로 보였습니다. 아두이노에서는 Baud rate만 9600으로

맞춰주시면 위의 세팅과 송수신이 가능합니다.







  이제 문자열 message에 "ABC"라는 데이터를 시리얼 통신으로 실어서 보내볼 차례입니다.

["AB" & "C"]는 C언어에서 문자열 연산자인 "+"로 ["AB" + "C"]를 쓰는것과 같습니다.

"ABC"로 해도 상관없지만, 작은 차이점을 보여드리려 넣었습니다.

message 문자열을 실어 보내려는데 "Write" 프로시져 안에 "String_To_Stream"이라는

만들어진 함수를 거쳐가도록 되어있습니다. 앞서 말씀드린 Stream으로의 변환을 별도의

함수에서 처리하도록 한 것입니다. 그럼 그 함수를 보겠습니다.




  이 함수는 제가 작성한 것이 아닙니다. Stream으로의 변환과정을 모르고 오류를 붙잡고

있다가 구글 그룹스에 어떤 분이 올려주신 내용을 보고 그 의미를 알 수 있었습니다.

함수의 내용은  String 문자열을 입력받아서 그 길이만큼 원소 하나하나를 스트림으로

변환해서 반환해주는 기능입니다. 빨간 줄이 그어진 라인이 그 프로세스를 나타냅니다.

이렇게 변환하는 함수를 거쳐야지만 "port.Write()"에서 처리할 수 있는 스트림으로

인자를 줄 수 있습니다. 이 소스코드를 얻은 구글 그룹스 토픽 주소는 아래와 같습니다.

https://groups.google.com/forum/#!topic/comp.lang.ada/zGWrE29GkE0









  아두이노에서는 Ada 프로그램이 보낸 문자열을 출력하고, 그 안에 "C"라는 문자가 있다면

"DEF"라는 응답을 보내도록 되어있습니다. Ada 프로그램이 보낸 데이터 "ABC"는

아래와 같이 아두이노 시리얼 창에서 확인되었습니다. 이제 Ada 프로그램이

아두이노의 응답 "DEF"를 받아야합니다.






  "port.Read() Procedure는 Write와 다르게 스트림 오프셋이란 것이 추가로 붙습니다.

밑에서 "last"라는 이름의 스트림 오프셋을 확인하면 정수로 3이 출력됩니다.

즉 받은 메시지가 "DEF"이므로 그 길이가 출력됩니다.

아래는 GPS 실행창에서 아두이노가 보낸 응답과 오프셋을 출력한 이미지입니다.







  Ada 언어에서의 UART 시리얼 통신 소개를 마칩니다.

다음은 Visual Studio와 Qt 프레임워크에서의 C++ 시리얼 통신 방법을

정리해볼 생각입니다.



댓글 없음:

댓글 쓰기