3-1) CRC16 논리적 흐름을 그대로 구현

3-2) 최대한 간략화하여 구현

3-3) 사전 연산 테이블 활용하여 구현

 

매크로로 CRC16 구현을 위한 단계 정리

1) 사용할 배열 및 변환 Table 선언

2) HEX값을 2진수 수로 변환
 2-1) 2진수로 변환한 길이 (lenData)를 저장
 2-2) 값 뒤에 16비트 추가 : 0x1021 연산한 결과값(나머지) 표시를 위한 위치
3) 앞에서부터 1021을 lenData 길이만큼 XOR 반복 연산
 3-1) 앞자리가 1이면 XOR 연산
 3-2) 앞자리가 0이면 다음 값으로 
4) 연산결과 나머지를 HEX값으로 변환

 

1) 사용할 변수 및 변환 Table 선언

Dim crcValue
crcValue = Array("0", "0", "0", "1", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "0", "1")
    
Dim hexArray
hexArray = Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F")
    
Dim binArray
binArray = Array("0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111")

 ① crcValue : 0x1021을 2진수 형태의 배열로 정리

 ② hexArray : 16진수 변환을 위한 16진수 숫자 순서 배열

 ③ binArray : 16진수를 2진수로 변환했을 때의 값 배열

 

2) 입력된 HEX값을 2진수 수로 변환

For i = 1 To lenData
    binAssy = binAssy & binArray(Val("&H" & Mid(crcData, i, 1)))
Next i
lenData = Len(binAssy)
binAssy = binAssy & "0000000000000000"

 

 ① 입력값 (crcData)을 앞자리부터 하나씩 2진수 값으로 변환하고 변환한 결과를 binAssy에 누적 저장

 ex) crcData = 0x1A3B → binAssy = 0001101000111011

 ② 2진수로 변환한 데이터 길이값 저장 

 ex) binAssy = 0001101000111011 → lenData = 16

 ③ 변환결과 뒤에 16비트 추가 

 ex)  binAssy = 0001101000111011 binAssy = 00011010001110110000000000000000

 

3) 앞에서부터 1021을 lenData 길이만큼 XOR 반복 연산

For i = 1 To lenData
    If Left(binAssy, 1) = 1 Then
        binAssy = Mid(binAssy, 2)
        For j = 1 To 16
            binCalAssy = binCalAssy & (Mid(binAssy, j, 1) Xor crcValue(j - 1))
        Next j
        binAssy = binCalAssy & Mid(binAssy, 17)
        binCalAssy = ""
    Else
        binAssy = Mid(binAssy, 2)
    End If
Next i

 ① 데이터 길이만큼 반복 연산

 ② 앞자리가 1이면 앞자리 삭제하고 0x1021과 XOR 연산

 ③ 앞자리가 0이면 앞자리 삭제

 ④ 연산결과 최종적으로 16비트 길이의 나머지값이 남게 된다.

 

4) 연산결과를 HEX값으로 변환

For i = 1 To 4
    binTemp = Mid(binAssy, i * 4 - 3, 4)
    For j = 0 To 15
        If binTemp = binArray(j) Then
            crcResult = crcResult & hexArray(j)
        End If
    Next j
Next i

 ① 4비트 단위로 나눠서 HEX값으로 변환

 

 

 

전체 수식

'이진수 변환 후 비트 시프트
Function crcBin(crcData As String, Optional crcIntValue As String = "FFFF") 'CRC16-CITT 계산 함수
    Dim crcValue
    crcValue = Array("0", "0", "0", "1", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "0", "1")   'CRC 다항식 0x1021
    Dim hexArray
    hexArray = Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F")    '16진수 배열
    Dim binArray
    binArray = Array("0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111")    '2진수 배열
    
    Dim i As Integer, j As Integer     '반복 구문용 변수
    Dim lenData As Integer          'data 길이 변수
    Dim binData As String           '2진수 변환값
    Dim crcIntBin As String         '초기값 2진수 변환
    Dim binAssy As String           '2진수 출력값 누적 저장
    Dim binCalAssy As String        'XOR 연산 중간값
    Dim crcResult As String         '16진수 변환값
    
'입력 Data 정리
    crcData = Replace(crcData, " ", "")     '공백이 있으면 공백 제거
    crcData = UCase(crcData)                '입력값의 알파벳이 소문자면 대문자로 변경
    lenData = Len(crcData)                  'data 길이 확인
    
'초기값 및 입력값을 이진수로 변경
    For i = 1 To 4
        crcIntBin = crcIntBin & binArray(Val("&H" & Mid(crcIntValue, i, 1)))    '추출값을 16진수 숫자로 변환하여 해당 위치 Array값 추출하고 결과 누적
    Next i
    For i = 1 To lenData
        binAssy = binAssy & binArray(Val("&H" & Mid(crcData, i, 1)))      '추출값을 16진수 숫자로 변환하여 해당 위치 Array값 추출하고 결과 누적
    Next i
    binAssy = binAssy & "0000000000000000"     '뒤에 16비트 추가
    
'XOR 연산
'초기값 연산 : 0xffff
    For j = 1 To 16
        binCalAssy = binCalAssy & (Mid(binAssy, j, 1) Xor Mid(crcIntBin, j, 1))
    Next j
    binAssy = binCalAssy & Mid(binAssy, 17)
    binCalAssy = ""
       
'정규 연산 : 0x1021
    lenData = Len(binAssy) - 16 '연산 반복할 횟수
    For i = 1 To lenData
        If Left(binAssy, 1) = 1 Then            '앞자리가 1일 경우 xor 연산
            binAssy = Mid(binAssy, 2)           '앞자리 삭제
            For j = 1 To 16
                binCalAssy = binCalAssy & (Mid(binAssy, j, 1) Xor crcValue(j - 1))
            Next j
            binAssy = binCalAssy & Mid(binAssy, 17)
            binCalAssy = ""
        Else
            binAssy = Mid(binAssy, 2)
        End If
    Next i
    
'연산값을 16진수로 변경
    For i = 1 To 4
        binTemp = Mid(binAssy, i * 4 - 3, 4)
        For j = 0 To 15
            If binTemp = binArray(j) Then
                crcResult = crcResult & hexArray(j)
            End If
        Next j
    Next i
    
    crcBin = crcResult
      
End Function

 

* 위의 전체 수식에는 초기값 0xFFFF를 적용한 후 계산하도록 설정되어 있고,

 임의의 초기값을 입력할 수도 있도록 되어 있다.

 필요없다면 해당 부분 수식을 삭제하거나 Function의 FFFF를 0000으로 변환하면 된다.

 

 

 

 

결과적으로 특정한 HEX 데이터를 입력할 경우 체크섬 데이터가 나오게 되고,

만약 이미 동일한 CRC16 방식을 이용하여 계산한 체크섬이 뒤에 붙어 있는 데이터라면 결과는 0x0000이 나오게 된다.

 

 

실행한 예시

* 나는 해당 함수를 personal의 모듈에 넣어두고 사용하기에 함수를 사용할때 personal.xlsb!를 입력하였다.

 

0xF2 0xA5 0x9A 0x1F라는 임의의 데이터에 CRC16 함수를 적용한 결과값이 0xB4 0x77이고,

이를 원본 데이터 뒤에 붙여서 CRC16 함수를 적용하면 0x00 0x00으로

데이터 및 CRC16 함수에 문제가 없다는 것을 확인할 수 있다.

 

 

 

 

 

 

사실 짧은 데이터 하나 두개 정도를 체크섬에 문제가 없는지 확인하는 정도라면 현재의 수식으로도 충분하다.

연산 시간이 쓸데없이 길어지기는 하지만 짧은 주기로 시리얼 통신을 하는 것도 아니니까.

 

하지만 처음에 봤던 c언어로 된 간결한 코드를 흉내라도 내고 싶다는 생각에

좀더 간결하게 작성할 수 있는 방안을 찾아보기로 하였다.

 

Posted by 지마군
,

순환 중복 검사 CRC(cyclic redundancy check)

주고받는 데이터에 통신과정에서 발생한 오류가 없는지  검증하는 방법

일정한 바이트 형태의 데이터를 주고받는 과정에서

발신자는 사전에 지정한 값을 연산(XOR)한 나머지 값을 송신하는 데이터 마지막에 붙이고

수신자는 수신한 데이터에 동일한 연산을 거쳐서 결과값이 동일한지 (연산결과가 0인지) 확인.

 

1. CRC16-CITT

 → 체크섬에 일반적으로 쓰이는 연산 방식 중 하나

1) 발신자는 데이터에 다항식 0x1021을 XOR 연산한 나머지 값을 데이터 마지막에 2byte 형태로 추가.

2) 수신자는 수신한 데이터에 다항식 0x1021을 XOR 연산하여 나머지 값이 0이 나오는지 확인.

* XOR : 배타적 논리합. 비교하는 두 값이 같으면 0, 다르면 1

** 보안을 위해 다항식 연산 전에 발신자와 수신자가 사전에 정한 초기값으로

    데이터를 1차적으로 변환한 후 연산하기도 한다.

 

2. 다항식 0x1021의 의미

1) CRC16이라는 이름과 같이 16비트(2바이트)의 데이터를 사용

2) 다항식으로 표현 시 : x^16 + x^12 + x^5 + 1

3) 이진법으로 표시 시 : 1 0001 0000 0010 0001 

  → 이진법으로 표시하면 17비트가 되는데, 앞자리 1은 각단계에서 연산 확인 용으로만 쓰여서 생략한 것인지?

 

3. 연산 과정

1) 체크섬을 표시할 데이터 준비

 e.g. 0xF4 0x55 = 11110100 01010101

 *  예시는 2byte지만 적용 가능한 데이터 길이 제한 없음

 

2) 데이터 뒤에 0을 16자리 추가

  → 0xF4 0x55 0x00 0x00

  → 11110100 01010101 00000000 00000000

 

3) 데이터 앞자리부터 다항식 0x1021을 XOR 연산

4) 연산 결과 (나머지 값)

 → 1101 0101 0101 0101 = 0xD5 0x55

 

5) 발신할 데이터 뒤에 나머지 값을 붙인다.

 → 원본 : 0xF4 0x55

 → 발신 : 0xF4 0x55 0xD5 0x55

 

6) 수신자는 수신한 데이터로 동일한 연산을 시행한다.

  → 수신 데이터 : 0xF4 0x55 0xD5 0x55 = 11110100 01010101 11010101 01010101

 

7) 연산 결과

 → 0x00 0x00 = 00000000 00000000

 → 나머지값을 발신자가 채워서 보냈기 때문에 연산한 결과값이 0x00 0x00이 된다.

 

8) 결과값이 0x00 0x00이 아니라면 통신과정에서 문제가 발생했다는 의미

 

 

 

4. 목표

 1) CRC16의 연산과정을 엑셀 매크로 함수 형태로 구현하기

 2) 매크로 연산 효율 최적화

 사실, 이미 구현은 성공했는데, 그 과정에서 익힌 지식을 잊지 않기 위해 순차적으로 정리할 예정

Posted by 지마군
,

rss