Lecture 2. LED 제어하기(GPIO)
강의목표
- nRF52 개발 보드에 내장된 RGB LED를 켜고 끌 수 있다.
- 딜레이 함수를 이용하여 시간을 지연시킬 수 있다.
- nRF52 시리즈의 GPIO 제어 방법을 알 수 있다.
- 기본적인 nRF52 레지스터 사용법을 알 수 있다.
준비물
- Roverdyn nRF52 개발 보드 × 1 (구매하기)
- 전원 공급용 Type-C USB 케이블 × 1
- Windows 데스크톱 혹은 랩톱 × 1
- VS Code 개발 환경
GPIO 레지스터로 LED 제어하기
nRF52시리즈는 레지스터를 이용하여 직접 하드웨어를 제어할 수 있습니다. 이를 바탕으로 사용자는 각 하드웨어를 제어하기 위한 드라이버를 제작할 수 있으며 그 중 GPIO는 General Purpose Input/Output의 약자로 기본적인 입출력을 제어할 수 있습니다. GPIO는 입력 및 출력 제어를 각각 할 수 있으며, 입력 제어는 핀으로 들어오는 값이 False/True인지 알 수 있으며 출력은 반대로 False/True 출력이 가능합니다.
GPIO 출력(Output)
만약 Active Low인 LED가 22번핀에 연결되어 있다면 22번 핀의 출력을 Flase(0)으로 설정하는 것으로 LED를 활성화(=켬) 할 수 있습니다. 만약 GPIO를 High 값으로 설정할 경우 GPIO는 MCU 칩의 Vref 값에 따라 전압이 출력되며, 만약 Vref = 3.3V인 경우 High로 GPIO핀을 설정하면 3.3V 값이 출력됩니다.
GPIO 입력(Input)
GPIO 핀으로 입력을 받는 경우에는 반대로 외부의 전원 혹은 IC로부터 False 혹은 True 값을 입력 받을 수 있습니다. 만약 외부의 신호원으로부터 0.8V 이상의 전압이 입력된다면 GPIO는 High로 인식되며 0.8V 미만의 전압이 입력될 경우 Low로 인식됩니다.
본 튜토리얼은 GPIO의 Output 기능을 이용하여 개발 보드에 내장된 LED를 제어합니다. 이를 위해서 GPIO핀의 입출력 방향 설정, 입출력 값 설정 등을 하여야 하며 그 결과 개별 LED를 제어할 수 있습니다.
로버다인의 nRF52 개발 보드는 빨강(Red), 초록(Green), 파랑(Blue) 색상의 LED가 내장되어 있으며 각각 다음 핀에 연결 되어 있습니다.
- 빨강 : P0.22
- 초록 : P0.23
- 파랑 : p0.24
LED는 Active Low이므로 해당 핀의 출력을 0(Low)로 설정 하여야 LED가 켜지며 반대로 1(High)로 설정할 경우 LED가 꺼지게 됩니다.
GPIO 설정 및 사용하기
GPIO의 레지스터는 몇 가지 설정을 하여 사용할 수 있습니다. 다음은 기본적인 GPIO의 설정입니다.
- NRF_GPIO->DIRSET : 핀의 출력 방향을 설정합니다.
- NRF_GPIO->OUTSET : 핀을 출력으로 설정하였을 때 핀을 High로 설정합니다.
- NRF_GPIO->OUTCLR : 핀을 출력으로 설정하였을 때 핀을 Low로 설정합니다.
GPIO 레지스터의 값을 입력하기 위해서는 설정하고자 하는 핀의 비트를 0 혹은 1로 설정하여 제어할 수 있습니다. 이를 확인하기 위해 먼저 nRF52 시리즈의 데이터 시트를 확인합니다.
위 데이터시트는 nRF52832의 데이터시트입니다. GPIO 레지스터 챕터에서 확인할 수 있는 내용으로, NRFGPIO->DIRSET 레지스터에 관한 내용입니다. 해당 데이터시트를 자세히 보면 Bit number가 0인 부분의 아이디는 ‘A’로 표시되어 있으며, 해당 비트를 0으로 설정하면 해당 핀(PIN0)을 Input으로 설정하고 1로 설정하면 Output으로 설정됩니다. 만약 22번핀(P0.22)를 출력으로 설정하고자 한다면 NRF_GPIO->DIRSET = (1 << 22) 으로 설정하면 위 데이터시트상 ID가 ‘W’인 22번 핀의 입출력 방향이 출력으로 설정됩니다. 만약 중복으로 설정하고자 한다면 Or(|) 연산자를 붙여 설정하면 됩니다.
Zephyr RTOS는 간단한 Sleep 함수가 있습니다. 메인 쓰레드에서 사용할 수 있는 k_msleep 이라는 함수는 매개 변수로 밀리초(ms) 단위로 입력받아 CPU의 동작을 중지 시킬 수 있습니다. 예를 들어 k_msleep(1000)으로 설정하면 1초간 지연 시킨다는 뜻입니다. 또 다른 함수로는 k_usleep이 있으며 k_msleep 함수와 비슷하지만 매개 변수로는 마이크로초 단위(us)를 입력 받습니다.
보다 자세한 내용은 아래 소스코드를 참조해주세요.
소스 코드
/*
MIT License
Copyright (c) 2024. Seonguk Jeong, Roverdyn Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include
/* LED pin no. definition */
#define LED_RED 22
#define LED_GREEN 23
#define LED_BLUE 24
int main(void)
{
// Debug message for start
printk("Application start.\n");
// Set gpio direction for LEDs & turn off
NRF_GPIO->DIRSET = (1 << LED_RED) | (1 << LED_GREEN) | (1 << LED_BLUE);
NRF_GPIO->OUTSET = (1 << LED_RED) | (1 << LED_GREEN) | (1 << LED_BLUE);
// Main loop
while(true){
// Turn off all LEDs & Turn on Red
NRF_GPIO->OUTSET = (1 << LED_RED) | (1 << LED_GREEN) | (1 << LED_BLUE);
NRF_GPIO->OUTCLR = (1 << LED_RED);
printk("Red LED on\n");
k_msleep(1000);
// Turn off all LEDs & Turn on Green
NRF_GPIO->OUTSET = (1 << LED_RED) | (1 << LED_GREEN) | (1 << LED_BLUE);
NRF_GPIO->OUTCLR = (1 << LED_GREEN);
printk("Green LED on\n");
k_msleep(1000);
// Turn off all LEDs & Turn on Blue
NRF_GPIO->OUTSET = (1 << LED_RED) | (1 << LED_GREEN) | (1 << LED_BLUE);
NRF_GPIO->OUTCLR = (1 << LED_BLUE);
printk("Blue LED on\n");
k_msleep(1000);
}
return 0;
}
빌드 및 컴파일, 다운로드
작성한 소스 코드는 빌드 및 컴파일을 한 후 최종적으로는 디버거 혹은 프로그래머 장비를 이용하여 개발 보드에 다운로드할 수 있습니다. 개발 보드에 관한 설정은 “nRF52 개발환경 구축하기” 튜토리얼을 확인하여 설정할 수 있습니다.
작성한 코드를 빌드하기 위해서는 우선 다운로드 하고자 하는 보드를 설정해주어야 합니다. 대상 보드를 정의하기 위하여 좌측 패널의 메뉴 중 “Run and Debug” 메뉴를 클릭한 후 “NRF CONNECT:WELCOME” 패널 내부의 “Create a new board” 메뉴를 클릭합니다.
화면 상단에 패널이 나타나면 새로운 보드의 이름을 작성한 후 엔터키를 눌러줍니다. 보드의 이름은 영문으로 작성하여야 합니다.
다음으로 보드의 이름을 한번 더 작성합니다. 다만 이 이름은 기계가 인식할 수 있는 방법으로 작성합니다. 대게 자동으로 작성되어 있으므로 엔터키를 눌러 다음으로 넘어갑니다.
보드가 사용하는 MCU의 종류를 입력합니다. nRF52 개발 보드는 nRF52810-QFAA 혹은 nRF52832-QFAA 두 종류를 사용합니다.
새로 작성한 보드의 정보가 저장될 위치를 설정합니다. 기본적으로 프로젝트 내부의 “board” 폴더가 지정되어 있습니다.
마지막으로 보드의 제조사를 설정합니다. 해당 내용은 작성하지 않아도 동작에 지장이 없습니다.
위와 같이 새 보드의 정의 및 파일 생성이 완료되었다면 빌드 환경을 구성할 수 있습니다. 빌드 환경은 위의 보드 추가 방법과 같이 동일한 “Run & Debug” 패널의 “NRF CONNECT: APPLICATION” 항목에서 설정할 수 있습니다.
APPLICATION 패널 중 우측에 위치한 “Add Build Configuration” 아이콘을 눌러 설정창을 열어줍니다.
빌드 환경 설정 창이 나타나면 위 그림과 같이 “Custom board” 항목을 눌러준 다음 이전에 생성한 보드를 선택합니다. 위 예제에서는 “nrf52_devboard_52832” 항목을 선택하였습니다. 이는 사용자가 만든 보드 이름에 따라 달라질 수 있습니다. 또한 Configuration 항목을 prj.conf 로 선택합니다. 이후 우측 하단의 “Build configuration” 버튼을 눌러 빌드 환경을 구성합니다.
이상 없이 빌드가 완료되었다면 다시 APPLICATION 패널로 이동하여 “Flash All Linked Devices” 항목을 눌러 컴파일된 펌웨어의 다운로드를 시작합니다. 이 때, 타겟 보드는 컴퓨터와 Jlink 혹은 기타 디버거, 프로그래머와 연결이 되어 있어야 합니다.
위와 같이 빌드를 시작하며 컴파일이 완료된 후 자동으로 컴파일된 펌웨어를 개발 보드로 전송합니다. 전송이 완료되면 개발 보드를 재부팅 하거나 전원을 분리하였다가 다시 꽂으면 새로운 펌웨어로 동작합니다.
동작 시켜보기
이 예제를 실행시키기 위해서는 프로그램을 컴파일 및 다운로드한 후 리셋 버튼을 누르거나 전원을 분리 후 재인가 하여 동작시킬 수 있습니다. 동작 시 LED는 1초 간격으로 내장된 LED가 켜지거나 꺼집니다. 켜지는 순서는 순서대로 빨강(Red) – 초록(Green) – 파랑(Blue)이며 무한히 반복됩니다.
위 소스코드를 수정하여 RGB 색상을 조합하여 다양한 색상을 만들 수 있습니다. 예를 들어 보라색을 표현하고자 한다면 빨간색 LED와 파란색 LED를 켜면 됩니다. 반대로 노란색을 얻고자 한다면 빨간색 LED와 초록색 LED를 켜면 됩니다. 흰색 LED는 모든 LED를 켜서 얻을 수 있습니다.