반응형
/*

CIMAGE 안에 있는 JPEG 라이브러리를 이용해서

DC를 캡쳐한것을 JPEG, BMP로 저장하는 소스코드

긁어다 쓰는것도 엄청 복잡하구만 ㅠ.ㅠ



*JPEG 을 컴파일할때 NOT USING MFC 항목을 체크할것

*BMP는 MSDN을 참조

*JPG는 CODEGURU  참조



*ppt 를 전체화면으로 뛰웠을때 파워포인트를 캡쳐해서

저장하는 프로그램이라고 하면 될것임;;

*/

#include <windows.h>
#include <stdio.h>

extern "C" {
#include "jpeglib.h"
}

#include <setjmp.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

struct ima_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
 jmp_buf setjmp_buffer; /* for return to caller */
};

typedef ima_error_mgr *ima_error_ptr;

void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC) ;
void errhandler(LPTSTR pszFile, HWND hwnd);

////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//Header file code
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
HANDLE DDBToDIB(HBITMAP bitmap, DWORD dwCompression);

//In the relevant header file (up near the top):
BOOL DibToSamps(HANDLE          hDib,
               int                         nSampsPerRow,
               struct jpeg_compress_struct cinfo,
               JSAMPARRAY                  jsmpPixels,
               LPTSTR                    pcsMsg);

BOOL JpegFromDib( HANDLE    hDib,     //Handle to DIB
                int        nQuality, //JPEG quality (0-100)
                LPTSTR    csJpeg,   //Pathname to target jpeg file
                LPTSTR   pcsMsg);  //Error msg to return

BOOL BuildSamps(HANDLE                      hDib,
               int                         nSampsPerRow,
               struct jpeg_compress_struct cinfo,
               JSAMPARRAY                  jsmpArray,
               LPTSTR                    pcsMsg);

RGBQUAD QuadFromWord(WORD b16);

////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//Source file code
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
//This function takes the contents of a DIB
//and turns it into a JPEG file.
//
//The DIB may be monochrome, 16-color, 256-color, or 24-bit color.
//
//Any functions or data items beginning with "jpeg_" belong to jpeg.lib,
//and are not included here.
//
//The function assumes 3 color components per pixel.
/////////////////////////////////////////////////////////////////////////////
BOOL JpegFromDib(HANDLE hDib,     //Handle to DIB
                int        nQuality, //JPEG quality (0-100)
                LPTSTR    csJpeg,   //Pathname to jpeg file
                LPTSTR   pcsMsg)   //Error msg to return
{
   //Basic sanity checks...
   if (nQuality < 0 || nQuality > 100 ||
       hDib   == NULL ||
       pcsMsg == NULL ||
       csJpeg == "")
   {
       if (pcsMsg != NULL)
           pcsMsg = "Invalid input data";

       return FALSE;
   }

   pcsMsg = "";

   LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)hDib;

   byte *buf2 = 0;

   //Use libjpeg functions to write scanlines to disk in JPEG format
   struct jpeg_compress_struct cinfo;
   struct jpeg_error_mgr       jerr;

   FILE*      pOutFile;     //Target file
   int        nSampsPerRow; //Physical row width in image buffer
   JSAMPARRAY jsmpArray;    //Pixel RGB buffer for JPEG file

   cinfo.err = jpeg_std_error(&jerr); //Use default error handling (ugly!)

   jpeg_create_compress(&cinfo);

   if ((pOutFile = fopen(csJpeg, "wb")) == NULL)
   {
       pcsMsg = "Cannot open ";
//  pcsMsg += csJpeg;
       jpeg_destroy_compress(&cinfo);
       return FALSE;
   }

   jpeg_stdio_dest(&cinfo, pOutFile);

   cinfo.image_width      = lpbi->biWidth;  //Image width and height, in pixels
   cinfo.image_height     = lpbi->biHeight;
   cinfo.input_components = 3;              //Color components per pixel
                                            //(RGB_PIXELSIZE - see jmorecfg.h)
   cinfo.in_color_space   = JCS_RGB;       //Colorspace of input image

   jpeg_set_defaults(&cinfo);

   jpeg_set_quality(&cinfo,
                    nQuality, //Quality: 0-100 scale
                    TRUE);    //Limit to baseline-JPEG values

   jpeg_start_compress(&cinfo, TRUE);

   //JSAMPLEs per row in output buffer
   nSampsPerRow = cinfo.image_width * cinfo.input_components;

   //Allocate array of pixel RGB values
   jsmpArray = (*cinfo.mem->alloc_sarray)
               ((j_common_ptr) &cinfo,
                JPOOL_IMAGE,
                nSampsPerRow,
                cinfo.image_height);

   if (DibToSamps(hDib,
                  nSampsPerRow,
                  cinfo,
                  jsmpArray,
                  pcsMsg))
   {
       //Write the array of scan lines to the JPEG file
       (void)jpeg_write_scanlines(&cinfo,
                                  jsmpArray,
                                  cinfo.image_height);
   }

   jpeg_finish_compress(&cinfo); //Always finish

   fclose(pOutFile);

   jpeg_destroy_compress(&cinfo); //Free resources

   if (pcsMsg != "")
       return FALSE;

   else
       return TRUE;
}

////////////////////////////////////////////////////////////////
//This function fills a jsmpArray with the RGB values
//for the CBitmap.
//
//It has been improved to handle all legal bitmap formats.
//
//A jsmpArray is a big array of RGB values, 3 bytes per value.
//
//Note that rows of pixels are processed bottom to top:
//The data in the jsamp array must be arranged top to bottom.
////////////////////////////////////////////////////////////////
BOOL DibToSamps(HANDLE                      hDib,
               int                         nSampsPerRow,
               struct jpeg_compress_struct cinfo,
               JSAMPARRAY                  jsmpPixels,
               LPTSTR                    pcsMsg)
{
  //Sanity...
  if (hDib == NULL    ||
    nSampsPerRow <= 0 || pcsMsg == NULL)
  {
    if (pcsMsg !=NULL)
       pcsMsg="Invalid input data";
    return FALSE;
  }

  int r=0, p=0, q=0, b=0, n=0,
      nUnused=0, nBytesWide=0, nUsed=0, nLastBits=0, nLastNibs=0, nCTEntries=0,
      nRow=0, nByte=0, nPixel=0;
  BYTE bytCTEnt=0;
  LPBITMAPINFOHEADER pbBmHdr= (LPBITMAPINFOHEADER)hDib; //The bit count tells you the format of the bitmap: //Decide how many entries will be in the color table (if any)

  switch (pbBmHdr->biBitCount)
  {
     case 1:
        nCTEntries = 2;   //Monochrome
        break;

     case 4:
        nCTEntries = 16;  //16-color
        break;

     case 8:
        nCTEntries = 256; //256-color
        break;

     case 16:
     case 24:
     case 32:
        nCTEntries = 0;   //No color table needed
        break;

     default:
        pcsMsg = "Invalid bitmap bit count";
        return FALSE; //Unsupported format
  }

  //Point to the color table and pixels
  DWORD     dwCTab = (DWORD)pbBmHdr + pbBmHdr->biSize;
  LPRGBQUAD pCTab  = (LPRGBQUAD)(dwCTab);
  LPSTR     lpBits = (LPSTR)pbBmHdr +
                     (WORD)pbBmHdr->biSize +
                     (WORD)(nCTEntries * sizeof(RGBQUAD));

  //Different formats for the image bits
  LPBYTE   lpPixels = (LPBYTE)  lpBits;
  RGBQUAD* pRgbQs   = (RGBQUAD*)lpBits;
  WORD*    wPixels  = (WORD*)   lpBits;

  //Set up the jsamps according to the bitmap's format.
  //Note that rows are processed bottom to top, because
  //that's how bitmaps are created.
  switch (pbBmHdr->biBitCount)
  {
     case 1:
        nUsed      = (pbBmHdr->biWidth + 7) / 8;
        nUnused    = (((nUsed + 3) / 4) * 4) - nUsed;
        nBytesWide = nUsed + nUnused;
        nLastBits  = 8 - ((nUsed * 8) - pbBmHdr->biWidth);

        for (r=0; r < pbBmHdr->biHeight; r++)
        {
           for (p=0,q=0; p < nUsed; p++)
           {
              nRow=(pbBmHdr->biHeight-r-1) * nBytesWide;
              nByte =  nRow + p;

              int nBUsed = (p <(nUsed -1)) ? 8 : nLastBits;
     
     for(b=0; b < nBUsed;b++)
              {
                 bytCTEnt = lpPixels[nByte] << b;
                 bytCTEnt = bytCTEnt >> 7;

                 jsmpPixels[r][q+0] = pCTab[bytCTEnt].rgbRed;
                 jsmpPixels[r][q+1] = pCTab[bytCTEnt].rgbGreen;
                 jsmpPixels[r][q+2] = pCTab[bytCTEnt].rgbBlue;

                 q += 3;
              }
           }
        }
        break;

     case 4:
        nUsed      = (pbBmHdr->biWidth + 1) / 2;
        nUnused    = (((nUsed + 3) / 4) * 4) - nUsed;
        nBytesWide = nUsed + nUnused;
        nLastNibs  = 2 - ((nUsed * 2) - pbBmHdr->biWidth);

        for (r=0; r < pbBmHdr->biHeight;r++)
        {
           for (p=0,q=0; p < nUsed;p++)
           {
              nRow=(pbBmHdr->biHeight-r-1) * nBytesWide;
              nByte = nRow + p;

              int nNibbles = p;
     for(n=0; n < nNibbles;n++)
              {
                 bytCTEnt=lpPixels[nByte] << (n*4);
     bytCTEnt=bytCTEnt >> (4-(n*4));

                 jsmpPixels[r][q+0] = pCTab[bytCTEnt].rgbRed;
                 jsmpPixels[r][q+1] = pCTab[bytCTEnt].rgbGreen;
                 jsmpPixels[r][q+2] = pCTab[bytCTEnt].rgbBlue;

                 q += 3;
              }
           }
        }
        break;

     default:
     case 8: //Each byte is a pointer to a pixel color
        nUnused = (((pbBmHdr->biWidth + 3) / 4) * 4) -
                  pbBmHdr->biWidth;

        for (r=0;r < pbBmHdr->biHeight; r++)
        {
           for (p=0,q=0; p < pbBmHdr->biWidth; p++,q+=3)
           {
              nRow   = (pbBmHdr->biHeight-r-1) * (pbBmHdr->biWidth + nUnused);
              nPixel =  nRow + p;

              jsmpPixels[r][q+0] = pCTab[lpPixels[nPixel]].rgbRed;
              jsmpPixels[r][q+1] = pCTab[lpPixels[nPixel]].rgbGreen;
              jsmpPixels[r][q+2] = pCTab[lpPixels[nPixel]].rgbBlue;
           }
        }
        break;

     case 16: //Hi-color (16 bits per pixel)
        for (r=0;r < pbBmHdr->biHeight; r++)
        {
           for (p=0,q=0; p < pbBmHdr->biWidth; p++,q+=3)
           {
              nRow    = (pbBmHdr->biHeight-r-1) * pbBmHdr->biWidth;
              nPixel  = nRow + p;

              RGBQUAD quad = QuadFromWord(wPixels[nPixel]);

              jsmpPixels[r][q+0] = quad.rgbRed;
              jsmpPixels[r][q+1] = quad.rgbGreen;
              jsmpPixels[r][q+2] = quad.rgbBlue;
           }
        }
        break;

     case 24:
        nBytesWide =  (pbBmHdr->biWidth*3);
        nUnused    =  (((nBytesWide + 3) / 4) * 4) -
                      nBytesWide;
        nBytesWide += nUnused;

        for (r=0;r < pbBmHdr->biHeight;r++)
        {
           for (p=0,q=0;p < (nBytesWide-nUnused); p+=3,q+=3)
           {
              nRow = (pbBmHdr->biHeight-r-1) * nBytesWide;
              nPixel  = nRow + p;

              jsmpPixels[r][q+0] = lpPixels[nPixel+2]; //Red
              jsmpPixels[r][q+1] = lpPixels[nPixel+1]; //Green
              jsmpPixels[r][q+2] = lpPixels[nPixel+0]; //Blue
           }
        }
        break;

     case 32:
        for (r=0; r < pbBmHdr->biHeight; r++)
        {
           for (p=0,q=0; p < pbBmHdr->biWidth; p++,q+=3)
           {
              nRow    = (pbBmHdr->biHeight-r-1) *
                         pbBmHdr->biWidth;
              nPixel  = nRow + p;

              jsmpPixels[r][q+0] = pRgbQs[nPixel].rgbRed;
              jsmpPixels[r][q+1] = pRgbQs[nPixel].rgbGreen;
              jsmpPixels[r][q+2] = pRgbQs[nPixel].rgbBlue;
           }
        }
        break;
  }   //end switch

return TRUE;
}

////////////////////////////////////////
//This function turns a 16-bit pixel
//into an RGBQUAD value.
////////////////////////////////////////
RGBQUAD QuadFromWord(WORD b16)
{
  BYTE bytVals[] =
  {
    0,  16, 24, 32,  40, 48, 56, 64,
    72, 80, 88, 96, 104,112,120,128,
    136,144,152,160,168,176,184,192,
    200,208,216,224,232,240,248,255
  };

  WORD wR = b16;
  WORD wG = b16;
  WORD wB = b16;

  wR <<= 1; wR >>= 11;
  wG <<= 6; wG >>= 11;
  wB <<= 11; wB >>= 11;

  RGBQUAD rgb;

  rgb.rgbReserved = 0;
  rgb.rgbBlue     = bytVals[wB];
  rgb.rgbGreen    = bytVals[wG];
  rgb.rgbRed      = bytVals[wR];

  return rgb;
}


void main()
{
PBITMAPINFOHEADER pbih;
BITMAP bmp;
   PBITMAPINFO pbmi;
   WORD    cClrBits;
 LPBYTE lpBits;

HWND hPPT;
HDC hdc;
HDC memDC;
HBITMAP  m_Bitmap;


int cx, cy;

printf("dll 파일이 불려 집니다. ");
hPPT=FindWindow("screenClass",NULL);  //파워포인트 전체화면 차일드 윈도우
if (hPPT == NULL) {
 printf("파워포인트가 실행중이지 않습니다 ");
} else {
  hdc=GetDC(hPPT);

cx = GetSystemMetrics(SM_CXSCREEN);
cy = GetSystemMetrics(SM_CYSCREEN);

memDC = CreateCompatibleDC(hdc);
m_Bitmap = CreateCompatibleBitmap(hdc, cx, cy);
SelectObject ( memDC, m_Bitmap );

 StretchBlt(  memDC,
                    0, 0,
                    cx, cy,                    // qt 해상도 width, height
                    hdc,
                    0, 0,
                    cx, cy,                   // cx -> width, cy -> height
                    SRCCOPY);
}

   // Retrieve the bitmap color format, width, and height.
   if (!GetObject(m_Bitmap, sizeof(BITMAP), (LPSTR)&bmp))

   // Convert the color format to a count of bits.
   cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
   if (cClrBits == 1)
       cClrBits = 1;
   else if (cClrBits <= 4)
       cClrBits = 4;
   else if (cClrBits <= 8)
       cClrBits = 8;
   else if (cClrBits <= 16)
       cClrBits = 16;
   else if (cClrBits <= 24)
       cClrBits = 24;
   else cClrBits = 32;

   // Allocate memory for the BITMAPINFO structure. (This structure
   // contains a BITMAPINFOHEADER structure and an array of RGBQUAD
   // data structures.)

    if (cClrBits != 24)
        pbmi = (PBITMAPINFO) GlobalAlloc(LPTR,
                   sizeof(BITMAPINFOHEADER) +
                   sizeof(RGBQUAD) * (1<< cClrBits));

    // There is no RGBQUAD array for the 24-bit-per-pixel format.

    else
        pbmi = (PBITMAPINFO) GlobalAlloc(LPTR,
                   sizeof(BITMAPINFOHEADER));

   // Initialize the fields in the BITMAPINFO structure.

   pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
   pbmi->bmiHeader.biWidth = bmp.bmWidth;
   pbmi->bmiHeader.biHeight = bmp.bmHeight;
   pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
   pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
   if (cClrBits < 24)
       pbmi->bmiHeader.biClrUsed = (1<<cClrBits);

   // If the bitmap is not compressed, set the BI_RGB flag.
   pbmi->bmiHeader.biCompression = BI_RGB;

   // Compute the number of bytes in the array of color
   // indices and store the result in biSizeImage.
   // For Windows NT, the width must be DWORD aligned unless
   // the bitmap is RLE compressed. This example shows this.
   // For Windows 95/98/Me, the width must be WORD aligned unless the
   // bitmap is RLE compressed.
   pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
                                 * pbmi->bmiHeader.biHeight;
   // Set biClrImportant to 0, indicating that all of the
   // device colors are important.
   pbmi->bmiHeader.biClrImportant = 0;

HANDLE  hDib = DDBToDIB(m_Bitmap, BI_RGB);
JpegFromDib(hDib, 75, "test.jpg", " ");
GlobalFree(hDib);

// CreateBMPFile(hPPT, "test.bmp",  pbmi, m_Bitmap, memDC) ;
}

HANDLE DDBToDIB(HBITMAP bitmap, DWORD dwCompression)
{
       // 리턴되는 HANDLE에는
       // "비트맵 정보 -> 팔레트 정보 -> 비트맵 데이터"의 순서로
       // 데이터가 저장된다.
       // 256색 이상의 비트맵은 팔레트 정보가 없다.

       BITMAP                  bm;
       BITMAPINFOHEADER        bi;
       LPBITMAPINFOHEADER      lpbi;
       DWORD                   dwLen;
       HANDLE                  hDIB;
       HANDLE                  handle;
       HDC                     hDC;
       HPALETTE                hPal;

       // The function has no arg for bitfields
       if( dwCompression == BI_BITFIELDS ) return NULL;

       // 팔레트가 없는 경우 디폴트 팔레트를 사용
     
       if (hPal == NULL)
               hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE);
 

       // 비트맵 정보를 얻어온다.
      GetObject(bitmap, sizeof(BITMAP), (LPSTR)&bm);

       // Initialize the bitmapinfoheader
       bi.biSize               = sizeof(BITMAPINFOHEADER);
       bi.biWidth              = bm.bmWidth;
       bi.biHeight             = bm.bmHeight;
       bi.biPlanes             = 1;
       bi.biBitCount           = bm.bmPlanes * bm.bmBitsPixel;
       bi.biCompression        = dwCompression;
       bi.biSizeImage          = 0;
       bi.biXPelsPerMeter      = 0;
       bi.biYPelsPerMeter      = 0;
       bi.biClrUsed            = 0;
       bi.biClrImportant       = 0;

       // 256색 이하일 경우에만 색상의 수가 의미가 있다.
       // 즉, 256색 이하일 경우에만 팔레트가 주어진다.
       int nColors = 0;
       if(bi.biBitCount <= 8) nColors = (1 << bi.biBitCount);
       
       // 헤더의 크기
       dwLen = bi.biSize + nColors * sizeof(RGBQUAD);

       // We need a device context to get the DIB from
       hDC = ::GetDC(NULL); // Screem DC를 얻음
       // Screen DC에 팔레트를 설정
       hPal = SelectPalette(hDC, hPal, FALSE);
       RealizePalette(hDC);

       // BITMAPINFOHEADER와 팔레트를 위한 메모리 할당
       hDIB = GlobalAlloc(GMEM_FIXED, dwLen);

       if (!hDIB){ // 메모리 할당 실패
               SelectPalette(hDC, hPal, FALSE); // 팔레트 복귀
               ::ReleaseDC(NULL, hDC);
               return NULL;
       }

       lpbi = (LPBITMAPINFOHEADER)hDIB;
       *lpbi = bi;

       // 구조체에 정보를 얻어오기 위해 데이터를 위한 버퍼를 NULL로 준다.
       // 특히 이미지 크기를 나타내는 biSizeImage를 얻어온다.
       GetDIBits(
               hDC,                             // DC 핸들
               bitmap,                          // 비트맵 핸들
               0L,                              // start scan line
               (DWORD)bi.biHeight,              // 줄 수
               (LPBYTE)NULL,                    // 비트맵 데이터를 위한 버퍼
               (LPBITMAPINFO)lpbi,              // 비트맵 정보 구조체에 대한 포인터
               (DWORD)DIB_RGB_COLORS            // 색상 정보
       );

       bi = *lpbi;

       // If the driver did not fill in the biSizeImage field, then compute it
       // Each scan line of the image is aligned on a DWORD (32bit) boundary
       if (bi.biSizeImage == 0){
               bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8)
                       * bi.biHeight;

               // If a compression scheme is used the result may infact be larger
               // Increase the size to account for this.
               if (dwCompression != BI_RGB)
                       bi.biSizeImage = (bi.biSizeImage * 3) / 2;
       }

       // 비트맵 데이터를 저장하기 위해 메모리를 재할당 한다.
       dwLen += bi.biSizeImage;
       if (handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE))
               hDIB = handle;
       else{
               GlobalFree(hDIB);

               // Reselect the original palette
               SelectPalette(hDC,hPal,FALSE);
               ::ReleaseDC(NULL,hDC);
               return NULL;
       }

       // 실제 비트맵의 데이터를 얻어온다.
       lpbi = (LPBITMAPINFOHEADER)hDIB;
       BOOL bGotBits = GetDIBits(
               hDC,                             // DC 핸들
               bitmap,                          // 비트맵 핸들
               0L,                              // start scan line
               (DWORD)bi.biHeight,              // 줄 수
               (LPBYTE)lpbi + (bi.biSize + nColors * sizeof(RGBQUAD)),
                                                // 비트맵 데이터를 위한 버퍼
               (LPBITMAPINFO)lpbi,              // 비트맵 정보 구조체에 대한 포인터
               (DWORD)DIB_RGB_COLORS            // 색상 정보
       );

       if( !bGotBits )
       {
               GlobalFree(hDIB);
               
               SelectPalette(hDC,hPal,FALSE);
               ::ReleaseDC(NULL,hDC);
               return NULL;
       }

       // 팔레트를 복귀
       SelectPalette(hDC,hPal,FALSE);
       ::ReleaseDC(NULL,hDC);

       return hDIB;
       // hDIB에 대해서는 호출한 쪽에서 메모리를 해제해 주어야 한다.
       // GlobalFree(/* HANDLE */ hDIB);
}



void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi,
                 HBITMAP hBMP, HDC hDC)
{
   HANDLE hf;                 // file handle
   BITMAPFILEHEADER hdr;       // bitmap file-header
   PBITMAPINFOHEADER pbih;     // bitmap info-header
   LPBYTE lpBits;              // memory pointer
   DWORD dwTotal;              // total count of bytes
   DWORD cb;                   // incremental count of bytes
   BYTE *hp;                   // byte pointer
   DWORD dwTmp;
HANDLE                  hDIB;


   pbih = (PBITMAPINFOHEADER) pbi;
   lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

   if (!lpBits)
        errhandler("GlobalAlloc", hwnd);

   // Retrieve the color table (RGBQUAD array) and the bits
   // (array of palette indices) from the DIB.
   if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,
       DIB_RGB_COLORS))
   {
       errhandler("GetDIBits", hwnd);
   }

   // Create the .BMP file.
   hf = CreateFile(pszFile,
                  GENERIC_READ | GENERIC_WRITE,
                  (DWORD) 0,
                   NULL,
                  CREATE_ALWAYS,
                  FILE_ATTRIBUTE_NORMAL,
                  (HANDLE) NULL);
   if (hf == INVALID_HANDLE_VALUE)
       errhandler("CreateFile", hwnd);
   hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M"
   // Compute the size of the entire file.
   hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
                pbih->biSize + pbih->biClrUsed
                * sizeof(RGBQUAD) + pbih->biSizeImage);
   hdr.bfReserved1 = 0;
   hdr.bfReserved2 = 0;

   // Compute the offset to the array of color indices.
   hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
                   pbih->biSize + pbih->biClrUsed
                   * sizeof (RGBQUAD);

   // Copy the BITMAPFILEHEADER into the .BMP file.
   if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
       (LPDWORD) &dwTmp,  NULL))
   {
      errhandler("WriteFile", hwnd);
   }

   // Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
   if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
                 + pbih->biClrUsed * sizeof (RGBQUAD),
                 (LPDWORD) &dwTmp, ( NULL)) )
       errhandler("WriteFile", hwnd);

   // Copy the array of color indices into the .BMP file.
   dwTotal = cb = pbih->biSizeImage;
   hp = lpBits;
   if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
          errhandler("WriteFile", hwnd);

   // Close the .BMP file.
   if (!CloseHandle(hf))
          errhandler("CloseHandle", hwnd);

   // Free memory.
   GlobalFree((HGLOBAL)lpBits);
}

// 윈도우로 짜면 윈도우 화면에 에러를 보여줄 예정
void errhandler(LPTSTR pszFile, HWND hwnd)
{
 printf("pszFile");
}

'프로그래밍 > MFC' 카테고리의 다른 글

MFC 디렉토리 생성  (0) 2013.08.14
쓰레드 생성 (펌글)  (0) 2013.08.14
쓰레드 생성.  (0) 2013.08.14
유니코드  (0) 2013.08.14
CEvent 클래스  (0) 2013.08.14
반응형












리눅스 커널을 보던 중 Container_of 라는 매크로라는 녀석을 자주 보게 된다.

container_of - cast a member of a structure out to the containing structure

  
SYNOPSIS
container_of  (ptr, type, member);

ARGUMENTS

ptr
the pointer to the member.

type
the type of the container struct this is embedded in.

member
the name of the member within the struct.

하는 역활은 한 구조체의 멤버만 알고 있을 경우 그 구조체의 시작 주소를 구할 때 사용한다. 예를 들어 다음과 같은 경우에 사용된다.

struct tagTEMP
{
   int X;
   int Y;

  struct list *next;
};

void calc(struct list *node )
{
   struct tagTEMP *pTEMP;

  pTEMP = container_of(next, struct tagTEMP, next );
}

'프로그래밍 > 리눅스 드라이버' 카테고리의 다른 글

쓰레드 동기화(synchronization)  (0) 2013.08.14
make 간단 설명  (0) 2013.08.14
GCC 옵션 간단하게 정리하기  (0) 2013.08.14
모듈 프로그래밍 #2  (0) 2013.08.14
모듈 프로그래밍 #1  (0) 2013.08.14
반응형

동기화가 뭔지에 대해선 설명하지 않겠다. 리눅스에서 쓰레드 동기화를 위해 사용하는 mutex 와 semaphore 에 대해서 간략한 사용방법을 설명한다.

1. 뮤텍스(mutex)

리눅스에서 사용하는 mutex는 pthread에서 사용하는 mutex를 사용한다. 이 pthread mutex는 프로세스간 동기화에도 사용할 수는 있으나 리눅스에서는 잘 사용하지 않고 쓰레드간 동기화에 사용한다. 이 mutex 관련 함수는 다음과 같다.

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr );
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

 

pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr )

mutex 를 생성하고 초기화 한다.

pthread_mutexattr_t : mutex 생성시 속성 값으로 { FAST, RECURSIVE, ERROR CHECKING } 이 있으며 보통 NULL 을 준다.

또한 pthread_mutex_init 함수를 사용하지 않고 상수들을 이용하여 정적으로 초기화 할 수 있다.

pthread_mutex_t   mutex_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t   mutex_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP ;
pthread_mutex_t   mutex_lock = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;

 

 

pthread_mutex_lock(pthread_mutex_t *mutex)

lock을 실행한다. 이미 다른곳에서 lock이 된 경우 , unlock 될 때까지 블록킹 된다.

 

pthread_mutex_trylock(pthread_mutex_t *mutex)

lock을 실행한다. 이미 다른곳에서 lock이 된경우, EBUSY를 리턴 한다.

 

 

pthread_mutex_unlock(pthread_mutex_t *mutex)

unlock을 실행한다.

 

pthread_mutex_destroy(pthread_mutex_t *mutex)

mutex를 삭제한다.

 

 

2. 세마포어(semaphore)

mutex와 유사하지만, mutex는 하나의 접근만 허용하지만 semaphore는 여러개의 접근도 허용하게 해준다. 또한 semaphore는 프로세스 , 쓰레드 에서 사용 될 수 있으며, SYSTEM V, POSIX 로 구분 된다.

 

2.1 SYSTEM V

SYSTEM V 는 전통적 semaphore 방식이다.

int semget(key_t key, int nsems, int semflag);
int semctl(int semid, int semnum, int cmd, union semun arg);
int semop(int semid, struct sembuf *sops, size_t nsops);

 

int semget(key_t key, int nseems, int semflag)

semaphore를 생성한다.

key_t : semaphore 식별 ID 로 IPC_PRIVATE (0 ) 또는 임의의 수, ftok() 함수에서 리턴하는 값으로 사용할수 있다.

nseems : semaphore 집합 갯수

semflag : IPC_CREATE 일 경우 key id 가 없으면 생성하도록 한다. IPC_EXCL 일 경우 key id가 존재 시 실패를 리턴한다.(이 경우 기존 미리 생성된 semaphore를 사용못한다)

 

semctl(int semid, int semnum, int cmd, union semun arg )

semaphore를 제어한다.

semid: semaphore 식별 ID

semnum : semaphore 집합내의 위치

cmd : 제어 명령

GETVAL : semaphore 값을 구한다.
GETPID : semaphore에 가장 최근에 접근했던 프로세서 ID 구한다.
GETNCNT : semaphore값이 증가하기를 기다리는 프로세스의 갯수
GETZCNT : semaphore값이 0이 되기를 기다리는 프로세스의 갯수
GETALL : semaphore 집합의 모든 semaphore 값을 구한다.
SETVAL : semaphore 값을 설정한다.
SETALL : semaphore 집합의 모든 semaphore 값을 설정
IPC_STAT : 정보를 구한다.
IPC_SET : semaphore 소유권과 접근허가를 설정한다.
IPC_RMID : semaphore 집합을 삭제한다.

arg: semaphore의 값을 설정하거라 값을 얻을 때 사용하는 변수

union semun{
   int                  val;
   struct   semid_ds   *buf;
   unsigned short int  *arrary;
}

 

semop(int semid, struct sembuf *sops, size_t nsops)

semaphore값을 변경하는 함수이다.

semid: semaphore 식별 ID

sops : semaphore 설정값 변경을 위한 값

struct sembuf {
    short sem_num;   semaphore  집합 번호
    short sem_op;     semaphore 증감값
    short sem_flg;     옵션(IPC_NOWAIT : 호출 시 실행 못했을 때 실패 리턴. SEM_UNDO : 프로세서 종료시 semaphore 설정을 원래 상태로 복귀 하는 옵션. 보통 이 옵션 사용) 
}

nsops : 변경하려는 갯수

 

2.2 POSIX semaphore

int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t* sem);
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem, int *sval);
int sem_destroy(sem_t *sem);

sem_init(sem_t *sem, int pshared, unsigned int value)

semaphore 값을 value로 초기화 한다.

pshared : 0 이면 현 프로세스 에서 사용, 그외에는 여러 프로세스 에서 사용



sem_wait(sem_t *sem)

semaphore 값을 1 줄이고 , 0 일 경우 대기한다.



sem_trywait(sem_t *sem)

sem_wait와 동일한 기능을 하며 0일 경우 EAGAIN을 리턴한다.



sem_post(sem_t *sem)

semaphore 값을 1 증가 시킨다.



sem_getvalue(sem_t *sem, int *sval)

semaphore 값을 sval에 저장한다.



sem_destroy(sem_t *sem )

semaphore 삭제하고 해제한다.

반응형

리눅스에서 프로그래밍 하다 보면 꼭 필요한 유틸리티이다. make 유틸은

소스관리를 돕고 컴파일 되어야 하는 소스 파일도 관리한다.

main.c , sub.c , ui.c test.c 라는 파일이 있을 때 보통

gcc –o main main.c sub.c ui.c test.c

이렇게 컴파일 한다. 이걸 일일이 타이핑 하던지 쉘 스크립트로 구성해서

할 수 있지만, 그렇게 편한 방법은 아니다. 그래서 make 유틸을 사용하여

Makefile 을 구성하면 좀더 편안한 구성이 될수 있다.

1. 일반적 사용 예

main: main.o sub.o ui.o test.o

gcc –o main main.o sub.o ui.o test.o

main.o: main.c

gcc –c main.c

sub.o: sub.c

gcc –c sub.c

ui.o: ui.c

gcc –c ui.c

test.o: test.c

gcc –c test.c

위는 Makefile로 구성한 컴파일 예이다.

한 라인씩 간단하게 설명해 보면,

1. main 이 구성되기 위해선 main.o sub.o ui.o test.o 오브젝트 파일이 필요로 한다.

파일이 없다면, 각 파일 구성 규칙으로 이동한다.

2. 각 파일 구성 규칙은 단순하게 gcc 의 c 옵션으로 오브젝트파일만을 생성한다.

3. 모든 오브젝트 파일이 존재하게 되면 main 구성 규칙으로 이동하여

Linking을 하게 된다.

2. 매크로 사용

OBJECT = main.o sub.o ui.o test.o

main: $(OBJECT)

gcc –o main main.o sub.o ui.o test.o

main.o: main.c

gcc –c main.c

sub.o: sub.c

gcc –c sub.c

ui.o: ui.c

gcc –c ui.c

test.o: test.c

gcc –c test.c

매크로를 사용하여 좀더 간단하게 구성할 수 있다. 사용법 또한 일반 변수에

값을 대입 하듯이 사용하면 되고, 사용법은 $(매크로이름) 과 같이 사용한다.

또한 make 유틸에는 내부 매크로가 있다. 이는 좀더 Makefile 구성을

간략하게 생략하게 한다든지 확장을 위해서 있는 매크로로 몇몇개 자주쓰는

매크로 위주로 간략한 설명만 하도록 하겠다.

$* 확장자가 없는 현재의 목표 이름

main.o: main.c
            gcc –c $*.c
$* 는 목표 이름 main.o 에서 main 이 된다.
$@ 현재 목표 이름
main: $(OBJECT)
            gcc –o $@ main.o sub.o ui.o test.o
$@ 는 목표 이름 main 이 된다.
$< 조건 중 첫번째 이름

main.o: main.c test.h
         gcc –c $<
$<는 main.c test.h 중 첫번째 main.c가 선택된다.
$? 현재의 목표 이름 보다 더 최근에 갱신된 이름

main.o: main.c test.h
         gcc –c $?
test.h가 수정되었다면 test.h가 선택
$^ 조건에 있는 내용을 치환

main: main.c test.h 
       gcc –o $^
$^는 main.c test.h 를 뜻한다.

 

반응형

* 코드

-o : 출력 파일명을 정하는 옵션

예) test.c 를 컴파일 하여 execute 실행 파일로 만든다.

$ gcc -o execute test.c

-c : 컴파일만 하는 옵션

예) test.c를 컴파일 한다. (test.o 오브젝트 파일 생성)

$ gcc –c test.c

-I : 소스에서 include 하는 헤더의 위치를 지정한다.

예) 위 소스에서 lib.h 파일이 /home/test/include 에 존재 시.

$ gcc –o execute test.c –I/home/test/include

-L  : 소스에서 사용하는 라이브러리 파일이 있는 위치를 지정한다.

예) 위 소스에서 libtest.a 파일이 /home/test/lib 에 존재 시

$ gcc –o execute test.c –I/home/test/include –L/home/test/lib

-l(소문자 L): 소스에서 사용하는 라이브러리 파일을 명시.

예) 위 소스에서 libtest.a 파일을 명시한다. 이때 lib 와 .a 는 생략한다.

$ gcc –o execute test.c –I/home/test/include –L/home/test/lib –ltest


'프로그래밍 > 리눅스 드라이버' 카테고리의 다른 글

쓰레드 동기화(synchronization)  (0) 2013.08.14
make 간단 설명  (0) 2013.08.14
모듈 프로그래밍 #2  (0) 2013.08.14
모듈 프로그래밍 #1  (0) 2013.08.14
I/O Method  (0) 2013.08.14
반응형

모듈을 커널에 올릴때 insmod 명령어를 사용한다고 앞 모듈 프로그래밍 #1 문서에서 간략하게 설명했다. (솔직히 부실하다.. 추후 좀더 자세하게 모듈이 커널에 올라가는 원리에 대해서 커널 소스를 분석해서 설명해 봐야 겠다)

insmod 명령어가 실행되면 커널은 해당 모듈의 초기화 함수를 호출한다. 또한 rmmod 로 커널에서 모듈을 삭제시는 모듈 삭제 함수가 호출된다.

초기화 함수, 삭제 함수는 모듈 소스에서

module_init

module_exit

이 두 매크로 함수에 정의해준다. 매크로 함수명을 보면 대충 이해갈수 있겠지만 module_init은 초기화 함수, module_exit는 모듈을 커널에서 삭제시 호출된다.

static int hello_module_init(void) 
{ 
	printk("hi~\n"); 
	return 0; 
} 

static int hello_module__exit(void) 
{ 
	printk("I'm exit\n"); 
} 

module_init(hello_init); 
module_exit(hello_exit); 
	

위와 같은 방법으로 매크로 함수에 init 함수와 exit 함수를 등록한다.

'프로그래밍 > 리눅스 드라이버' 카테고리의 다른 글

쓰레드 동기화(synchronization)  (0) 2013.08.14
make 간단 설명  (0) 2013.08.14
GCC 옵션 간단하게 정리하기  (0) 2013.08.14
모듈 프로그래밍 #1  (0) 2013.08.14
I/O Method  (0) 2013.08.14
반응형

  커널의 모듈(Module)은 리눅스 커널안에 포함되지 않고 필요시 동적으로 커널과 결합해서 사용하는 커널 프로그램이다. 이 말은 커널을 동적으로 변경으로 필요시 바꿀수 있다는 말과 일치하게 되며, 메모리의 공간적 여유도 확보에 유리할수 있다. (필요 없는 것들은 동작안시키면 되니깐...) 하지만 동적으로 커널과 연결되다 보니 초기 연결시 시간이 오래 걸리고 , 커널 모드 프로그램이므로 잘못된 모듈일 경우 심각한 오류를 발생시킬수 있다.

  모듈을 커널에서 올리고 내리는 명령어는 각각 insmod 와 rmmod 이다. 뭐 리눅스 명령어다 보니 manpage 나 이런거 참조하면 사용방법에 대해서 알수 있을것이다. 모듈이 커널에 올라가게 되면 모듈에서 다른 커널이나 모듈에 제공하는 함수들을 등록한다. 모듈이나 커널에서 제공하는 모든 함수 리스트는 /proc/kallsyms 에서 확인하면 된다.

cat /proc/kallsyms
[함수주소] [함수종류] [함수이름]
0xFFFC420 T Register_Driver

위와 같은 형태로 출력된다.

'프로그래밍 > 리눅스 드라이버' 카테고리의 다른 글

쓰레드 동기화(synchronization)  (0) 2013.08.14
make 간단 설명  (0) 2013.08.14
GCC 옵션 간단하게 정리하기  (0) 2013.08.14
모듈 프로그래밍 #2  (0) 2013.08.14
I/O Method  (0) 2013.08.14
반응형

디바이스 레지스터(Device Register)들에 대한 접근 방법은 Memory-Mapped I/OI/O Mapped I/O 가 있다.

Memory-Mapped I/O : 메모리의 일부 공간을 디바이스의 레지스터 공간으로 mapping 한다.

I/O mapped I/O: 메모리와 I/O 영역이 별도의 공간의 저장되어 Memory-Mapped I/O처럼 메모리 용량이 줄어드는 문제는 없어진다. 메모리에 대한 데이터 Access는 Load, Store 로 이뤄지며, I/O 입출력은 Input, Output 으로 이뤄진다.


Memory-Mapped I/O의 레지스터 접근 예)

violatile dword *PMM;
PMM = (volatile dword *)0x000111F3

I/O Mapped I/O 레지스터 접근 )

unsigned inb( unsigned port);
void outb(unsigned char byte, unsigned port);

 

'프로그래밍 > 리눅스 드라이버' 카테고리의 다른 글

쓰레드 동기화(synchronization)  (0) 2013.08.14
make 간단 설명  (0) 2013.08.14
GCC 옵션 간단하게 정리하기  (0) 2013.08.14
모듈 프로그래밍 #2  (0) 2013.08.14
모듈 프로그래밍 #1  (0) 2013.08.14
반응형

ATMEGA32L은 3개의 외부 인터럽트(INT0,INT1,INT2)가 존재한다. 위 인터럽트 발생 조건은 다음과 같다.

  1. 하강 엣지
  2. 상승 엣지
  3. 로우레벨
  4. 논리적인 변화

*엣지(edge) : 뭐 내가 하드웨어적인 이해가 부족한 가운데 이 용어에 대해서 이해한 바로는 “어떤 신호가 변화되는 시점”이라 생각한다. 다시 풀이하자면 어떤 신호가 HIGH 레벨( 이진세계에서는 1 )로 계속 발생되고 있는 가운데 변화가 이루어지는 순간 이라고 할수 있다. (이진 세계에서는 1의 변화는 0 이다 ).

3번 로우 레벨 조건은 대충 신호가 일정시간 Low 레벨로 발생 되었을 때 발생한다고 한다. 그 일정시간 수치는 어디에서 어떻게 측정하는지는 모르겠다. 4번 같은 경우는 이건 모르겠다. 인터럽트 발생조건에서 INT2 는 1번과 2번의 엣지에 관련한 조건으로만 인터럽트가 발생한다.

MCUCR 레지스터는 인터럽트 발생조건을 이 레지스터에서 지정할 수 있다. 이 레지스터의 3,2,1,0비트에서 조건을 지정할수 있다.( 0 base ) 이 3,2비트는 INT1 에 관한 조건을 설정을 하고, 1, 0비트는 INT0 에 관해 조건을 설정한다.

  • 0  0 : 로우레벨
  • 0  1 : 논리조건
  • 1  0 : 하강엣지
  • 1  1 : 상승엣지

MCUCRS 레지스터에서는 INT2 인터럽트 조건을 지정한다. 이 레지스터의 6비트에 조건을 설정한다.

  • 0 : 하강엣지
  • 1 : 상승엣지

GICR 레지스터는 INT0, INT1, INT2 레지스터 중 사용할 레지스터를 선택할 수 있는 레지스터이다. 각 7,6,5비트에 INT1, INT0, INT2 순서대로 설정(1)할 경우 각 인터럽트들을 사용할 수 있다.

GIFR 레지스터 각 인터럽트가 발생했을 경우 각 비트에 1로 셋 된다고 하는데 그냥 패쓰…

SREG 레지스터의 7비트는 Global Interrupt 를 사용할 건지 안할건 지 설정하는 부분이 이 부분을 설정해줘야 인터럽트가 발생할 수 있다.

(……..여기까지 이해한 부분……)



'프로그래밍 > 마이컴' 카테고리의 다른 글

PWM (펄스폭 변조)  (0) 2026.07.19
ADC (아날로그 읽기)  (0) 2026.07.19
UART 통신  (0) 2026.07.19
SPI 통신  (0) 2026.07.19
I2C 통신  (0) 2013.08.14
반응형


I2C가 뭔지 헤메다가 몇자 적는다.

용어적 설명부터

I²C  는 필립스에서 개발한 직렬 컴퓨터 버스이며 마더보드, 임베디드 시스템, 휴대전화등에 저속의 주변 기기를 연결하기 위해 사용된다. I²C 라는 이름은 $2의 약자이며 아이-스퀘어-씨 라고 발음한다.

I²C 는 풀업 저항이 연결된 직렬 데이터(SDA)와 직렬 클럭(SCL)이라는 두 개의 양 방향 오픈 컬렉터 라인을 사용한다. 최대 전압은 +5 V 이며, 일반적으로 +3.3V 시스템이 사용되지만 다른 전압도 가능하다.

I²C 레퍼런스 디자인은 7 비트의 주소 공간을 가지며, 이 중 16개는 예약되어 있으므로, 동일한 버스에 최대 112개의 노드를 연결할 수 있다. 가장 일반적으로 사용되는 I²C 버스의 모드는 표준 모드인 100 kbit/s와, 저속 모드인 10 kbit/s 가 사용된다. 최신 리비전의 I²C 는 보다 빠르게 동작하며, 패스트(fast) 모드인 400 kbit/s와 고속(high-speed) 모드인 3.4 Mbit/s를 지원한다. 최대 1008 노드까지 연결 가능한 10 비트 주소 지정 등의 확장된 기능들을 지원한다.

란다.. 휴!!~~ 처음엔 이 용어 부터도 너무 어려웠다.
우선 용어부터 풀어보자면 필립스에서 개발한 직렬 통신 프로토콜이다?? 뭐 그다지 풀어야 할 내용은 없는데 필립스 에서 개발했다는것만 기억할련다 ^^ 또한 I2C (아이 투 씨), 아이-스퀘어-씨 라고 발음 한다고 한다. 또한 위에서 언급되었듯이 직렬 데이터(SDA)와 직렬 클럭(SCL)이라는 라인을 이용해 통신을 하게 된다. 전압으로 최대 5V 를 이용될수 있으며 3.3V이 보통 사용되지만 다른 전압도 가능하다고 되어 있다. 뭐 SDA 와 SCL 이라는 두 라인으로 I2C 통신을 한다는 말이다.

"SDA라 읽컫는 라인과 SCL 이라 일컫는 라인을 통해 통신을 한다."

또한 I2C는 마스터 모드와 슬레이브 모드 두개가 존재하며 마스터가 슬레이브를 호출하는 형태의 통신이다.

각 슬레이브는 각각 주소(7비트)로 구분되어 지며 112개의 슬레이브가 존재할수 있다.














 I2C 데이터 전송

I2C 데이터 전송 방법은

  • 데이터 전송 시작 : START
  • 데이터 전송지 주소 : Address
  • 데이터 속성 (읽기/쓰기) : R/W
  • 데이터 : Data
  • 데이터 수신 여부 : Ack
  • 데이터 전송 종료 : STOP


즉 데이터 전송시작(START) -> 주소(Address) -> 읽기 또는 쓰기(R/W) ->상대편 수신여부->데이터 -> 상대편 수신여부(Ack) -> 종료 (STOP ) 형태로 데이터를 보내주면 되는 것이다.






 시작 : 1비트
 주소 : 7비트
 읽기 또는 쓰기 : 1비트
 데이터 : 8비트
 수신 여부 : 1비트
 종료 : 1비트

뭐 이런 형태라고 책에는 나와 있는데 처음에는 많이 헤멨다. 저게 무슨 말인지 ㅡㅡ;;;
START와 STOP 은 어떻게 구분하지 라는 의문도 들었다.

뭐 찾아보니 HIGH, LOW 변화로 START 와 STOP 을 구분하게 된다는 설명이다.
START는 SCL 선이 HIGH를 유지하는 상태에서 SDA선이 HIGH에서 LOW으로 변화되는 시점을 시작 이라는 시점으로 판단한다는 거다.
STOP은 SCL선이 HIGH일때, SDA선이 LOW에서 HIGH로 올라가는 시점을 중지 라는 시점으로 판단한다.
그럼 데이터는 ??? 데이터는 SCL이 HIGH에서 LOW로 떨어지는 순간 SDA의 상태(HIGH인지, LOW인지)를 판단하여 1, 0 으로 판단한다고 한다. 모르겠다 ㅡㅡ;;; 그림을 보자.

















자, 그림으로 보면 SCL이 HIGH인 순간 SDA의 상태를 보고 1, 0을 판단하고 있다.
비트전환 이라고 해서 SDA의 값의 전환을 주기 위한 시간도 있다. ㅋㅋㅋ 뭐 대충 알았다. 아니 그냥 알아 들었다고 하고 그냥 어떻게 코드로 표현하는지 보자.

#define SDA_IN          PINC_Bit4
#define SDA_OUT         PORTC_Bit4
#define SDA             PORTC_Bit4
#define SCL             PORTC_Bit5

/*------------------------------------------------------------------------------------------------*/
/*      I2CStart                                                                  */
/*              _____         ________                                                              */
/*      SDA:       |_______|                                                                      */
/*                  ___     ___                                                                     */
/*      SCL:  ___|   |___|   |______                                                              */
/*------------------------------------------------------------------------------------------------*/
void I2CStart(void)
{
    DDRC = 0xFC; // SDA OUTPUT MODE
    SDA_OUT=1;
    USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=0; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=1; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
}

위 코드는 I2C 통신을 하기 위해서 START를 보내는 부분이다. START의 판단 조건은 SCL이 HIGH인 상태에서 SDA가 HIGH에서 LOW로 내려가는 시점이라고 했다.
SDA_OUT=1 // SDA가 HIGH 유지
Delay(1)
SCL=0 ; Delay(2); // SCL이 LOW
SCL=1; Delay(1); // SCL을 HIGH로 변환

SDA_OUT=0; Delay(1); // SDA를 HIGH에서 LOW로 변환
SCL=0; Delay(2); // SCL을 LOW로
SCL=1; Delay(1); // SCL을 HIGH로 변경
SDA_OUT=1; Delay(1) // SDA를 HIGH
SCL=0;Delay(2); // SCL을 LOW로..

BYTE I2CWrite_Data(BYTE val)
{
    BYTE i = 0;
    BYTE error = 0;
    DDRC = 0xFC;
    USEC_Delay(1);
    for(i=0x80; i>0; i/=2)
    {
        if (i & val) SDA_OUT = 1;      
        else SDA_OUT = 0;
        USEC_Delay(1);
        SCL = 1; USEC_Delay(1);
        SCL = 0; USEC_Delay(1);
    }
    SDA_OUT = 0; USEC_Delay(1);
    SCL = 1;
    error = SDA_IN; USEC_Delay(1);
    SCL = 0; USEC_Delay(1);
    SDA_OUT = 1; USEC_Delay(1);
    return error;
}
BYTE I2CRead_Data(BYTE ACKn)
{
    BYTE i,val=0;
    DDRC = 0xEC;
    for (i=0x80;i>0;i/=2)
    {
        SCL=1;
        if (SDA_IN) val=(val | i); USEC_Delay(1);
        SCL=0; USEC_Delay(1);
    }
    DDRC = 0xFC;
    SDA_OUT=!ACKn;
    SCL=1; USEC_Delay(1);
    SCL=0; USEC_Delay(1);
    SDA_OUT=1;
    return val;
}
void I2CStart(void)
{
    DDRC = 0xFC; // SDA OUTPUT MODE
    SDA_OUT=1;
    USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=0; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=1; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
}
void I2CStop(void)
{
    DDRC = 0xFC; // SDA OUTPUT MODE
    SDA_OUT=0;
    USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=1; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
}
void main()
{
 Data = 0x40;
 I2CStart();      // START
 I2CWrite_Data( 0x07 ) // Address (3)
 I2CRead_Data(1)    // ACK
 I2CWrite_Data(Data); // Data
 I2CRead_Data(1);   // ACK
 I2CStop();      // STOP
 
}


위 코드는 슬레이브(0x03, 000 0011) 에게 쓰기 모드로 데이터 0x40을 전송하는 코드이다.

부랴부랴 이해했던 내용을 작성하려다 보니 내용상 허술하기도 하고 부실하기도 하다.

'프로그래밍 > 마이컴' 카테고리의 다른 글

PWM (펄스폭 변조)  (0) 2026.07.19
ADC (아날로그 읽기)  (0) 2026.07.19
UART 통신  (0) 2026.07.19
SPI 통신  (0) 2026.07.19
ATMEGA32L External Interrupt  (0) 2013.08.14

+ Recent posts