티스토리 뷰

인터럽트를 사용하지 않으면 반복문을 통해 항상 체크를 해야할 것이다. 이를 Pollng 방식이라하며 I/O의 요청을 주기적으로 탐색하는 SW 적인 방법이다. 인터럽트를 알기 전까지는 그렇게 해왔지만 이는 메모리 낭비이며 다른 일을 수행하지 못하도록 하는 원인이다.

이번에는 인터럽트를 사용하기 위해서 어떤 레지스터를 다뤄야할지 정리해보려고 한다. 
NXP사의 S32K144 보드를 사용하며 Keil로 코딩한다.

흐름

알아볼 예제로는 스위치 입력이 들어왔을 때 인터럽트를 사용하여 7-segment를 제어할 것이다. 
PORT_init 함수에서 segment 출력을 설정할 것이고, 스위치를 입력으로 설정하면서 어떤 트리거로 인터럽트를 실행할 것인지 설정한다.
NVIC init 함수에서 특정 인터럽트를 활성화하고 pending을 없애고 우선순위를 정한다.
IRQHandler 함수에는 인터럽트 발생 시 실행할 내용을 작성할 것이다.

PCR 레지스터

우선 PCR의 ISF, IRQC 비트를 설정해야한다.

ISF

ISF는 interrupt status flag 비트로 인터럽트가 감지되면 1로 세팅된다.
인터럽트가 끝나도 1로 남아있기에 사용자가 직접 clear 해야한다.

IRQC

인터럽트 어떻게 트리거될지 선택하는 비트다. 

1000 1001 1010 1011 1100
logic 0 rising-edge falling-edge either edge logic 1

이번 실습에서는 falling-edge 일 때 인터럽트를 감지하도록 1010으로 세팅할 것이다.

NVIC 레지스터 설정

인터럽트 우선순위와 enable 설정 
사용하고자 하는 인터럽트를 설정할 수 있다.
인터럽트로 사용할 포트와 포트별 IRQ 번호를 확인해야하는데 해당 번호는 S32K144.h 헤더파일에서 찾을 수 있다. 

예제에서는 ICPR, ISER, IP 레지스터만 관리한다.

ISER : 특정 인터럽트 활성화 32개 인터럽트 중 특정 비트를 set

ICER : 비활성화 32개 인터럽트 중 특정 비트 비활성화

ISPR : 인터럽트 pending 상태 강제 전환

ICPR : 인터럽트 대기 해제(pending 해제)

IABR : 인터럽트 활성 여부(동작 중인지)

IPRn : 인터럽트 우선 순위 
8비트이지만 상위 4비트만 사용해 16level로 구분

전체코드

더보기
더보기
더보기
#include "device_registers.h"
#include "clocks_and_modes.h"

int lpit0_ch0_flag_counter = 0; /*< LPIT0 timeout counter */
unsigned int FND_DATA[10]={0x7E, 0x0C,0xB6,0x9E,0xCC,0xDA,0xFA,0x4E,0xFE,0xCE};
unsigned int FND_SEL[4]={0x0100,0x0200,0x0400,0x0800};
unsigned int j=0; /*FND select pin index */
unsigned int num=0,num0=0,num1=0,num2=0,num3=0;
/*num is Counting value, num0 is '1', num2 is '10', num2 is '100', num3 is '1000'*/
unsigned int External_PIN=0; /* External_PIN:SW External input Assignment */
unsigned int Dtime = 0; /* Delay Time Setting Variable*/

void PORT_init (void)
{
		//PORTC,D Data Clock Set
	  	PCC->PCCn[PCC_PORTC_INDEX]|=PCC_PCCn_CGC_MASK;   /* Enable clock for PORTC */
		PCC->PCCn[PCC_PORTD_INDEX]|=PCC_PCCn_CGC_MASK;   /* Enable clock for PORTD */

	  //PortC,D Data Direction Set
		PTC->PDDR &= ~(1<<11);		/* Port C11 Port Input set, value '0'*/
		PTD->PDDR |= 1<<1|1<<2|1<<3|1<<4|1<<5|1<<6|1<<7 		/* Port D1~7:  FND Data Direction = output */
				     |1<<8|1<<9|1<<10|1<<11; /*FND Select Direction */

		// PORTC_11 pin GPIO and Falling-edge Set
		PORTC->PCR[11] |= PORT_PCR_MUX(1); // Port C11 mux = GPIO
		PORTC->PCR[11] |=(10<<16); // Port C11 IRQC : interrupt on Falling-edge

		//PORTD FND Control Pin PCR Set, PTD1~PTD7:FND_DATA Control , PTD8~PTD11:FND_Select Control
	  PORTD->PCR[1]  = PORT_PCR_MUX(1);	/* Port D1: MUX = GPIO  */
	  PORTD->PCR[2]  = PORT_PCR_MUX(1);	/* Port D2: MUX = GPIO  */
	  PORTD->PCR[3]  = PORT_PCR_MUX(1);	/* Port D3: MUX = GPIO  */
	  PORTD->PCR[4]  = PORT_PCR_MUX(1);	/* Port D4: MUX = GPIO  */
	  PORTD->PCR[5]  = PORT_PCR_MUX(1);	/* Port D5: MUX = GPIO  */
	  PORTD->PCR[6]  = PORT_PCR_MUX(1);	/* Port D6: MUX = GPIO  */
	  PORTD->PCR[7] = PORT_PCR_MUX(1); 	/* Port D7: MUX = GPIO */

	  PORTD->PCR[8] = PORT_PCR_MUX(1); 	/* Port D8: MUX = GPIO */
	  PORTD->PCR[9] = PORT_PCR_MUX(1); 	/* Port D9: MUX = GPIO */
	  PORTD->PCR[10] = PORT_PCR_MUX(1); 	/* Port D10: MUX = GPIO */
	  PORTD->PCR[11] = PORT_PCR_MUX(1); 	/* Port D11: MUX = GPIO */
}
void WDOG_disable (void)
{
  WDOG->CNT=0xD928C520;     /* Unlock watchdog 		*/
  WDOG->TOVAL=0x0000FFFF;   /* Maximum timeout value 	*/
  WDOG->CS = 0x00002100;    /* Disable watchdog 		*/
}

void LPIT0_init (uint32_t delay)
{
   uint32_t timeout;
   /*!
    * LPIT Clocking:
    * ==============================
    */
  PCC->PCCn[PCC_LPIT_INDEX] = PCC_PCCn_PCS(6);    /* Clock Src = 6 (SPLL2_DIV2_CLK)*/
  PCC->PCCn[PCC_LPIT_INDEX] |= PCC_PCCn_CGC_MASK; /* Enable clk to LPIT0 regs       */

  /*!
   * LPIT Initialization:
   */
  LPIT0->MCR |= LPIT_MCR_M_CEN_MASK;  /* DBG_EN-0: Timer chans stop in Debug mode */
                                        /* DOZE_EN=0: Timer chans are stopped in DOZE mode */
                                        /* SW_RST=0: SW reset does not reset timer chans, regs */
                                        /* M_CEN=1: enable module clk (allows writing other LPIT0 regs) */

  timeout=delay* 40000;
  LPIT0->TMR[0].TVAL = timeout;      /* Chan 0 Timeout period: 40M clocks */
  LPIT0->TMR[0].TCTRL |= LPIT_TMR_TCTRL_T_EN_MASK;
                              /* T_EN=1: Timer channel is enabled */
                              /* CHAIN=0: channel chaining is disabled */
                              /* MODE=0: 32 periodic counter mode */
                              /* TSOT=0: Timer decrements immediately based on restart */
                              /* TSOI=0: Timer does not stop after timeout */
                              /* TROT=0 Timer will not reload on trigger */
                              /* TRG_SRC=0: External trigger soruce */
                              /* TRG_SEL=0: Timer chan 0 trigger source is selected*/
}

void delay_ms (volatile int ms){
   LPIT0_init(ms);           /* Initialize PIT0 for 1 second timeout  */
   while (0 == (LPIT0->MSR & LPIT_MSR_TIF0_MASK)) {} /* Wait for LPIT0 CH0 Flag */
               lpit0_ch0_flag_counter++;         /* Increment LPIT0 timeout counter */
               LPIT0->MSR |= LPIT_MSR_TIF0_MASK; /* Clear LPIT0 timer flag 0 */
}
void NVIC_init_IRQs(void){
	S32_NVIC->ICPR[1] |= 1<<(61%32); // Clear any pending IRQ61
	S32_NVIC->ISER[1] |= 1<<(61%32); // Enable IRQ61
	S32_NVIC->IP[61] =0xB; //Priority 11 of 15
}

void PORTC_IRQHandler(void){

	PORTC->PCR[11] &= ~(0x01000000); // Port Control Register ISF bit '0' set

	//PORTC_Interrupt State Flag Register Read
	if((PORTC->ISFR & (1<<11)) != 0){
		External_PIN=1;
	}

	// External input Check Behavior Assignment
	switch (External_PIN){
		case 1:
			num += 1;
			External_PIN=0;
			break;
		default:
			break;
	}

	PORTC->PCR[11] |= 0x01000000; // Port Control Register ISF bit '1' set
}

int main(void)
{

	WDOG_disable();/* Disable Watchdog in case it is not done in startup code */
	PORT_init();            /* Configure ports */
	SOSC_init_8MHz();        /* Initialize system oscilator for 8 MHz xtal */
	SPLL_init_160MHz();     /* Initialize SPLL to 160 MHz with 8 MHz SOSC */
	NormalRUNmode_80MHz();  /* Init clocks: 80 MHz sysclk & core, 40 MHz bus, 20 MHz flash */
	NVIC_init_IRQs(); /*Interrupt Pending, Endable, Priority Set*/

	Dtime = 500; // Delay Reset Value

	num=0000; // FND Reset Value

	while(1){ /* Loop Start*/

		num3=(num/1000)%10;
		num2=(num/100)%10;
		num1=(num/10)%10;
		num0= num%10;

		PTD->PSOR = FND_SEL[j];
		PTD->PCOR =0xff;
		PTD->PSOR = FND_DATA[num3];
	    delay_ms(Dtime/300);
		PTD->PCOR = 0xfff;
		j++;

		PTD->PSOR = FND_SEL[j];
		PTD->PCOR =0xff;
		PTD->PSOR = FND_DATA[num2];
	    delay_ms(Dtime/300);
   	    PTD->PCOR = 0xfff;
		j++;

		PTD->PSOR = FND_SEL[j];
		PTD->PCOR =0xff;
		PTD->PSOR = FND_DATA[num1];
	    delay_ms(Dtime/300);
	    PTD->PCOR = 0xfff;
		j++;

		PTD->PSOR = FND_SEL[j];
		PTD->PCOR =0xff;
		PTD->PSOR = FND_DATA[num0];
	    delay_ms(Dtime/300);
  	    PTD->PCOR = 0xfff;
		j=0;
		}
}

코드해석

1. PORT_init()

void PORT_init (void)
{
		//PORTC,D Data Clock Set
	  	PCC->PCCn[PCC_PORTC_INDEX]|=PCC_PCCn_CGC_MASK;   /* Enable clock for PORTC */
		PCC->PCCn[PCC_PORTD_INDEX]|=PCC_PCCn_CGC_MASK;   /* Enable clock for PORTD */

	  //PortC,D Data Direction Set
		PTC->PDDR &= ~(1<<11);		/* Port C11 Port Input set, value '0'*/
		PTD->PDDR |= 1<<1|1<<2|1<<3|1<<4|1<<5|1<<6|1<<7 		/* Port D1~7:  FND Data Direction = output */
				     |1<<8|1<<9|1<<10|1<<11; /*FND Select Direction */

		// PORTC_11 pin GPIO and Falling-edge Set
		PORTC->PCR[11] |= PORT_PCR_MUX(1); // Port C11 mux = GPIO
		PORTC->PCR[11] |=(10<<16); // Port C11 IRQC : interrupt on Falling-edge

		//PORTD FND Control Pin PCR Set, PTD1~PTD7:FND_DATA Control , PTD8~PTD11:FND_Select Control
	  PORTD->PCR[1]  = PORT_PCR_MUX(1);	/* Port D1: MUX = GPIO  */
	  PORTD->PCR[2]  = PORT_PCR_MUX(1);	/* Port D2: MUX = GPIO  */
	  PORTD->PCR[3]  = PORT_PCR_MUX(1);	/* Port D3: MUX = GPIO  */
	  PORTD->PCR[4]  = PORT_PCR_MUX(1);	/* Port D4: MUX = GPIO  */
	  PORTD->PCR[5]  = PORT_PCR_MUX(1);	/* Port D5: MUX = GPIO  */
	  PORTD->PCR[6]  = PORT_PCR_MUX(1);	/* Port D6: MUX = GPIO  */
	  PORTD->PCR[7] = PORT_PCR_MUX(1); 	/* Port D7: MUX = GPIO */

	  PORTD->PCR[8] = PORT_PCR_MUX(1); 	/* Port D8: MUX = GPIO */
	  PORTD->PCR[9] = PORT_PCR_MUX(1); 	/* Port D9: MUX = GPIO */
	  PORTD->PCR[10] = PORT_PCR_MUX(1); 	/* Port D10: MUX = GPIO */
	  PORTD->PCR[11] = PORT_PCR_MUX(1); 	/* Port D11: MUX = GPIO */
}
  • 목적: I/O 포트 clock, 입출력 설정
    • 포트 C의 11번 핀을 입력으로 설정한다.
    • 10이 이진수로 1010이므로 PCR 설정 (위쪽 PCR 레지스터에서 falling edge 트리거가 1010)
    • 포트 D의 여러 핀을 7세그먼트 디스플레이 제어용으로 출력으로 설정한다.

2. WDOG_disable()

  • 목적: 워치독 타이머 비활성화
    워치독 타이머는 시스템이 정상적으로 동작하지 않으면 자동으로 리셋을 시도합니다. 이를 비활성화하여 리셋을 방지한다.

3. LPIT0_init()

void LPIT0_init (uint32_t delay)
{
  uint32_t timeout;
  PCC->PCCn[PCC_LPIT_INDEX] = PCC_PCCn_PCS(6);    /* Clock Src = 6 (SPLL2_DIV2_CLK)*/
  PCC->PCCn[PCC_LPIT_INDEX] |= PCC_PCCn_CGC_MASK; /* Enable clk to LPIT0 regs       */

  LPIT0->MCR |= LPIT_MCR_M_CEN_MASK;  /* DBG_EN-0: Timer chans stop in Debug mode */
                                        /* DOZE_EN=0: Timer chans are stopped in DOZE mode */
                                        /* SW_RST=0: SW reset does not reset timer chans, regs */
                                        /* M_CEN=1: enable module clk (allows writing other LPIT0 regs) */
  timeout=delay* 40000;
  LPIT0->TMR[0].TVAL = timeout;      /* Chan 0 Timeout period: 40M clocks */
  LPIT0->TMR[0].TCTRL |= LPIT_TMR_TCTRL_T_EN_MASK;
                              /* T_EN=1: Timer channel is enabled */
                              /* CHAIN=0: channel chaining is disabled */
                              /* MODE=0: 32 periodic counter mode */
                              /* TSOT=0: Timer decrements immediately based on restart */
                              /* TSOI=0: Timer does not stop after timeout */
                              /* TROT=0 Timer will not reload on trigger */
                              /* TRG_SRC=0: External trigger soruce */
                              /* TRG_SEL=0: Timer chan 0 trigger source is selected*/
}
  • 목적: LPIT(Low Power Interrupt Timer) 0을 설정하여 시간 지연을 생성한다. 이 타이머는 주로 저전력 모드에서 타이머 인터럽트를 처리하는 데 사용된다.
    • 타이머의 타임아웃 값을 설정하고, 40MHz 시스템 클럭을 기준으로 타이머를 활성화한다.

4. delay_ms()

void delay_ms (volatile int ms){
   LPIT0_init(ms);           /* Initialize PIT0 for 1 second timeout  */
   while (0 == (LPIT0->MSR & LPIT_MSR_TIF0_MASK)) {} /* Wait for LPIT0 CH0 Flag */
               lpit0_ch0_flag_counter++;         /* Increment LPIT0 timeout counter */
               LPIT0->MSR |= LPIT_MSR_TIF0_MASK; /* Clear LPIT0 timer flag 0 */
}
  • 목적: LPIT0 타이머를 사용하여 밀리초 단위로 지연을 생성한다. delay_ms() 함수가 호출되면 지정된 시간 동안 대기한다.

 

5. NVIC_init_IRQs()

void NVIC_init_IRQs(void){
	S32_NVIC->ICPR[1] |= 1<<(61%32); // Clear any pending IRQ61
	S32_NVIC->ISER[1] |= 1<<(61%32); // Enable IRQ61
	S32_NVIC->IP[61] =0xB; //Priority 11 of 15
}
  • 목적: 인터럽트 벡터를 설정하여 외부 스위치 입력에 대한 인터럽트를 처리할 준비를 한다.
    • ICPR 로 IRQ61 인터럽트를 clear 하고
    • ISER 비트로 해당 인터럽트를 활성화한 후
    • IP 비트로 우선순위를 설정한다.

61은 IRQ 번호로 포트A~E 마다 다르며 S32K144.h 파일에서 확인 가능하다.

61%32 만큼 shift 하는 이유는 ISER0, ISER1 두 개가 있고, ICPR0, ICPR1이 있기에
61번은 ISER1 레지스터의 32비트 중 29번째를 의미하기 때문이다. 

6. PORTC_IRQHandler()

void PORTC_IRQHandler(void){

	PORTC->PCR[11] &= ~(0x01000000); // Port Control Register ISF bit '0' set

	//PORTC_Interrupt State Flag Register Read
	if((PORTC->ISFR & (1<<11)) != 0){
		External_PIN=1;
	}

	// External input Check Behavior Assignment
	switch (External_PIN){
		case 1:
			num += 1;
			External_PIN=0;
			break;
		default:
			break;
	}

	PORTC->PCR[11] |= 0x01000000; // Port Control Register ISF bit '1' set
}
  • 목적: 인터럽트 발생시 실행되는 함수.
    • 포트 C의 11번 핀에서 하강 에지(falling edge)가 발생하면 num 값을 1 증가시킨다.

7. main()

  • 목적: 메인 루프에서 7세그먼트 디스플레이를 갱신한다.
    • num 값을 4개의 개별 숫자(num3, num2, num1, num0)로 분리하여 각각 7세그먼트 디스플레이에 표시한다.
    • 각 숫자는 FND_SEL 배열을 사용하여 순차적으로 선택하고, FND_DATA 배열을 통해 7세그먼트 디스플레이에 표시된다.
    • 각 디지털 표시 후, delay_ms()로 잠시 지연을 주어 디스플레이가 변경된다.
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
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
글 보관함