크하하하하하 여러분의 열렬한 성원 덕분에 11월의 챌린저가 되었습니다. 앞으로도 더욱 열심히 정진하는 교육생 최진철이 되도록 하겠습니다. 여러분 모두 쌩유. 😘😘

그럼 바로 본론으로 .

7주차에는 리눅스 쉘 문법과 라이브러리 구성 방법에 대해 상세히 배워보았습니다. 8주차에는 이전까지의 학습 교과 평가와 라즈베리 파이를 활용한 리눅스 시스템 프로그래밍을 진행하였습니다.

Build 시스템

Make

make는 자동으로 실행 파일을 빌드해주기 위한 툴입니다. C 기반의 여러 임베디드 시스템에서 주로 사용되니 기본 Make 시스템을 이해해보도록 합시다!

Make file의 종류

  1. GNUmakefile

  2. makefile

  3. Makefile

    동일한 3가지가 있을 때 위의 적힌 순서대로 수행 .

    -f : 옵션으로 특정한 makefile의 수행이 가능하다.

Makefile 작성하기

조건

  • makefile은 rule들로 구성된다
  • rule은 target을 가짐
    • target은 pre-requisites를 갖는다
    • target는 recipe를 실행한다.
  • comment를 명시할 수 있다
  • macro / or / variable를 사용한다.
  • split -line으로 여러 라인에 걸쳐 한 줄을 연장할 수 있다.
$ cat Makefile
#
# hello world  //comment 
# 
VAR = world

hello:$(VAR)  #world라는 파일이 있어야 된다. 
	@echo hello\
	$(VAR)

:execute
$ make -f Makefile 
make: *** No rule to make target 'world', needed by 'hello'.  Stop.
#dir 에 world가 없어서 타겟 생성이 안된다.
$ nano Makefile 
$ touch world
$ ls
__exer__  __exer__.tar  makefile  Makefile  world
$ make -f Makefile #world 파일 생성 후에는 됨 
hello world

⚠️shell 과 makfile 에서의 문법 차이
shell : $echo $(VAR)
⇒ VAR을 명령어로 인식

makefile : 변수 확장
변수 표기법: $(VAR) 또는 $VAR
make는 VAR 변수의 값을 찾아 해당 위치에 문자 그대로 대체한다.
make$(VAR)world로 확장합니다.

→ 이처럼 문법이 유사한 듯하나 다른 것들이 존재하니 유의해서 사용하자

자식 makefile 부르기

make 파일을 계층 구조화 하여 복잡 - 거대한 makefile 시스템을 만들 수 있다. 이처럼 거대한 빌드 시스템을 구성할 수 있지만 그 과정에서 변수는 프로세스를 넘어다닐 수 없습니다.
⇒ 이때 export 를 활용 하여 특정 변수를 자식 makefile에서 사용할 수 있도록 만들 수 있습니다.

🤩자동 변수

⇒ 외워 둘 필요가 있다

  • automatic variables
  • $@ : target
  • $^ : all prereq.list
  • $? : updated prereq. list
  • $< : 1번째 prereq
  • $(@D) ; dir of target
  • $(@F) : name of target
  • $(<D) : dir of 1번째 prereq
  • $(<F) : 1번째 prereq의 이름
      $ cat makefile 
      all:hello
      
      hello: hello.o world.o
      	@echo compile $@ to $^
      
      compare: hello.o world.o
      	@echo compile $@ to $<
      $ make
      compile hello to hello.o world.o
      $ make compare
      compile compare to hello.o
      ```

싱글 첨자 규칙 [single suffix rule]

해당 내용은 그리 중요하지 않을 수 있지만 직접 구현하면서 의아했던 내용을 정리해보도록 하겠습니다.

리눅스에는 Implicit rule이 존재합니다 그 중 .c: 와 같은 오래된 형태의 패턴 룰이 존재하는데 이는 make 빌드 중에 동일한 패턴 발견시 수행할 명령어를 지정할 수 있습니다.

$ make -p |cat -n |head -1250| tail -50
  1213	# Not a target:
  1214	.c:
  1215	#  Builtin rule
  1216	#  Implicit rule search has not been done.
  1217	#  Modification time never checked.
  1218	#  File has not been updated.
  1219	#  recipe to execute (built-in):
  1220		$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@

위의 결과에서 확인할 수 있듯이 .c: 는 $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@ 명령어를 수행합니다.

그리고 이 suffix rule을 비울 수 있습니다.

$ cat makefile
.SUFFIXES:  ##SUFFIXES : 룰 비우기

all:
hello: hello.c
.c: 
	@echo compile $< to $@
$ make -p | grep .SUFFIXES
.SUFFIXES:                  #아무 값도 나오지 않는다.

makefile 내에서 .SUFFIXES를 사용한다면 기존의 SUFFIXES를 비울 수 있습니다.
그렇다면 .SUFFIXES : .k 를 추가한다면 어떻게 될까요?

저는 비워진 이후 .K만 추가될 것이라 생각했지만 그렇지 않았습니다.

$ cat makefile 
.SUFFIXES:.c .k

all:
hello: hello.c
world: world.k
.c: 
	@echo compile $< to $@
.k:
	@echo use k
$ make -p | grep .SUFFIXES
.SUFFIXES: .out .a .ln .o .c .cc .C .cpp .p .f .F .m .r .y .l .ym 
.yl .s .S .mod .sym .def .h .info .dvi .tex .texinfo .texi 
.txinfo .w .ch .web .sh .elc .el .k # ->k 추가 

출력에서 확인할 수 있듯이 내장 SUFFIXES뒤에 사용자 정의 rule이 추가되었습니다.

💡.SUFFIXES: 에 아무것도 안 쓰면 → 모든 내장 suffix rule을 완전히 비활성화
.SUFFIXES: 에 하나라도 쓰면 → 내장된 수십 개의 suffix rule이 전부 자동으로 깨어남

이러한 차이를 발견할 수 있었습니다 .

그렇다면 .k 만을 추가하고 싶다면 어떻게 해야할까요?

.SUFFIXES:   # 다 비우기 
.SUFFIXES:.k # 비운 다음에 추가

all:
hello: hello.c
world: world.k
.c: 
	@echo compile $< to $@
.k:
	@echo use k

이렇게 먼저 다 비운 뒤 추가를 하면 .k만 출력되는 것을 확인해볼 수 있습니다. 다들 실습으로 확인해보시길 바랍니다!!!


CMake

다음은 또 다른 build 시스템인 CMake에 대해 간략히 정리해보겠습니다.
Cmake는 빌드 파일로 CMakeLists.txt를 가지며 해당 파일 내에 필요한 라이브러리,타겟 파일,소스 파일, 빌드 형식등에 대한 메타 데이터를 가집니다

빌드 명령어

cmake -S [source dir] - B [build dir]

$ cat CMakeLists.txt 
cmake_minimum_required(VERSION 3.2)
message("hello world")
project(Helloworld)

add_executable (${CMAKE_PROJECT_NAME} hello.c world.c
$ ls
build  CMakeLists.txt  hello.c  world.c  world.h
$ cmake -B build 
$ ls build/Makefile #makefile 생성 됨  
build/Makefile
$ ls
build  CMakeLists.txt  hello.c  world.c  world.h
$ cmake --build build # build 파일 안에 Makefile로 빌드
[ 33%] Building C object CMakeFiles/Helloworld.dir/hello.c.o
/home/ros2man/work/CMake/project/hello.c: In function ‘main’:
/home/ros2man/work/CMake/project/hello.c:6:9: warning: implicit declaration of function ‘world’ [-Wimplicit-function-declaration]
    6 |         world();
      |         ^~~~~
[ 66%] Building C object CMakeFiles/Helloworld.dir/world.c.o
[100%] Linking C executable Helloworld
[100%] Built target Helloworld
$ ./build/Helloworld 
hello
world
===============> 실행 가능 

위의 코드는 간략하게 Cmake 빌드의 구성을 예시로 들었습니다.

현업에서 주로 사용되는 Build 시스템을 파악하여 연습 과정을 가져야겠다는 생각을 했습니다. 복잡한 계층 Build 시스템에서 경로 오류가 발생할 경우 난감했던 상황이 종종 있었는데 이제는 이해하면서 해결할 수 있을 것만 같은 느낌이 듭니다.
👍레쯔 꼬우 ~


리눅스 시스템 프로그래밍

porting?

→ 시스템 프로그래밍 구조 , 파일 시스템, 정보와 권한

To Embedded Linux 

linux @arm/ppc (non pc ) ⇒ embedded linux  (by “porting”) 

⇒ 포팅의 과정 

1. bootloader(”u-boot”)
    1. linux /Makefile  → make → 1. vmlinux
2. vmlinux → vmlinux.gz → zImage  // kernel image
3. device tree
4. root file system 
	ext4 on “ / == root “

리눅스의 파일 시스템

  • 모든 것을 파일로 취급한다. 폴더 , 디렉토리 , 키보드, 모니터와 같은 장치까지 + 드라이버 정보
    • 파일의 종류
      • 일반 파일
      • 소켓
      • device
    • /proc : 커널이 관리하는 정보를 저장한다.
    • /etc : 설정 파일 보관
    • /bin : 시스템 부팅과 기본 복구 시 반드시 필요한 가장 기본적인 실행 파일들
    • /sys : 드라이버가 관리하는 정보
    • /dev : 장치 정보

라즈베리파이를 이용한 DHT-11센서 사용

Raspberry Pi GPIO Pinout

  1. 해당 사이트에서 라즈베리파이의 Pinout을 확인 .
  2. 7번 핀 = GPIO4에 SIGNAL , Vcc (3.3v) , GND 연결
  3. app build 후 실행 결과 확인

pinout 확인 예시

$ gpio readall
#1. pin 번호를 확인
+-----+-----+---------+------+---+---Pi 4B--+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 | ALT0 | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 | ALT0 | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 1 | ALT5 | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 1 | ALT5 | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI | ALT0 | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO | ALT0 | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK | ALT0 | 0 | 23 || 24 | 1 | OUT  | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi 4B--+---+------+---------+-----+-----+

🌡️DHT 온습도 센서 동작 방식

  • return

    	5byte { 1   1     1 1 1}
                            RH     T checksum 
                       상대습도 온도

    온도,습도 모두 decimal , integral 로 두 바이트씩

  • Communication Process

    • single wire 통신 : 별도의 클럭핀 없이 데이터 핀의 전압 레벨이 VCC와 GND 사이에서 바뀐다.

      • 기본 상태 : 풀업 저항 →high
      • Data 핀의 high /low 지속 시간으로 구분 된다. ⇒ data 핀의 전압이 변화 open drain

      ⇒ 전압의 지속 시간으로 모든 것을 구분함

  • Start Signal
    mcu sends out Start Signal : ⇒ 최소 18ms 동안 pull-down voltage → DHT11에서 탐지

    이후에 전압의 유지 시간으로 0과 1을 구분해 값을 읽어들인다

  • Data 0 : 26~ 28us의 전압 지속

  • Data 1 : 70us의 전압 지속

코드 구현

  • 센서 활성화
    // 1. DHT11 깨우기: 18ms 이상 Low 신호 전송
    pinMode(DHT_PIN, OUTPUT);
    digitalWrite(DHT_PIN, LOW);
    delay(18);
  • 응답 대기 모드
    pinMode(DHT_PIN, INPUT);
    for (i = 0; i < MAX_TIMINGS; i++) {
    	//MAX_TIMINGS :  85 번 동안만 신호 감지를 하겠다. 
        while (digitalRead(DHT_PIN) == laststate) {
            counter++;
            delayMicroseconds(1);
     // ms 단위로 counter값 증가  
  • 비트 데이터 수집
    // 첫 4번 펄스는 시작 신호이므로 무시 (i >= 4부터 데이터 시작)
    if ((i >= 4) && (i % 2 == 0))
    {
        data[j / 8] <<= 1;               // 왼쪽으로 시프트 (MSB 먼저 오므로)
        if (counter > THRESHOLD)         // 70μs 이상 → 비트 1
        // THRESHOLD:  40 -> 27과 70을 구분 
            data[j / 8] |= 1;            // 1 설정
        j++;
    }
  • 체크섬 검증
    if ((j >= 40) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)))
        printf("Humidity = %d.%d %% Temperature = %d.%d C\n", ...);
    체크썸 방식 = > data[4] == ((data[0] + data[1] + data[2] + data[3])& 0xFF 해서 1바이트 값만 확인 .

빌드

빌드 방법

cc -o dht11_app dht11_app.c -lwiringPi
sudo ./dht11_app

" root 권한이 필요 (GPIO 접근 때문) "


Feedback

이렇게 8주차에는 직접 실습을 진행하며 빌드 시스템과 센서 사용법에 대해 더 자세히 알아갈 수 있는 시간을 가질 수 있었다. 더 다양한 센서 및 시스템을 개발해보며 학습하는 시간을 가져야겠다는 느낌을 받음

.. 교과 평가 후기

범위가 넓은 만큼 생각보다 많이 틀렸음 . 2개 정도 ..ㅎㅎ 문제를 잘 못 보고 이상하게 풀었는데 다음 교과평가는 더 꼼꼼하게 풀도록 해야겠씀니다.

이만 8주차 VEDA 기록 끗. !!!!

이것은 원인을 모르겠는 피사의 빵판...
기울여야지만 FND가 정상적으로 작동하는 기적을 맛보았노라..

원인을 아시는 분은 댓글로 알려주세욥 ㅠㅠㅠㅠ


P.S.

이번 주는 맛집 사진을 찍은 것이 없네요...

양상추가 빠진 맥도날드에서 생각보다 양상추는 위대함을 깨달았습니다.
++ 돼지벅스라는 강력한 녀석의 등장으로 맛집 탐방은 뜸해질 예정 .
왜 why? 바로 단백질 , 포만감 G.O.A.T 이기 때문입니다.

그럼에도 독자분들을 위해 분발하도록 하겠습니다!

profile
세상의 어려운 문제를 해결하자

0개의 댓글