본 자료는
1. 시리얼 컴포넌트(SerialPort Component) 간략 설명
2. 스레드 생성 및 제어(수신 스레드)
3. 스레드에서 UI 컨트롤 접근하기
순서로 진행합니다.
본 시리얼(Serial) 통신 예제는 C#으로 제작되었으며 수신 처리는 스레드(Thread)로 동작합니다.
Ascii Mode(Text Mode)로 데이터를 송/수신하도록 구성되어 있습니다.
송/수신 프레임(Frame)의 구분은 STX/ETX로 하지 않고 Time Interval을 사용하여 구분합니다.
수신 스레드(Thread)에서 데이터가 ReadTimeOut 설정 시간 동안 입력되지 않는 경우
TimeOut Exception이 발생합니다. ReadTimeOut 시간은 TimeOut Interval 시간보다 같거나
더 작게 설정해서 사용해야 합니다.
데이터를 저장 시에는 스레드(Thread)가 UI 접근을 직접할 수 없으므로 델리게이트(Delegate)를
사용해야 합니다.
모든 통신 환경에 쉽고 편리하게 적용할 수 있도록 새롭게 만들어진 컴포넌트입니다. SmartSerialPort를 사용하여 외부의 디바이스와 연동(통신 프로그래밍)시 문제 및 적용에 어려움이 있는 경우 회사로 연락주시면 IEC-Series와 디바이스 연동 작업을 지원하도록 하겠습니다. 지원 시 SmartSerialPort를 기준하여 지원하며 기능 변경 및 추가 지원을 하도록 하겠습니다.
1. 시리얼 컴포넌트(SerialPort Component) 간략 설명
시리얼(Serial) 포트, 다른 이름으로 직렬 포트라고 하며 시리얼 통신을 하는 경우 SerialPort 컴포넌트(Component)를 사용합니다.
생성자는 총 7개이며 사용되는 속성으로는 BaudRate, IsOpen, Parity, PortName, ReadBufferSize, ReadTimeout, RtsEnable, StopBits 등이 있습니다.
다음은 SerialPort Component 간략 설명이며 자세한 사용 방법은 첨부된 소스를 참고 바랍니다.
포트 이름 지정 public string PortName { get; set; } |
|
---|---|
사용법 C# 예제 |
// 포트 이름의 종류는 COM1, COM2, COM3, COM4, COM7 // 포트 이름을 COM1으로 설정 serialPort1.PortName = "COM1"; |
(인자) get; set; (반환값) string : 포트 이름 |
시리얼 통신을 위한 통신 속도 설명 public int BaudRate { get; set; } |
|
---|---|
사용법 C# 예제 |
// 통신 속도의 종류는 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000이며 256000은 지원하지 않습니다. // 통신 속도를 9600으로 설정 serialPort1.BaudRate = 9600; |
(인자)get; set; (반환값) int : 통신 속도 |
데이터 비트 설명 public int DataBits { get; set; } |
|
---|---|
사용법 C# 예제 |
// 데이터 비트를 8비트로 설정. 보통 8비트로 사용 serialPort1.DataBits = 8; |
(인자) get; set; (반환값) int : 데이터 비트 |
시리얼 포트의 상태(Open/Close) 확인 시 사용 public bool IsOpen { get; } |
|
---|---|
사용법 C# 예제 |
// 포트가 오픈 되었는지 확인 if (serialPort1.IsOpen == false) { return; } |
(인자) get; (반환값) bool : 포트가 오픈 되었으면 True 반환 |
수신 버퍼 사이즈 설정 public int ReadBufferSize { get; set; } |
|
---|---|
사용법 C# 예제 |
// 수신 버퍼 사이즈를 4kbyte로 지정 serialPort1.ReadBufferSize = 4096; |
(인자) get; set; (반환값) int : 수신 버퍼 사이즈 |
SerialPort 입력 버퍼에서 1바이트를 동기적으로 읽습니다. public int ReadByte(); |
|
---|---|
사용법 C# 예제 |
// 수신 버퍼에서 1바이트 읽기 serialPort1.ReadByte(); |
(인자) 없음 (반환값) int : Int32로 캐스팅된 바이트이며 스트림의 끝을 읽은 경우 -1을 반환 |
수신 제한 시간 설정. public int ReadTimeout { get; set; } Serial통신에서 송/수신 Frame의 구분은 STX/ETX로 하지 않고 Time Interval을 사용하여 구분합니다. 즉 Frame과 Frame사이의 일정 시간 간격(Time Interval)을 두어 데이터 송/수신 시에 Frame을 구분합니다. 이때 ReadTimeOut값은 송/수신 Interval값보다 같거나 작게 설정하여 사용해야 합니다. ① 수신 데이터 Interval이 100ms인 경우 |
|
---|---|
사용법 C# 예제 |
try { for (i = 0; i < serialPort1.ReadBufferSize; i++)
}{ // ReadByte()를 통해서 읽은 데이터를 수신 버퍼에 저장
}recBuff[i] = Convert.ToByte(serialPort1.ReadByte()); // ReadTimeout 시간 내에 데이터가 입력되지 않는 경우 TimeoutException 발생 catch (TimeoutException tExp) { // 입력된 데이터 개수 카운트
} iRecCnt = i; |
(인자) get; set; (반환값) int : 읽기 작업을 마쳐야 하는 제한 시간(밀리초) |
RTS(Request to Send) 신호를 사용할 수 있는지를 나타내는 값을 가져오거나 설정 public bool RtsEnable { get; set; } |
|
---|---|
사용법 C# 예제 |
// IEC667를 사용하는 경우 RtsEnable 속성값을 반드시 TRUE로 설정하여 사용 해야 합니다. IEC266, IEC1000은 해당하지 않음 // RTS 신호를 사용. serialPort1.RtsEnable = true; |
(인자) get; set; (반환값) bool : RTS 속성을 사용하는 경우 TRUE로 설정 |
바이트 당 정지 비트의 표준 개수를 가져오거나 설정 public StopBits StopBits { get; set; } |
|
---|---|
사용법 C# 예제 |
// 스톱 비트의 종류는 "None", "One", "Two", "OnePointFive" // 정지 비트를 One으로 설정. 보통 One으로 사용 serialPort1.StopBits = System.IO.Ports.StopBits.One; |
(인자) get; set; (반환값) StopBits |
2. 스레드 생성 및 제어(수신 스레드) 2-1. ReadByte() 를 통해서 읽은 데이터를 수신 버퍼에 저장
시리얼(Serial) 통신에 관련된 모든 기능을 담당하는 CSerialPort클래스로서 세부 내용은 다음과 같이 이루어져 있습니다.
// Client 연결 및 수신 스레드 함수
private void ComPortReadFunc()
{
// SerialPortClose()를 호출하는 경우 수신 스레드 동작 종료
while (true)
{
{
// ReadBufferSize는 Max값으로 고정되며 Max값 이상의 값이 입력되는 경우
// BufferOverRun이 발생하거나 데이터가 단편화됩니다.
// ex) ReadBufferSize가 4096이며 4096초과의 값이 입력
for (i = 0; i < serialPort1.ReadBufferSize; i++)
{
recBuff[i] = Convert.ToByte(serialPort1.ReadByte());
// ReadTimeout 시간 안에 데이터가 입력되지 않는 경우
catch (TimeoutException tExp)
{
iRecCnt = i;
// 입력된 데이터가 존재하면
if (iRecCnt != 0)
{
스트림에서 바이트를 읽고 스트림 내 위치를 한 바이트씩 앞으로 이동하거나 스트림 끝일 경우 -1을 반환 public int ReadByte(); |
|
---|---|
사용법 C# 예제 |
// ReadByte()를 통해서 읽은 데이터를 수신 버퍼에 저장 serialPort1.ReadByte() |
(인자) 없음 (반환값) Int32 : 캐스팅된 부호 없는 바이트이거나 스트림의 끝에 있는 경우 -1을 반환 |
2-2. 수신된 바이트 데이터를 유니코드로 변환
시리얼(Serial)통신은 Ascii Mode(Text Mode)만 지원되며 Binary 모드는 지원하지 않으므로 수신 데이터를 아스키(ASCII)로 변환해 주어야 합니다.
// 수신된 바이트 데이터를 유니코드 문자열로 변환
// recBuff의 데이터를 0부터 iRecCnt 바이트 만큼 ASCII 모드로 변환하여 strRecData에 저장
strRecData = Encoding.ASCII.GetString(recBuff, 0, iRecCnt);
ASCIIEncoding.GetString 메서드. 바이트 배열의 바이트 범위를 문자열로 디코딩합니다. public override string GetString ( byte[] bytes, int byteIndex, int byteCount
) |
|
---|---|
사용법 C# 예제 |
// 수신된 바이트 데이터를 유니코드 문자열로 변환 strRecData = Encoding.ASCII.GetString(recBuff, 0, iRecCnt); |
(인자) byte[] bytes : 디코딩할 바이트 시퀀스를 포함하는 바이트 배열입니다. (인자) int byteIndex : 디코딩할 첫 번째 바이트의 인덱스입니다. (인자) int byteCount : 디코딩할 바이트 수입니다. (반환값) string : 지정된 바이트 시퀀스에 대한 디코딩 결과가 포함된 String입니다. |
2-3. 스레드를 종료하기 위한 검사
스레드(Thread)의 종료를 검사하는 경우에 AutoResetEvent를 사용하는 목적은 스레드(Thread)처리 루틴에서 플래그(Flag)값을 검사하는 시간이 상황에 따라 다르기 때문에 스레드 STOP을 처리하는 함수에서 스레드 함수가 완전히 종료될 때까지 스레드 STOP 처리를 함수의 Return 즉, Blocking 하도록 처리합니다.
하나 이상의 대기중인 스레드가 계속 진행될 수 있도록 이벤트의 상태를 신호로 설정합니다. public bool Set() |
|
---|---|
사용법 C# 예제 |
m_StopCompletionEvent.Set(); |
(인자) 없음 (반환값) bool : 동작이 성공하면 TRUE를 반환합니다. |
32 비트 부호있는 정수를 사용하여 시간 간격을 지정하고 대기 전에 동기화 도메인을 종료할지 여부를 지정하여 현재 WaitHandle이 신호를 수신할 때까지 현재 스레드를 차단합니다. public virtual bool WaitOne( int millisecondsTimeout, bool exitContext ) |
|
---|---|
사용법 C# 예제 |
// 대기 시간은 1초이며 동기화된 컨텍스트의 도메인을 갖고 오지 않음 m_StopCompletionEvent.WaitOne((int)1000, false) |
(인자) int millisecondsTimeout : 대기할 시간(밀리초)이거나, 무기한 대기할 경우 Timeout.Infinite(-1)입니다. (인자) bool exitContext : 대기 전에 컨텍스트에 대한 동기화 도메인을 종료하고(동기화된 컨텍스트에 있는 경우) 이 도메인을 다시 가져오려면 true이고, 그렇지 않으면 false입니다. (반환값) bool : 현재 인스턴스가 신호를 받으면 true이고, 그렇지 않으면 false입니다. |
3. 스레드에서 UI컨트롤 접근하기
스레드는 UI 컴포넌트(Component)에 직접 접근할 수 없으며, 이 경우 스레드는 델리게이트(Delegate)를 사용하여 UI에 접근이 가능합니다.
스레드에서 UI컨트롤 접근의 순서
① 스레드 내부에서 델리게이트 함수를 호출
② Invoke()에서 실제 UI에 접근하는 함수를 인자로 받아 호출
③ 실제 UI에 접근으로 진행됩니다.
// 스레드에서 UI 컨트롤을 접근하기 위한 delegate 함수 선언 및 정의
delegate void ListRecCallback(string strtext);
// 실제 UI에 접근하는 함수. 델리게이트(Delegate)의 인자로 사용
private void RecListAdd(string strRecData)
{
// 수신데이터를 ListBox에 추가하는 함수
private void DelegateRecListAdd(string strRecData)
{
this.Invoke(new ListRecCallback(RecListAdd), strRecData); // ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ②
// Client 연결 및 수신 스레드 함수
private void ComPortReadFunc()
{
string strRecData;
// 바이트 배열 수신 버퍼 선언
byte[] recBuff = new byte[serialPort1.ReadBufferSize];
m_Stop = false;
int i = 0;
try
{
{
{
{
recBuff[i] = Convert.ToByte(serialPort1.ReadByte());
catch (TimeoutException tExp)
{
if (iRecCnt != 0)
{
strRecData = Encoding.ASCII.GetString(recBuff, 0, iRecCnt);
// 수신된 데이터를 ListBox에 추가한다.
DelegateRecListAdd(strRecData); // ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ①
// 스레드를 종료하기 위한 검사
if (m_Stop == true)
{
Thread.Sleep(100);
// 스레드에 종료 대기 해제. 다음 문장 진행
m_StopCompletionEvent.Set();
catch (Exception exp)
{
{
// Open이 안되거나 생성이 안 된 경우 무조건 예외를 발생할수있다.
exp.ToString();
// 스레드에 종료 대기 해제. 다음 문장 진행
m_StopCompletionEvent.Set();
return;