카테고리 없음

임베디드 기말 정리

_JIONE_ 2022. 12. 19. 00:48

08

 

클럭신호(clock signal)란 높은 상태와 낮은 상태 사이에서 진동하는 특정 유형의 신호이다.

 

SysTick은 모든 Arm에서 타이머/카운터 작업을 수행해 지속적인 interrucpt를 생성한다. 

#include "msp.h"
#define SYSTEM CLOCK 3000000 //3 초
/**
* main.c
*/
uint32_t i;
void SysTick_Init(void){ // 초기값 세팅
	SysTick->LOAD = 0x00FFFFFF; // 로드될 때 초기값
	SysTick->CTRL = 0x00000005; // 클럭이 동작하도록 세팅
}

void SysTick_Wait(uint32_t n){ // 1500150 대입 => 0.5 초를 기다림.
	SysTick->LOAD = n-1;
	SysTick->VAL = 0;
	while((SysTick->CTRL & 0x00010000)==0){} // 16 번째 비트가 1 일 때까지 반복
}

void SysTick_Wait50ms(uint32_t delay){ // 6 대입
	for(i=0; i<delay; i++){ // 0.5*6 = 3 초
		SysTick_Wait(1500150); // 333.3 에 뭘 곱해야 0.5 초가 되는지 대입해서 0.5 초 리턴받음
	}
}

void main(void)
{
	SysTick_Init();
	WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD; // stop watchdog timer
	P1->DIR |=BIT0;
	int j;
	while(1){
		P1->OUT = BIT0;
		SysTick_Wait50ms(6); // 0.5*6 = 3 초
		P1->OUT = 0x00;
		SysTick_Wait50ms(6);
		P1->OUT = BIT0;
		SysTick_Wait50ms(5); // 0.5*5 = 2.5 초
		P1->OUT = 0x00;
		SysTick_Wait50ms(5);
		P1->OUT = BIT0;
        SysTick_Wait50ms(4); // 0.5*4 = 2 초
		P1->OUT = 0x00;
		SysTick_Wait50ms(4);
		P1->OUT = BIT0;
		SysTick_Wait50ms(3); // 0.5*3 = 1.5 초
		P1->OUT = 0x00;
		SysTick_Wait50ms(3);
		P1->OUT = BIT0;
		SysTick_Wait50ms(2); // 0.5*2= 1 초
		P1->OUT = 0x00;
		SysTick_Wait50ms(2);
		for( j=0; j<9; j++){
			P1->OUT = BIT0;
			SysTick_Wait50ms(1); // 0.5*1 = 0.5 초
 			P1->OUT = 0x00;
			SysTick_Wait50ms(1);
		}
	}
}
//48Mhz마다 20.83333ns가 감소한다.

48Mhz -> 48 X 1000 X 1000
= 1/(48 X 1000 X 1000)
= 0.02083333 X 10^(-6)
= 20.83333 X 10^(-9)

Event handler란 어떤 이벤트가 발생했을 때, 이벤트를 처리하기 위해 코드를 수행하는 것이다.

Software architecture는 이벤트 감지 방법 및 호출 방법에 따라 달라진다.

 

Architecture 1 : Round - Robin 

== poling 방식

순서대로(우선순위 X), 모든 actions는 동일하다.

특정 이벤트 발생여부를 감지하고, 발생시 event handler를 실행시킨다.

 

최악의 경우, 1) 전체 반복문 시간만큼 다 기다려야 한다.

2) 하나의 event가 유난히 느리다면 응답 시간이 점점 길어진다.

3) 하나의 새로운 event handler 추가시, 다른 event의 마감 시간을 놓칠 수 있다. (실시간성 보장X)

 

단, 단일 task일 때(=즉 공유하는 데이터가 없을 때 = no ISRs) Simplicity를 장점으로 가진다. 

 

디지털 멀티미터처럼, 입력 개수와 이벤트 수가 적고 응답시간이 큰 제약 사항이 아닐때 사용한다. (전자렌지, 오븐)

 

응답 시간을 줄이려면?

중요한 task는 매번 다시 검사하도록 한다.


Interrupt

processor는 하고 있던 일을 멈추고 발생한 interrupt(=event 중 하나)를 immediately하게 처리한 후 이어서 진행한다.

ISR : interrupt service routine

 

장점: 이벤트 처리 시간, delay의 일정함(멈추고 바로 하니까)

 

multitasking system에 우선순위대로 실행되어야 할 때는 interrupt가 필수이다. (priority-based multitasking system)

중요한 코드를 먼저 실행할 수 있기 때문이다.

poll loop보다 소프트웨어 복잡도가 훨씬 적어진다.

 

interrupt 발생 요인은?

  1. I/O 장치에서 데이터 도착
  2. I/O 장치에 이전에 보낸 요청이 끝났을 때
  3. sensor의 값이 바뀌었을 때
  4. 사용자의 action (press BTN)
  5. 에러 및 결함 감지 (cpu 외부 - 하드웨어 전용, 내부 - 예외)
  6. 타이머 완료(60초마다 뭔가를 하고 싶을 때)
  7. 정전

Interrupt : Hardware + Software

HW side

simple mode에서는

interrupt 발생시,  HW는 current task에 대한 정보를 주소로 저장한다.

HW는 PC가 ISR의 시작 코드를 가리키도록 한다.

 

ISR은 interrupt service routine의 약자로, 각 핀 별로 이벤트 발생시 실행되어야 할 코드를 호출한다.

SW는 ISR에 대한 정보를 HW에게 제공한다.

 

Cpu가 interrupt 발생요인을 찾는 방법?

연결된 모든 interrupt에 있는 ISR을 확인한다. 

Cpu에는 데이터 핀 뿐만 아니라 interrupt pin(line)도 여러개 달려있는데,

해당 interrupt pin에 맞는 ISR을 인덱스로 ISR Table에서 찾아 이벤트를 발생시켜 해결한다.

 

cf. ISR은 함수 형태로 구현된다. 

interrupted SW는 register를 저장할 수 없기에 이와 관련된 정보는 ISR이 모두 저장한다.

 

nested interrupt의 처리?

덜 중요한 interrupt가 실행 중인데, 아주 중요한 interrupt가 들어오면

hold하고 더 중요한 innterupt를 처리한다.

 

즉시 중요하지는 않다면 약간의 지연을 두어 해결한다.

 

interrupt 발생시, 먼저 해결한 후 기존 task 다시 진행
nested interrupt의 해결, 우선순위 높은 것(#3)부터 해결

 정말 중요한 task가 있다면 interrupt가 끼어들지 못하도록 코드를 짜야한다.


I/O with Interrupt

 

BUSY-WAIT LOOP: I/O 장치가 준비되었는지 반복문으로 계속 확인하니 비효율적이다.

Interrupt request signal: I/O 장치가 processor에게 준비되었다고 알리는 방식을 사용하는게 좋다.

 

광범위한 계산을 하고 주기적으로 결과를 화면에 출력해야 할 때

SW는 COMPUTE와 DISPLAY에 대한 routine을 가지고 있어야 한다.

processor는 DISPLAY(interrupt 해결 코드)를 실행하기 위해 COMPUTE #4번(현재 task)까지의 실행을 완료하고 다음 DISPLAY를 수행한다. DISPLAY 실행 전  COMPUTE #4번의 시작 위치를 temporary location에 저장한다. interrupt를 처리하면 COMPUTE #5의 주소로 돌아가 다시 실행하기 시작한다.

(DISPLAY는 ISR이다. COMPUTE 보다 시간이 적게 걸린다.)

 

interrupt-acknowledge signal이 processor로부터 device로 간다.

Interrupt가 발생했음을 알려준 후, divece가 이를

인식하면 device는 해당 interrupt request를 삭제한다.

 

공유 데이터의 문제점은?

ISR에 의해 shared data가 update되면 불일치성에 유의해야 한다. ISR은 작업 코드에 대한 처리의 일부를 off하기 때문이다.

 

불일치성의 예시

하드웨어 타이머가 매 1초마다 마이크로프로세서에 신호를 보낸다. 

마이크로프로세서는 interrupt를 발생시켜 HW로부터 값을 읽어온다.

 

온도를 서서히 올리다가 0.0000001초 차이로 같아야하는데

하나는 80초, 하나는 81로 잘못읽어서 기계가 멈춘다.

 

두 값을 직접 불러와도 불가능

이를 해결하기 위해서는 interrupt 키워드를 명시하면 된다.

두 값을 읽는 순간에는 timer interrupt 장치를 꺼버리는 것이다.

 

꺼버림


Architecture 2: Round - Robin with interrupt

polling loop에서 interrupt를 감지하기 위해 ISR이 flag를 사용하여 표시한다.

시간이 중요한 응답의 경우 ISR이 처리하고, 오래걸리는 코드는 handler가 처리한다.

 

최악의 경우

ISR이라면 중간 우선순위 처리 중에 더 높은 우선순위가 들어오면

두 개 다 걸린 시간을 합쳐서, 즉 나보다 높은 우선순위에 대한 실행시간을 모두 합쳐서 계산한다.

 

그럼에도 ISR을 사용하는 이유는 우선순위대로 처리한다는 장점때문이다.

다만 ISR과 handler는 데이터를 공유하기 때문에 shared data로 인한 문제는 불가피하다.


Architecture 3: Function - queue scheduling

큐에 새로운 job이 들어왔는데

이전의 것보다 먼저 실행되어야 하는 대상이라면

기존의 것을 뒤로 밀어내고

방금 넣은 것을 맨 앞에 위치할수 있도록 한다.

 

Funtion pointer(*task)

함수의 주소를 가리키는 변수로 큐의 우선순위가 높은 것을 앞으로 땡기는 역할을 한다. 

 

장점: 코드 변경에도 향상되고 안정된 응답시간을 보인다.

단점: 복잡성이 올라가며 function queue를 유지해야 한다.


Architecture 4: RTOS

 

scheduler에 의해 task를 우선순위대로 처리한다.

우선순위가 높은 것이 준비되면 Cpu를 선점한다.

 

task는 이벤트가 발생하기 전까지 대기하다

ISR이 task로 하여금 block state로 가게한다.

task가 스스로 delay 시간을 가질 수도 있다.

 

interrupt에도 우선순위가 존재한다.

높은 우선순위에 대한 응답시간이 굉장히 빠르며

코드를 변경해도 낮은 우선순위 일이 높은 우선순위의 응답시간에 영향을 미치지 않는다.

 

단, 소프트웨어가 점점 복잡해지고 runtime overhead가 발생한다.

RTOS는 우선 순위가 높은 작업을 실행하기 위해 언제든지 우선 순위가 낮은 작업을 중단할 수 있으며,

결과적으로 우선 순위가 높은 작업에 대한 최악의 응답시간은 거의 0이다.

 

가장 간단한 RTOS에서, task는 세 가지 상태에 존재할 수 있다.

1) Running: 하나의 task가 cpu에 할당되어 돌아가고 있다.

2) Ready: cpu에 할당받기를 기다리는 상태로, 필요한 모든 데이터를 사용할 준비가 되어있다.

3) Blocked: 이벤트나 데이터가 발생하는 것을 기다리는 상태이다. task가 cpu에서 끝나면 이 상태로 이동된다.