6월1일 API(bitmapviewer) RFID(

2010. 6. 1. 15:452010년/6월



비트맵 파일을 열어볼수있는 API로  Bitmap Viewer을 만들어 보았다.
기본적으로 비트맵의 기본정보를 오른쪽 상단에 적어주고 왼쪽 상단에 그림을 띄우게 했다.

사실 우리가 윈도우에 익숙해진 아니 길들어져있기때문에 모든 파일을 형식을 잘 모른다.
윈도우에 생성되는 파일은 대부분 PE이다 그리고 비트맵 파일 같은경우도 마찬가지이다.
그래서 파일의 특징은 처음 거의 54바이트까지는 비트맵에 대한 정보가 들어있다. 예를 들어 첫 2바이트는 Magic Number라해서 비트맵이라는것을 알려주기위해서 BM이 들어가있고(아스키코드로  66 77이다)3 번째 바이트에 파일의 크기가  가로크기 세로크기 압축유무 비트수라든지 실질적인 비트맵의 시작주소까지 들어가있다.

비트맵의 시작주소를 알게 되면 그다음부터는 하나의 픽셀로 저장된다.  54번째부터 55 56 번째 바이트까지 색의 3요소인 RGB  거꾸로  54번째 Blue  55번째 Green 56번째 Red로 3바이트씩 픽셀하나와 색상이 나와이있다. 

그래서 한픽셋을 하나씩 대화상자에 찍어줘야하고 위치는 위에서 아래가 아닌  
왼쪽 아래부터   오른쪽 위에 까지  찍어나간다. 

-Default-



-WorkSpace-


-Resource View-

 
이제소스 코드를 하나씩 보자
=Main.cpp=

#include <windows.h>//API함수들의 원형과 사용하는 상수들이 정의 되어 있다.
#include "MsgProc.h"
//전역변수 선언
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LPCTSTR lpszClass = TEXT("First"); //const char *  //lpszClass : 윈도우 클래스를 정의하는데 사용

HINSTANCE g_hInst; //전역변수
typedef struct DMESSAGEMAP //대화상자에 정보를 넘겨주기위한 구조체 
{
  UINT iMessage;
  BOOL (*lpfnMsgProc)(HWND,WPARAM,LPARAM);
}DMESSAGEMAP;
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lPSZCmdParam, int nCmdShow)
{//엔트리포인트는 main()이 아니라 WinMain(),이다.
//윈도우를 만들고 화면에 표시하기만 할 뿐, 대부분의 일은 아래의 함수에서 이루어진다.
//WinMain에서 하는 가장 중요한 일은 메인 윈도우를 만드는 일이다.
//메인윈도우를 대신해서 기본적으로 대화상자를 메인으로 잡아서 생성했다. 
  DialogBox(g_hInst,MAKEINTRESOURCE(IDD_DIALOG1),NULL,AboutDlgProc);
}  

//사용자와 시스템이 보내오는 메시지를 처리하는 함수. (운영체제에 의해 호출)
BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT iMessage, WPARAM wParam,LPARAM lParam)
{
  int i;
  static DMESSAGEMAP dMessageMaps[]=
  {
    {WM_INITDIALOG,OnInitDialog},  //iMessage를 처리하기위한 대화상자에 
    {WM_COMMAND,OnDlgCommand},   //INIDIALOG,COMMAND, PAINT를 각각함수로 구현 
    {WM_PAINT,OnPaintDlg}
    
  };
  for(i=0;i<sizeof(dMessageMaps)/sizeof(dMessageMaps[0]);++i)
  {  
    if(dMessageMaps[i].iMessage==iMessage)
      return (*dMessageMaps[i].lpfnMsgProc)(hDlg,wParam,lParam);
  }
  return FALSE;
  
}

함수와윈도우 생성에 대한 간단한 내용만 있다  실질적인 내용은  MsgProc.cpp에 있다.

=MsgProc.h=
#ifndef __MSGPROC_H_ // MSGPROC.h 파일이 있는지 검사 
#define __MSGPROC_H_

#include <windows.h> //API함수들의 원형과 사용하는 상수들이 정의 되어 있다.
#include "resource.h" //만들어지 resurce대한 정의
//main,cpp에서 바꾼 WM_PAINT WM_COMMAND  대한 함수의 정의
BOOL CALLBACK AboutDlgProc(HWND,UINT,WPARAM,LPARAM); 
BOOL OnInitDialog(HWND, WPARAM, LPARAM);
BOOL OnDlgCommand(HWND, WPARAM, LPARAM);
BOOL OnPaintDlg(HWND , WPARAM , LPARAM );
extern HINSTANCE g_hInst;  //중복선언을 피하기 위해 선언
#endif

=resource.h=
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.

// Used by rc.rc
//
#define IDD_DIALOG1                     101  //대화상자
#define IDR_MENU1                       102   //메뉴
#define IDC_EDIT1                       1001  
#define IDC_EDIT2                       1002
#define IDC_EDIT3                       1003
#define IDC_EDIT4                       1004
#define IDC_EDIT5                       1005
#define IDC_EDIT6                       1006
#define IDC_EDIT7                       1007
#define IDC_EDIT8                       1008
#define IDC_EDIT9                       1009
#define IDC_EDIT10                      1010
#define IDC_EDIT11                      1011
#define IDC_EDIT12                      1012
#define IDC_EDIT13                      1013
#define IDC_NEW                         1014
#define IDC_90                          1015
#define IDC_OK                          1016
#define IDC_180                         1017
#define IDC_BC                          1018
#define ID_40001                        40001
#define ID_40002                        40002
#define ID_40003                        40003
#define ID_40004                        40004
#define ID_40005                        40005
#define ID_40006                        40006

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40007
#define _APS_NEXT_CONTROL_VALUE         1015
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

resource같은경우는 자동으로 만들어주기때문에 추가되는내용이 없다.

=MsgProc=

#include "MsgProc.h"
#define BSIZE 20000000 //사진 파일 크기 
char buf[BSIZE+1];  //비트맵 파일들어갈 장소
HANDLE hFile;    //open할 파일을 넣을 핸들
TCHAR tt[128];    //간단한 BM을 찍기위한 배열
TCHAR *bb={TEXT("ALL")}; //used color값이0이면 ALL을 찍기위함
int *result;  //4바이트 단위로 찍기위함 int포인터
short *sRe;    //2바이트 단위로 찍기위한 short포인터 
DWORD dwRead;  //4바이트 크기
HDC hdc;    //대화상자 DC
HDC MemDC;    //메모리DC

PAINTSTRUCT ps;  //WM_PAINT로 저장할 구조체 
int iwidth;    // 사진의 가로길이
int iheight;  // 사진의 세로길이
int abc=0;    // 사진 회전에 필요한 간단한 변수
int BLCL=0;    // 사진 흑백 칼라 지정할 간단한 변수
//int basu=1;
OPENFILENAME OFN; //파일을 열기위한 구조체 OFN선언
TCHAR lpstrFile[MAX_PATH]; //충반한 길이의 버퍼제공하기위해 입력받는 파일명을 MAX_PATH(260)크기만큼선언
void vopen(HWND hDlg)  //파일을 열기위한 함수 생성 
{


  memset(&OFN,0,sizeof(OPENFILENAME));//처음에 디포르로놓기위하여 0으로 메모리셋을함
  OFN.lStructSize = sizeof(OPENFILENAME); //구조체의 크기를 지정
  OFN.hwndOwner = hDlg;//윈도우 대화상자핸들들을 지정
  OFN.lpstrFilter = TEXT("모든 파일(*.*)\0*.*\0"); //필터들이며 open받을 파일의 형식을 지정
  OFN.lpstrFile = lpstrFile;//파일이름 에디트에 처음 나타낼 문자열지정..최종적으로 선택한 파일의전체경로반환
  OFN.nMaxFile = MAX_PATH; //lpstrFile의 길이를 지정 최소는 256
  if(GetOpenFileName(&OFN) != 0//파일을 열기 성공시에 아래 실행
  {
    hFile = CreateFile(OFN.lpstrFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    //OPENFILENAME에 저장되어있는 hFile핸들에 넣어준다.
  } 
  if(INVALID_HANDLE_VALUE !=hFile)
  {
    ReadFile(hFile,buf,BSIZE,&dwRead,NULL); //hFile핸들에 들어있는비트맵정보를 
    //BSIZE크기만큼 buf에 넣는다. 
    wsprintf(tt,TEXT("%c%c"),buf[0],buf[1]); //일단 buf[0]buf[1]에 내용을 tt에 넣는다. BM이들어간다.
  }
  else  //hFile핸들 값이 실패시 에러 메시지 출력...
  {
    wsprintf(tt,TEXT("Error"));
  }
  SetWindowText(hDlg,tt);

  //************************************************************************
  //***************************실질적인 출력부분****************************
  //************************************************************************

  SetDlgItemText(hDlg,IDC_EDIT1,tt); //Magic 넘버 출력 비트맵이기때문에 BM이 찍힌다. 

  result=(int *)(&buf[2]);    
  //파일 크기 출력
  SetDlgItemInt(hDlg,IDC_EDIT2,(*result),FALSE);
  //이미지 가로크기는 int *로 넣고 IDC_EDIT2출력
  result=(int *)(&buf[18]);    
  iwidth=(*result);        //iwidth에 대입
  SetDlgItemInt(hDlg,IDC_EDIT3,(*result),FALSE);
  //이미지 세로크기 IDC_EDIT3에 출력
  result=(int *)(&buf[22]);    
  iheight=(*result);        //iheight에대입
  SetDlgItemInt(hDlg,IDC_EDIT4,(*result),FALSE);

  //비트맵의플레인수 출력(항상1)
  sRe=(short *)(&buf[26]);
  SetDlgItemInt(hDlg,IDC_EDIT5,*(sRe),FALSE);
  //비트 수 출력(24)
  sRe=(short *)(&buf[28]);
  SetDlgItemInt(hDlg,IDC_EDIT6,*(sRe),FALSE);
  //압축 유무 출력
  result=(int *)(&buf[30]);
  SetDlgItemInt(hDlg,IDC_EDIT7,*(result),FALSE);
  //비트맵 크기 출력
  result=(int *)(&buf[34]);
  SetDlgItemInt(hDlg,IDC_EDIT8,*(result),FALSE);
  //미터당 가로 해상도
  result=(int *)(&buf[38]);
  SetDlgItemInt(hDlg,IDC_EDIT9,*(result),FALSE);
  //미터당 세로 해상도
  result=(int *)(&buf[42]);
  SetDlgItemInt(hDlg,IDC_EDIT10,*(result),FALSE);

  //used color와 중요하게 색상 사용유무  없으면 ALL 출력
  result=(int *)(&buf[46]);
  if(0!=(*result))
  {
    SetDlgItemInt(hDlg,IDC_EDIT11,*(result),FALSE);
  }
  else
  {
    SetDlgItemText(hDlg,IDC_EDIT11,bb);
  }
  result=(int *)(&buf[50]);
  if(0!=(*result))
  {
    SetDlgItemInt(hDlg,IDC_EDIT12,*(result),FALSE);
  }
  else
  {
    SetDlgItemText(hDlg,IDC_EDIT12,bb);
  }
  // 실질적 데이터 시작주소 (보통 54)
  result=(int *)(&buf[10]);
  SetDlgItemInt(hDlg,IDC_EDIT13,*(result),FALSE);

  BLCL=0//초기값으로 칼라를 0을 줌
  InvalidateRect(hDlg,NULL,TRUE); //WM_PAINT로 전달 
  CloseHandle(hFile); //핸들종료 
}
void vprint()
{
  int icnt;  
  int cout;  
  int ib=54;    //실질적인 데이터 시작값
  if(abc%4==0//90도회전하기전에 출력
  {
    for(icnt=iheight;0<icnt;--icnt)
    {
      for(cout=0;iwidth>cout;++cout)
      {
        //픽셀한개씩 메모리 DC화면에 X좌표는 cout Y좌표는 icnt 색상은 buf안에 54,55,56d을사용
        SetPixel(MemDC,cout,icnt,RGB(buf[ib+2],buf[ib+1],buf[ib]));
        //buf 3증가해서 다음색상을 지정 
        ib=ib+3;

      }
    }
  }
  else if(abc%4==1//90도회전버튼 한번 눌렸을때
  {
    for(icnt=iheight;0<icnt;--icnt)
    {
      for(cout=0;iwidth>cout;++cout)
      {  //X좌표와 Y좌표만 변경
        SetPixel(MemDC,icnt,cout,RGB(buf[ib+2],buf[ib+1],buf[ib]));
        ib=ib+3;
      }
    }

  }
  else if(abc%4==2//90도회전버튼 두번 눌렷을때
  {
    for(icnt=0;iheight>icnt;++icnt)  // 화면이 거꾸로이기때문에 가로길이 세로길이 for변경
    {
      for(cout=0;iwidth>cout;++cout)
      {
        SetPixel(MemDC,cout,icnt,RGB(buf[ib+2],buf[ib+1],buf[ib]));
        ib=ib+3;
      }
    }


  }
  else if(abc%4==3//90도 회전 버튼 세번 눌렀을대 
  {

    for(icnt=0;iheight>icnt;++icnt)  //세로 크기만큼 가로크기 만큼 변경하고 
    {
      for(cout=iwidth;0<cout;--cout)
      {  
        //출력픽셀로 가로 세로 변경
        SetPixel(MemDC,icnt,cout,RGB(buf[ib+2],buf[ib+1],buf[ib]));
        ib=ib+3;
      }
    }
  }

}
void hprint() //가로 길이가 홀수 일때와 짝수일때에 따라 픽셀 수조정하는 비트맵
{        //위 vprint()와 나머지는 같음
  int icnt;
  int cout;  
  int ib=54;
  if(abc%4==0)
  {

    for(icnt=iheight;0<icnt;--icnt)
    {
      for(cout=0;iwidth>=cout;++cout)
      {
        SetPixel(MemDC,cout,icnt,RGB(buf[ib+2],buf[ib+1],buf[ib]));
        ib=ib+3;
      }
    }
  }
  else if(abc%4==1)
  {
    for(icnt=iheight;0<icnt;--icnt)
    {
      for(cout=0;iwidth>=cout;++cout)
      {
        SetPixel(MemDC,icnt,cout,RGB(buf[ib+2],buf[ib+1],buf[ib]));
        ib=ib+3;
      }
    }

  }
  else if(abc%4==2)
  {
    for(icnt=0;iheight>icnt;++icnt)
    {
      for(cout=0;iwidth>=cout;++cout)
      {
        SetPixel(MemDC,cout,icnt,RGB(buf[ib+2],buf[ib+1],buf[ib]));
        ib=ib+3;
      }
    }


  }
  else if(abc%4==3)
  {

    for(icnt=0;iheight>icnt;++icnt)
    {
      for(cout=iwidth;0<=cout;--cout)
      {

        SetPixel(MemDC,icnt,cout,RGB(buf[ib+2],buf[ib+1],buf[ib]));
        ib=ib+3;
      }
    }
  }

}
void blackC() //흑백모드 비트맵 이미지를 처리 지정
{

  int icnt;
  int cout;  
  int ib=54;
  if(abc%4==0)
  {

    for(icnt=iheight;0<icnt;--icnt)
    {
      for(cout=0;iwidth>=cout;++cout)
      {
        //기본적으로 RGB의 값은  다같아버리면 회색으로 출력되므로 중간값을기준으로 다같게 지정
        SetPixel(MemDC,cout,icnt,RGB(buf[ib+1],buf[ib+1],buf[ib+1]));
        ib=ib+3;
      }
    }
  }
  else if(abc%4==1)
  {
    for(icnt=iheight;0<icnt;--icnt)
    {
      for(cout=0;iwidth>=cout;++cout)
      {

        SetPixel(MemDC,icnt,cout,RGB(buf[ib+1],buf[ib+1],buf[ib+1]));
        ib=ib+3;
      }
    }

  }
  else if(abc%4==2)
  {
    for(icnt=0;iheight>icnt;++icnt)
    {
      for(cout=0;iwidth>=cout;++cout)
      {
        SetPixel(MemDC,cout,icnt,RGB(buf[ib+1],buf[ib+1],buf[ib+1]));
        ib=ib+3;
      }
    }


  }
  else if(abc%4==3)
  {

    for(icnt=0;iheight>icnt;++icnt)
    {
      for(cout=iwidth;0<=cout;--cout)
      {

        SetPixel(MemDC,icnt,cout,RGB(buf[ib+1],buf[ib+1],buf[ib+1]));
        ib=ib+3;
      }
    }
  }


}
void blackSC()
{

  int icnt;
  int cout;  
  int ib=54;
  if(abc%4==0)
  {


    for(icnt=iheight;0<icnt;--icnt)
    {
      for(cout=0;iwidth>cout;++cout)
      {

        SetPixel(MemDC,cout,icnt,RGB(buf[ib+1],buf[ib+1],buf[ib+1]));
        ib=ib+3;
      }
    }
  }
  else if(abc%4==1)
  {
    for(icnt=iheight;0<icnt;--icnt)
    {
      for(cout=0;iwidth>cout;++cout)
      {
        SetPixel(MemDC,icnt,cout,RGB(buf[ib+1],buf[ib+1],buf[ib+1]));
        ib=ib+3;
      }
    }

  }
  else if(abc%4==2)
  {
    for(icnt=0;iheight>icnt;++icnt)
    {
      for(cout=0;iwidth>cout;++cout)
      {


        SetPixel(MemDC,cout,icnt,RGB(buf[ib+1],buf[ib+1],buf[ib+1]));
        ib=ib+3;
      }
    }


  }
  else if(abc%4==3)
  {

    for(icnt=0;iheight>icnt;++icnt)
    {
      for(cout=iwidth;0<cout;--cout)
      {

        SetPixel(MemDC,icnt,cout,RGB(buf[ib+1],buf[ib+1],buf[ib+1]));
        ib=ib+3;
      }
    }
  }


}
BOOL OnInitDialog(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
  return TRUE;

}
BOOL OnDlgCommand(HWND hDlg, WPARAM wParam, LPARAM lParam)
{  
  //버튼의 클릭과 메뉴 클릭 지정  함수 
  switch(LOWORD(wParam))
  {

  case ID_40001:    //메뉴의 파일열기 선택시 vopen()함수 호출
    vopen(hDlg);
    break;
  case IDCANCEL://대화상자 X버튼 클릭시 메시지띄우고 종료 
    if(MessageBox(hDlg,TEXT("종료하시겠습니까"),TEXT("알림"),MB_OKCANCEL)==IDOK)
    {
      EndDialog(hDlg,IDCANCEL);
    }
    else
    {
      break;
    }
  case IDC_NEW: //새로고침 버튼 클릭시 회전을 0도로 변경
    abc=0;
    InvalidateRect(hDlg,NULL,TRUE);
    break;
  case IDC_90:  //한번 클릭시마다 abc값을 변경해서 이미지 회전 변경
    abc++;
    InvalidateRect(hDlg,NULL,TRUE);
    break;
  case IDC_180:  //180도버튼은 abc값이 2번띄는거랑 같기에 지정
    abc=abc+2;
    InvalidateRect(hDlg,NULL,TRUE);
    break;
  case IDC_BC:  //흑백과 칼라를 지정 BLCD%2가 0이면 칼라 1이면 흑백
    BLCL++;
    InvalidateRect(hDlg,NULL,TRUE);
    break;
  case ID_40003: //메뉴의 닫기 버튼 클릭시 메시지박스 실행
    if(MessageBox(hDlg,TEXT("종료하시겠습니까"),TEXT("알림"),MB_OKCANCEL)==IDOK)
    {
      EndDialog(hDlg,IDOK);
    }
    else
    {
      break;
    }

  }
  return FALSE;
}
BOOL OnPaintDlg(HWND hDlg, WPARAM wParam, LPARAM lParam)
{

  hdc=BeginPaint(hDlg,&ps); //PAINTSTRUCT 내용과 hDlg값의 화면을 hdc로 지정
  HBITMAP MyBitmap,OldBitmap; //비트맵을 보여줄 메모리DC와 화면DC을 설정하기위해 설정
  MemDC=CreateCompatibleDC(hdc);  //화면에 DC을 그대로 MemDC로 메모리에 똑같이 복사.
  if(abc%2==0)  //화면이 0도나 180도일경우 화면DC을 메모리DC에제대로 복사
  {
    MyBitmap=CreateCompatibleBitmap(hdc,iwidth,iheight); 
  }else if(abc%2==1//화면이 90도나 270도일경우 화면DC의 가로와 세로길이 변경후 복사
  {
    MyBitmap=CreateCompatibleBitmap(hdc,iheight,iwidth);
  }
  OldBitmap=(HBITMAP)SelectObject(MemDC,MyBitmap);  //OldBitmap에 기존DC 복사

  if(iwidth%2==0//가로길이가 짝수에따라서 출력형태 변경
  {
    if(BLCL%2==1)
    {
      blackSC();
    }
    else
    {
      vprint();
    }
  }
  else
  {
    if(BLCL%2==1)
    {
      blackC();
    }
    else
    {
      hprint();
    }
  }    
  if(abc%2==0//회전후 출력시 0도 180도일경우 그대로 출력
  {
    BitBlt(hdc,70,70,iwidth,iheight,MemDC,0,0,SRCCOPY);
  }
  else if(abc%2==1//회전후 출력이 80도나 270도일경우 가로 세로 변경해서 출력
  {
    BitBlt(hdc,70,70,iheight,iwidth,MemDC,0,0,SRCCOPY);
  }


  SelectObject(MemDC,OldBitmap); //메모리 MyBitmap을 OldBitmap으로 변경
  DeleteObject(MyBitmap); //메모리 오브젝트 삭제
  DeleteDC(MemDC);  //메모리 DC 삭제
  EndPaint(hDlg,&ps); //PAINT 종료
  return 0;
}

================================================================================
================================================================================

RFID
Radio Frequency Identification
라디오 주파수로 인해  인식하는 것이라것이라고 생각하면 쉽다.


전송은
address 모드
고유의 UID가있는데  그것이 맞을때 실행이 하게된다.
예를 들어서 교통카드를  찍을때. 교통카드는 찍혀지만
다른 카드는 실행이 안된다.

nonaddress 모드
교통카드가 2개 있을때 이 교통카드 2개는 UID를 구분할수 없다.
그래서 이럴경우 nonaddress모드이다.

seleted모드
처음 한번만 UID를 비교한다.

일단 기본적으로 RFID을 하기위해서는

여러가지 설정이 필요한데  특히.. 속도와 데이타비트 스탑비트 패러디 등을 맞춰야한다




u_char caString[] = { 
    0X0D, 0X00, 0X71, 0X00, 0X3F,
    0X00, 0X00, 0X00, 0X0A, 0X00,
    0X00, 0XB2, 0X58
    };
라는  데이터를 전송했다.

그럼 저 13개는 무슨뜻일까?...

이는 메뉴얼을 보고 알아봐야한다.



1.0X0D는 전체 길이다. 13개라서  13이다

2.0x00 com-address인데 이것은 무저껀 0이다.

3. CONTROL-BYTE로 어떤것이 동작할지 보는것이다. (P42쪽이후)

0x71을 선택했다 그래서 메뉴얼을 보게 되면


대충 LED와 beeper을 display를 한다.


4번째와 5번째가 OS이고 6번째와 7번째가 OSF 그리고 8번째 9 번째가 OS-Time이다

OS에 대해서 보자
일단 기본적으로 4번째와 5번째 비트에서 하위 비트만 사용한다.
그리고 하위에 5,4가 beeper에 대한 내용이고 3,2가 LED red   1,0 LED Green을 나타내며
그 안에 00 ,01,10,11 에따라서 동작이 방법이 틀리다 깜박이는 경우는 FLASH라서   00 11 11 11 이 들어간다. 그래서 0x3F가 된다.


OSF는 간단한 Hz에 대한내용이다.


이는 일단 8Hz를 사용했다 그래서 전부 00을 넣어서 상위는  0x00이고  하위도 0x00이다 물론 Hz를 바꾸려면 다른값을 각각 지정해서 넣으면 된다.

OS-Time 은
언제까지 동작하나 이다. 그래서 넣는값에 따라서 틀리다.


0x0001을 넣게 되면...  0.1초이다. 그래서 10을 넣으면 1초 가 된다. 그래서 0x0A가 들어가게 된다.

CRC는 기본적으로 오류 검사합이라고.. 전송된 데이터가 제대로 보냈는지 검사하는 경우이다. 

자세한 내용은 내일~!