[Tiva] TM4C 시리즈에서 외부인터럽트 구현

임베디드/TI 2014.07.26 22:09

 MCU를 활용하는 사람이면 거의 대다수가 맨 처음 프로그램을 구동할 때GPIO(General Purpose Input Output)를 구현하여 동작을 확인하는 경험을 해보셨을 것이라 생각합니다. TI에서 제공하는 Tiva C 시리즈인 TM4C MCU 시리즈 또한 이를 잘 구현해 놓았지요.


 ATmega시리즈로 순식간에 인터럽트를 설정하시던 분들도 Tiva 시리즈를 다룰 때 다소 해메시는 분들도 계실것이라 생각합니다. 하지만 그렇다고 처음부터 겁을 먹을 필요는 없습니다. 코드로 표현하는 방식이 다를 뿐 다른 MCU들 처럼 구현할 수 있는 기능은 똑같으니 말이지요.


 이번에는 MCU 프로그램의 필수 요소라 할 수 있는 Interrupt를 구현해보도록 하겠습니다. 인터럽트와 관련이 있는 부분은 직접 주석으로 적어두었으므로 하나씩 읽으면서 익혀두셨으면 합니다.

 

 단, 처음에 Input으로 설정한 핀을 그대로 입력하고자 하는 곳에 꽃게 되면 플로팅 상태가 되기 때문에 입력값이 들쭉날쭉 하는 현상이 벌어집니다. 플로팅 상태의 입력을 안정하게 하는 방법은 다음 포스트를 참고해주시길 바랍니다.

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_gpio.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_nvic.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
 
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif
 
    //자신이 설정하고자 하는 인터럽트 핸들러를 설정한다.
void INT_IntHandler(void) {
    ROM_IntDisable(INT_GPIOA);
    GPIOIntClear(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6);
 
    UARTprintf("Hello, Interrput!\n");
    ROM_IntEnable(INT_GPIOA);
}
 
 
void ConfigureUART(void) {
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
 
    //
    // Enable UART0
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
 
    //
    // Configure GPIO Pins for UART mode.
    //
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
 
    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
 
    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000);
}
 
int main(void) {
 
    //
    // Enable lazy stacking for interrupt handlers. This allows floating-point
    // instructions to be used within interrupt handlers, but at the expense of
    // extra stack usage.
    //
    ROM_FPULazyStackingEnable();
 
    //
    // Set the clocking to run directly from the crystal.
    //
    ROM_SysCtlClockSet(    SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
 
    //INTERRUPT를 사용할 GPIO핀을 활성화 시킨다.
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
 
    // Initialize the UART.
    ConfigureUART();
 
    //인터럽트를 받아들일 핀을 Input으로 설정한다.
    ROM_GPIOPinTypeGPIOInput(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6);
 
    //인터럽트되는 핀의 입력 형태를 선택한다. LOW에서 HIGH 상태로 넘어갈 때 인터럽트를 발생시키고자 하면
    //GPIO_RISING_EDGE를 선택한다.
    ROM_GPIOIntTypeSet(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6, GPIO_RISING_EDGE);
 
    //PORTA의 GPIO 인터럽트를 허용한다.
    GPIOIntEnable(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6);
 
    //PORTA에서 인터럽트가 발생하였을 때 실행할 함수를 설정한다.
    GPIOIntRegister(GPIO_PORTA_BASE, INT_IntHandler);
 
    //인터럽트가 시작되기 전 Clear 시켜준다.(인터럽트 관련 레지스터를 0으로 만든다.)
    GPIOIntClear(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6);
 
    ROM_IntEnable(INT_GPIOA);
 
    while (1) {
    }
}


4번 핀과 6번 핀에 HIGH_EDGE 입력이 들어오면 mcu에서 인터럽트가 발생하여 설정된 함수를 실행한다.



  • 유수민 2015.07.12 20:39 ADDR 수정/삭제 답글

    안녕하세요 글 잘보고있습니다. 다름이 아니라 같은 gpio에 rising edge와 falling edge 일 때 타이머카운트 값을 읽고 싶습니다.
    그럴땐 어떻게 해야하나요? 우선 초기화 부분에서
    ROM_GPIOIntTypeSet(GPIO_PORTA_BASE, GPIO_PIN_6, GPIO_RISING_EDGE);
    ROM_GPIOIntTypeSet(GPIO_PORTA_BASE, GPIO_PIN_6, GPIO_FALLING_EDGE);
    이런식으로 falling엣지 모드를 추가 해주었구요
    if(mode==0)
    {
    now_count = gCount;
    mode=1;
    }
    else
    {
    pre_count=gCount;
    mode=0;
    }
    duty_count = now_count - pre_count;
    이런 식으로 라이징 엣지 걸렷을때랑 폴링엣지 걸렸을 때를 mode로 나눠주어 그 때의 타이머 카운트 값을 서로 빼준식으로 둘의 차이를 구하려고 하는데
    값이 잘 안나오네요 이렇게 하는거 맞나요?

  • 유수민 2015.07.12 22:44 ADDR 수정/삭제 답글

    질문하나더요! tm4c123 쓰는데요 타이머 카운터수 말고 내부타이머카운터수를 이용해서 pwm 간격을 측정하고 싶은데 어떤 레지스터를 사용해야 하나요?

  • Justin T. 2015.07.13 01:01 신고 ADDR 수정/삭제 답글

    다음과 같이 초기화를 해주시면 원하시는 방식대로 동작될 것으로 보입니다.
    ROM_GPIOIntTypeSet(GPIO_PORTA_BASE, GPIO_PIN_6, GPIO_BOTH_EDGE);

    그리고 레지스터와 관련된 부분은 해당 마이크로컨트롤러의 DataSheet를 직접 찾아서 보시는 것을 권장드립니다.
    http://pdf1.alldatasheet.com/datasheet-pdf/view/543230/TI/TM4C123GH6PM.html

  • 유수민 2015.07.13 17:18 ADDR 수정/삭제 답글

    답변감사드립니다. 내부카운터 쓰는것은 레지스터 찾아서 했는데요.SysClock1 = TimerValueGet64(TIMER1_BASE); 이런식으로 카운터 수를 얻었습니다.
    문제가 오버플로우가 생겨서 원하는 값이 일정시간에 얻을 수없었습니다. 그래서 내부카운터수를 리셋시켜주고싶은데 아무리찾아봐도 그런기능은 없는거 같아서요
    어떻게 해결해야할지 알려주실수있나요?

  • Justin T. 2015.07.13 20:13 신고 ADDR 수정/삭제 답글

    설계를 바꾸는 것이 좀 더 쉬운 접근이 될 거 같습니다.
    아무래도 PWM의 rising edge와 falling edge 간의 간격을 측정하시는 것을 원하시는 것으로 보이는데
    rising edge의 순간에서의 timer 값과 falling edge의 순간에서의 timer 값을 따로 받으신 후 그 차이를 계산하는 쪽을 해보셨는지요?
    Tivaware의 API를 보시면 레지스터를 reset 하는 기능이 있을 수 있으니 자세히 읽어보셨으면 합니다.

  • 유수민 2015.07.13 22:24 ADDR 수정/삭제 답글

    네 따로 받아서 차이를 계산했습니다. 리셋기능을 모두 사용해보았지만 되질않아서요 .. ㅠㅠ

  • Justin T. 2015.07.13 22:31 신고 ADDR 수정/삭제 답글

    흠.. 혹시 영어가 되신다면 TI e2e나 stackoverflow에 문의해보셨으면 합니다.
    지금 당장은 Debug 모드로 한 줄씩 검토하며 안되는 부분을 직접 찾아보셨으면 합니다.

  • 유수민 2015.07.13 23:06 ADDR 수정/삭제 답글

    예 알겠습니다. 귀찮으셧을텐데 친절히 답변해주셔서 감사합니다.

  • 유수민 2015.07.15 01:06 ADDR 수정/삭제 답글

    죄송한데 질문하나만 더 해도 될까요? gpio외부인터럽트와 uart인터럽트가 서로 충돌을 합니다. 인터럽트 우선순위도 정해주었습니다. 하지만 작동이 되질 않네요ㅜㅜ
    혹시 이럴때는 어떻게 해주어야 하는지 아시면 자문을 여쭤봐도 될까요? 물어볼데가 없어서 이렇게 매번 귀찮게 해드려서 죄송합니다. ㅠㅠ

  • Justin T. 2015.07.15 01:16 신고 ADDR 수정/삭제 답글

    질문이 구체적이지 않아 상황이 어떠한지 잘 파악이 되지 않는군요..
    GPIO와 UART 포트가 MCU나 LaunchPad상의 설계구조 때문으로 추측됩니다.
    아마도 LaunchPad의 DataSheet를 참조하시면 회로도를 보실 수 있는데 여기서 힌트를 얻으실 수 있을 것으로 보입니다.
    그리고 왠만한 해결방법은 해당 기기에 첨부된 DataSheet를 꼼꼼히 읽어보시면 자신이 어느 부분에서 잘못되었는지를 찾으실 수 있습니다. 위에서 말씀드렸듯이 TI e2e 쪽의 전문가들의 자문이 좀 더 도움이 되실것이라고 생각합니다.
    https://e2e.ti.com/support/microcontrollers/tiva_arm/

  • JG 2016.05.18 21:18 ADDR 수정/삭제 답글

    안녕하세요 외부 인터럽트 관련 질문이있습니다.
    센서들을 외부 인터럽트로 설정한 후 그 센서 값들을 텍스트 혹은 dat 파일로 설정해서 받는 방법이 있을까요?

    • Justin T. 2016.05.19 00:34 신고 수정/삭제

      저장되는 매체가 무엇이냐에 따라 방벙은 다양합니다.
      만약 pc에 저장되는 것이라면 uart를 통해 피씨에 전송한 후 이를 피씨에서 수신하여 파일로 저장해주는 프로그램을 따로 만들어주셔야 할 듯 합니다.