컴퓨터 / IT

LED 컨트롤러 제작기 2편

드라이빙필 2019. 7. 31. 11:44
반응형

1편 내용대로 하드웨어 준비가 끝난 상태에서 이제 내가 원하는 LED 조명 효과를 만들어 보는 과정을 적어 보고자 합니다. STM32 MCU나 개발 환경에 대해 익숙하지 않은 분들은 이게 뭔 소린가 하실 수도 있어서 조심스럽긴 하지만 제작 과정을 기록하는 의미도 있어서 정리해 봤습니다.

 

개발 환경은 ST에서 최근에 출시한 STM32CubeIDE를 사용해 봤습니다. 새로 나온 제품이고 아직은 버그가 많다 보니 회사에서 본격적인 개발용으로는 사용하기 어려운 환경이지만 집에서 간단하게 DIY 하는 목적으로는 아주 훌륭합니다. 특히 여러 개로 분리된 개발환경이 하나로 통합되어 있는지라 별다른 추가 소프트웨어가 필요하지 않다는 점은 편리하지요.

 

 

STM32CubeIDE - STMicroelectronics

STM32CubeIDE - Integrated Development Environment for STM32, STM32CubeIDE-RPM, STM32CubeIDE-Lnx, STM32CubeIDE-Win, STM32CubeIDE-DEB, STM32CubeIDE-Mac, STMicroelectronics

www.st.com

처음 실행하면 위와 같은 화면이 반기는데요. Start new STM32 project 버튼을 눌러 주면 아래와 같이 Target Selection 창이 뜨는데요. 여기서 자기가 가지고 있는 STM32 MCU나 개발 보드를 선택하면 됩니다.

 

제가 구입한 NUCLEO-G431KB를 입력해 주면 오른 쪽 화면에 해당 제품들이 나열됩니다.

 

보드를 선택하고 Next 버튼을 누르면 아래와 같은 프로젝트 설정 창이 뜹니다.

 

프로젝트 이름만 적당한 걸로 적어 주고 Finish 버튼을 누릅니다.

 

개발 보드에 구성된 회로에 따라 MCU의 핀들을 자동으로 설정해 줄까 하고 물어보는데요. Yes를 눌러줍니다.

 

이제 개발을 위한 초기 설정이 끝났습니다. PC와 시리얼 통신을 위한 USART2와 디버깅을 위한 핀들 그리고 LED 하나(LD2) 설정된 것을 볼 수 있습니다.

 

이 글에 앞서 1편에서 언급했듯이 구입한 LED 스트립은 APA102C라는 컨트롤러 IC가 내장되어 있습니다. 이 IC는 SPI라는 통신 인터페이스로 동작하기 때문에 MCU 쪽에서 이에 대한 지원을 추가해 줘야 됩니다. NUCLEO-G431KB의 경우 아두이노 나노의 핀 규격을 따르게 설계되어 있는데요. 그래서 거기에 맞게 PB5 핀을 SPI MISO 핀으로, PB3 핀을 SPI SCK 핀으로 설정하기로 하였습니다.

 

MCU 화면의 PB3 핀을 클릭하면 나오는 메뉴에서 SPI1_SCK를 선택해 줍니다.

 

마찬가지로 PB5 핀을 클릭하고 SPI1_MOSI를 선택해 줍니다.

 

두 핀을 설정하고 나면 오렌지 색으로 바뀐 걸 볼 수 있는데요. 아직 설정이 다 끝나지 않아서 그렇습니다. 다른 핀들처럼 초록 색이 돼야 기본적인 설정이 완료된 것입니다.

 

화면 왼쪽의 Connectivity를 누르면 아래로 메뉴가 나타나는 데요. 여기서 SPI1을 클릭합니다. 그리고 나서 오른쪽 Mode 설정에서 Transmit Only Master를 선택합니다.

 

이제 세부 설정을 해줘야 됩니다. 일단 Mode 설정 아래 쪽으로 눈을 내리면 Parameter Settings에 빨간 x표가 보입니다. 그리고 그곳을 클릭하면 세부 설정이 보이는데요. 이때 빨간색 박스로 표시한 부분들을 APA102C에 맞게 변경해 줘야 됩니다. 일단 빨간 x가 표시된 이유는 Baud Rate가 너무 높아서입니다. CPU 코어의 클럭이 높은 제품이다 보니 기본 설정으로는 SPI의 속도가 너무 빠르게 설정된 것이죠.

 

APA102C의 최대 SPI 속도는 정확히 모르겠습니다만 제 경험상 10MBits/s 까지는 잘 동작합니다. 그러니 그 값을 넘지 않도록 Prescaler를 지정해 줍니다. Prescaler가 크면 속도는 그에 반비례하여 줄어듭니다. 저는 32로 선택하였고 5.3125 MBits/s로 표시됨을 알 수 있죠. 나머지 설정들(Data Size, CPOL, CPHA)는 위와 동일하게 맞추면 됩니다. 상단의 Parameter Settings 옆에 빨간 x 표가 사라지고 초록 색 v 표로 바뀐 것도 볼 수 있습니다.

 

추가로 SPI DMA 설정을 해 줍니다. 이 부분은 꼭 해줄 필요는 없지만 CPU가 효율적으로 일하는데 도움이 됩니다. 특히 OS를 이용하여 멀티 태스크 환경으로 동작시키는 경우에 더욱 유용합니다. 이 글에서는 별 의미가 없을 순 있겠지만 향후를 대비한 추가 설정 정도로 생각하시면 됩니다. DMA Setting 탭을 클릭하고 Add 버튼을 누른 다음 SPI1_TX를 선택해 주시면 끝.

 

설정을 마치고 나면 SPI1 핀들이 초록 색으로 바뀐 걸 볼 수 있습니다.

 

STM32CubeIDE 메뉴에서 Code Generation 버튼을 누른 후 잠시 기다려 줍니다. 오른쪽 폴더 목록이 잠깐 바뀌면서 변경되는 것을 볼 수 있는데요. 작업이 끝났으면 끝났다고 알려 주면 좋은데 그렇질 않다 보니 분위기상 알아서 해야 됩니다. ㅎㅎ 몇 번 하다 보면 감이 오긴 하지만 이 점은 향후에 개선되었으면 좋겠습니다.

 

왼쪽의 망치 모양의 Build 버튼을 누른 후 컴파일 과정이 끝나길 기다리면 위와 비슷한 메시지가 나오면서 완료됩니다. 

 

프로젝트 창의 Src 폴더에 main.c가 있는데요. 여기서 main() 함수를 찾은 후 맨 아래를 보시면 while 루프가 보입니다. 여기다 내가 필요한 코드를 넣으시면 됩니다. 혹시 STM32를 처음 사용하시는 분들이라면 Code Generater가 생성한 코드에서 USER CODE BEGIN과 USER CODE END 표시를 주의해서 보셔야 됩니다. BEGIN과 END 사이에만 사용자 코드를 넣어야 합니다. 그러면 추후에 하드웨어 변경이 있더라도 사용자 코드는 유지해 주는 것이죠.

 

APA102C의 프로그래밍 방법은 아주 간단합니다. 위와 같은 데이터 포맷만 지켜주고 연속으로 전송만 해주면 끝이죠. Start Frame과 End Frame 각각 0 또는 1로 구성된 4 바이트 데이터를 앞뒤로 붙이고 LED 각각에 해당하는 LED Frame 데이터를 중간에 연속으로 이어 붙이면 됩니다. 제 경우는 LED가 60개이므로 전체 데이터 크기는 아래와 같습니다.

 

Start Frame (4 바이트) + LED Frame (60 x 4 바이트) + End Frame (4 바이트) = 248 바이트

 

이제 색깔이 계속 변하면서 숨 쉬듯이 천천히 변하는 코드를 만들어 보겠습니다.

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  #define LED_COUNT	60		// LED 갯수
  #define LED_INTERVAL	50		// LED 색깔이 변하는 간격

  uint8_t ledFrameBuf[LED_COUNT+2][4];	// SPI에 전송할 LED 색상 및 밝기 정보(Frame)
  uint8_t ledColorBright = 0x80;	// 각 컬러별 기본 밝기, 최대 밝기는 0xFF
  int i, j, k, m, br;

  // LED 모두 끔
  memset(&ledFrameBuf[0][0], 0, 4);			// start frame
  memset(&ledFrameBuf[1][0], 0xE0, LED_COUNT*4);	// LED frame (밝기와 컬러 모두 0으로 설정)
  memset(&ledFrameBuf[LED_COUNT+1][0], 0xFF, 4);	// end frame
  HAL_SPI_Transmit_DMA(&hspi1, ledFrameBuf[0], (LED_COUNT+2)*4);	// SPI 전송
  HAL_Delay(LED_INTERVAL);	// 지연 시간

  while (1)
  {
	for(k=0; k<7; k++) // LED 컬러를 순차적으로 바꾸는 루프
	{
		m = k % 7;
		for(j=0; j<63; j++) // LED 밝기를 순차적으로 변경하는 루프
		{
			br = (j<32) ? j : (62-j);
			for(i=1; i<=LED_COUNT; i++) // 모든 LED에 대한 루프
			{
				ledFrameBuf[i][0] = 0xE0 | br;		// 밝기
				ledFrameBuf[i][1] = (m==0 || m==1 || m==5 || m==6) ? ledColorBright : 0;	// Blue
				ledFrameBuf[i][2] = (m==1 || m==2 || m==3 || m==6) ? ledColorBright : 0;	// Green
				ledFrameBuf[i][3] = (m==3 || m==4 || m==5 || m==6) ? ledColorBright : 0;	// Red
			}
			HAL_SPI_Transmit_DMA(&hspi1, ledFrameBuf[0], (LED_COUNT+2)*4);	// SPI 전송
			HAL_Delay(LED_INTERVAL);	// 지연 시간
		}
	}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

LED 전체가 7가지 컬러 패턴으로 반복되며 각 컬러 별로 밝아졌다 어두워졌다 하도록 코딩해 봤습니다. 좀 더 개선 여지는 있으나 일단 잘 동작합니다. ^^ 참고로 빌드 과정에서 memset 때문에 에러가 날 수 있는데요.

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */

main.c 맨 위에 string.h를 include 시켜 주시면 됩니다.

 

NUCLEO 보드에서 실행하기 위해 다운로드가 필요한데요. 벌레 모양의 Debug 버튼을 누르면 됩니다.

 

처음 한 번은 어디서 실행할 거냐고 물어 봅니다. 여기서 STM32를 선택하고 OK.

 

여기서도 그냥 OK 누르면 됩니다.

 

디버그 모드로 화면 전환할 거냐고 물어보는 건데 위 화면처럼 체크하고 Switch 하시면 됩니다.

 

화면이 바뀌고 main 함수에서 가만히 멈춰 있는데 Resume 버튼 누르시면 실행이 됩니다.

 

드디어 작업 완료! 컬러 종류나 변하는 속도 등은 코드에서 간단히 바꿀 수 있고 다른 효과도 입맛에 따라 만들어 볼 수 있으니 기성품 사용하는 것보다 훨씬 재밌습니다. ^^

 

 

반응형

'컴퓨터 / IT' 카테고리의 다른 글

Crucial Ballistix Elite 3600  (0) 2019.08.18
XBOX ONE S 무선 컨트롤러  (0) 2019.08.18
LED 컨트롤러 제작기 1편  (2) 2019.07.14
Sabrent ROCKET NVMe SSD  (5) 2019.07.14
노트북 무선랜 업그레이드  (0) 2019.06.15