본문 바로가기
RP2040 FreeRTOS

RP2040-FreeRTOS Part 1 - 프로젝트 설정

by 천재개발자 2025. 9. 9.
반응형

Learn Embedded Systems 유튜브 따라하기

원본영상(링크는 게시물 하단에): FreeRTOS on the Raspberry Pi Pico (RP2040) Part 1: VSCode Setup and Blinky Test! [UPDATED]

 

 

FreeRTOS(Free Realtime Operating System): 오픈 소스 실시간 운영 체제

RTOS를 사용하지 않는 일반적인 펌웨어 개발 방식을 Bare Metal Programming이라고함.

베어메탈 방식은 최상위 무한 루프내에 필요한 태스크 코드들이 순서대로 실행됨.

 

센서 데이터를 수집하여 서버로 값을 전송하는 펌웨어를 개발한다면 아래와 같이 작성하게됨.

#define HTTP_RETRY_COUNT 5

int main(){
	int n_temp = 0;
	int n_http_status = 0;
	while(1){
		n_temp = read_sensor();
		n_http_status = http_post(n_temp, HTTP_RETRY_COUNT); // 전송 실패하면 최대 5번 재시도
		if(n_http_status != 200){
			write_err_log(n_http_status);
		}
	}
}

 

베어메탈 방식에서는 http_post()를 처리하는 동안 센서 정보를 읽을 수가 없다.

만약 센서데이터가 거의 실시간으로 수집되어야하는 중요한 데이터거나

다른 장치와의 통신 연결을 유지하기 위해 짧은 시간 내 반복적으로 KEEP ALIVE 패킷을 보내야하는 펌웨어라면

http 통신, 로그 저장 등의 태스크로 인해 문제가 발생한다.

 

RP2040과 같은 멀티코어 MCU에서는 각각 다른 코어에서 역할을 분리함으로써 이 문제를 해결할 수 있긴하지만

연동해야될 장치가 많아지거나 복잡한 태스크를 수행해야하는 경우 개발자가 직접 핸들링하기가 쉽지 않다.

 

실시간 운영 체제(RTOS)를 사용하면 손쉽게 여러 태스크들이 동시에 실행(되는것 처럼)되도록 구현할 수 있다.(단일 코어에서도)

RTOS를 사용하면 태스크 관리(스택, 상태 등)를 위해 베어메탈 방식 보다 더 많은 메모리 공간이 필요하다는 단점이 있지만

복잡한 펌웨어의 경우 코드의 복잡성을 크게 줄일 수 있다는 장점이 크다.

 

위의 센서데이터 예제 코드를 RTOS로 구현하면 아래와 같은 구조로 작성할 수 있다.(실제 작동하는 코드는 아님)

#define HTTP_RETRY_COUNT 5

static QueueHandler sensor_q = NULL; Task간 센서 데이터 공유에 사용할 큐(센서태스크->http태스크)

// 센서에서 값을 읽고 sensor_q에 값 밀어넣기
void sensor_task(void *p_param){
	int n_temp = 0;
	while(1){
    	n_temp = read_sensor();
        queue_push(sensor_q, n_temp); // 큐에 측정 값 넣기(가짜함수)
    }
}

// sensor_q에 값이 있으면 꺼내서 서버로 보내기
void http_task(void *p_param){
	int *p_temp = NULL;
	while(1){
		p_temp = queue_pop(sensor_q); // 큐에서 측정값 가져옴(가짜 함수)
		if(p_temp == NULL) continue;
        
		n_http_status = http_post(*p_temp, HTTP_RETRY_COUNT); // 전송 실패하면 최대 5번 재시도
		if(n_http_status != 200){
			write_err_log(n_http_status);
		}
	}
}

int main(){
	// sensor_q 큐 생성
	sensor_q = create_queue(); // 가짜 함수

	create_task(sensor_task, ...); // sensor_task 생성 및 실행(가짜 함수)
	create_task(http_task, ...); // http_task 생성 및 실행(가짜 함수)

	// 여기서 부터는 실행되지 않는 코드다.
	// 실행해야될 코드들은 반드시 task내에서만 실행해야함.
	// 모든 코드들의 실행은 RTOS가 처리해야함
	while(1){};
}

 

sensor_task와 http_task는 서로에게 직접적인 영향을 미치지 않는다.

FreeRTOS는 알아서 MCU의 각 실행 Tick마다 각 Task들의 코드를 번갈아서 실행한다.

 

유튜브 실습 따라하기

유튜브에서는 기본 프로젝트 템플릿을 제공한다.

https://github.com/LearnEmbeddedSystems/rp2040-freertos-template/tree/main

  • lib/: FreeRTOS 커널 등 프로젝트에 필요한 라이브러리들
  • src/: 우리가 작성할 펌웨어 소스코드, CMakeLists.txt들
  • CMakeLists.txt: 최상위 CMakeLists.txt파일로써 프로젝트명, 라이브러리 설정 등 담당 add_subdirectory(src)가 src파일내의 CMakeLists.txt를 참고하도록 설정
  • pico_sdk_import.cmake: Pico C SDK 폴더에서 복사해와도됨

* 나는 이 템플릿을 그대로 사용하지않고 직접 따라서 작성해서 사용했음.(내 컴퓨터 개발환경에 맞게)

 

템플릿의 main.c 코드를 그대로 실행해보면 Pico 보드의 LED가 깜빡 거리는걸 확인할 수 있음.

 

나는 freertos.org에서 FreeRTOS 202406.01 LTS 버전을 직접 다운받아서 컴파일&빌드 했었는데 

LED가 제대로 깜빡거리지않아서 main.c 파일을 아래와 같이 수정했음.

#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "pico/stdlib.h"

void led_task(void *pvParameters){
    const uint LED1_PIN = 14; // 내가 직접 제작한 커스텀 보드의 1번 LED
    const uint LED2_PIN = 15; // 내가 직접 제작한 커스텀 보드의 2번 LED
    gpio_init(LED1_PIN);
    gpio_init(LED2_PIN);
    gpio_set_dir(LED1_PIN, GPIO_OUT);
    gpio_set_dir(LED2_PIN, GPIO_OUT);
    while(true){
        gpio_put(LED1_PIN, 1);
        gpio_put(LED2_PIN, 0);
        vTaskDelay(pdMS_TO_TICKS(500)); // pdMS_TO_TICKS 함수 추가
        gpio_put(LED1_PIN, 0);
        gpio_put(LED2_PIN, 1);
        vTaskDelay(pdMS_TO_TICKS(500)); // pdMS_TO_TICKS 함수 추가
    }
}

int main(){
    stdio_init_all();
    
    xTaskCreate(led_task, "LED_Task", 256, NULL, 1, NULL);
    vTaskStartScheduler();

    while(1){};
}

 

 

영상링크:

https://youtu.be/2cDpGoCyrHQ?si=4KwMN_D4LmJwrqqo

 

 

게시물 썸네일 출처: Photo by Vishnu Mohanan on Unsplash  

반응형