C 언어의 제어문은 프로그램의 흐름을 제어하며, 주요 제어문으로 조건문반복문, 흐름을 조정하는 breakcontinue가 있습니다.

1. 조건문

1-1. if문

  • 용도: 조건에 따라 코드 실행.
  • 형식:
     
if (조건) { ... } else { ... }

1-2. switch문

  • 용도: 여러 값에 따라 실행 분기.
  • 형식:
     
switch (표현식) {
    case 값1: ... break;
    default: ...
}

2. 반복문

2-1. for문

  • 특징: 초기값, 조건, 증감 모두 포함.
  • 형식:
     
for (초기값; 조건; 증감) { ... }

2-2. while문

  • 특징: 조건이 참일 때만 실행.
  • 형식:
     
while (조건) { ... }

2-3. do-while문

  • 특징: 최소 한 번 실행 후 조건 검사.
  • 형식:
     
do { ... } while (조건);

3. break와 continue

3-1. break

  • 특징: 반복문이나 switch문을 즉시 종료.
  • 형식:
     
if (조건) break;

3-2. continue

  • 특징: 반복문의 현재 반복 건너뛰기.
  • 형식:
     
if (조건) continue;

예제

짝수 출력 (break, continue 사용):

#include <stdio.h>

int main() {
    for (int i = 1; i <= 10; i++) {
        if (i % 2 != 0) continue; // 홀수 건너뜀
        if (i > 6) break;         // 6 초과 시 종료
        printf("%d\n", i);
    }
    return 0;
}

 

조건문과 반복문은 프로그램의 기본 흐름을 관리하고, break와 continue로 유연하게 제어할 수 있습니다.

'C' 카테고리의 다른 글

C) 연산자  (0) 2024.11.25
C) 변수와 자료형  (0) 2024.11.23
C) 개발 환경 설정: 컴파일러 설치 및 설정  (1) 2024.11.22
C) C 언어 소개  (0) 2024.11.22
C 언어로 OOP 스타일의 Stack 구현하기  (0) 2024.11.22

C 언어에서 연산자는 데이터 조작과 논리를 구현하기 위한 핵심 도구입니다. 산술, 비교, 논리, 비트 연산자에 대해 살펴보겠습니다.

1. 산술 연산자 (Arithmetic Operators)

수학적 계산을 수행합니다.

+ 덧셈 a + b a와 b의 합
- 뺄셈 a - b a에서 b를 뺌
* 곱셈 a * b a와 b의 곱
/ 나눗셈 a / b a를 b로 나눔
% 나머지 연산 a % b a를 b로 나눈 나머지

2. 비교 연산자 (Relational Operators)

두 값 간의 관계를 비교하고, 결과는 참(1) 또는 거짓(0)입니다.

== 같음 a == b 참: a와 b가 같음
!= 같지 않음 a != b 참: a와 b가 다름
> a > b 참: a가 b보다 큼
< 작음 a < b 참: a가 b보다 작음
>= 크거나 같음 a >= b 참: a가 b 이상
<= 작거나 같음 a <= b 참: a가 b 이하

3. 논리 연산자 (Logical Operators)

논리값(참 또는 거짓)을 결합하거나 부정합니다.

&& 논리 AND a && b 참: 둘 다 참일 때만 참
|| 논리 OR a || b 참: 하나라도 참일때
! 논리 NOT !a 참: a가 거짓일 때 참

4. 비트 연산자 (Bitwise Operators)

값을 비트 단위로 연산합니다.

& 비트 AND a & b a와 b의 대응 비트가 모두 1이면 1
| 비트 OR a | b a와 b의 비트중 하나라도 1이면 1
^ 비트 XOR a ^ b a와 b의 대응 비트가 다를 때 1
~ 비트 NOT ~a a의 비트를 반전
<< 왼쪽 시프트 a << 2 a를 왼쪽으로 2비트 이동
>> 오른쪽 시프트 a >> 2 a를 오른쪽으로 2비트 이동

 

연산자우선순위 및 결합 방향

C 언어의 연산자 우선순위는 코드의 실행 순서를 결정하며, 동일한 우선순위를 가진 연산자는 결합 방향에 따라 처리됩니다. 아래는 주요 연산자의 우선순위를 정리한 표입니다.

1 () [] -> . 괄호, 배열, 구조체 접근 왼쪽 → 오른쪽
2 ++ -- + - ! ~ 단항 연산자 (전위, 부정, 비트 반전 등) 오른쪽 → 왼쪽
3 * / % 곱셈, 나눗셈, 나머지 왼쪽 → 오른쪽
4 + - 덧셈, 뺄셈 왼쪽 → 오른쪽
5 << >> 비트 시프트 왼쪽 → 오른쪽
6 < <= > >= 관계 연산자 왼쪽 → 오른쪽
7 == != 같음, 다름 왼쪽 → 오른쪽
8 & 비트 AND 왼쪽 → 오른쪽
9 ^ 비트 XOR 왼쪽 → 오른쪽
10 | 비트 OR 왼쪽 → 오른쪽
11 && 논리 AND 왼쪽 → 오른쪽
12 || 논리 OR 왼쪽 → 오른쪽
13 ?: 삼항 연산자 오른쪽 → 왼쪽
14 = += -= *= /= %= 대입 및 복합 대입 연산자 오른쪽 → 왼쪽
15 , 쉼표 연산자 왼쪽 → 오른쪽

 

연산자는 코드의 논리와 기능을 구현하는 핵심 도구입니다. 다양한 연산자를 이해하고 적재적소에 활용하면 효율적이고 간결한 코드를 작성할 수 있습니다.

'C' 카테고리의 다른 글

C) 제어문: 조건문, 반복문  (0) 2024.11.28
C) 변수와 자료형  (0) 2024.11.23
C) 개발 환경 설정: 컴파일러 설치 및 설정  (1) 2024.11.22
C) C 언어 소개  (0) 2024.11.22
C 언어로 OOP 스타일의 Stack 구현하기  (0) 2024.11.22

프로그래밍에서 변수는 데이터를 저장하는 상자이며, 자료형은 이 상자가 어떤 종류의 데이터를 담을지 정의합니다. 자료형은 시스템 아키텍처(32비트, 64비트)와 운영체제(Linux, Windows)에 따라 크기와 범위가 다를 수 있습니다. 또한, **오버플로우(Overflow)**와 **언더플로우(Underflow)**와 같은 특수 상황도 이해해야 합니다.

1. 자료형의 정의와 변수 선언

  • 변수: 데이터를 저장하기 위한 이름이 있는 공간.
  • 자료형: 저장할 데이터의 유형(숫자, 문자 등)과 크기를 정의.

자료형 예제

int age = 25;        // 정수형
float pi = 3.14;     // 실수형
char grade = 'A';    // 문자형
 

2. 기본 자료형과 운영체제 및 아키텍처별 차이

자료형 32bit Linux 64bit Linux 32bit Windows 64bit Windows 범위(signed) 범위(unsigned)
char 1 byte 1 byte 1 byte 1byte -128 ~ 127 0 ~ 255
short 2 byte 2 byte 2 byte 2 byte -32,768 ~ 32,767 0 ~ 65,535
int 4 byte 4 byte 4 byte 4 byte -2,147,483,648 ~ 2,147,483,647 0 ~ 4,294,967,295
long 4 byte 8 byte 4 byte 4 byte -2,147,483,648 ~ 2,147,483,647 (32비트 기준) 0 ~ 4,294,967,295
long long 8 byte 8 byte 8 byte 8 byte -9,223,372,036,854,775,808 ~ ... 0 ~ 18,446,744,073,709,551,615
float 4 byte 4 byte 4 byte 4 byte 1.2E-38 ~ 3.4E+38 N/A

double 8 byte 8 byte 8 byte 8 byte 2.3E-308 ~ 1.7E+308
long double 12~16  byte 12~16  byte 8 byte 8 byte 환경에 따라 다름

운영체제와 아키텍처 차이

  1. Linux vs Windows:
    • Linux: long은 64비트에서 8바이트, Windows에서는 4바이트.
    • long double은 Linux에서 더 높은 정밀도(12~16바이트)를 가짐.
  2. 32비트 vs 64비트:
    • 64비트 환경에서는 주소 크기가 증가하므로 일부 자료형(long)이 더 큰 크기를 가질 수 있음.

3. signed와 unsigned의 차이

  • signed: 양수와 음수를 모두 표현.
  • unsigned: 음수를 제외하고 양수 범위를 2배로 확장.

예제 코드

signed int s = -10;    // 양수와 음수 모두 가능
unsigned int u = 300;  // 음수 불가능, 양수 범위 확장

4. 오버플로우와 언더플로우

오버플로우(Overflow)

변수에 저장 가능한 최대값을 초과했을 때 다시 최소값으로 돌아가는 현상.
예: unsigned char에서 255를 초과하면 0으로 돌아감.

언더플로우(Underflow)

변수에 저장 가능한 최소값 아래로 내려갔을 때 최대값으로 돌아가는 현상.
예: unsigned char에서 0 아래로 가면 255로 돌아감.

예제 코드

#include <stdio.h>

int main() {
    unsigned char max = 255;
    unsigned char min = 0;

    max += 1;  // 255를 넘어서 0으로 돌아감
    min -= 1;  // 0 아래로 가서 255로 돌아감

    printf("Overflow: %u\n", max);
    printf("Underflow: %u\n", min);

    return 0;
}

 

5. 자료형 안전하게 사용하기

  1. 최대값과 최소값 확인
    <limits.h>를 사용하여 자료형의 최대값과 최소값을 확인할 수 있습니다.
  2. 초기화 습관
    선언된 변수는 항상 초기화하세요. 초기화하지 않으면 쓰레기 값(Random Value)을 가질 수 있습니다.
#include <stdio.h>
#include <limits.h>

int main() {
    printf("Max int: %d, Min int: %d\n", INT_MAX, INT_MIN);
    printf("Max unsigned int: %u\n", UINT_MAX);
    return 0;
}

 

6. 실습 코드: 자료형과 오버플로우 테스트

#include <stdio.h>
#include <limits.h>

int main() {
    unsigned int u_max = UINT_MAX; // 최대값
    int s_max = INT_MAX;           // 최대값
    int s_min = INT_MIN;           // 최소값

    printf("Unsigned int max: %u\n", u_max);
    printf("Signed int max: %d\n", s_max);
    printf("Signed int min: %d\n", s_min);

    // 오버플로우와 언더플로우 테스트
    u_max += 1; // 최대값 초과
    s_max += 1; // 최대값 초과
    s_min -= 1; // 최소값 초과

    printf("After Overflow: Unsigned: %u, Signed max: %d, Signed min: %d\n", u_max, s_max, s_min);

    return 0;
}

 

 

자료형은 프로그래밍의 기초이자, 안전한 코딩의 핵심입니다.
운영체제와 아키텍처에 따라 자료형의 크기와 동작이 달라질 수 있으므로 항상 이를 염두에 두고 작성해야 합니다. 또한, 오버플로우언더플로우 같은 특수 상황을 이해하면 버그를 예방할 수 있습니다.

'C' 카테고리의 다른 글

C) 제어문: 조건문, 반복문  (0) 2024.11.28
C) 연산자  (0) 2024.11.25
C) 개발 환경 설정: 컴파일러 설치 및 설정  (1) 2024.11.22
C) C 언어 소개  (0) 2024.11.22
C 언어로 OOP 스타일의 Stack 구현하기  (0) 2024.11.22

이번 포스팅에서는 64비트 MinGW를 설치하고 Visual Studio Code(VS Code)에서 코딩하는 방법을 알아보겠습니다.
64비트 컴파일러는 더 큰 메모리 공간과 향상된 성능을 제공하므로 최신 시스템에서 사용을 권장합니다.

1. MinGW-w64 소개

MinGW-w64란?

MinGW-w64는 MinGW의 확장판으로, 64비트와 32비트 환경 모두를 지원합니다.

  • GCC 기반으로 다양한 플랫폼에서 동작하며, 특히 64비트 Windows 개발에 최적화되어 있습니다.

2. MinGW-w64 설치

1단계: 다운로드

  1. MinGW-w64 공식 다운로드 페이지로 이동합니다.
  2. 64-bit GCC 버전(예: GCC x.x.x x86_64-posix-seh)을 선택하여 설치 파일을 다운로드합니다.
    • SEH(Synchronous Exception Handling): 64비트 Windows에 적합.
    • Posix 스레드 모델 사용 추천.

2단계: 설치

  1. 다운로드한 설치 파일을 실행하고, 설치 경로를 지정합니다.
    • 예: C:\mingw64
  2. 설치가 완료되면, bin 디렉토리를 환경 변수(Path)에 추가합니다.
    • 예: C:\mingw64\bin

3단계: 설치 확인

  1. 명령 프롬프트를 열고, 아래 명령어를 입력합니다: 
  2. gcc --version
  3. 설치된 GCC 버전 정보가 출력되면 정상적으로 설정된 것입니다.

3. Visual Studio Code와 연동

1단계: VS Code 설치 및 확장팩 추가

  1. VS Code 공식 홈페이지에서 다운로드 후 설치합니다.
  2. C/C++ 확장팩을 설치합니다.
    • VS Code 왼쪽의 확장 탭(Ctrl+Shift+X)에서 C/C++을 검색하고 설치.

64비트 MinGW-w64와 VS Code를 사용하면 최신 시스템의 성능을 최대한 활용할 수 있습니다.
개발 환경을 성공적으로 설정했다면, 간단한 프로그램을 작성해 실습을 시작해 보세요!

'C' 카테고리의 다른 글

C) 연산자  (0) 2024.11.25
C) 변수와 자료형  (0) 2024.11.23
C) C 언어 소개  (0) 2024.11.22
C 언어로 OOP 스타일의 Stack 구현하기  (0) 2024.11.22
C) C언어로 회원가입/로그인 시스템 구현하기!  (1) 2023.03.19

안녕하세요! 오늘은 프로그래밍 언어의 역사에서 빠질 수 없는 C 언어에 대해 알아보겠습니다. C 언어는 현대 프로그래밍의 기초를 닦은 중요한 언어로, 그 역사와 특징, 그리고 다양한 사용 용도까지 함께 살펴보겠습니다.

C 언어의 역사

C 언어는 1972년, 벨 연구소의 데니스 리치(Dennis Ritchie)에 의해 개발되었습니다.
당시에는 UNIX 운영체제를 효율적으로 개발하기 위해 만들어졌는데, 이 과정에서 C 언어는 이전의 B 언어와 어셈블리 언어의 단점을 극복하며 큰 주목을 받았습니다.

  • 1978년: 데니스 리치와 브라이언 커니핸(Brian Kernighan)이 저술한 "The C Programming Language"가 출판되면서 C 언어는 대중화되기 시작했습니다.
  • 1989년: ANSI(미국 표준 협회)에서 ANSI C 표준을 제정하여 언어의 일관성을 강화했습니다. 이후 ISO 표준으로도 채택되었습니다.
  • 현대: 오늘날에도 C 언어는 다양한 시스템과 소프트웨어의 핵심 언어로 자리 잡고 있습니다.

C 언어의 특징

C 언어는 다음과 같은 강력한 특징을 지니고 있습니다:

1. 저수준 언어와 고수준 언어의 조화

C 언어는 어셈블리 수준의 저수준 언어와, 고수준 언어의 가독성과 추상화된 개념을 동시에 제공합니다.
이를 통해 하드웨어 제어효율적인 프로그래밍이 모두 가능해졌습니다.

2. 이식성

C 언어로 작성된 프로그램은 다양한 플랫폼에서 쉽게 실행할 수 있습니다.
이는 C 언어가 시스템 간의 독립성을 제공하여, 유닉스, 윈도우, 리눅스 등 다양한 환경에서 사용되도록 했습니다.

3. 풍부한 라이브러리

C 표준 라이브러리는 기본적인 데이터 처리, 문자열, 파일 입출력 등 많은 기능을 제공합니다.

4. 구조적 프로그래밍

C 언어는 함수 중심의 구조적 프로그래밍을 지원합니다. 이를 통해 프로그램을 모듈화하고 유지보수성을 높이는 데 큰 도움이 됩니다.

5. 속도와 효율성

C 언어는 하드웨어에 밀접하게 접근할 수 있어 빠른 실행 속도를 자랑합니다.
그래서 운영체제, 임베디드 시스템 등 성능이 중요한 분야에서 주로 사용됩니다.

C 언어의 사용 용도

C 언어는 아래와 같은 다양한 분야에서 활용되고 있습니다:

1. 운영체제 개발

C 언어는 UNIX와 같은 운영체제를 개발하기 위해 만들어졌습니다.
현재도 Linux 커널이나 Windows 운영체제의 많은 부분이 C 언어로 작성되어 있습니다.

2. 임베디드 시스템

소형 기기(마이크로컨트롤러, IoT 기기 등)와 같은 임베디드 시스템의 소프트웨어를 작성하는 데 C 언어가 많이 사용됩니다.

3. 컴파일러 및 인터프리터

다른 프로그래밍 언어의 컴파일러와 인터프리터를 만드는 데에도 사용됩니다. 예를 들어, Python의 일부 컴포넌트는 C로 구현되었습니다.

4. 게임 개발

그래픽 처리와 하드웨어 제어가 중요한 게임 개발에서도 C는 주요 언어로 사용됩니다.

5. 과학 및 공학 분야

수학적 계산이나 시뮬레이션 같은 고성능 연산이 필요한 분야에서도 C 언어가 널리 사용됩니다.

 

C 언어는 단순하지만 강력한 도구로서, 컴퓨터 과학의 핵심을 이해하는 데 필수적인 언어입니다.
C 언어의 탄생 배경과 강력한 특징을 알게 되셨다면, 이제 직접 코드를 작성하며 그 매력을 경험해 보시는 건 어떨까요?

앞으로 이 블로그에서 C 언어의 기본부터 심화 주제까지 다뤄보겠습니다.

C 언어로 OOP 스타일의 Stack 구현하기

최근 C 언어에서 객체지향 프로그래밍(OOP)을 구현하려고 시도하면서, C++에서 많이 사용되는 벡터(Vector)를 구현하기 전에 먼저 스택(Stack)을 구현해보기로 했습니다. 자료구조를 공부하면서 Stack의 push 연산부터 구현해보기로 했고, 이를 OOP 스타일로 어떻게 구현할 수 있는지 살펴보았습니다.

이번에 구현한 스택은 C 언어에서 객체지향 프로그래밍의 개념을 적용하여, 스택에 데이터를 push, pop, clear, delete하는 메서드를 갖는 구조체를 작성하는 방식입니다.

프로젝트 링크

이 프로젝트는 GitHub - Object-Oriented-C-Language에서 확인할 수 있습니다. 이곳의 algorithm.h와 algorithm.c 파일을 참고하여 스택을 구현했습니다.

algorithm.h (헤더 파일)

#include "main.h"

#ifndef __ALGORITHM_H
#define __ALGORITHM_H

#pragma pack(push, 1)
typedef struct STACK {
    void** data;               // 스택에 저장될 데이터
    size_t top;                // 스택의 탑(가장 최근에 저장된 데이터)
    size_t size;               // 스택이 차지하는 메모리 크기
    size_t byteSize;           // 각 데이터 항목의 크기
    void (*push)(struct STACK*, void*);  // push 함수 포인터
    void* (*pop)(struct STACK*);         // pop 함수 포인터
    void (*clear)(struct STACK*);        // clear 함수 포인터
    void (*delete)(struct STACK*);       // delete 함수 포인터
} Stack;
#pragma pack(pop)

Stack new_stack(size_t type);  // 새로운 스택 생성 함수

#define new_Stack(type) new_stack(sizeof(type))  // 매크로를 사용하여 타입에 맞는 스택 생성
#endif

이 헤더 파일에서는 Stack 구조체를 정의하고, 스택을 조작하는 메서드들을 함수 포인터로 연결했습니다. 각 메서드는 스택에 데이터를 추가하거나 제거하고, 스택을 초기화하거나 삭제하는 기능을 합니다. 또한, new_stack() 함수는 새로운 스택을 생성하는 함수로, 각 데이터 항목의 크기를 인자로 받습니다.

algorithm.c (구현 파일)

#include "algorithm.h"

// stackPush: 스택에 데이터를 추가하는 함수
// stack: 스택 객체, data: 스택에 넣을 데이터
static void stackPush(Stack* stack, void* data) {
    // 스택에 저장된 데이터의 크기를 확장하기 위해 realloc 호출
    // 현재 스택의 크기보다 하나 더 큰 크기로 재할당
    stack->data = (void**)realloc(stack->data, (stack->top + 1) * sizeof(void*));
    
    // 새 공간에 데이터를 할당하고, 해당 위치에 data 복사
    *(stack->data + stack->top) = malloc(stack->byteSize);  // 새 데이터를 위한 메모리 할당
    memcpy(*(stack->data + stack->top), data, stack->byteSize);  // 실제 데이터 복사

    // 스택의 top을 증가시키고, 스택의 크기를 다시 계산
    stack->top++;
    stack->size = stack->top * sizeof(void*);
}

// stackPop: 스택에서 데이터를 꺼내는 함수
// stack: 스택 객체
static void* stackPop(Stack* stack) {
    // 스택의 맨 위에서 데이터를 제거하고, 그 데이터를 반환
    void* removedData = *(stack->data + stack->top - 1);

    // top을 하나 줄이고, 스택 크기 재계산
    stack->top--;
    stack->size = stack->top * sizeof(void*);
    
    // 스택 메모리를 재할당하여 크기 줄이기
    stack->data = (void**)realloc(stack->data, stack->size);
    
    // 꺼낸 데이터 반환
    return removedData;
}

// stackClear: 스택에 저장된 모든 데이터를 해제하고, 스택을 비우는 함수
// stack: 스택 객체
static void stackClear(Stack* stack) {
    // 스택에 있는 각 데이터를 해제
    for (size_t i = 0; i < stack->top; i++) {
        free(*(stack->data + i));  // 각 데이터 메모리 해제
    }
    
    // 스택 정보를 초기화
    stack->top = 0;   // 스택의 top을 0으로 초기화
    stack->size = 0;   // 스택의 크기도 0으로 초기화
    free(stack->data); // 스택 데이터 메모리 해제
    stack->data = NULL;  // 데이터 포인터 초기화
}

// stackDelete: 스택을 삭제하는 함수
// stack: 스택 객체
static void stackDelete(Stack* stack) {
    // 스택을 비운 후
    stackClear(stack);
    
    // 스택 관련 정보를 초기화
    stack->byteSize = 0;  // 각 데이터 항목의 크기 초기화
    stack->data = NULL;    // 데이터 포인터 초기화
}

// new_stack: 새로운 스택을 생성하는 함수
// type: 스택에 저장될 데이터 타입의 크기
Stack new_stack(size_t type) {
    Stack stack;
    stack.data = NULL;  // 초기 데이터 포인터는 NULL
    stack.push = stackPush;   // push 함수 포인터 연결
    stack.pop = stackPop;     // pop 함수 포인터 연결
    stack.clear = stackClear; // clear 함수 포인터 연결
    stack.delete = stackDelete; // delete 함수 포인터 연결
    stack.top = 0;  // 초기 top 값은 0
    stack.size = 0; // 초기 크기는 0
    stack.byteSize = type;  // 데이터 항목 크기 설정 (type)
    
    return stack;  // 초기화된 스택 객체 반환
}

각 함수 설명:

  1. stackPush:
    • 스택에 데이터를 추가하는 함수입니다.
    • realloc을 사용해 기존의 data 배열을 확장하고, 새 데이터를 할당하여 복사합니다.
  2. stackPop:
    • 스택에서 데이터를 제거하는 함수입니다.
    • top을 감소시키고, 스택의 크기를 다시 계산하여 realloc을 사용해 메모리를 조정합니다.
  3. stackClear:
    • 스택에 저장된 모든 데이터를 해제하고 스택을 초기화합니다.
  4. stackDelete:
    • stackClear를 호출하여 스택을 비운 후, 스택 구조체 내의 추가적인 메모리 해제를 수행합니다.
  5. new_stack:
    • 새로운 스택을 생성하는 함수입니다. 타입 크기를 인자로 받아 스택 구조체를 초기화하고 반환합니다.

매크로 new_Stack:

매크로 new_Stack(type)은 new_stack(sizeof(type))을 호출하는 방식으로, 스택을 쉽게 생성할 수 있게 도와줍니다. 예를 들어, new_Stack(int)를 사용하면 int 타입에 맞는 스택을 생성할 수 있습니다.

예시 사용법:

int main() {
    Stack intStack = new_Stack(int);  // int 타입의 스택 생성
    int value = 10;

    intStack.push(&intStack, &value);  // 값 10을 스택에 push
    int* poppedValue = (int*)intStack.pop(&intStack);  // 값을 pop하고 출력

    printf("Popped value: %d\n", *poppedValue);  // 출력: Popped value: 10

    intStack.delete(&intStack);  // 스택 삭제
    return 0;
}

결론

C 언어에서 객체지향적인 방식으로 스택을 구현하면, 코드의 재사용성과 유지보수성이 높아지며, 다양한 데이터 타입을 처리할 수 있는 유연성을 갖게 됩니다. void* 포인터를 활용해 어떤 데이터 타입도 저장할 수 있도록 구현한 것이 큰 특징입니다. 이를 통해 C 언어에서도 객체지향 프로그래밍의 핵심 개념을 적용할 수 있다는 점에서 유용한 학습이 되었습니다.

얼마 전 친구들이 디코에서 회원가입/로그인 시스템을 만들고 있길래 재밌어 보여서 C로 만들어 봤다.

https://github.com/sunwookim05/Member-management-system

 

GitHub - sunwookim05/Member-management-system

Contribute to sunwookim05/Member-management-system development by creating an account on GitHub.

github.com

먼저 main.h 를 만든다 모든 코드의 베이스가 됄 코드이다.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>

#pragma once
#ifndef __MAIN_H
#define __MAIN_H

#define import extern
#define final const
#define null NULL
#define false 0
#define true 1

typedef char *String;
typedef char byte;
typedef char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char ubyte;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef uint8_t boolean;

#pragma warning(pop)
#endif

그리고 전에 만들었던 Scanner-with-C 를 활용하고 싶어 Scanner와 System 클래스를 구현했다.

 

System.h

#include "main.h"

#pragma once
#ifndef __SYSTEM_H
#define __SYSTEM_H

struct __stdin_t;

#pragma pack(push, 1)
/**
 * The {@code System} class contains several useful class fields
 * and methods. It cannot be instantiated.
 *
 * Among the facilities provided by the {@code System} class
 * are standard input, standard output, and error output streams;
 * access to externally defined properties and environment
 * variables; a means of loading files and libraries; and a utility
 * method for quickly copying a portion of an array.
 *
 * @since   1.0
 */
typedef struct _System{
    struct __stdout_t{
        /**
        * @brief System out print function
        * @param format
        * @return void
        */
        void (*print)(const String, ...);
        /** 
        * @brief System out println function
        * @param format
        * @return void
        */
        void (*println)(const String, ...);
    }out;
    struct __stdin_t{
        int (*read)();
    } in;
}SYSTEM;
#pragma pack(pop)

#pragma warning(pop)
#endif

Scanner.h

#include "main.h"

#pragma once
#ifndef __Scanner_H
#define __Scanner_H

struct __stdin_t;

#pragma pack(push, 1)
/** 
 * @brief Scanner class
 */
typedef struct Scanner{
    /**
    * @brief User input char
    * @param void
    * @return char
    */    
    char (*nextChar)(void);

    /**
     * @brief User input Byte
     * @param void
     * @return byte
     */
    byte (*nextByte)(void);

    /**
     * @brief User input Short
     * @param void
     * @return int16_t
     */
    int16_t (*nextShort)(void);

    /**
     * @brief User input Int
     * @param void
     * @return int32_t
     */
    int32_t (*nextInt)(void);

    /**
     * @brief User input Long
     * @param void
     * @return int64_t
     */
    int64_t (*nextLong)(void);

    /**
     * @brief User input Unsigned Byte
     * @param void
     * @return ubyte
     */
    ubyte (*nextUByte)(void);

    /**
     * @brief User input Unsigned Short
     * @param void
     * @return uint16_t
     */
    uint16_t (*nextUShort)(void);

    /**
     * @brief User input Unsigned Int
     * @param void
     * @return uint32_t
     */
    uint32_t (*nextUInt)(void);

    /**
     * @brief User input Unsigned Long
     * @param void
     * @return uint64_t
     */
    uint64_t (*nextULong)(void);

    /**
     * @brief User input boolean
     * @param void
     * @return boolean
     */
    boolean (*nextBoolean)(void);

    /**
     * @brief User input Float
     * @param void
     * @return float
     */
    float (*nextFloat)(void);

    /**
     * @brief User input Double
     * @param void
     * @return double
     */
    double (*nextDouble)(void);

    /**
     * @brief User input Long Double
     * @param void
     * @return long double
     */
    long double (*nextLDouble)(void);

    /**
     * @brief User input String no Spaces
     * @param void
     * @return String
     */
    String (*next)(void);

    /**
     * @brief User input String with Spaces
     * @param void
     * @return String
     */
    String (*nextLine)(void);
}Scanner;
#pragma pack(pop)

Scanner new_Scanner(struct __stdin_t);

#pragma warning(pop)
#endif

Systemout.c

#include "main.h"
#include "System.h"

void print(const String format, ...) {va_list ap;char buf[4096];va_start(ap, format);vsprintf(buf, format, ap);va_end(ap);fprintf(stdout, "%s", buf);}
void println(const String format, ...) {va_list ap;char buf[4096];va_start(ap, format);vsprintf(buf, format, ap);va_end(ap);fprintf(stdout, "%s\n", buf);}

SYSTEM System = {print, println};

Scanner.c

#include "main.h"
#include "System.h"
#include "Scanner.h"

char nextChar(void){
    char c;
    scanf("%c", &c);
    getchar();
    return c;
}

int8_t nextByte(void){
    int8_t b;
    scanf("%hhd", &b);
    getchar();
    return b;
}

int16_t nextShort(void){
    int16_t s;
    scanf("%hd", &s);
    getchar();
    return s;
}

int32_t nextInt(void){
    int32_t i;
    scanf("%d", &i);
    getchar();
    return i;
}

int64_t nextLong(void){
    int64_t l;
    scanf("%lld", &l);
    getchar();
    return l;
}

uint8_t nextUByte(void){
    uint8_t b;
    scanf("%hhu", &b);
    getchar();
    return b;
}

uint16_t nextUShort(void){
    uint16_t s;
    scanf("%hu", &s);
    getchar();
    return s;
}

uint32_t nextUInt(void){
    uint32_t i;
    scanf("%u", &i);
    getchar();
    return i;
}

uint64_t nextULong(void){
    uint64_t l;
    scanf("%llu", &l);
    getchar();
    return l;
}

boolean nextBoolean(void){
    String s = (String)calloc(0, sizeof(char) * 5);
    scanf("%s", s);
    if(atoi(s)|| !strncmp(s, "true", 4) || !strncmp(s, "True", 4) || !strncmp(s, "TRUE", 4)){
        free(s);
        return true;
    }else{
        free(s);
        return false;
    }
}

float nextFloat(void){
    float f;
    scanf("%f", &f);
    getchar();
    return f;
}

double nextDouble(void){
    double d;
    scanf("%lf", &d);
    getchar();
    return d;
}

long double nextLDouble(void){
    long double ld;
    scanf("%Lf", &ld);
    getchar();
    return ld;
}

String next(void){
    String s = (String)calloc(0, sizeof(char) * 4096);
    scanf("%s", s);
    s = (String)realloc(s, sizeof(char) * (strlen(s) + 1));
    getchar();
    return s;
}

String nextLine(void){
    String s = (String)calloc(0, sizeof(char) * 4096);
    scanf("%[^\n]", s);
    getchar();
    return s;
}

/**
* Constructs a new {@code Scanner} that produces values scanned
* from the specified input stream. Bytes from the stream are converted
* into characters using the underlying platform's
*
* @param  source An input stream to be scanned
*/
Scanner new_Scanner(struct __stdin_t source){
    Scanner scanner = {
        .nextChar = nextChar,
        .nextByte = nextByte,
        .nextShort = nextShort,
        .nextInt = nextInt,
        .nextLong = nextLong,
        .nextUByte = nextUByte,
        .nextUShort = nextUShort,
        .nextUInt = nextUInt,
        .nextULong = nextULong,
        .nextBoolean = nextBoolean,
        .nextFloat = nextFloat,
        .nextDouble = nextDouble,
        .nextLDouble = nextLDouble,
        .next = next,
        .nextLine = nextLine
    };
    return scanner;
}

다음으론 User 의 정보를 저장하고 처리할 수 있는 User 클래스를 구현하기 위해 User.h 와 User.c 를 만들었다.

 

User.h

#include "main.h"

#pragma once
#ifndef __User_H
#define __User_H

typedef struct __USERDATA__{
    String id;
    String password;
}USERDATA;

typedef struct __USERS__{
    void (*writeLog)(uint8_t, USERDATA, FILE *);
    boolean (*logout)(USERDATA *);
    boolean (*signup)(USERDATA *, FILE *);
    boolean (*login)(USERDATA *, FILE *);
}USERS;

typedef enum __MENU__{
    SIGNUP = 1, LOGIN, LOGOUT, USERINFO,  EXIT
}__MENU__;

typedef uint8_t MENU;

USERS new_User();
USERDATA new_UserData();
void delete_UserData(USERDATA *);

#pragma warning(pop)
#endif

User.c

#include "User.h"
#include "System.h"
#include "Scanner.h"

import SYSTEM System;

void delete_UserData(USERDATA *info){
    free(info->id);
    free(info->password);
}

void writeLog(uint8_t logCate, USERDATA info, FILE *fp){
    String logCateStr[3] = {"SIGNUP", "LOGIN", "LOGOUT"};
    time_t t = time(NULL);
    struct tm tm = *localtime(&t);
    fp = fopen("log.txt", "a");
    fprintf(fp, "%d-%d-%d %d:%d:%d %s\nID: %s\nPassword: %s\n\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, logCateStr[logCate - 1], info.id, info.password);
    fclose(fp);
}

boolean signup(USERDATA *info, FILE *fp){
    Scanner sc = new_Scanner(System.in);
    fp = fopen("user.txt", "r");
    System.out.println("Sign up");
    System.out.print("ID: ");
    info->id = sc.next();
    System.out.print("Password: ");
    info->password = sc.next();

    USERDATA user = new_UserData();
    while (fscanf(fp, "%s", user.id) != EOF){
        if(!strcmp(info->id, user.id)){
            delete_UserData(&user);
            return false;
        }
    }
    delete_UserData(&user);
    fclose(fp);

    fp = fopen("user.txt", "a");
    fprintf(fp, "%s %s\n", info->id, info->password);
    fclose(fp);
    return true;
}

boolean login(USERDATA *user, FILE *fp){
    Scanner sc = new_Scanner(System.in);
    fp = fopen("user.txt", "r");
    System.out.println("Login");
    System.out.print("ID: ");
    user->id = sc.next();
    System.out.print("Password: ");
    user->password = sc.next();
    
    USERDATA info = new_UserData();
    
    while (fscanf(fp, "%s %s", info.id, info.password) != EOF){
        if(!strcmp(user->id, info.id) && !strcmp(user->password, info.password)){
            delete_UserData(&info);
            fclose(fp);
            return true;
        }
    }
    fclose(fp);
    delete_UserData(&info);
    return false;
}

boolean logout(USERDATA *info){
    Scanner sc = new_Scanner(System.in);
    System.out.print("Do you want to logout? (y/n): ");
    char bf = sc.nextChar();
    if(bf == 'y' | 'Y'){
        info->id = NULL;
        info->password = NULL;
        delete_UserData(info);
        System.out.println("Logout success");
        return true;
    }else{
        System.out.println("Logout failed");
        return false;
    }
}

USERS new_User(){
    USERS user;
    user.logout = logout;
    user.signup = signup;
    user.login = login;
    user.writeLog = writeLog;
    return user;
}

USERDATA new_UserData(){
    USERDATA info;
    info.id = (String)calloc(0, sizeof(char) * 20);
    info.password = (String)calloc(0, sizeof(char) * 20);
    return info;
}

마지막으로 클래스들을 활용하여 호출할 메인 클레스를 만든다.

 

main.c

#include "main.h"
#include "System.h"
#include "Scanner.h"
#include "User.h"

import SYSTEM System;

int main(void){
    Scanner sc = new_Scanner(System.in);
    USERS users = new_User();
    USERDATA user = new_UserData();
    FILE *fp;
    boolean isSignup = false;
    boolean isLogin = false;
    boolean isLogout = true;
    MENU menu = 0;

    while (true){
        System.out.print("Select menu\n1. Sign up\n2. Login\n3. Logout\n4. LOGININFO\n5. EXIT\n");
        menu = sc.nextUByte();
        if(menu == SIGNUP){
            isSignup = users.signup(&user, fp);
            System.out.println(isSignup ? "Sign up success" : "Sign up failed");
            if (isSignup) users.writeLog(SIGNUP, user, fp);
        }else if(menu == LOGIN){
            if(isLogin){
                System.out.println("Already login");
            }else{
                isLogin = users.login(&user, fp);
                System.out.println(isLogin ? "Login success" : "Login failed");
                users.writeLog(LOGIN, user, fp);
            }
        }else if(menu == LOGOUT){
            if(!isLogin){
                System.out.println("Already logout");
            }else{
                users.writeLog(LOGOUT, user, fp);
                isLogout = users.logout(&user);
                if(isLogout) isLogin = false;
            }
        }else if(menu == USERINFO){
            if(isLogin) System.out.println("ID: %s\nPassword: %s", user.id, user.password);
            else System.out.println("Please login");
        }else if(menu == EXIT){
            break;
        }else{
            System.out.println("Wrong menu");
        }
    }

    delete_UserData(&user);
    return 0;
}

콘솔에 이렇게 입력하면 실행이 된다.

gcc -c src\main.c -I inc\

gcc -c src\Scanner.c -I inc\

gcc -c src\Systemout.c -I inc\

gcc -c src\User.c -I inc\

gcc -o main main.o Scanner.o Systemout.o User.o

.\main

 

C 언어 로 자바 입출력 문을 구현 해보도록 하자

 

먼저 main.h 를 만든뒤 코드를 작성하자

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>

#ifndef _MAIN_H
#define _MAIN_H

#define import extern
#define final const
#define null NULL
#define false 0
#define true 1

typedef char *String;
typedef char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef uint8_t boolean;

#pragma pack(push, 1)
typedef struct _System{
    struct _OUT_{
        void (*print)(const String, ...);
        void (*println)(const String, ...);
    }out;
}Sys;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct _Scanner{
    char (*nextChar)(void);
    int8_t (*nextByte)(void);
    int16_t (*nextShort)(void);
    int32_t (*nextInt)(void);
    int64_t (*nextLong)(void);
    uint8_t (*nextUByte)(void);
    uint16_t (*nextUShort)(void);
    uint32_t (*nextUInt)(void);
    uint64_t (*nextULong)(void);
    boolean (*nextBoolean)(void);
    float (*nextFloat)(void);
    double (*nextDouble)(void);
    String (*next)(void);
    String (*nextLine)(void);
}Scanner;
#pragma pack(pop)

#endif

main.h 에는 클래스 원형을 구조체와 함수포인터로 만들어 준다.

 

이제 Systemout.c 파일을 만들자

#include "main.h"

void print(const String format, ...) {
	va_list ap;char buf[4096];
    va_start(ap, format);
    vsprintf(buf, format, ap);
    va_end(ap);
    fprintf(stdout, "%s", buf);
}
void println(const String format, ...) {
	va_list ap;char buf[4096];
    va_start(ap, format);
    vsprintf(buf, format, ap);
    va_end(ap);fprintf(stdout, "%s\n", buf);
}

Sys System = {print, println};

main.h 에 인클루드한 stdarg.h 를 사용하여 커스텀print 들을 구현한뒤 Sys System 으로 선언한뒤 print, println 을 함수포인터에 대입한다.

 

이제 Scanner.c 를 만들자

#include "main.h"

char nextChar(void){
    char c;
    scanf("%c", &c);
    getchar();
    return c;
}

int8_t nextByte(void){
    uint8_t b;
    scanf("%hhd", &b);
    getchar();
    return b;
}

int16_t nextShort(void){
    uint16_t s;
    scanf("%hd", &s);
    getchar();
    return s;
}

int32_t nextInt(void){
    uint32_t i;
    scanf("%d", &i);
    getchar();
    return i;
}

int64_t nextLong(void){
    uint64_t l;
    scanf("%lld", &l);
    getchar();
    return l;
}

uint8_t nextUByte(void){
    uint8_t b;
    scanf("%hhu", &b);
    getchar();
    return b;
}

uint16_t nextUShort(void){
    uint16_t s;
    scanf("%hu", &s);
    getchar();
    return s;
}

uint32_t nextUInt(void){
    uint32_t i;
    scanf("%u", &i);
    getchar();
    return i;
}

uint64_t nextULong(void){
    uint64_t l;
    scanf("%llu", &l);
    getchar();
    return l;
}

boolean nextBoolean(void){
    String s = (String)malloc(sizeof(char) * 5);
    scanf("%s", s);
    if(atoi(s) >= 1 || strcmp(s, "true") == 0){
        free(s);
        return true;
    }else{
        free(s);
        return false;
    }
}

float nextFloat(void){
    float f;
    scanf("%f", &f);
    getchar();
    return f;
}

double nextDouble(void){
    double d;
    scanf("%lf", &d);
    getchar();
    return d;
}

String next(void){
    String s = (String)calloc(0, sizeof(char) * 1000);
    scanf("%s", s);
    getchar();
    String str = s;
    return str;
}

String nextLine(void){
    String s = (String)calloc(0, sizeof(char) * 1000);
    scanf("%[^\n]", s);
    getchar();
    String str = s;
    return str;
}

Scanner sc = {
    nextChar,
    nextByte,
    nextShort,
    nextInt,
    nextLong,
    nextUByte,
    nextUShort,
    nextUInt,
    nextULong,
    nextBoolean,
    nextFloat,
    nextDouble,
    next,
    nextLine
};

Scanner 의 기능들을 구현한뒤 Scanner 의 함수포인터에 대입을 해준다.

 

마지막으로 ScannerTest.c 파일을 만든뒤 간단하게 테스트를 해보자

#include "main.h"

import Sys System;
import Scanner sc;

int main(void){
    System.out.print("int8_t: ");
    int8_t a = sc.nextByte();
    System.out.print("int16_t: ");
    int16_t b = sc.nextShort();
    System.out.print("int32_t: ");
    int32_t c = sc.nextInt();
    System.out.print("int64_t: ");
    int64_t d = sc.nextLong();
    System.out.print("uint8_t: ");
    int8_t e = sc.nextUByte();
    System.out.print("uint16_t: ");
    uint16_t f = sc.nextUShort();
    System.out.print("uint32_t: ");
    uint32_t g = sc.nextUInt();
    System.out.print("uint64_t: ");
    uint64_t h = sc.nextULong();
    System.out.print("boolean: ");
    boolean i = sc.nextBoolean();
    System.out.print("char: ");
    char j = sc.nextChar();
    System.out.print("float: ");
    float k = sc.nextFloat();
    System.out.print("double: ");
    double l = sc.nextDouble();
    System.out.print("String: ");
    String m = sc.next();
    System.out.print("String with space: ");
    String n = sc.nextLine();

    System.out.println(" ");
    System.out.println("int8_t: %d", a);
    System.out.println("int16_t: %d", b);
    System.out.println("int32_t: %d", c);
    System.out.println("int64_t: %lld", d);
    System.out.println("uint8_t: %u", e);
    System.out.println("uint16_t: %u", f);
    System.out.println("uint32_t: %u", g);
    System.out.println("uint64_t: %llu", h);
    System.out.println("boolean: %s", i ? "true" : "false");
    System.out.println("char: %c", j);
    System.out.println("float: %f", k);
    System.out.println("double: %f", l);
    System.out.println("String: %s", m);
    System.out.println("String with space: %s", n);

    free(m);
    free(n);

    return 0;
}

다른 파일에 있는 기능들을 사용하기위해 extern 을 import 로 매크로 정의한뒤 System 과 Scanner 을 import 한다.

그리고 입력 받은뒤 간단하게 출력하는예제를 작성 해보았다.

 

이제 터미널에 다음과 같은 명령어들을 입력해보자

gcc -c Systemout.c
gcc -c Scanner.c
gcc -c ScannerTest.c
gcc -o Scanner Systemout.o Scanner.o ScannerTest.o
.\Scanner

이렇게 터미널에 입력하면 간단한 예제가 실행됀다.

오늘은 여러 C 파일을 한개의 실행 파일로 만드는 법을 알아보려한다.

 

먼저 C 파일이 a.c b.c main.c 가 있다고 하면 

 

gcc -c a.c

gcc -c b.c

gcc -c main.c

 

콘솔이나 터미널에 위 처럼 입력하면 a.o, b.o, main.o 가 생성이 된다.

 

gcc -o (실행파일 이름) a.o b.o main.o 

 

위처럼 입력한다.

 

그러면 실행파일명.exe 를 실행 해보면 a.c, b.c, main.c 에 있는 함수들이 다 동작하는 것을 알 수있다. 

2022년 4월달에 열린 기능 경기 대회에 출전을 했다. 아쉽게도 입상은 못했지만 3과제에 관한 자신감이 생겨 소스코드와 분석을 올리려한다.

 

이번 대회에선 가스래인지 시뮬레이션을 주제로 한다.

 

기능대회 과제지:

[제3과제]공개과제.pdf
0.68MB

깃허브 링크: https://github.com/sunwookim05/2022-Regional-Skills-Competition-sorce

 

GitHub - sunwookim05/2022-Regional-Skills-Competition-sorce

Contribute to sunwookim05/2022-Regional-Skills-Competition-sorce development by creating an account on GitHub.

github.com

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
 * All rights reserved.</center></h2>
 *
 * This software component is licensed by ST under BSD 3-Clause license,
 * the "License"; You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                        opensource.org/licenses/BSD-3-Clause
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <stdlib.h>
#include "lcd1602.h"
#include "control_hardware.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef char *String;
typedef enum { // enum 으로 0부터 5까지 스텟을 정의한다.
	OVER, SAFE, OFF, ONN, AUTO, ON
} gstat;
typedef uint8_t boolean;// boolean 으로 0이면 false, 1이면 true를 정의한다.
typedef enum {//false, true를 정의한다.
	false, true
} _BOOL;
typedef struct {// 스텟을 정의한다.
	boolean over, safe, off, onn, au, on;
} Statflag;
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define SW1 HAL_GPIO_ReadPin(SW_ON_GPIO_Port, SW_ON_Pin) // SW1을 읽는다.
#define SW2 HAL_GPIO_ReadPin(SW_AUTO_GPIO_Port, SW_LOCK_Pin) // SW2를 읽는다.
#define SW3 HAL_GPIO_ReadPin(SW_AUTO_GPIO_Port, SW_AUTO_Pin) // SW3을 읽는다.
#define SW4 HAL_GPIO_ReadPin(SW_A_GPIO_Port, SW_A_Pin) // SW4을 읽는다.
#define SW5 HAL_GPIO_ReadPin(SW_B_GPIO_Port, SW_B_Pin) // SW5을 읽는다.
#define BUZZER(X) HAL_GPIO_WritePin(BUZZ_GPIO_Port, BUZZ_Pin, X) // BUZZER를 제어한다.
#define LED(N, X) HAL_GPIO_WritePin(LED##N##_GPIO_Port, LED##N##_Pin, !X) // LED(N)를 제어한다.
#define NOW(X) (HAL_GetTick() - X) // 현재시간에 X 를 뺸 값 을 정의한다.
#define TEMPUP(X) (X == 1 ? 900 : X == 2 ? 800 : X == 3 ? 700 : X == 4 ? 600 : X == 5 ? 500 : X == 6 ? 400 : X == 7 ? 300 : X == 8 ? 200 : X == 9 ? 100 : 0)
#define TEMPDOWN(X) (X < 10 ? 2900 : X >= 300 ? 100 : X >= 200 ? 200 : X >= 100 ? 400 : X >= 40 ? 700 : X >= 20 ? 1100 : X >= 15 ? 1600 : X >= 10 ? 2200 : 0)
#define LEDCLEAR LED(1, false); LED(2, false); LED(3, false); LED(4, false); LED(5, false) // LED를 모두 끈다.
/* USER CODE END PD */`

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc;
TIM_HandleTypeDef htim6;
/* USER CODE BEGIN PV */
int temp = 20, fire = 0, fireset = 0, altemp = 20, autemp = 80;// 온도와 불의 세기, 알람온도, 오토모드일때의 최대 온도 설정치를 정의한다.
boolean ledRingFlag = false;// LEDRing을켜고 끄는 역할을 하는 변수이다.
Statflag stat;// 스텟을 정의한다.
gstat gasstat = OFF;// 가스레인지의 상태를 정의한다.
uint32_t led_ring_data[10][12] = { //LEDRing 의 데이터를 배열안에 저장한다 (0xFF 이런식으로도 가능)
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
		{ 13, 0, 0,0, 13, 0, 0, 0, 13, 0, 0, 0 },
		{ 76, 0, 0, 0, 76, 0, 0, 0, 76, 0, 0, 0 },
		{ 255, 0, 0, 0, 255, 0, 0, 0,255, 0, 0, 0 },
		{ 255, 0, 13, 0, 255, 0, 13, 0, 255, 0, 13, 0 },
		{ 255, 0, 76, 0, 255, 0, 76, 0, 255, 0, 76, 0 },
		{ 255, 0, 255, 0, 255,0, 255, 0, 255, 0, 255, 0 },
		{ 255, 13, 255, 13, 255, 13, 255,13, 255, 13, 255, 13 },
		{ 255, 76, 255, 76, 255, 76, 255, 76,255, 76, 255, 76 },
		{ 255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255 }
};
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC_Init(void);
static void MX_TIM6_Init(void);
static void MX_NVIC_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void read_adc(uint16_t *cds, uint16_t *vr) { // ADC를 읽어서 cds와 vr에 저장한다.
	HAL_ADC_Start(&hadc);
	HAL_ADC_PollForConversion(&hadc, 1000);
	*cds = HAL_ADC_GetValue(&hadc);
	HAL_ADC_PollForConversion(&hadc, 1000);
	*vr = HAL_ADC_GetValue(&hadc);
	HAL_ADC_Stop(&hadc);
}

void lcd_print() {// LCD에 온도와 불의 세기를 출력한다.
	String statfont[6] = { "OVER HEAT", "SAFE LOCK", "OFF      ", "ON(NONE) ",
		"AUTO ADJ ", "ON       " };
	String bf = (char *)malloc(sizeof(char) * 16);
	if (stat.over)
		gasstat = OVER;
	else if (stat.safe)
		gasstat = SAFE;
	else if (stat.off)
		gasstat = OFF;
	else if (stat.onn)
		gasstat = ONN;
	else if (stat.au)
		gasstat = AUTO;
	else if (stat.on)
		gasstat = ON;
	sprintf(bf, "TEMP:%03d%cC  %c:%d ", temp, 0xDF, 1, fireset);
	lcd_gotoxy(0, 1);
	lcd_puts(bf);
	sprintf(bf, "[%.9s][%03d]", statfont[gasstat], altemp);
	lcd_gotoxy(0, 0);
	lcd_puts(bf);
	free(bf);
}

void led(uint16_t vr) {// LED를 켜고 끄는 역할을 한다.
	vr = ((uint8_t) ((float) vr / 1023.75) + 1);
	LED(1, (vr == 1));
	LED(2, (vr == 2));
	LED(3, (vr == 3));
	LED(4, (vr == 4));
	LED(5, (vr == 5));
	autemp = (vr == 2 ? 100 : vr == 3 ? 140 : vr == 4 ? 180 : vr == 5 ? 220 : 80);
}

typedef struct _IO{// 함수에 대한 포인터를 구조체에 정의하여 사용한다.
	void (*Lcd_Print)();
	void (*Led)(uint16_t);
	void (*Read_ADC)(uint16_t*, uint16_t*);

}IOcon;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { // TIM6의 이벤트 콜백 함수
	if (htim->Instance == htim6.Instance && ledRingFlag) { // LEDRing 제어를 한다.
		ledRingFlag = false;// LEDRing에대한 플레그를 false 로 설정.
		led_ring_update(led_ring_data[fire]);// LEDRing을 업데이트한다.
	}
}

void setUp(IOcon *io){ // 초기화를 수행한다.
	io->Lcd_Print = lcd_print; // LCD에 출력할 함수를 설정한다.
	io->Led = led;// LED에 출력할 함수를 설정한다.
	io->Read_ADC = read_adc;// ADC의 값을 받아오는 함수를 설정한다.
}

/* USER CODE END 0 */

/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void) {
	/* USER CODE BEGIN 1 */
	IOcon io; // 함수를 저장하는 구조체를 선언한다.
	boolean swFlag = false, buzflag = false, alflag = false; // 스위치, 부저, 알람 플레그를 선언한다.
	uint16_t cds, vr; // CDS, VR의 값을 저장하는 변수를 선언한다.
	uint32_t last = NOW(0); // 마지막 시간을 저장하는 변수를 선언한다.
	uint32_t flast = NOW(0); // 마지막 시간을 저장하는 변수를 선언한다.
	uint32_t tuplast = NOW(0); // 마지막 시간을 저장하는 변수를 선언한다.
	uint32_t tdownlast = NOW(0); // 마지막 시간을 저장하는 변수를 선언한다.
	uint32_t buzlast = NOW(0); // 마지막 시간을 저장하는 변수를 선언한다.
	/* USER CODE END 1 */

	/* MCU Configuration--------------------------------------------------------*/

	/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
	HAL_Init();

	/* USER CODE BEGIN Init */

	/* USER CODE END Init */

	/* Configure the system clock */
	SystemClock_Config();

	/* USER CODE BEGIN SysInit */

	/* USER CODE END SysInit */

	/* Initialize all configured peripherals */
	MX_GPIO_Init();
	MX_ADC_Init();
	MX_TIM6_Init();

	/* Initialize interrupts */
	MX_NVIC_Init();
	setUp(&io);// 초기화를 수행한다.
	/* USER CODE BEGIN 2 */
	LcdInit();
	lcd_cgram(1, 0); // 1이란 아스키 코드에 출력할 문자를 설정한다.
	lcd_puts("\fSmart Gas Range\n             001"); // LCD에 출력한다.
	HAL_Delay(2000); // 2초 대기한다.
	HAL_TIM_Base_Start_IT(&htim6); // TIM6의 이벤트 콜백 함수를 시작한다.
	ledRingFlag = true; // LEDRing에대한 플레그를 true로 설정한다.
	io.Lcd_Print(); // LCD에 출력한다.

	/* USER CODE END 2 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1) {
		/* USER CODE END WHILE */

		/* USER CODE BEGIN 3 */
		io.Read_ADC(&cds, &vr); // CDS, VR의 값을 읽어온다.
		if (!SW2) // 스위치2를 누르면
			stat.safe = true; // 안전상태를 true로 설정한다.
		else { // 스위치2를 누르지 않으면
			if (!SW1) { // 스위치1을 누르면
				if (cds > 3000) { // CDS의 값이 3000이상이면
					stat.on = true; // 전원상태를 true로 설정한다.
					stat.onn = false; // 켜졌지만 아무상태도 아닌상태를 false로 설정한다.
				} else { // CDS의 값이 3000미만이면
					stat.onn = true; // 켜졌지만 아무상태도 아닌상태를 true로 설정한다.
					stat.on = false; // 전원 상태를 false로 설정한다.
				}
				stat.off = false; // 꺼진 상태를를 false로 설정한다.
			} else { // 스위치1을 누르지 않으면
				if (temp < 150) { // 온도가 150미만이면
					stat.over = false; // 온도가 150도미만이면 과열상태를 false로 설정한다.
					buzflag = false; // 부저를 끈다.
				}
				stat.safe = false; // 안전상태를 false로 설정한다.
				stat.onn = false; // 켜졌지만 아무상태도 아닌상태를 false로 설정한다.
				stat.on = false; // 전원상태를 false로 설정한다.
				stat.off = true; // 꺼진 상태를 true로 설정한다.
			}
		}
		if (stat.off || stat.over || stat.safe) { // 꺼진 상태, 과열상태, 안전상태이면
			fireset = 0; // 불의 세기 설정을 0으로 설정한다.
			LEDCLEAR; // LED를 모두 끈다.
		} else if (stat.onn) { // 켜졌지만 아무상태도 아닌상태이면
			LEDCLEAR; // LED를 모두 끈다.
			fireset = 1; // 불의 세기 설정을 1으로 설정한다.
		} else if (stat.au && !stat.onn && cds > 3000) { // 켜진 상태이면서 켜졌지만 아무상태도 아닌상태이면서 CDS의 값이 3000이상이면
			io.Led(vr); // VR의 값에 따라 LED를 켜거나 끈다.
			if (1 < autemp - temp) // 온도가 1도 초과이면
				fireset = 9; // 불의 세기 설정을 9으로 설정한다.
			if (autemp - temp < -1) // 온도가 -1도 미만이면
				fireset = 1; // 불의 세기 설정을 1으로 설정한다.
		} else if (stat.on) { // 전원이 켜진 상태이면
			LEDCLEAR; // LED를 모두 끈다.
			fireset = vr / 511.875 + 1; // 불의 세기 설정을 VR의 값에 따라 설정한다.
		}
		if (!SW3 || !SW4 || !SW5) { // 스위치3,4,5를 누르면
			if (!swFlag) { // 스위치를 누르지 않았으면
				if (!SW3) // 스위치3을 누르면
					stat.au = (!stat.au ? true : false); // 안전상태를 true로 설정하거나 false로 설정한다.
				if (!SW4) // 스위치4를 누르면
					altemp -= 20; // 알람 온도를 20도 내링다.
				if (!SW5) // 스위치5를 누르면
					altemp += 20; // 알람 온도를 20도 올린다.
				if (altemp > 280) // 알람 온도가 280도 초과이면
					altemp = 280; // 알람 온도를 280로 설정한다.
				if (altemp < 20) // 알람 온도가 20도 미만이면
					altemp = 20; // 알람 온도를 20로 설정한다.
			}
			swFlag = true; // 스위치를 누른 상태로 설정한다.
		} else { // 스위치를 누르지 않았으면
			swFlag = false; // 스위치를 누른 상태로 설정하지 않는다.
		} 
		if (NOW(last) >= 10) { // 10ms 이상 시간이 지났으면
			if (NOW(flast) >= 100) { // 100ms 이상 시간이 지났으면
				if (fire < fireset) { // 불의 세기가 불의 세기 설정값보다 작으면
					fire++; // 불의 세기를 1씩 증가시킨다.
					io.Lcd_Print(); // LCD를 출력한다.
					ledRingFlag = true; // LEDRING을 업데이트 하는 플레그를 켠다.
				}
				if (fire > fireset) { // 불의 세기가 불의 세기 설정값보다 크면
					fire--; // 불의 세기를 1씩 감소시킨다.
					io.Lcd_Print(); // LCD를 출력한다.
					ledRingFlag = true; // LEDRING을 업데이트 하는 플레그를 켠다.
				}
				flast = NOW(0); // 시간을 현제로 초기화한다.
			}
			last = NOW(0); // 시간을 현제로 초기화한다.
		}
		if (NOW(tuplast)>= TEMPUP(fire) && TEMPUP(fire) != 0) { // 온도업데이트 시간이 지났으면
			temp++; // 온도를 1씩 증가시킨다.
			tuplast = NOW(0); // 시간을 현제로 초기화한다.
			if (temp > 300) // 온도가 300도 초과이면
				stat.over = true; // 과열상태를 true로 설정한다.
			io.Lcd_Print(); // LCD를 출력한다.
		}
		if (NOW(tdownlast) >= TEMPDOWN(temp - 20) && TEMPDOWN(temp - 20) != 0) { // 온도업데이트 시간이 지났으면
			temp--; // 온도를 1씩 감소시킨다.
			if (temp < 20) // 온도가 20도 미만이면
				temp = 20; // 온도를 20로 설정한다.
			tdownlast = NOW(0); // 시간을 현제로 초기화한다.
			io.Lcd_Print(); // LCD를 출력한다.
		}
		if (!buzflag && stat.over) { // 불이 과열상태이고  부저 플레그가 아니면
			buzlast = NOW(0); // 부저 시간을 현제로 초기화한다.
			buzflag = true; // 부저 플레그를 true로 설정한다.
		}
		if (altemp < temp && altemp > 20) { // 알람 온도가 온도보다 작고 20도 미만이면
			if (!alflag) { // 알람 플레그가 아니면
				buzlast = NOW(0); // 부저 시간을 현제로 초기화한다.
				alflag = true; // 알람 플레그를 true로 설정한다.
			}
		} else { // 알람 온도가 온도보다 작거나 20도 미만이면
			BUZZER(false); // 부저를 끈다.
			alflag = false; // 알람 플레그를 false로 설정한다.
		}
		if (buzflag) { // 부저 플레그가 true이면
			if ((NOW(buzlast) >= 100 && NOW(buzlast) <= 200) || (NOW(buzlast) >= 300 && NOW(buzlast) <= 400) || (NOW(buzlast) >= 500 && NOW(buzlast) <= 600)) // 부저 시간이 100~200, 300~400, 500~600이면
				BUZZER(true); // 부저를 켠다.
			else // 부저 시간이 100~200, 300~400, 500~600이 아니면
				BUZZER(false); // 부저를 끈다.
		} else if (alflag) { // 알람 플레그가 true이면
			if ((NOW(buzlast) >= 0 && NOW(buzlast) <= 100) // 부저 시간이 0~100이면
					|| (NOW(buzlast) >= 200 && NOW(buzlast) <= 300)) // 부저 시간이 200~300이면
				BUZZER(true); // 부저를 켠다.
			else // 부저 시간이 0~100, 200~300이 아니면
				BUZZER(false); // 부저를 끈다.
			if (NOW(buzlast) >= 1000) // 부저 시간이 1000이상이면
				buzlast = NOW(0); // 부저 시간을 현제로 초기화한다.
		}
		io.Lcd_Print(); // LCD를 출력한다.
	}
	/* USER CODE END 3 */
}

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void) {
	RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
	RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };

	/** Configure the main internal regulator output voltage
	 */
	__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

	/** Initializes the RCC Oscillators according to the specified parameters
	 * in the RCC_OscInitTypeDef structure.
	 */
	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
	RCC_OscInitStruct.HSIState = RCC_HSI_ON;
	RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
	RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
	RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_4;
	RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
		Error_Handler();
	}

	/** Initializes the CPU, AHB and APB buses clocks
	 */
	RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
			| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
	RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
	RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
	RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

	if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) {
		Error_Handler();
	}
}

/**
 * @brief NVIC Configuration.
 * @retval None
 */
static void MX_NVIC_Init(void) {
	/* TIM6_DAC_IRQn interrupt configuration */
	HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
}

/**
 * @brief ADC Initialization Function
 * @param None
 * @retval None
 */
static void MX_ADC_Init(void) {

	/* USER CODE BEGIN ADC_Init 0 */

	/* USER CODE END ADC_Init 0 */

	ADC_ChannelConfTypeDef sConfig = { 0 };

	/* USER CODE BEGIN ADC_Init 1 */

	/* USER CODE END ADC_Init 1 */

	/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
	 */
	hadc.Instance = ADC1;
	hadc.Init.OversamplingMode = DISABLE;
	hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
	hadc.Init.Resolution = ADC_RESOLUTION_12B;
	hadc.Init.SamplingTime = ADC_SAMPLETIME_160CYCLES_5;
	hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
	hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
	hadc.Init.ContinuousConvMode = DISABLE;
	hadc.Init.DiscontinuousConvMode = DISABLE;
	hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
	hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
	hadc.Init.DMAContinuousRequests = DISABLE;
	hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
	hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
	hadc.Init.LowPowerAutoWait = DISABLE;
	hadc.Init.LowPowerFrequencyMode = DISABLE;
	hadc.Init.LowPowerAutoPowerOff = DISABLE;
	if (HAL_ADC_Init(&hadc) != HAL_OK) {
		Error_Handler();
	}

	/** Configure for the selected ADC regular channel to be converted.
	 */
	sConfig.Channel = ADC_CHANNEL_0;
	sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
	if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
		Error_Handler();
	}

	/** Configure for the selected ADC regular channel to be converted.
	 */
	sConfig.Channel = ADC_CHANNEL_1;
	if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
		Error_Handler();
	}
	/* USER CODE BEGIN ADC_Init 2 */

	/* USER CODE END ADC_Init 2 */

}

/**
 * @brief TIM6 Initialization Function
 * @param None
 * @retval None
 */
static void MX_TIM6_Init(void) {

	/* USER CODE BEGIN TIM6_Init 0 */

	/* USER CODE END TIM6_Init 0 */

	TIM_MasterConfigTypeDef sMasterConfig = { 0 };

	/* USER CODE BEGIN TIM6_Init 1 */

	/* USER CODE END TIM6_Init 1 */
	htim6.Instance = TIM6;
	htim6.Init.Prescaler = 31;
	htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
	htim6.Init.Period = 9999;
	htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
	if (HAL_TIM_Base_Init(&htim6) != HAL_OK) {
		Error_Handler();
	}
	sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
	if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig)
			!= HAL_OK) {
		Error_Handler();
	}
	/* USER CODE BEGIN TIM6_Init 2 */

	/* USER CODE END TIM6_Init 2 */

}

/**
 * @brief GPIO Initialization Function
 * @param None
 * @retval None
 */
static void MX_GPIO_Init(void) {
	GPIO_InitTypeDef GPIO_InitStruct = { 0 };

	/* GPIO Ports Clock Enable */
	__HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();

	/*Configure GPIO pin Output Level */
	HAL_GPIO_WritePin(GPIOA, LED_RING_Pin | BUZZ_Pin, GPIO_PIN_RESET);

	/*Configure GPIO pin Output Level */
	HAL_GPIO_WritePin(GPIOA,
			LED1_Pin | LED2_Pin | LED3_Pin | LED4_Pin | LED5_Pin, GPIO_PIN_SET);

	/*Configure GPIO pin Output Level */
	HAL_GPIO_WritePin(GPIOB,
			LCD_RS_Pin | LCD_RW_Pin | LCD_EN_Pin | LCD_D4_Pin | LCD_D5_Pin
					| LCD_D6_Pin | LCD_D7_Pin, GPIO_PIN_RESET);

	/*Configure GPIO pins : SW_A_Pin SW_B_Pin */
	GPIO_InitStruct.Pin = SW_A_Pin | SW_B_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

	/*Configure GPIO pins : SW_ON_Pin SW_AUTO_Pin SW_LOCK_Pin */
	GPIO_InitStruct.Pin = SW_ON_Pin | SW_AUTO_Pin | SW_LOCK_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	/*Configure GPIO pin : LED_RING_Pin */
	GPIO_InitStruct.Pin = LED_RING_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	HAL_GPIO_Init(LED_RING_GPIO_Port, &GPIO_InitStruct);

	/*Configure GPIO pins : LED1_Pin LED2_Pin LED3_Pin LED4_Pin
	 LED5_Pin BUZZ_Pin */
	GPIO_InitStruct.Pin = LED1_Pin | LED2_Pin | LED3_Pin | LED4_Pin | LED5_Pin
			| BUZZ_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	/*Configure GPIO pins : LCD_RS_Pin LCD_RW_Pin LCD_EN_Pin LCD_D4_Pin
	 LCD_D5_Pin LCD_D6_Pin LCD_D7_Pin */
	GPIO_InitStruct.Pin = LCD_RS_Pin | LCD_RW_Pin | LCD_EN_Pin | LCD_D4_Pin
			| LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void) {
	/* USER CODE BEGIN Error_Handler_Debug */
	/* User can add his own implementation to report the HAL error return state */

	/* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

일단 기본적으로 간단한 동작이 많아서  간단한 동작을 수행하기위한 Stat, Flag 등을 구조체에 담은뒤 출력 제어를 함수로 만들고 IOcon 으로 사용 할수있게 io.---- 으로서 사용 가능 하게했다.

 

그리고 나머지는 메인문에서 Flag 에 따라 제어할수있도록하여 과제지에 나온 동작들을 수행 가능하게했다.

+ Recent posts