반응형

MCU는 전원이 꺼지고 켜진 경우 (POR : Power on Reset)
처리된 데이터(static 변수, 지역 변수, 매개 변수) 들은  메모리 상에서 남아있지 않고 사라진다.
그래서 MCU가 꺼지고 다시 켜졌을 경우, 데이터를 저장해야 할 때가 있다.
아두이노는 이 역할을 하는 내장 메모리가 있으며, 이를 EEPROM이라고 한다. (하드 디스크처럼 기간과 횟수가 그리 많지 않음.)


(초기 ROM(Read Only Memory)로 오로지 공장에서 출하 과정에서 메모리에  write가 되면, 그 이 후부턴 read 밖에 할 수 없었음. read 만 할 수 있는 문제를 해결하기 위해 자외선을 통해 몇 번의 write를 할 수 있는 EPROM이 개발됨. 조금 더 시간이 흘러 자외선이 아닌 전기로 write가 가능한 EEPROM(Electrically Erasable Programmable ROM)이 개발 됨.

2byte 정수를 1byte 배열로 나눠서 저장하는 글을 작성하면서 EEPROM 이야기를 하는 이유는
EEPROM에 데이터를 저장할 때 한 개의 주소에 1Byte(8bit) 씩 저장하므로 1byte를 초과하는 데이터는 나눠서 저장해야 하기 때문이다.
그리고 CRC-16 결과 값인 2byte 정수를 1byte 배열로 나눠서 저장한다.

CRC(cyclic redundancy check )  = 데이터의 무결성을 체크하기 위해 사용.

예제는 간단하다.
비트 마스킹을 통해 원하는 변수를 추출하고 다운 캐스팅을 통해 형 변환을 해주면 된다.

#include <stdio.h>

#define EEP_DATA_SIZE 2

static unsigned char eepImage[EEP_DATA_SIZE];

static void EepHandler_2byte_to_1byte(void){
    int l_result = 0x0102;            				// 2byte = 0x01_02
    
    eepImage[0] = (unsigned char)(l_result >> 8);	// 0x01_02 >> 8 = 0x00_01
    eepImage[1] = (unsigned char)(l_result & 0x00FF);  // 0x01_02 & FF = 0x00_02

}

int main()
{
    EepHandler_2byte_to_1byte();
    
    printf("EEPImage[0] : %d\n", eepImage[0]);
    printf("EEPImage[1] : %d\n", eepImage[1]);
    
    return 0;
}
728x90
반응형
반응형

Build의 순서는 아래와 같다.

소스코드 -> 전처리 -> 컴파일 -> 링크 -> 실행

C언어를 처음 공부할 때, #define을 사용하지 않으면 전처리 과정을 거치지 않는다고 생각한 적이 있다.

#으로 시작하는 문장은 전부 전처리기 지시자임에도 #include는 전처리기라고 생각하지 않았다. 항상 C언어를 실습할 때, 의무적으로 #inlcude <stdio.h>를 작성한 폐해이다.

전처리란 무엇인가?

컴파일 전에 처리해야 하는 일이고 전처리를 수행하는 장치를 전처리기라고 한다.

전처리기는 헤더 파일을 불러오거나, 소스 파일 내부의 특정 문자열을 상수 또는 문자로 치환하거나, 조건에 따라서 코드의 일부를 컴파일하거나 컴파일하지 못하게 하는 선택 기능을 제공한다.

궁금증이 하나 발생한다.

#define은 왜 사용하는가? 단순히 변수나 함수를 선언 및 호출하면 되지 않는가? 일반 변수 및 함수와 매크로(#define)의 차이는 무엇인가?

이를 이해하기 위해선 컴파일 단계를 이해할 필요가 있다.

컴파일이란 무엇인가? 기계어로 번역하는 번역기이다. 왜 기계어로 번역하는가? 소스 코드는 사람이 이해하기 쉬운 문장이며, 기계는 이해할 수 없다. 그러므로 기계가 이해할 수 있는 문장으로 번역해줘야 한다.

C언어가 생기기 이 전, 개발자들은 기계(컴퓨터, CPU)를 직접 제어하기 위해 기계어(어셈블리어)를 직접 작성하였다. 하지만 CPU는 다양했고, CPU의 종류에 따라 어셈블리어 작성하는 방법도 각기 달랐다고 하며, 간단한 프로그램에도 코드가 굉장히 길어졌다. 

이 문제점을 해결하기 위해, 기계어를 직접 사람이 작성하지 않아도 되는 번역기를 개발했는데 그것이 컴파일러이다. 개발자들은 더 이상 기계의 종류를 상관하지 않아도 된다.


C언어는 소스코드 중복을 줄이고 코드의 재사용성을 높이기 위해서 함수를 제공한다. 하지만, 함수는 호출될 때 *스택 프레임이 일어나기 때문에 속도가 느려지는 단점이 있다. 따라서 코딩할때는 매크로를 함수와 같은 기능으로 작성하지만 실제 기계어로 번역될 때는 직접 코딩한 것처럼 처리(치환, 전처리 단계에서 매크로가 소스코드로 변환)된다.

함수 호출 시, 스택이라는 메모리 공간에 매개 변수, 반환 포인터 값, 지역 변수 순서로 저장되는 값을 스택 프레임이라고 한다.

즉, 저장 공간인 스택을 메모리 낭비를 줄이고 실행 속도를 높이기 위해 매크로를 사용한다. 하지만 매크로는 소스코드로 바로 변환되므로 실행 파일 크기가 커지며, 재귀호출을 할 수 없다는 단점을 가지므로 적절하게 사용하는 것이 중요하다. 

요즘엔 컴퓨터의 메모리가 굉장히 넉넉하기 때문에 메모리 걱정을 하지 않을 때가 많지만, 메모리 공간이 넉넉치 않은 임베디드 시스템과 같은 환경에서는 매크로를 많이 사용한다.


아래 매크로 함수와 일반 함수 예시를 보겠다.

#include <stdio.h>

#define MUL(x, y) x*y

int mul(x, y)
{
	return x*y;
}
int main()
{
	int a, b;
    
    scanf("%d %d", &a, &b);
    printf("%d", MUL(a+1, b+1));
    printf("%d", mul(a+1, b+1));
}

// result
// a=3, b=4 입력
// 8
// 20

왜 결과값이 8, 20으로 다른 값이 나올까?  매크로는 전처리 단계에서 소스 코드로 변경된다고 했다. 

MUL(a+1, b+1)을 소스 코드로 치환되면 a+1*b+1 이다.

그러므로 3+1*4+1로 계산되어 8이라는 결과가 출력된다. mul과 같은 결과를 내기 위해선, 

#define MUL(x, y) ((x) * (y)) 로 변경하면 된다. 소스코드로 치환하게 되면, ((a+1)*(b+1))이다.

728x90
반응형
반응형

안녕하세요.

 

오랜만에 C언어에 대해 글을 씁니다. 

 

오늘은 제목과 같이 #pragma pack(push,1)을 사용하지 않고 구조체만으로 정렬하는 방법에 대해 이어쓰겠습니다.

 

혹시 이해가 안가신다면 전에 글을 읽어주시기 바랍니다.

 

https://coding-yoon.tistory.com/27

 

[C]BMP구조체 : #pragma pack(push, 1) 사용하지 않고 정렬하기

안녕하세요. 오랜만에 C언어에 대해 글을 써봅니다. 여러분들 BMP를 불러올 때 어떻게 하시나요? https://dojang.io/mod/page/view.php?id=703 C 언어 코딩 도장: 81.2 비트맵 구조체 작성하기 비트맵 파일의 구조..

coding-yoon.tistory.com

Lenna.bmp
0.75MB

#include   // strcpy 함수가 선언된 헤더 파일
#include 
#include 
#include 
#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS  // strcpy 보안 경고로 인한 컴파일 에러 방지


//pragma pack(push,1)
struct BMPINFO_14  //BMP 파일 구조체
{
//unsigned short bfType;           // BMP 파일 매직 넘버
unsigned int   bfSize;           // 파일 크기
unsigned short bfReserved1;      // 예약
unsigned short bfReserved2;      // 예약
unsigned int   bfOffBits;        // 비트맵 데이터의 시작 위치
};

struct BMP_FILE_44 // BMP 비트맵 정보 구조체
{
unsigned int   biSize;           // 현재 구조체의 크기
int            biWidth;          // 비트맵 이미지의 가로 크기
int            biHeight;         // 비트맵 이미지의 세로 크기
unsigned short biPlanes;         // 사용하는 색상판의 수
unsigned short biBitCount;       // 픽셀 하나를 표현하는 비트 수
unsigned int   biCompression;    // 압축 방식
unsigned int   biSizeImage;      // 비트맵 이미지의 픽셀 데이터 크기
int            biXPelsPerMeter;  // 그림의 가로 해상도(미터당 픽셀)
int            biYPelsPerMeter;  // 그림의 세로 해상도(미터당 픽셀)
unsigned int   biClrUsed;        // 색상 테이블에서 실제 사용되는 색상 수
unsigned int   biClrImportant;   // 비트맵을 표현하기 위해 필요한 색상 인덱스 수
};

struct RGB_3 //24비트 비트맵 구조체
{
unsigned char rgbtBlue;          // 파랑
unsigned char rgbtGreen;         // 초록
unsigned char rgbtRed;           // 빨강
};

//#pragma pack(pop)

int main()
{
int i, j, k, ii;

char image[80] = "Lenna.bmp";
char image_copy[80]="Lenna1.bmp";
printf("원본 이미지 : Lenna.bmp\n");
printf("편집 이미지 : Lenna1.bmp\n");

FILE* fp;

//binary read모드
fp = fopen(image, "rb");
if (fp == NULL)
return 1;
/////////////////////////////////BM/////////////////////////////////////
fseek(fp, 0, SEEK_SET);
int buffer = fgetc(fp);
fseek(fp, 1, SEEK_SET);
int buffer1 = fgetc(fp);

if ((buffer != 0x42 || buffer1 != 0x4D) )
{
printf("%x%x", buffer, buffer1);
fclose(fp);
return 3;
}
fseek(fp, 2, SEEK_SET);
////////////////////////////////BM/////////////////////////////////////
//binary write모드
FILE* outfp = fopen(image_copy, "wb");
if (outfp == NULL)
{
fclose(fp);
fprintf(stderr, "생성 불가능 %s.\n", image_copy);
return 2;
}

BMP_FILE_44 bitmapInfoHeader;
BMPINFO_14 bitmapFileHeader;

//
fread(&bitmapFileHeader, sizeof(BMPINFO_14), 1, fp);
fread(&bitmapInfoHeader, sizeof(BMP_FILE_44), 1, fp);
fwrite("BM", 1, 2, outfp);
fwrite(&bitmapFileHeader, sizeof(BMPINFO_14), 1, outfp);
fwrite(&bitmapInfoHeader, sizeof(BMP_FILE_44), 1, outfp);



int biHeight = abs(bitmapInfoHeader.biHeight);
int biWidth = bitmapInfoHeader.biWidth;

int padding = (4 - (biWidth * sizeof(RGB_3)) % 4) % 4; //패딩 처리

//**************************2차동적할당**********************************//
RGB_3** RGB;
RGB = (RGB_3**)malloc(sizeof(RGB_3*) * biHeight);
RGB[0] = (RGB_3*)malloc(sizeof(RGB_3) * biHeight * biWidth);
for (ii = 1; ii < biHeight; ii++)
{
RGB[ii] = RGB[ii - 1] + biWidth;
}
//**************************2차동적할당**********************************//

for (i = 0; i < biHeight; i++)
{
for (j = 0; j < biWidth; j++)
{
fread(&RGB[i][j], sizeof(RGB_3), 1, fp);
}
}

for (i = 0; i < biHeight; i++)
{
for (j = 0; j < biWidth; j++)
{
// 이미지 밝기
int x, y, z;
x = RGB[i][j].rgbtBlue + 75;
y = RGB[i][j].rgbtGreen + 75;
z = RGB[i][j].rgbtRed + 75;
RGB[i][j].rgbtBlue = ((x > 255) ? 255 : x);
RGB[i][j].rgbtGreen = ((y > 255) ? 255 : y);
RGB[i][j].rgbtRed = ((z > 255) ? 255 : z);
}
}

for (i = 0; i < biHeight; i++)
{
for (j = 0; j < biWidth; j++)
{

fwrite(&RGB[i][j], sizeof(RGB_3), 1, outfp);
}
fseek(fp, padding, SEEK_CUR);
for (k = 0; k < padding; k++)
{
fputc(0x00, outfp);
}
}


printf("Complete");



fclose(fp);

fclose(outfp);
free(RGB[0]);
free(RGB);
return 0;

}


이를 실행시켜보면

 

아주 잘읽혀집니다.

 

구조체가 어떻게 정렬하는지는 앞 글에 있고, 조심해야할 점에 대해 적도록 하겠습니다. 

 

Lenna.bmp를 1바이트 정렬없이 읽었으며, 우리가 출력할 Lenna1.bmp를 자세히 보아야 합니다.

 

Lenna1.bmp를 출력하기 위해 Binary Write로 열고 구조체를 사용해야 합니다.

 

하지만 구조체에는 다음과 같이 

 

'BM' 부분이 비어 있게 되어 bmp로 작성할 수 없게 됩니다. 

 

그러면 어떻게 해야 될까요? 

 

아주 간단합니다. 우리가 다시 수동으로 적어주면 됩니다. 이렇게

bitmap 파일 헤더를 읽고,

bitmap 정보 헤더를 읽고,

 

당연히 우리는 bitmap파일헤더에 BM이 존재하지 않기 때문에

 

bmp의 가장 첫 2바이트는 BM이라는거 기억나시죠?

 

fwrite를 통해 BM을 적어주면 됩니다. 

 

그리고 이어서 bitmap파일 헤더, bitmap정보 헤더에 대해 fwrite를 해줍니다. 

 

어떻게 보면 당연한 이야기일 수 있겠지만, 저는 파일 입출력 구조에 대해 이해를 못했을 땐, 왜 이렇게 사용하는지 조차

 

이해가 가지 않았습니다. 파이썬을 하면 이런거 할 일은 없지만, 파일 구조에 대해 공부할 땐 bmp만한게 없는 것 같습니다.

 

 

728x90
반응형
반응형

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>


#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS

typedef uint8_t  BYTE;
typedef uint32_t DWORD;
typedef int32_t  LONG;
typedef uint16_t WORD;


/*********************************************************STRUCTS***********************************************************/
#pragma pack(push, 1)
typedef struct tagBITMAPFILEHEADER
{
WORD bfType;  //specifies the file type
DWORD bfSize;  //specifies the size in bytes of the bitmap file
WORD bfReserved1;  //reserved; must be 0
WORD bfReserved2;  //reserved; must be 0
DWORD bfOffBits;  //species the offset in bytes from the bitmapfileheader to the bitmap bits
}BITMAPFILEHEADER;


typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;  //specifies the number of bytes required by the struct
LONG biWidth;  //specifies width in pixels
LONG biHeight;  //species height in pixels
WORD biPlanes; //specifies the number of color planes, must be 1
WORD biBitCount; //specifies the number of bit per pixel
DWORD biCompression;//specifies the type of compression
DWORD biSizeImage;  //size of image in bytes
LONG biXPelsPerMeter;  //number of pixels per meter in x axis
LONG biYPelsPerMeter;  //number of pixels per meter in y axis
DWORD biClrUsed;  //number of colors used by the bitmap
DWORD biClrImportant;  //number of colors that are important
}BITMAPINFOHEADER;

typedef struct
{
BYTE  rgbtBlue;
BYTE  rgbtGreen;
BYTE  rgbtRed;
}RGBTRIPLE;

typedef struct
{
BYTE hslHue;
BYTE hslSaturation;
BYTE hslLightness;
}HSL;
#pragma pack(pop)
/*********************************************************STRUCTS***********************************************************/

#define filterWidth 3
#define filterHeight 3

void Filters(int biHeight, int biWidth, RGBTRIPLE** triple);
int CopyBitmapFile(char* filename, BITMAPINFOHEADER* bitmapInfoHeader, char* copy_filename);

int main()
{
char image[80], image_copy[80];
printf("원본 이미지 파일 : ");
scanf("%s", image);

printf("편집 이미지 파일 : ");
scanf("%s", image_copy);


BITMAPINFOHEADER bitmapInfoHeader;
memset(&bitmapInfoHeader, 0, sizeof(BITMAPINFOHEADER));
CopyBitmapFile(image, &bitmapInfoHeader, image_copy);

}


int CopyBitmapFile(char* filename, BITMAPINFOHEADER* bitmapInfoHeader, char* copy_filename)
{
int i, j, k;
FILE* filePtr; 
BITMAPFILEHEADER bitmapFileHeader; 
memset(&bitmapFileHeader, 0, sizeof(BITMAPFILEHEADER));

filePtr = fopen(filename, "rb");
if (filePtr == NULL)
return 1;

FILE* outptr = fopen(copy_filename, "wb");
if (outptr == NULL)
{
fclose(filePtr);
fprintf(stderr, "Could not create %s.\n", copy_filename);
return 2;
}


fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);

if (bitmapFileHeader.bfType != 0x4D42)
{
printf("이미지 파일 찾을 수 없엉");
fclose(filePtr);
fclose(outptr);
return 3;
}


fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);


fwrite(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, outptr);

fwrite(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, outptr);

int padding = (4 - (bitmapInfoHeader->biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

int biHeight = abs(bitmapInfoHeader->biHeight);
int biWidth = bitmapInfoHeader->biWidth;

RGBTRIPLE** triple;
triple = (RGBTRIPLE**)malloc(sizeof(RGBTRIPLE*) * biHeight);
int ii;
for (ii = 0; ii < biHeight; ii++)
{
triple[ii] = (RGBTRIPLE*)malloc(sizeof(RGBTRIPLE) * biWidth);
}


for (i = 0; i < biHeight; i++)
{
for (j = 0; j < biWidth; j++)
{
fread(&triple[i][j], sizeof(RGBTRIPLE), 1, filePtr);
}
}



for (i = 0; i < biHeight; i++)
{
for (j = 0; j < biWidth; j++)
{
// brighter image
int x, y, z;
x = triple[i][j].rgbtBlue  + 75;
y = triple[i][j].rgbtGreen + 75;
z = triple[i][j].rgbtRed + 75;
triple[i][j].rgbtBlue = ((x > 255) ? 255 : x);
triple[i][j].rgbtGreen = ((y > 255) ? 255 : y);
triple[i][j].rgbtRed = ((z > 255) ? 255 : z);
}
}

for (i = 0; i < biHeight; i++)
{
for (j = 0; j < biWidth; j++)
{

fwrite(&triple[i][j], sizeof(RGBTRIPLE), 1, outptr);
}

fseek(filePtr, padding, SEEK_CUR);

for (k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}

fclose(filePtr);
fclose(outptr);

for(int p = 0; p < biHeight; p++ )
{
free(triple[p]);
}
free(triple);

return 0;
}

728x90
반응형
반응형

3_1.cpp
0.00MB

 

안녕하세요 2차원 배열 동적할당하기 3편입니다. 

 

2편에서는 1차 배열을 2차 배열처럼 표현하였는데 단점으로 배열 사용할 때 인덱스가 복잡하여 사용하기 어려웠습니다.

 

이제는 1편의 2차 배열을 조금 정리하여 이쁘게 표현해보도록 하겠습니다. 

 

 

int** mat;
mat = (int**)malloc(sizeof(int*) * column);

mat[0] = (int*)malloc(sizeof(int) * row *column);


for (int i = 1; i < column; i++) {
     mat[i] = mat[i - 1] + row;
      }

 

이제 포인터 한 번 찍어보도록 하겠습니다.

이렇게 표현하면 그냥 배열처럼 사용할 수 있습니다. 

 

for (int i = 0; i < column; i++)
{
     for (int j = 0; j < row; j++) {
          printf("%d ", &mat[i][j]);
          }
printf("\n");
}

 

 

3 x 3 행렬

5 x 5 행렬도 찍어보겠습니다. 

 

5 x 5 행렬

메모리를 찍어보니 끊어지지 않고 4바이트(sizeof(int))만큼 증가하는걸 볼 수 있습니다. 

 

메모리를 사용했으니 이제 제자리에 돌려놔야겠죠?? 메모리 할당은 조금 복잡하나 해제는 아주 간단합니다.

 

free(mat[0]);

free(mat);

 

 

속도도 1편보다 빠릅니다. (사람 눈에는 차이가 없습니다. 워낙 빨라서...)

 

저는 전공이 하드웨어나 영상처리 쪽이 아니기 때문에 C를 많이 사용하지 않습니다. 당연히 요즘은 파이썬...

 

1학년 때 처음 배운게 C언어였고, 이제 4학년부터는 AVR을 공부하고 캡스톤을 준비해야 하니, 

 

예전에 제가 배웠던 혹은 중요하게 생각하는 부분, 글쓰기를 계속해 나가도록 하겠습니다.

 

728x90
반응형

+ Recent posts