콘텐츠로 이동

주변 장치 시작하기

이 튜토리얼은 시작하기 개요를 마친 직후에 이어집니다. SiFli-SDK 툴체인이 설치되어 있고, hello_world를 한 번 이상 플래시에 기록했으며, 시리얼 콘솔을 여는 방법을 알고 있어야 합니다. 각 튜토리얼은 의도적으로 작게 구성했습니다. 개념 하나, 주변 장치 하나, 명확한 성공 조건 하나를 다룹니다.

예제는 sf32lb52-lcd_n16r8 같은 SF32LB52-DevKit-LCD 계열 보드를 가정합니다. 다른 보드를 사용한다면 빌드 명령의 보드 이름을 바꾸고, 해당 보드의 지원 파일에서 핀 이름, 주변 장치 인스턴스, 스토리지 레이아웃을 확인하십시오.

이 시리즈를 통해 재현 가능한 개발 습관을 익히십시오. 보드 수준의 동작 하나를 검증하고 성공 조건을 명확히 정한 뒤, 다음 주변 장치로 넘어가기 전에 정상 동작이 확인된 기준 구성을 보존하십시오. 임베디드 C에 익숙하더라도 SF32 SDK를 처음 사용하는 개발자에게 실용적인 내용을 담았습니다.

시작하기 전에

모든 튜토리얼에서 같은 기본 루프를 사용합니다.

scons --board=<board_name> -j8
# then flash with the download script or tool used in Get Started

시리얼 터미널을 계속 열어 두십시오. rt_kprintf() 출력과 FinSH msh> 프롬프트를 볼 수 있으면 초보자가 흔히 겪는 실수 대부분을 더 쉽게 해결할 수 있습니다.

아래 C 코드는 전체 소스 파일이 아니라 발췌입니다. 프로젝트와 SDK 버전에 필요한 헤더, 선언, 보드별 초기화, 반환값 검사, 정리 코드를 추가하십시오.

표: 시작하기 전에
항목 확인 사항
보드 이름 지원되는 보드 구성 중 하나와 일치해야 합니다.
시리얼 포트 문제없이 열리고 부팅 출력이 표시됩니다.
플래시 도구 케이블을 바꾸지 않고 보드를 반복해서 다시 프로그래밍할 수 있습니다.
회로도 또는 보드 파일 GPIO, ADC, PWM, RTC 리소스를 선택하기 전에 준비해야 합니다.
한 번에 하나씩 변경 작은 수정마다 빌드, 플래시 기록, 검증을 수행합니다.

목표: 펌웨어에서 GPIO 핀을 제어합니다. 임베디드 개발에서 첫 빌드에 성공하는 것과 같은 기본 단계입니다.

소요 시간: 15분

시작 위치: example/hal/gpio 또는 example/rt_device/gpio

SF32의 RT-Thread 기반 SDK는 표준 RT-Thread PIN 디바이스 API로 GPIO를 제공합니다. 최소한의 깜빡임 루프는 다음과 같습니다.

#include <rtthread.h>
#include <rtdevice.h>

#define LED_PIN GET_PIN(1, 0)  /* replace with your board's LED pin */

int main(void)
{
    rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);

    while (1)
    {
        rt_pin_write(LED_PIN, PIN_HIGH);
        rt_thread_mdelay(500);
        rt_pin_write(LED_PIN, PIN_LOW);
        rt_thread_mdelay(500);
    }
}
  1. 가장 적합한 GPIO 예제를 프로젝트 디렉터리에 복사하거나, 학습 중에는 해당 예제를 직접 수정하십시오.
  2. 회로도 또는 보드 지원 파일에서 LED 핀을 찾으십시오. 포트와 핀 번호를 추측하지 마십시오.
  3. hello_world에 사용한 것과 같은 보드 이름으로 빌드하고 플래시에 기록하십시오.
  4. LED가 예상한 속도로 깜빡이는지 확인하십시오.
  5. 지연 시간을 100 ms로 바꾸고 다시 빌드한 다음, 눈에 보이는 깜빡임 속도가 달라지는지 확인하십시오.

성공 기준: LED가 반복해서 토글되고, 시리얼 콘솔에 정상 부팅 출력이 계속 표시되며, 지연 시간을 바꾸면 깜빡임 속도가 달라집니다.

동작하지 않을 때: LED의 활성 극성, LED와 GPIO 사이에 트랜지스터가 있는지, 핀이 다른 기능에 이미 사용되고 있는지, 선택한 보드 이름이 실제 하드웨어와 일치하는지 확인하십시오.

배운 내용: PIN 디바이스 추상화, 보드별 핀 정의, 기본적인 편집/빌드/플래시 기록 루프.

GPIO 입력으로 버튼 읽기

목표: 디지털 입력을 읽고 버튼 누름에 반응합니다.

소요 시간: 20분

시작 위치: example/rt_device/gpio

버튼을 읽을 때도 입력 모드에서 같은 PIN API를 사용합니다. 배선과 논리 레벨을 확인하기 위해 단순한 읽기부터 시작하십시오.

SiFli SDK에서 RT-Thread PIN 디바이스 API는 rt_pin_mode()로 패드의 풀업 또는 풀다운을 구성하지 않습니다. active-low 버튼이라면 보드가 풀업 저항을 제공하는지 확인하거나, 보드의 pinmux 설정에서 HAL_PIN_Set(...) 호출로 풀을 구성하십시오. 패드, 기능, CPU 도메인은 보드마다 다르므로 핀 할당을 무작정 복사하지 말고 보드 지원 파일을 참조하십시오.

rt_pin_mode(BUTTON_PIN, PIN_MODE_INPUT);

if (rt_pin_read(BUTTON_PIN) == PIN_LOW)
{
    /* button pressed, active-low */
}

간단한 테스트를 넘어서는 애플리케이션에서는 빠르게 반복하는 루프로 계속 폴링하지 말고 인터럽트를 사용하십시오. 그러면 핀을 반복해서 읽지 않고도 펌웨어가 입력 에지에 반응할 수 있습니다. 나머지 설계가 이벤트 사이에 CPU를 슬립 상태로 둘 수 있다면 전력도 줄일 수 있습니다.

rt_pin_attach_irq(BUTTON_PIN, PIN_IRQ_MODE_FALLING, button_isr, RT_NULL);
rt_pin_irq_enable(BUTTON_PIN, PIN_IRQ_ENABLE);
  1. 보드에서 사용할 수 있는 버튼이나 입력 핀을 확인하십시오.
  2. 먼저 폴링으로 입력을 읽고, 버튼을 누르거나 놓을 때의 원시 값을 출력하십시오.
  3. 버튼이 active-high인지 active-low인지 확인하십시오.
  4. 기본 입력 읽기가 동작하면 인터럽트 핸들러 방식으로 전환하십시오.
  5. 버튼을 누를 때마다 튜토리얼 1의 LED가 토글되도록 하십시오.
  6. 간단한 디바운스 방식을 추가하십시오. 짧은 시간 동안 이벤트를 무시하거나, 잠시 기다린 뒤 입력 레벨을 다시 확인할 수 있습니다.

성공 기준: 물리적인 버튼 누름 한 번에 의도한 펌웨어 동작이 한 번 수행되고, 바운스로 인한 반복 트리거가 없습니다.

동작하지 않을 때: 필요한 풀업 또는 풀다운이 보드에서 제공되거나 pinmux로 구성되어 있는지 확인한 다음, 버튼 극성, 인터럽트 에지 선택, 그리고 핀이 부트 모드, 디버그 또는 다른 주변 장치와 공유되고 있지 않은지 확인하십시오.

배운 내용: 디지털 입력 구성, active-high와 active-low 논리, 인터럽트 기반 입력, 계속 폴링하면 전력이 낭비될 수 있는 이유. 시스템 수준의 이유는 저전력 설계 개요를 참조하십시오.

셸 사용과 디버그 출력

목표: 디버깅이 어려워지기 전에 FinSH 콘솔과 rt_kprintf()에 익숙해집니다.

소요 시간: 15분

시작 위치: example/system/finsh

RT-Thread의 FinSH 셸은 부트 로그에 사용하는 것과 같은 UART에서 대화형 msh> 프롬프트를 제공합니다. 다음 습관을 일찍부터 들이십시오.

  • 간단한 디버그 로깅에는 UART에 직접 쓰는 대신 rt_kprintf()를 사용하십시오.
  • list_thread, list_device 같은 내장 셸 명령과 메모리/상태 명령으로 실행 중인 시스템을 점검하십시오.
  • 로그는 짧고 의미 있게 유지하십시오. 과도한 로깅은 타이밍과 전력 동작을 바꿀 수 있습니다.

  • 앞의 예제 중 하나를 플래시에 기록하고 시리얼 터미널을 다시 여십시오.

  • Enter 키를 눌러 msh> 프롬프트를 표시하십시오.
  • list_thread와 list_device를 실행하십시오.
  • 튜토리얼 2의 버튼 핸들러에 rt_kprintf() 호출을 추가하십시오.
  • 버튼을 눌렀을 때 로그가 표시되는지 확인하십시오.

성공 기준: 셸에서 스레드와 디바이스를 점검할 수 있고, 직접 작성한 로그 메시지를 물리적 이벤트와 대응시킬 수 있습니다.

동작하지 않을 때: 시리얼 보레이트, 포트 선택, 셸 컴포넌트가 활성화되어 있는지, 다른 도구가 시리얼 포트를 열어 둔 상태인지 확인하십시오.

배운 내용: 디버거를 연결하지 않고 실행 중인 시스템을 점검하는 방법, 그리고 모든 문제를 펌웨어에 대한 맹목적인 추측으로 만들지 않으면서 부담이 적은 로깅을 사용하는 방법.

ADC로 아날로그 센서 읽기

목표: 포텐쇼미터, 간단한 센서 또는 배터리 감지 입력의 아날로그 전압을 샘플링합니다.

소요 시간: 20분

시작 위치: example/rt_device/adc/channel

이 RT-Thread 예제에서는 BSP_USING_ADC, BSP_USING_ADC1, RT_USING_ADC를 활성화해야 합니다. 그렇지 않으면 SDK가 bat1을 등록하지 않습니다. SF32LB52에서 채널 7은 내부적으로 VBAT에 연결되어 있으므로 범용 외부 센서 입력이 아닙니다. 사용하는 칩과 보드에서 지원하는 ADC 채널과 핀 먹스를 선택하십시오. 문서화된 SF32LB52 구성에서 채널 0은 PA28을 사용합니다. 현재 SiFli SDK 예제에서 rt_adc_read()는 원시 ADC 카운트가 아니라 0.1 mV 단위의 전압 환산 값을 반환합니다. 사용하는 칩과 드라이버 버전에서 단위와 변환 방법을 확인하십시오.

rt_adc_device_t adc = (rt_adc_device_t)rt_device_find("bat1");
rt_adc_enable(adc, channel);
rt_uint32_t voltage_0p1mV = rt_adc_read(adc, channel);
  1. 보드에서 ADC를 지원하는 핀과 채널을 확인하십시오.
  2. 입력 전압 범위와 분압기 또는 아날로그 프런트엔드 회로가 있는지 확인하십시오.
  3. 등록된 ADC 디바이스를 찾아 채널을 활성화하고 값을 읽으십시오.
  4. 해당 드라이버에 문서화된 변환을 적용하십시오. 현재 SF32LB52 예제에서는 반환값을 10으로 나누어 밀리볼트 값을 구합니다. 원시 카운트/기준 전압 공식을 다시 적용하지 마십시오. 외부 분압기가 있다면 분압 전 전압을 계산할 때 분압비를 반영하십시오.
  5. 튜토리얼 3에서 사용한 셸로 결과를 출력하십시오.
  6. 입력을 천천히 바꾸면서 보고되는 값도 부드럽게 변하는지 확인하십시오.

성공 기준: 보고된 값이 예상한 방향으로 변하고, 문서화된 스케일과 보드 수준의 분압비를 적용했을 때 합리적인 입력 전압 범위에 들어옵니다.

동작하지 않을 때: 핀 먹싱, 채널 번호, 드라이버에 문서화된 스케일과 캘리브레이션, 입력 범위, 입력이 플로팅 상태인지, 센서가 샘플링 전에 전원 또는 안정화 시간이 필요한지 확인하십시오.

배운 내용: RT-Thread 디바이스 드라이버 전반에서 사용하는 rt_device_find() 패턴. 이 패턴은 I2C, SPI, PWM, 스토리지, 커스텀 드라이버에서 다시 사용하게 됩니다.

PWM 출력 구동

목표: PWM 신호를 생성하여 LED 밝기를 조절하거나 간단한 버저를 구동합니다.

소요 시간: 20분

시작 위치: example/rt_device/pwm

PWM 디바이스 이름은 보드에서 활성화된 타이머 드라이버에 따라 달라집니다. SDK는 pwm3, pwmlp1, pwma1 같은 이름을 사용하며, 아래의 pwm3는 예시일 뿐 범용 이름이 아닙니다. 보드 구성에서 등록된 디바이스 이름과 핀 먹스를 확인하십시오.

struct rt_device_pwm *pwm = (struct rt_device_pwm *)rt_device_find("pwm3");
rt_pwm_set(pwm, channel, period_ns, pulse_ns);
rt_pwm_enable(pwm, channel);
  1. PWM을 지원하는 채널을 선택하고 핀 먹스를 확인하십시오.
  2. 주변 장치에 맞는 주기를 선택하십시오. LED는 눈에 띄게 깜빡이지 않을 만큼 높은 주기로, 버저는 가청 범위의 주기로 설정하십시오.
  3. 듀티 사이클을 바꾸어 LED가 점점 밝아졌다 어두워지는지 확인하십시오.
  4. 주기를 바꾸어 버저에서 몇 가지 고정 음을 시험하십시오.
  5. PWM을 비활성화하고 출력이 안전한 유휴 상태로 돌아오는지 확인하십시오.

성공 기준: 듀티 사이클을 바꾸면 밝기가 변하거나 주기를 바꾸면 음이 변하고, PWM을 비활성화하면 출력이 예상한 상태로 유지됩니다.

동작하지 않을 때: 핀이 PWM 인스턴스로 라우팅되어 있는지, 채널 번호가 올바른지, 주기와 펄스가 나노초 단위인지, 외부 부하에 구동용 트랜지스터가 필요한지 확인하십시오.

배운 내용: 주기/듀티 사이클 구성, 그리고 튜토리얼 4의 디바이스 드라이버 패턴을 다른 종류의 주변 장치에 적용하는 방법.

RTC로 시간 유지

목표: 온칩 RTC를 사용하여 실제 시각을 읽고 설정합니다.

소요 시간: 15분

시작 위치: example/rt_device/rtc

time_t now = time(RT_NULL);
struct tm *tm_now = localtime(&now);
rt_kprintf("%04d-%02d-%02d %02d:%02d:%02d\n",
           tm_now->tm_year + 1900, tm_now->tm_mon + 1, tm_now->tm_mday,
           tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);
  1. 셸 명령처럼 지원되는 방법으로 RTC를 한 번 설정하십시오.
  2. 현재 시각을 주기적으로 출력하십시오.
  3. 보드의 전원을 껐다 켠 뒤에도 시각이 유지되는지 확인하십시오.
  4. 시각이 유지되지 않으면 펌웨어를 수정하기 전에 보드의 백업 전원 경로를 확인하십시오.

유지 여부를 테스트할 때는 부팅할 때마다 RTC를 무조건 리셋하지 마십시오. 그렇게 하면 이전 시각이 유지되었는지를 알 수 없게 됩니다.

성공 기준: 전원이 공급되는 동안 시각이 정상적으로 증가하고, 유지 동작이 보드 하드웨어와 일치합니다.

동작하지 않을 때: RTC 컴포넌트 활성화, 클럭 소스, 백업 전원, 보드가 부팅 중에 RTC 상태를 리셋하는지 확인하십시오.

배운 내용: RT-Thread의 RTC 디바이스 위에서 동작하는 표준 C 시간 API, 그리고 펌웨어 동작이 하드웨어 설계에 좌우되는 초기 사례.

미니 프로젝트: 버튼으로 제어하는 센서 로거

여섯 개의 짧은 튜토리얼을 마친 뒤, 다음 기능을 하나의 작은 프로젝트로 결합하십시오.

  1. 부팅할 때 LED가 깜빡이도록 하십시오.
  2. 버튼으로 샘플링을 시작하고 중지하도록 하십시오.
  3. 샘플링 중에는 1초마다 ADC 값을 하나씩 읽으십시오.
  4. FinSH로 타임스탬프가 포함된 측정값을 출력하십시오.
  5. PWM 밝기나 버저 음으로 샘플링 상태를 표시하십시오.

통과 기준: 버튼의 반복 트리거, 로그 폭주, 예기치 않은 리셋 없이 프로젝트가 몇 분 동안 실행됩니다.

첫 주변 장치 브링업 체크리스트

  • 보드 이름, 시리얼 포트, 플래시 흐름이 정상으로 확인되었습니다.
  • GPIO 출력이 실제 LED 또는 측정한 핀에서 동작합니다.
  • GPIO 입력이 확인된 극성과 디바운스로 동작합니다.
  • FinSH 셸을 사용할 수 있고 list_thread / list_device가 동작합니다.
  • ADC 측정값이 그럴듯하며 공학 단위로 변환됩니다.
  • PWM 주기/듀티 설정이 예상한 물리적 출력을 만듭니다.
  • 유지 한계를 포함하여 RTC 동작을 이해했습니다.

다음 단계

이 내용에 익숙해지면 연결된 서브시스템으로 넘어가십시오. 다음 트랙에서는 주변 장치를 미들웨어와 결합합니다. I2C 센서, LVGL, BLE, 오디오 재생, 스토리지, 의도적인 공존 테스트를 다룹니다.

최종 업데이트: