Skip to content

GPIO

GPIO 구성

STEP2에는 16개의 GPIO 핀이 DSUB-25P 암포트에 배열되어 있으며 GPIO 포트의 이미지와 핀 매핑은 아래와 같습니다 (SETP3는 GPIO 포트를 제공하지 않습니다).


STEP2의 GPIO 포트 구성

  • GPIO0: pin 5 ~ pin 12

  • GPIO1: pin 1 ~ pin 4, pin 14 ~ pin 17

  • Pin 18, 19: GPIO1 파워 (기본적으로 5V; 사용자가 내부 jumper를 이용하여 12V로 변경 가능)

  • Pin 20-25: GPIO 접지

GPIO APIs

NRMKPlatform은 GPIO 포트를 사용하기 위한 사용자 API를 제공합니다. API는 NRMKPlatform과 함께 설치된 NRMKhw라는 라이브러리에 패키징되어 있으며 지원되는 모든 API 함수의 프로토타입은 NRMKFoundation 설치 폴더의 하위 폴더 helper/include/hw 에 있는 NRMKhw_tp.h 파일에서 찾을 수 있습니다.

  • NRMKhw_tp.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//GPIO
int tp_gpio_init(void);
void tp_gpio_setdirout(TP_PORT port, uint8_t regcode);
void tp_gpio_setdirin(TP_PORT port, uint8_t regcode);
void tp_gpio_set_dir(TP_PORT port, uint8_t regcode);

uint8_t tp_readport(TP_PORT port);
void tp_writeport(TP_PORT port, TP_PORT portval);

void tp_onpin(TP_PORT port, uint8_t  pincode);
void tp_offpin(TP_PORT port, uint8_t  pincode);
void tp_togglepin(TP_PORT port, uint8_t  pincode);

또한 다음과 같은 핀에 대한 정의와 포트에 대한 데이터 형식이 있습니다.

  • 핀: 핀 하나. 포트 번호와 함께 0~7 범위의 핀 번호를 사용

  • 포트 : 동시에 제어하고 액세스할 수 있는 핀 그룹. STEP2에서 포트는 아래의 코드와 같이 TP_PORT를 열거형으로 정의

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#define pin00   0x01
#define pin01   0x02
#define pin02   0x04
#define pin03   0x08
#define pin04   0x10
#define pin05   0x20
#define pin06   0x40
#define pin07   0x80
#define pin(i)  ((uint8_t)(1<<i))
#define gpio_allpin 0xFF

typedef enum _TP_UART_CLK_SRC{
    MODE01_8462MHz=0x00,        //01.8462 MHz (24 MHz / 13)     : Max baud=115.2K (default)
    MODE02_0000MHz=0x01,        //02.0000 MHz (24 MHz / 12)     : Max baud=125.0K
    MODE24_0000MHz=0x02,        //24.0000 MHz (24 MHz / 1)      : Max baud=1.5M
    MODE14_1690MHz=0x03         //14.7690 MHz (24 MHz / 1.625)  : Max baud=921.6k
} TP_UART_CLK_SRC;

typedef enum {
    GPIO_GDIR_INPUT  = 0,
    GPIO_GDIR_OUTPUT = 1,
} TP_GPIO_GDIR;

typedef enum {
    GPIO_LOW_LEVEL  = 0,
    GPIO_HIGH_LEVEL = 1,
} TP_GPIO_LEVEL;

typedef enum _TP_PORT{
    port0=0,
    port1=1
} TP_PORT;

GPIO API 함수는 여러 핀 또는 전체 포트를 동시에 제어하고 액세스합니다. 이를 통해 포트 방향을 바꾸거나, 포트에 값을 쓰거나 읽을 수 있습니다. 단일 핀 제어와 비교하여 더 높은 접근 속도를 낼 수 있도록 atomic operations 이 구현되어 있습니다. GPIO를 이용하여 값을 읽고 쓰기 전에 포트를 다음 함수 중 하나를 호출하여 초기화 해야 합니다.

  • int tp_gpio_init(void)

    모든 GPIO 포트를 초기화 합니다.

  • void tp_gpio_setdirout(TP_PORT port, uint8_t regcode)

    포트를 출력으로 설정합니다. regcode는 16진수이며 각 비트값은 포트에서 출력 핀을 결정합니다.

  • void tp_gpio_setdirin(TP_PORT port, uint8_t regcode)

    포트를 입력으로 설정합니다. regcode는 16진수이며 각 비트값은 포트에서 입력 핀을 결정합니다.

참고

regcode의 각 비트값은 포트에서 입력 (또는 출력) 핀을 결정합니다. 비트값 1은 입력 (또는 출력)을 0은 사용하지 않는 핀에 대한 값을 나타냅니다. 예를 들어 GPIO1의 0에서 3까지의 핀 4개를 사용하고 나머지 핀을 사용하지 않기 위해서는 2진수 표현으로 1111 이기 때문에 이를 16진수로 표현하여 아래와 같이 사용할 수 있습니다.

tp_gpio_setdirout(port1, 0xF)

  • void tp_gpio_set_dir(TP_PORT port, uint8_t regcode)

    포트의 방향을 선택합니다. regcode의 각 비트값은 포트의 입출력 방향을 결정합니다. 비트값 1은 출력 핀을 0은 입력 핀을 지정합니다. 예를 들어 GPIO1의 0-3 핀을 입력으로, 4-7 핀을 출력으로 각각 설정하기위해서는 tp_gpio_set_dir(port1, 0xF0) 를 호출하면 됩니다.

  • void tp_onpin(TP_PORT port, uint8_t pincode)

    pincode로 마스킹된 핀을 high level로 설정합니다. pincode의 각 비트값으로 어떤 핀을 high level로 설정할지 결정합니다. 예를 들어 GPIO1의 0, 2, 4, 6 핀을 high level로 설정하고 다른 핀을 유지하기 위해 사용자는 tp_onpin(port1, 0x55) 를 호출할 수 있습니다. 여기서 0x55 = 01010101이며, 비트 0, 2, 4, 6은 값이 1입니다.

  • void tp_offpin(TP_PORT port, uint8_t pincode)

    pincode로 마스킹된 핀을 low level로 리셋합니다. pincode의 각 비트값으로 어떤 핀을 low level로 설정할지 결정합니다. 사용방법은 tp_onpin과 동일합니다.


GPIO 포트의 초기화가 완료되면 다음의 API함수를 이용하여 값을 읽거나 쓸 수 있습니다.

  • void tp_writeport(TP_PORT port, uint8_t portval)

    포트에 값 portval을 씁니다. 이 함수를 사용하려면 먼저 포트가 출력으로 설정되어야 합니다.

  • uint8_t tp_readport(TP_PORT port)

    입력 포트에서 값을 읽습니다. 이 함수를 사용하려면 먼저 포트가 입력으로 설정되어야 하며 반환값은 포트의 핀 상태를 나타내는 unsigned 8비트 값 입니다.


GPIO 실시간 제어 예제

아래의 코드는 GPIO 핀을 실시간으로 제어하는 예제를 나타냅니다. 예제에서 GPIO0 핀 8개를 사용하여 xenomai 실시간 태스크로 8개의 PWM 신호를 생성합니다. 태스크 주기는 50us 이며 따라서 PWM의 간격은 100us입니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/io.h>

#include <native/task.h>
#include <native/timer.h>
#include <native/mutex.h>
#include <rtdk.h>

#include "NRMKhw_tp.h"  //NRMK gpio library

RT_TASK gpio_out_task;
uint8_t allstate=0;
TP_PORT test_port=0;

//output task
void gpio_out_run(void *arg)
{
    int state=0;
    rt_task_set_periodic(NULL, TM_NOW, 5e4);    //cycle: 50us
    while (1)
    {
        if (state==0)
            tp_writeport(test_port, 0xAA);
        else
            tp_writeport(test_port, 0x55);
        state^=1;
        rt_task_wait_period(NULL);
    }
}

void catch_signal(int sig)
{
    rt_task_delete(&gpio_out_task);
    exit(1);
}

int main(int argc, char* argv[])
{
    signal(SIGTERM, catch_signal);
    signal(SIGINT, catch_signal);
    /* Avoids memory swapping for this program */
    mlockall(MCL_CURRENT|MCL_FUTURE);

    /*Perform auto-init of rt_print buffers */
    rt_print_auto_init(1);

    if (tp_gpio_init()!=0) //initialize port
    {
        printf("GPIO Init. Error!\n");
        return 1;
    }

    tp_gpio_setdirout(test_port,  0xFF);//all pin as output

    printf("Running, check the GPIO state....\n");

    //create and start a realtime periodic task for writing gpio
    rt_task_create(&gpio_out_task, "gpio_out", 0, 99, 0);
    rt_task_start(&gpio_out_task, &gpio_out_run, NULL);

    pause();

    rt_task_delete(&gpio_out_task);

    return 0;
}
  • GPIO 라이브러리를 사용하기 위한 첫 번째 단계는 헤더 파일 NRMKhw_tp.h 를 포함하는 것입니다 (line 13). 이 헤더파일은 STEP2의 GPIO 제어를 위한 모든 함수의 프로토타입을 포함하고 있습니다.

  • GPIO 입출력 함수를 사용하기 전에 함수 tp_gpio_init() 을 호출하여 초기화해야 합니다 (line 51).

  • 본 예제에서는 test_port (port0) 를 출력으로 사용하기위해 tp_gpio_setdirout() 함수 사용하였습니다. 특히, test_port (port0) 의 모든 핀을 출력으로 설정하기 위해서 두 번째 파라미터를 0xFF 로 설정하였습니다 (line 57).

  • PWM 생성 작업은 예제코드 line 62-63에 의해 생성된 실시간 태스크 gpio_out_task 에서 수행됩니다. 실시간 태스크 함수 gpio_out_run 은 line 20-33에 구현되어 있습니다.

루프를 시작하기 전 rt_task_set_periodic(NULL, TM_NOW, 5e4)에서 50us로 작업주기가 설정됩니다 (나노초 단위이기 때문에 5e4 = 50000ns = 50us). 루프에서 port0 (test_port) 핀들의 출력 값은 tp_writeport(test_port, 0xAA)tp_writeport(test_port, 0x55) 에 의해 지속적으로 바뀝니다. 이 때 rt_task_wait_period() 함수가 다음 주기가 시작될 때까지 대기하기 때문에 일정한 작동 주기가 구현됩니다.

아래 그림은 오실로스코프를 이용하여 측정한 핀 GPIO0_0 및 GPIO0_1의 PWM 신호를 나타냅니다.


핀 GPIO0_0 및 GPIO0_1에서 생성된 PWM 신호