임베디드 시스템을 위한 SW 구조설계 3

2023. 4. 12. 10:23[Harman] 세미콘(semiconductor) 아카데미-반도체설계/임베디드 시스템을 위한 SW 구조설계

728x90

저번시간에 공부한 개념 복습 및 함수, 연산자, 플로팅연산을 공부하였고 STM32CubeIDE를 이용한 디버깅 사용법 숙지하였다.


  • 하드웨어는 가산과 승산으로만 이루어져있다.
  • 맨 끝자리 비트수를 이용하여 부호 표현을 할 수 있다. 

signed - 

unsigned 

  • 통신 코드 작성 시 BCD코드 + 3을 한 express3 코드를 많이 사용한다
  • 헌팅턴 가설 = 2진수 연산을 하기 위한 가설
  • 드모르간 정리 - 회로설계 시 AND게이트 -> OR게이트 / OR게이트 -> AND게이트로 바꿀 때 자주 사용
  • 반가산기 = 2개의 비트를 더하면 CARRY발생
  • 전가산기 = 반가산기를 2개 합친 것 (전가산기를 합쳐 ALU(덧셈회로)를 만든다)

STM32CubeIDE STM32CubeProg

 

https://www.st.com/en/microcontrollers-microprocessors/stm32f411re.html#tools-software

STM32보드를 이용한 디버거를 정상적으로 사용하기 위해서는 STM32CubeIDE와 STM32CubeProg를 다운로드해야한다.

 

  • 전처리기(Preprocesser)

#을 이용한 명령어들을 "전처리기" 라 한다. 전처리기(Preprocesser)는 프로그램을 컴파일할 때 컴파일 직전에 실행되는 별도의 프로그램이다.

프로그램의 실행 흐름이나 변수의 값에 영향을 주지 않는다.

  • define

#define A B (#define 상수 상수의 값)의 형태로 사용된다. A라는 상수를 만들어 B라는 값을 넣는다는 뜻이다. 이 define으로 설정한 상수는 ROM에 저장된다. define 을 이용하여 함수 또한 상수명에 집어넣을 수 있다.

HAL_GPIO_ReadPin함수를 getdata라는 상수에 넣는다.
함수대신 define으로 설정한 getdata를 이용하여 코드 작성 가능

 

 

  • include

#include는 컴파일러에게 명령을 알려주는 "의사 명령어"이다. 

 

#include는 <oo>과 "oo"의 2가지 방법으로 사용가능하다. < >과 " "의 차이는 해당 헤더파일이 존재하는 경로의 차이이다.

 

  1. #include < > = 컴파일러가 설치된 폴더에서 < >안의 헤더파일을 찾으라는 지시
  2. #include " " = 사용자의 프로젝트 폴더나 개발자가 추가한 디렉토리로 지정해준 경로에서 찾으라는 명령어 / 개발자가 만든 헤더파일이나 추가로 외부 라이브러리를 코드에 추가하고 싶은 경우 " "을 이용하여 추가한다.

컴파일러안의 해더파일 경로는 STM32CubeIDE를 통해 설정가능하다.

프로젝트 - Properties / C/C++ Build - Environment

위의 경로를 통해 우리가 사용하는 프로젝트의 헤더파일의 디렉토리 주소를 알 수 있고, 헤더파일 경로를 추가할 수 있다. (<>의 경로에 해당) -> 해당 기능을 통해 경로를 지정해주면 소스코드상으로 <>로 지정해주지 않아도 해당 헤더파일 사용 가능

 

Resource

프로젝트 - Properties에서 헤더파일 경로 지정 이외에도 프로그램 안의 링크, 빌드 등의 디렉토리주소를 알 수 있다.

 

  • 조건부 컴파일

앞에서 본 #define 전처리기는 상수를 "매크로"라고도 부른다. 형태는 #define 매크로 매크로값 으로도 볼 수 있다.

# 이후 if문을 이용하여 코드를 작성할 수도 있다. 

 

[#if ... #endif]

기존 C언어에서 사용하는 if문과 같은 개념이다. 대신 중괄호({ })를 사용하지 않고 #endif를 통해 #if의 끝을 알린다.

 
 

#if - debug 매크로 값이 2보다 이상이면 debug 2를 출력해야하지만 2보다 작은 1의 값을 가지므로 false라 출력하지 않는다. 다음 #if 1은 true값을 가지므로 1을 출력하고 #if 0은 false로 0을 출력하지 않는다.  다음 #endif를 통해 구문을 끝낸다. #if를 사용할 때는 반드시 #endif문이 존재해야한다.

더보기

컴퓨터는 true와 false를 구별하는 법이있다. 

false = 0

true = 0 이외의 값 

 

[#ifdef ... #endif]

#if는 매크로가 참인지 거짓인지를 기준으로 작동하였지만 #ifdef는 매크로가 정의되어 있는지 아닌지를 기준으로 동작한다.

 

debug는 정의되어있고 time은 주석처리했기 땨문에 정의되어 있지 않아 #ifdef문에서 debug쪽만 코드를 수행하게 되었다.

 

[#ifndef ... #endif]

[#ifdef ... #endif]가 매크로가 정의되어 있는지 아닌지를 기준으로 동작한다면 [#ifndef ... #endif]는 반대로 정의되어 있지 않다면 동작하고 정의되어있으면 동작하지 않는 구문이다. 

정의된 debug는 실행되지 않고 주석처리하여 정의되지 않는 time은 정상실행되는 것을 확인할 수 있다.

 

[#else / #elif]

기본 if문의 else와 동일하게 #if, #ifdef, #ifndef에도 #else를 이용 가능. 

#else , #elif를 사용한 예

 

 

[#if안에 사용하는 defined 연산자]

defined 연산자는 괄호안의 매크로가 존재하는지 판단한다. 

[미리 정의되어 있는 매크로]

C언어에는 컴파일러가 참고해야 할 정보를 알려주기 위해 몇개의 매크로를 미리 정의하여 제공하고 있다. 미리 정의된 매크로는 #define 으로 정의하지 않아도 사용 가능하나, 사용자가 재정의할 수는 없다.

미리 정의된 매크로 설명
__DATA__ 컴파일러가 코드를 실행한 날짜를 "Mmm dd yy" 형식으로 나타낸 문자열
__TIME 컴파일러가 코드를 실행한 시간을 ""hh:mm:ss"형식으로 나타낸 문자열
__FILE__ 현재 소스코드의 파일명을 나타내는 문자열
__LINE__ 현재 소스파일의 행 번호를 나타내는 상수

 


STM32CubeIDE에서 코드 작성 후 디버깅할때는 코드 입력 후 좌측 행 번호를 더블클릭하면 디버깅할 breakpoint를 설정할 수 있다.

breakpoint

해당 디버깅 창에서  Expressions - 알아볼 변수의 명 입력하면 breakpoint에 해당하는 순간의 그 변수의 값을 볼 수 있다.

좌측부터 "재연결 / 디버깅(breakpoint기준) / 디버깅 중지 / 연결해제 / 코드 한줄씩 실행 / breakpoint기준 코드 실행 

디버깅창에선 SFRs에서 MCU, Peripheral의 주소값과 할당값 또한 볼 수 있다.

 


 

  • static변수 (정적변수)

함수내에 사용되는 지역변수는 함수를 벗어나오는 순간 메모리에서 지워진다. 그러나 static 변수를 사용하면, 함수를 나와서도 메모리에 저장되어있는 변수이다. 전역변수와 지역변수의 장점을 같이 사용 가능한 변수.

전역변수로 설정된 a / 지역변수로 설정된 a - 함수를 벗어나면 값이 어떻게 변하는지 확인할 수 있다.

 

static 변수로 지정한 a - 함수를 벗어나도 데이터가 저장되어 있다.

 

함수내에 static 변수를 선언하고 값을 넣지 않으면 초기값(0)으로 설정된다.

 


  • 포인터 개념 개인공부

static변수 대신 포인터 변수를 사용하면 역참조 연산자를 이용해 static변수와 같이 함수를 벗어나도 메모리에 값이 저장되어 사용할 수 있다.

더보기
  1. 주소 연산자(&) = 변수의 주소를 나타내는 연산자
  2. 역참조 연산자(*) = 그 주소 내부에 있는 값에 접근하는 연산자
  3. 널(null)포인터 = 포인터가 아무것도 가리키지 않는다는 것을 의미 / NULL값 = 0 / 주로 포인터를 안전하게 초기화할 때 사용

ex) int *a = NULL;

코드 작성

  1. 해당 코드는 먼저 X = 100이란 변수를 선언한 후, 주소값을 저장할 포인터 연산자 ds를 선언하였다.
  2. 포인터 연산자 ds는 주소연산자(&)를 이용하여 x의 주소값을 입력한 후 main문에 있는 해당 함수들 plus, min의 매개변수로 넣어준다(현재 ds는 x의 주소값을 가진 상태)
  3. main문 밖의 함수 plus, min은 매개변수로 ds의 주소값을 받았으니 그 주소 내부에 있는 값을 받기위해 역참조연산자(*)를 사용한다
  4. 역참조 연산자로 받은 값을 해당 함수에 맞는 연산을 하기 위해 지역변수 a를 선언하여 값을 연산한 후 연산한 값을 다시 넣어준다

초기값 선언
1 더하는 함수 지난 후 값
1 빼는 함수 지난 후 값

정상적으로 동작되는 것 확인.