1. Serial 통신 개발
임베디드 개발 경험이 있다면 가장 먼저 접하는 개념일 것이다. 간단하게 설명하면, Serial 통신은 선 2가닥으로 1대 1 통신을 하는 방식이다. 좀 더 디테일한 내용은 임베디드 개념 정리 글에서 다루는 게 맞는 거 같아 여기서는 길게 늘어두지 않겠다.
> Serial 통신에 대한 설명은 아래 링크를 참고해주세요.
https://enginbear.tistory.com/25
암튼, 가장 먼저 접하기도 하면서 다른 통신보다 접근성이 높기에 Serial 통신을 채택했다. 무엇보다 앵간한 모든 아두이노는 USB를 통해 Serial 통신이 가능하기에 채택했다. 처음 글에서도 얘기했지만 사실 가독성이 떨어지더라도 Serial 통신을 이용해 “Data: xx” 이렇게 ASCII로 보내도 문제는 없다. 문제는 가독성의 문제다. 아두이노 내장 함수로 예를 들자면 Serial.print함수를 이용해 현재 값을 뿌린다고 가정하면, Serial 통신 데이터를 받아보는 컴퓨터 입장에서는 “Data: xx Data: xx Data: xx Data: xx Data: xx … “ 이렇게 쭉 이어져 나갈 것이다. 딜레이 없이 계속 뿌린다 가정한다면 눈으로 쫓아가기 힘들 것이다. 그렇다고 1초에 한 번씩 뿌린다 하면 그만큼 Sampling Time에서 손해를 보는 것이 아닌가. 멈춰놓고 보자니 현재 값을 보고 있는 것도 아니고.. Serial.println 함수를 사용한다고 달라지는 것도 아니다. 줄 바꿈을 한들 스크롤만 빠르게 내려갈 테니 말이다. 그래서 내가 학부생 때는 줄 바꿈 없이 /r (Carriage Return)을 이용해 커서를 앞으로 돌려 덮어썼다. 그러면 값이 제자리에서 갱신될테니 말이다. 근데 이것 또한 단점인 게, 다수의 값을 띄우기엔 제약사항이 많다.
2. Protocol
그래서 생각한 것은 통신 Protocol이다. Protocol을 사용해서 값을 읽어 해석하고 띄워주는 툴을 만들면 될 것 같았다. 사실 CAN 통신을 쓰면서 유사하게 적용하고자 한 것이다. CAN의 경우 각 메시지에 ID를 부여하고 DATA를 Byte 단위로 송신한다. 그 외에도 Frame은 복잡하지만 여기서 ID와 Data를 같이 보낸다는 거에서 착안했다. Serial 통신 역시 Byte단위로 데이터를 보내는데 통신하는 두 기기끼리 규약을 정하고 ID와 Data를 주고받는다면 컴퓨터에서 ID와 Data를 해석해 모니터에 띄워주면 되는 것이다. 여기서 문제는 한 Packet, 그니까 한 메시지 프레임이 몇 Byte으로 둘 것인가 이다. 이래저래 ID 개수와 Data의 Resolution을 고려해서 4 Byte로 정했다.
첫 번째 Byte는 ID와 Read/Write정보를 담고 두 번째, 세 번째 Byte는 Data를 담을 것이다. 즉 Data는 Unsigned 16 Bit Data의 형태로 전달할 수 있다. 그렇다면 남은 마지막 Byte는 어디에 쓰이는가? 바로 다음 글에서 설명할 데이터 무결성이다. 사실 CAN 통신의 경우 정해진 Data Length만큼 전송을 한 뒤에 송수신을 끝낸다. 반면에 Serial 통신은 고정된 1Byte 단위로 송수신을 끝내기 때문에, 내가 정한 Packet을 정상적으로 시작과 끝으로 4Byte를 받았다는 보장이 없다. 또한 전달받은 데이터가 송수신 과정에서 훼손이 됐을 가능성도 있다. 이를 방지하기 위해서 마지막에는 데이터 무결성을 확인하는 Byte를 할당한다. 해당 내용에 관해서는 다음 글에서 다루도록 하고, 내가 지정한 Packet을 아래 표로 이해해보자.
위와 같은 약속을 하고 보내면, 받는 입장에서 받은 Data를 위의 표를 참고하여 해석해서 R/W, ID, Data를 받는 구조이다. 이렇게 한다면 내가 보려고자 하는 변수별로 ID를 부여하고 Data를 전송한다면 Data를 처리하기가 편리할 것이다.
3. Code 구현
C언어는 위와 같이 구성을 하기가 편한 게, 구조체의 비트 필드를 활용하면 된다. Data Packet을 가리키는 Unsigned 32 Bit 구조체 하나를 선언하고 위의 구성 그대로 비트 할당을 하면 된다. 그리고 Union을 활용하면 4 Byte 배열 Type과 32 bit Data Type 두개로 사용할 수도 있다. 실제로 구현해보면 아래와 같은 모습일 것이다.
typedef union packet_tag{
uint8_t Byte[4];
struct{
uint32_t checksum:8;
uint32_t data:16;
uint32_t id:6;
uint32_t rw:2;
}R;
}packet;
Byte Order에 따라 비트필드 구성이 뒤바뀔 수 있다. 이는 송수신하는 대상 Device의 Byte Order도 고려하여 송수신을 신경 써야 한다. 그리고 추가로 R/W, ID, Data를 넣으면 Packet을 만들어주는 함수들을 구성해 분류해 두면 여러 프로젝트에서도 활용하기가 좋을 것이다. 위의 개념들은 아래 Github 주소에서 monitor_Packet.cpp / monitor_Packet.h에서 구현되어 있다.
Github 주소: https://github.com/DongjinJ/Arduino_Monitor.git
GitHub - DongjinJ/Arduino_Monitor: 아두이노 Data를 시각화 하는 GUI
아두이노 Data를 시각화 하는 GUI. Contribute to DongjinJ/Arduino_Monitor development by creating an account on GitHub.
github.com
임베디드 외주 제작 문의:-
구름이님의 오픈프로필
임베디드 외주 제작
open.kakao.com
'프로젝트 일지 > 임베디드 모니터' 카테고리의 다른 글
[임베디드 모니터] Arduino Demo Code 구현 (0) | 2023.04.06 |
---|---|
[임베디드 모니터] GUI 개발 (0) | 2022.06.08 |
[임베디드 모니터] Serial 통신 & 데이터 무결성 - 2 (0) | 2022.01.18 |
[임베디드 모니터] UI 고민 (0) | 2021.12.01 |
[임베디드 모니터] 개발 계획 (0) | 2021.09.06 |