STM32 NUCLEO-F401RE와 함께 떠나는 임베디드 개발 여정

Hyunwoo·2025년 1월 22일
0


This means I need to:
1. Create a detailed blog post that summarizes their learning progress on the NUCLEO-F401RE.
2. Organize the blog post in a logical way: Introduction, core concepts, code implementation, reflection, and future steps.
3. Use a slightly more formal but still accessible and engaging tone. It should be more descriptive and detailed than a simple forum post, but not a dense academic paper.
4. Incorporate emojis to make the blog post more fun and engaging.
5. Ensure that the code examples are still correct and easy to copy-paste.
6. Include specific information about their journey: Focus on the user's particular requests and the challenges they faced.
7. Reiterate the potential of NUCLEO-F401RE and future exploration opportunities. This will leave them with motivation and a sense of accomplishment.
8. Make it reasonably "long" to meet their specific requirement.

Essentially, I need to craft a blog post that summarizes their learning journey, explain their challenges and triumphs, while making it detailed and fun at the same time, with emojis for extra engagement. This is a big ask! Let's start writing! 😄

STM32 NUCLEO-F401RE와 함께 떠나는 임베디드 개발 여정: LED 깜빡임부터 꿈틀거리는 가능성까지! 🚀✨

안녕하세요! 오늘은 STM32 NUCLEO-F401RE 보드와 함께한 제 임베디드 개발 여정을 여러분과 공유하려고 합니다. 처음에는 간단한 LED 깜빡임부터 시작했지만, 점점 더 깊은 하드웨어 제어의 세계를 탐험하게 되었네요! 마치 작은 씨앗이 싹을 틔우고 쑥쑥 자라나는 듯한 기분이랄까요? 😉🌱

본격적인 시작: LED 를 깜빡이다! 💡

임베디드 개발의 기본은 역시 LED 제어죠! 처음에는 STM32Cube HAL 라이브러리의 HAL_GPIO_WritePin() 함수를 사용해서 LED 를 간단하게 켜고 끄는 것을 시도해 봤습니다. 마치 숨쉬는 것과 같이, LED 가 켜졌다 꺼졌다 하는 모습이 정말 신기했어요! ✨

// HAL 라이브러리를 이용한 LED 깜빡이기 (main.c 일부분)

HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET); // LED 켜기
HAL_Delay(200); // 200ms 대기
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET); // LED 끄기
HAL_Delay(200); // 200ms 대기

간단한 코드 몇 줄로 LED 를 깜빡일 수 있다는 것이 정말 편리했지만, 뭔가 아쉬움이 남았습니다. 더 하드웨어 레벨에서 직접 제어해보고 싶다는 "호기심" 이 솟아올랐죠! 🔥

레지스터 직접 접근, 하드웨어 제어의 깊은 세계로! 🕳️

HAL_GPIO_WritePin() 과 같은 라이브러리 함수를 사용하지 않고, 레지스터를 직접 조작해서 LED 를 제어해 보기로 했습니다. 처음에는 "주소" 라는 낯선 단어들이 복잡하게 느껴졌지만, 하나씩 뜯어보니 그 안에 하드웨어 동작 원리가 숨어있는 것을 알 수 있었습니다.

// 레지스터 주소 정의 (매크로 상수)
#define GPIOA_BASE        (0x40020000UL) // GPIOA 기준 주소
#define GPIOA_ODR_OFFSET    (0x14UL)      // ODR 레지스터 offset
#define RCC_AHB1ENR_OFFSET  (0x30UL)      // RCC AHB1ENR 레지스터 offset
#define RCC_BASE          (0x40023800UL)

#define RCC_AHB1ENR       (*(unsigned int*)(RCC_BASE + RCC_AHB1ENR_OFFSET)) // AHB1 클럭 Enable 레지스터
#define GPIOA_ODR         (*(unsigned int*)(GPIOA_BASE + GPIOA_ODR_OFFSET))   // GPIOA Output Data 레지스터

// ... (main 함수 안에서)

// GPIOA 클럭 활성화
RCC_AHB1ENR |= (1 << 0);

// PA5 핀 출력 모드로 설정
GPIOA_MODER &= ~(3 << (LED_PIN * 2));
GPIOA_MODER |= (1 << (LED_PIN * 2));

// LED 켜기 (PA5 핀 HIGH) -  `|` (OR) 연산 사용
GPIOA_ODR |= (1 << 5);

delay_ms(200);

// LED 끄기 (PA5 핀 LOW) - `&= ~` (AND-NOT) 연산 사용
GPIOA_ODR &= ~(1 << 5);

delay_ms(200);

핵심 개념:

  • 메모리 맵 (Memory Map): STM32F401RE 칩의 메모리 공간을 기능별로 나눈 지도 (기준 주소, offset)
  • 레지스터 (Register): 하드웨어 동작을 제어하는 특수한 메모리 공간.
  • 비트 연산 (Bitwise Operations): 비트 단위로 데이터를 조작하는 방법 (AND, OR, NOT, Shift).
  • |= (Bitwise OR 대입): 특정 비트를 1로 설정 (LED 켜기).
  • &= ~ (Bitwise AND-NOT 대입): 특정 비트를 0으로 설정 (LED 끄기).

레지스터 직접 접근은 HAL 라이브러리보다 더 복잡하지만, 하드웨어 동작 원리를 이해하는 데 큰 도움이 되었습니다. 레지스터 주소, offset, 비트 연산 등을 직접 다루면서, 마이크로컨트롤러가 어떻게 동작하는지 조금이나마 느낄 수 있었어요. 🤓

버튼 추가! LED 토글! 손 끝으로 세상을 제어하다! 🖐️🕹️

이제 버튼 입력에 따라 LED 를 켜고 끄는 기능을 구현해보기로 했습니다. 이전 LED 깜빡이기 코드는 특정 주기로 LED 가 자동으로 깜빡이는 코드였는데, 이제는 버튼을 눌러서 직접 제어할 수 있게 된 것이죠! 진정한 "상호작용" 의 시작이라고 할 수 있을까요? 😎

// 버튼 입력 감지 및 LED 제어 코드 (while 루프 안)

  if (!(GPIOC_IDR & (1 << BUTTON_PIN))) // 버튼 (PC13) 눌렸는지 확인
  {
    delay_ms(100); // Debouncing
    if (!(GPIOC_IDR & (1 << BUTTON_PIN))) // 채터링 확인 (Debouncing)
    {
      GPIOA_ODR ^= (1 << LED_PIN); // LED 토글 (켜짐 <-> 꺼짐)
      while (!(GPIOC_IDR & (1 << BUTTON_PIN))); // 버튼 뗄 때까지 대기 (Debouncing)
    }
  }
  • GPIOC_IDR (Input Data Register): GPIOC 핀의 입력 상태를 읽어오는 레지스터. & (1 << BUTTON_PIN) 를 사용해서 B1 버튼 (PC13) 의 입력 값만 추출합니다.
  • if (!(GPIOC_IDR & (1 << BUTTON_PIN))): 풀업 저항을 사용하므로, 버튼을 누르지 않았을 때는 1, 눌렀을 때는 0이 됩니다. ! 연산자로 논리 반전을 해서 버튼을 눌렀을 때 if 문이 실행되도록 했습니다.
  • Debouncing: delay_ms(100) 와 while 루프를 사용해서 버튼 채터링을 방지합니다.
  • GPIOA_ODR ^= (1 << LED_PIN);: XOR 연산자(^=) 로 LED 상태를 토글합니다. LED 가 켜져 있었다면 꺼지고, 꺼져 있었다면 켜집니다.

이 코드를 실행하면서 직접 버튼을 눌러 LED 를 제어하는 경험은 정말 짜릿했습니다! 마치 손가락 하나로 작은 세상을 움직이는 듯한 기분이 들었죠! 😉

"모스 부호" 까지?! 더 나아가기! 🚀

LED 를 켜고 끄는 데 성공했으니, 조금 더 욕심을 부려서 "모스 부호" 로 LED 를 깜빡여보기로 했습니다. 이전에는 단순한 on/off 였지만, 이제는 시간과 패턴을 이용한 LED 제어를 시도해 본 것입니다.

void Blink_LED_Morse(char* morse_pattern)
{
  int pattern_len = strlen(morse_pattern);
  for (int i = 0; i < pattern_len; i++)
  {
    char symbol = morse_pattern[i];
    if (symbol == '.') // 짧은 깜빡임
    {
      GPIOA_ODR |= (1 << LED_PIN); // LED 켜기
      delay_ms(SHORT_BLINK_DELAY);
      GPIOA_ODR &= ~(1 << LED_PIN); // LED 끄기
      delay_ms(SHORT_BLINK_DELAY); // 짧은 간격
    }
    else if (symbol == '-') // 긴 깜빡임
    {
      GPIOA_ODR |= (1 << LED_PIN); // LED 켜기
      delay_ms(LONG_BLINK_DELAY);
      GPIOA_ODR &= ~(1 << LED_PIN); // LED 끄기
      delay_ms(SHORT_BLINK_DELAY); // 짧은 간격
    }
    else if (symbol == ' ') // 단어 간 간격 (SOS 패턴에서는 사용 안 함)
    {
      delay_ms(LONG_BLINK_DELAY); // 긴 간격 (단어 간)
    }
  }
  delay_ms(LONG_BLINK_DELAY); // 패턴 전체 간 간격
}

// ... (while 루프 안에서)
Blink_LED_Morse("SOS");  // LED 를 모스 부호 "SOS" 패턴으로 깜빡이기

이제 LED 가 단순하게 깜빡이는 것이 아니라, ". . . --- . . ." 와 같이 모스 부호 패턴으로 깜빡이는 것을 볼 수 있습니다.

NUCLEO-F401RE 보드의 확장성:

NUCLEO-F401RE 보드에는 다양한 커넥터가 제공되어 있어서, 수많은 외부 모듈을 연결해서 기능을 확장할 수 있습니다. 저희가 사용한 버튼과 LED 외에도 수많은 센서, 통신 모듈, 디스플레이 등을 연결할 수 있습니다. (자세한 내용은 이전 답변 참고)

마무리 및 다음 목표:

오늘은 LED 깜빡임부터 시작해서, 버튼 제어, 레지스터 직접 접근, 모스 부호 패턴 등 다양한 경험을 할 수 있었습니다. 물론, 언어 모델은 너무 어려운 목표였지만... 😂 "소통" 이라는 주제를 가지고 더욱 재미있는 프로젝트들을 해볼 수 있겠죠! 다음에는 시리얼 통신, USB 키보드 기능, 외부 센서 연동 등 좀 더 재미있는 프로젝트를 만들어볼 예정입니다. 앞으로 더 많은 것을 배우고, 더 멋진 결과물을 만들 수 있도록 노력하겠습니다! 💪

이 여정이 여러분에게도 도움이 되었으면 좋겠네요! 혹시 더 궁금한 점이나, 의견이 있다면 언제든지 댓글로 알려주세요! 😊

profile
현우

0개의 댓글