티스토리 뷰
인터럽트를 사용하지 않으면 반복문을 통해 항상 체크를 해야할 것이다. 이를 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()로 잠시 지연을 주어 디스플레이가 변경된다.
'전공 > 컴퓨터 코딩 데이터' 카테고리의 다른 글
AVL 트리 정리 (0) | 2024.12.12 |
---|---|
C언어 정렬 shell, quick, heap sort 코드 구현, 정리 (0) | 2024.11.30 |
S32K144 MCU C언어 코딩하기(PC와 UART 통신) (0) | 2024.11.26 |
C언어 dijkstra 그래프 최단 경로 찾기 알고리즘 (0) | 2024.11.25 |
[컴퓨터구조] RISC-V 파이프라인 data/control hazards (0) | 2024.11.24 |
- Total
- Today
- Yesterday
- 방어동작
- 시계 줄
- mealy
- 알리익스프레스
- 알뜰 요금제
- 네이버페이
- 알뜰폰요금제
- 배송기간
- 오블완
- Liiv M
- 방향장
- 할인
- 10만포인트
- f-94w
- 교체
- 타란튤라
- f-91w
- 파스타
- 카카오페이
- 리브엠
- 메쉬 밴드
- 리브모바일
- 경북대
- 티스토리챌린지
- 맛집
- 카시오
- 문서 스캔
- 계산방법
- a모바일
- 북문
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |