시작하기
이번에는 버튼을 통해 GPIO 입력을 받을 수 있는 예제를 살펴보도록 하자.
예제는 \examples\peripheral\pin_change_int\pca10040\blank\ses 폴더에서 프로젝트를 실행시켜보자.
그리고 예제에 대한 설명을 확인하기 위해 노르딕 인포센터에서 Pin Change Interrupt Example 링크에 접속하자.
The Pin Change Interrupt Example demonstrates interrupts on PIN_IN change. PIN_OUT is configured as output and toggled in the PIN_IN change interrupt handler. The application starts with configuring the pins and configuring GPIOTE to give an interrupt on PIN_IN change. The interrupt handler toggles PIN_OUT (LED 1) when PIN_IN (button 1) is pressed or released. Setup You can find the source code and the project file of the example in the following folder: <InstallFolder>\examples\peripheral\pin_change_int LED assignments: LED 1 is toggled by interrupt. Button assignments: Button 1 triggers interrupt that toggles LED 1. Testing Test the Pin Change Interrupt Example application by performing the following steps: 1. Compile and program the application. 2. Press button 1 and check if LED 1 toggles. |
PIN_IN(버튼 1)은 버튼을 누르거나 놓을 때 인터럽트 핸들러가 동작하도록 설정하고 PIN_OUT(LED1)은 인터럽트 핸들러가 발생하면 토글 되도록 GPIOTE를 사용하여 설정했다고 한다.
그렇다면 GPIOTE가 어떤 것인지 먼저 살펴보도록 하자.
일단 GPIOTE는 GPIO Tasks and Events의 약자로 작업 및 이벤트를 사용하여 GPIO 핀에 액세스 하는 기능을 제공한다.
최대 32개의 I/O를 하나의 포트를 통해 액세스 및 제어할 수 있다고 한다.
즉, GPIO 인터럽트 핸들러 사용을 위해서는 GPIOTE 설정이 꼭 필요하다고 볼 수 있겠다.
이제 main.c의 본문 내용을 살펴보도록 하자.
#ifdef BSP_BUTTON_0
#define PIN_IN BSP_BUTTON_0
#endif
#ifndef PIN_IN
#error "Please indicate input pin"
#endif
#ifdef BSP_LED_0
#define PIN_OUT BSP_LED_0
#endif
#ifndef PIN_OUT
#error "Please indicate output pin"
#endif
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
nrf_drv_gpiote_out_toggle(PIN_OUT);
}
/**
* @brief Function for configuring: PIN_IN pin for input, PIN_OUT pin for output,
* and configures GPIOTE to give an interrupt on pin change.
*/
static void gpio_init(void)
{
ret_code_t err_code;
err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
err_code = nrf_drv_gpiote_out_init(PIN_OUT, &out_config);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
in_config.pull = NRF_GPIO_PIN_PULLUP;
err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(PIN_IN, true);
}
/**
* @brief Function for application main entry.
*/
int main(void)
{
gpio_init();
while (true)
{
// Do nothing.
}
}
main 함수는 gpio_init 함수 하나만을 호출하는 것으로 굉장히 간단해 보이니 바로 gpio_init 함수를 자세히 살펴보자.
가장 먼저 nrf_drv_gpiote_init 함수를 호출함으로써 GPIOTE를 사용할 수 있도록 하는 것으로 시작된다.
/** @brief Macro for configuring a pin to use as output. GPIOTE is not used for the pin. */
#define NRFX_GPIOTE_CONFIG_OUT_SIMPLE(init_high) \
{ \
.action = NRF_GPIOTE_POLARITY_LOTOHI, \
.init_state = init_high ? NRF_GPIOTE_INITIAL_VALUE_HIGH : NRF_GPIOTE_INITIAL_VALUE_LOW, \
.task_pin = false, \
}
nrfx_err_t nrfx_gpiote_out_init(nrfx_gpiote_pin_t pin,
nrfx_gpiote_out_config_t const * p_config)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
NRFX_ASSERT(m_cb.state == NRFX_DRV_STATE_INITIALIZED);
NRFX_ASSERT(p_config);
nrfx_err_t err_code = NRFX_SUCCESS;
if (pin_in_use(pin))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
if (p_config->task_pin)
{
int8_t channel = channel_port_alloc(pin, NULL, true);
if (channel != NO_CHANNELS)
{
nrf_gpiote_task_configure((uint32_t)channel,
pin,
p_config->action,
p_config->init_state);
}
else
{
err_code = NRFX_ERROR_NO_MEM;
}
}
else
{
pin_in_use_set(pin);
}
if (err_code == NRFX_SUCCESS)
{
if (p_config->init_state == NRF_GPIOTE_INITIAL_VALUE_HIGH)
{
nrf_gpio_pin_set(pin);
}
else
{
nrf_gpio_pin_clear(pin);
}
nrf_gpio_cfg_output(pin);
pin_configured_set(pin);
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrf_drv_gpiote_out_init 함수에 GPIOTE_CONFIG_OUT_SIMPLE(false) 값으로 PIN_OUT을 설정한다.
task_pin 값이 false이므로 pin_in_use_set 함수에 PIN_OUT(LED1, P0.17)이 전달 인자로 넘어간다.
init_state 값이 NRF_GPIOTE_INITIAL_VALUE_LOW이므로 초기값으로 GPIO 핀을 clear 시켜주며 이 시점에서 LED1은 켜지게 된다.
마지막으로 GPIO 핀을 output으로 설정해주는 것으로 끝나게 된다.
/**
* @brief Macro for configuring a pin to use a GPIO IN or PORT EVENT to detect any change on the pin.
* @details Set hi_accu to true to use IN_EVENT.
*/
#define NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(hi_accu) \
{ \
.sense = NRF_GPIOTE_POLARITY_TOGGLE, \
.pull = NRF_GPIO_PIN_NOPULL, \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.skip_gpio_setup = false, \
}
nrfx_err_t nrfx_gpiote_in_init(nrfx_gpiote_pin_t pin,
nrfx_gpiote_in_config_t const * p_config,
nrfx_gpiote_evt_handler_t evt_handler)
{
NRFX_ASSERT(nrf_gpio_pin_present_check(pin));
nrfx_err_t err_code = NRFX_SUCCESS;
/* Only one GPIOTE channel can be assigned to one physical pin. */
if (pin_in_use_by_gpiote(pin))
{
err_code = NRFX_ERROR_INVALID_STATE;
}
else
{
int8_t channel = channel_port_alloc(pin, evt_handler, p_config->hi_accuracy);
if (channel != NO_CHANNELS)
{
if (!p_config->skip_gpio_setup)
{
if (p_config->is_watcher)
{
nrf_gpio_cfg_watcher(pin);
}
else
{
nrf_gpio_cfg_input(pin, p_config->pull);
}
pin_configured_set(pin);
}
if (p_config->hi_accuracy)
{
nrf_gpiote_event_configure((uint32_t)channel, pin, p_config->sense);
}
else
{
m_cb.port_handlers_pins[channel - GPIOTE_CH_NUM] |= (p_config->sense) <<
POLARITY_FIELD_POS;
}
}
else
{
err_code = NRFX_ERROR_NO_MEM;
}
}
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrf_drv_gpiote_in_init 함수에 GPIOTE_CONFIG_IN_SENSE_TOGGLE(true) 값으로 PIN_IN과 in_pin_handler 함수를 설정한다.
channel_port_alloc 함수에 PIN_IN(버튼 1, P0.13)과 in_pin_handler, hi_accuracy 값을 전달 인자로 채널을 할당받는다.
skip_gpio_setup 값과 is_watcher 값이 false이므로 PIN_IN(버튼 1, P0.13)을 input으로 설정한다.
hi_accuracy 값이 true이므로 nrf_gpiote_event_configure 함수에 채널과 PIN_IN(버튼 1, P0.13), NRF_GPIOTE_POLARITY_TOGGLE 값으로 전달 인자로 넘기면서 핀이 변경(토글)되면 할당된 채널에 이벤트가 발생하도록 설정한다.
마지막으로 nrf_drv_gpiote_in_event_enable 함수로 PIN_IN과 true값을 전달 인자로 넘겨 이벤트를 활성시키는 것으로 끝난다.
즉, 버튼 1의 상태가 변경(토글)될 때마다 in_pin_handler 함수가 동작되면서 LED1의 출력이 변경(토글)되게 된다.
위 소스코드를 응용하여 (버튼 1:LED1), (버튼 2:LED2), (버튼 3:LED3), (버튼 4:LED4)이 동작하도록 수정해보자.
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
if(pin == 13) nrf_drv_gpiote_out_toggle(17);
else if(pin == 14) nrf_drv_gpiote_out_toggle(18);
else if(pin == 15) nrf_drv_gpiote_out_toggle(19);
else if(pin == 16) nrf_drv_gpiote_out_toggle(20);
else return;
}
/**
* @brief Function for configuring: PIN_IN pin for input, PIN_OUT pin for output,
* and configures GPIOTE to give an interrupt on pin change.
*/
static void gpio_init(void)
{
ret_code_t err_code;
err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
err_code = nrf_drv_gpiote_out_init(17, &out_config);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_gpiote_out_init(18, &out_config);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_gpiote_out_init(19, &out_config);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_gpiote_out_init(20, &out_config);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
in_config.pull = NRF_GPIO_PIN_PULLUP;
err_code = nrf_drv_gpiote_in_init(13, &in_config, in_pin_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(13, true);
err_code = nrf_drv_gpiote_in_init(14, &in_config, in_pin_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(14, true);
err_code = nrf_drv_gpiote_in_init(15, &in_config, in_pin_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(15, true);
err_code = nrf_drv_gpiote_in_init(16, &in_config, in_pin_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(16, true);
}
/**
* @brief Function for application main entry.
*/
int main(void)
{
gpio_init();
while (true)
{
// Do nothing.
}
}
앞으로
지금까지는 가장 기본적인 GPIO 출력과 입력에 대해 알아보았다.
다음에는 nRF52가 가지고 있는 여러 가지 종류의 타이머 사용 방법에 대해 알아보도록 하자.
'Embedded > nRF52 BLE 개발 안내서' 카테고리의 다른 글
nRF52 BLE 개발하기 - simple_timer (0) | 2021.01.14 |
---|---|
nRF52 BLE 개발하기 - timer (0) | 2021.01.12 |
nRF52 BLE 개발하기 - led_softblink (0) | 2021.01.02 |
nRF52 BLE 개발하기 - blinky (0) | 2021.01.01 |
nRF52 개발환경 구축하기 - SES를 설치하고 LED를 밝혀보자 (2) | 2020.12.31 |
댓글