__________

Designing the Future with Circuits

반도체 회로설계 취준기

하만(Harman) 세미콘 반도체 설계 과정/임베디드 시스템을 위한 SW 구조설계

하만(Harman) 세미콘 아카데미 15일차 - SW 구조설계(Pullup 저항, ATmega128 인터럽트, 스탑워치 설계)

semicon_circuitdesigner 2024. 3. 27. 11:03

[2024.03.27.수] 인천인력개발원 하만 세미콘 아카데미


임베디드 시스템을 위한 SW 구조설계


복습


1. 풀업 저항

  • 플로팅 상태를 없애기 위해 설치하는 저항
  • 다음과 같은 구조로 설치
스위치와 Vdd사이에 풀업저항 설치

 
2. 프로그램의 실행순서: Start - Function(함수 실행) - End [선형 구조]
 


인터럽트(Interrupt)


  • 함수 실행 시 정해진 시점에 진행이 아닌, 임의의 시점에 진행하는 것

1. 폴링 vs 인터럽트

  • 폴링
    • 코드 나열 순서에 의해 실행 결정
    • 모든 코드는 동일한 실행 우선순위
    • 코드 A에 의해 코드 B 실행에 지연 발생 가능
    • 정해진 순서에 따라 실행되므로 하드웨어의 지원이 불필요
    • 코드 작성 & 이해 용이
  • 인터럽트
    • 우선 순위에 따라 실행 순서 결정
    • 인터럽트에 따라 서로 다른 우선순위
    • 우선 순위가 낮은 코드 A에 의해 우선순위가 높은 코드 B의 실행 지연 발생 없음
    • 하드웨어에 의해 우선순위에 따른 처리
    • 코드 작성 & 이해 난이도와 복잡성이 높음

2. 인터럽트

  • 마이크로컨트롤러에 특정 작업의 즉시 처리를 요구하는 비정상적 사건
  • 인터럽트 발생 -> 현재 진행 작업 중단 후 인터럽트 처리 루틴(Interrupt Service Routine, ISR)로 이동
  • ISR 종료 후 실행중이던 곳으로 되돌아가 작업 재개

3. ATmega128 인터럽트

  • 35개의 인터럽트 사용 가능
  • 인터럽트 벡터: 인터럽트 발생 시 처리가 이동할 ISR의 메모리 주소
  • ATmega128의 인터럽트 벡터 테이블: 35개의 인터럽트에 대한 인터럽트 벡터 테이블(플래시 메모리의 0x0000~0x0045에 기록)
    ATmega128의 인터럽트 벡터 테이블
  • 인터럽트 처리 순서
    1. 인터럽트 발생
    2. 인터럽트 벡터 테이블의 해당하는 인터럽트 벡터가 저장된 테이블 위치로 이동
    3. 인터럽트 벡터 값에 해당하는 ISR로 이동
    4. ISR 처리
    5. 인터럽트 처리 이전에 수행하던 코드로 복귀

 
4. 인터럽트 처리 조건

  • 전역적 인터럽트
    • 상태 레지스터 SREG의 7번 'I'비트 세트
    • set하려면 sei(), clear하려면 cli()함수 사용 - setinterrupt, clearinterrupt
  • 개별 인터럽트 활성화 비트 세트
    • 인터럽트는 비활성화 상태가 기본값
    • 개별 인터럽트별로 활성화 비트 존재

6. ISR(Interrupt Service Routine)

  • 인터럽트 발생 시 처리 루틴
  • 하드웨어에 의해 호출(코드 상에는 ISR호출 파트 없음)
    #include <avr/interrupt.h>
    
    ISR (ADC_vect)
    {
    	//인터럽트 처리
    }​
  • 반환값과 매개변수의 데이터 형 없는 함수
  • 모든 ISR은 동일한 이름, 처리할 인터럽트 종류는 인터럽트 벡터 이름인 매개변수로 구분

7. 인터럽트 주의사항

  • 중첩 인터럽트
    • 인터럽트를 인터럽트 불가
    • ISR 실행 중 발생 인터럽트 처리 불가
    • ISR은 가능한 짧게 작성
  • 인터럽트 우선순위
    • 번호가 낮은 인터럽트의 우선순위가 우선
    • 우선순위가 높은 인터럽트 먼저 처리
    • Reset 인터럽트의 우선순위가 최상위
  • 최적화 방지의 필요성
    • ISR은 메인 코드와 무관한 코드로 간주될 수 있음(코드 내에서 호출되는 부분이 없으므로)
    • ISR에서 값을 변경하는 변수는 'volatile' 키워드를 사용 (volatile키워드: 변수 최적화 방지)
      volatile int value= 0;
      ISR(INTERRUPT_vect)
      {
      	value = (value + 1) %2;
      }​

8. 외부 인터럽트

  • Reset 제외 우선순위 최상의 인터럽트
  • 범용 입출력 핀의 값, 상태 변화 시 인터럽트 발생(정해진 8개 핀으로만 가능)
  • 외부 인터럽트 포트
PD0~PD3은 A그룹, PD4~PD7은 B그룹
  • 사용 예시
ISR(INT0_vect)
{
	state= (state+ 1) % 2;	//LED 상태 전환
}

void INIT_INT0(void)
{
	EIMSK I= (1 << INT0);	//INT0 인터럽트 활성화
	EICRA I= (1 << ISC01);	//하강 에지에서 인터럽트 발생, 
	sei ();			//전역적으로 인터럽트 허용
}
작동 회로

 
9. EIMSK 레지스터

  • EIMSK: External Interrupt MaSK
  • 외부 인터럽트 활성화 레지스터
  • 8개 외부 인터럽트의 개별 활성화 가능

 
10. EICRA 레지스터

  • 인터럽트 발생 시점 결정(INT0~INT3)
  • EICRB레지스터로 INT4~INT7 인터럽트 발생 시점 결정

실습 1: 인터럽트 응용 스탑워치 설계


1. 회로 구성

스위치에 커패시터와 저항을 우측 그림과 같이 연결
브레드보드 회로 구성

 
 
2. 프로젝트 생성 - test03-intr 프로젝트 생성 - test03-intr 우클릭 후 set startup project

 
 
3. 7seg 실습에서 사용한 상단 코드 가져오기

#define F_CPU 16000000L					//Board CLK 정보(16MHz)
#include <avr/io.h>
#include <avr/delay.h>

#include <avr/interrupt.h>

uint8_t digit[] = {	0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x67, 0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71 };
uint8_t character[] = { // -,
0x40 };
char arr[5];		//세그먼트 이미지 정보를 담을 안전공간

 
4. 인터럽트 설정(메인함수 내에서)

EIMSK = 0x70;	//0111 0000	//INT 4~INT 6 활성화
EICRB = 0x2a;	//4개의 B그룹(INT4~INT7)의 인터럽트 발생 시점 결정(00 10 10 10, 각 7 6 5 4에서의 INT발생 시점을 rising edge로 결정) 
SREG |= 0x80;	//status Register - 인터럽트 허용 상태 레지스터
sei()
  • 스위치 동작 시 HIGH에서 LOW로 변하는 하강 에지에 작동하도록 설정: EICRB = 0x2a;
  • sei(): 인터럽트 시작

 
5. 메인함수 밖에 4, 5, 6번 인터럽트 ISR 설정

ISR(INT4_vect){	//INT4 인터럽트 처리 루틴: sw1
	opt++;
	if (opt >= 3) opt = 0;
}

ISR(INT5_vect){	//INT5 인터럽트 처리 루틴: sw2
	state++;
	if (state >= 3) state = 0;
}

ISR(INT6_vect){	//INT6 인터럽트 처리 루틴: sw3

}

 
조건을 나타내는 상수는 #define을 통해 상수로 선언하는 것 추천

//상단에 상수 선언 후
#define OPTMAX 3
#define STATEEMAX 3

//ISR 코드 변경
ISR(INT4_vect){	//INT4 인터럽트 처리 루틴: sw1
	opt++;
	if (opt >= OPTMAX) opt = 0;
}

ISR(INT5_vect){	//INT5 인터럽트 처리 루틴: sw2
	state++;
	if (state >= STATEMAX) state = 0;
}

ISR(INT6_vect){	//INT6 인터럽트 처리 루틴: sw3
	
}

 
6. 메인함수 위에 volatile로 변수 선언 - 최적화 금지

volatile int opt = 0; state = 0;

 
7. 7seg 실습에서 사용한 코드를 가져와 붙여넣기

void seg(int sel, uint8_t c){
	PORTC |= 0X0F;
	PORTC &= ~(1 << (3-sel));
	PORTD = c;	//숫자 데이터 출력
	_delay_ms(2);
}


void FND_4(char *inf){	//segment Image 배열
	for (int i = 0; i < 4; i++){
		seg(i, *(inf+i));
	}
}

//16진수 segment image 배열
char* Display(unsigned long num){	//10진 정수를 입력받아 16진수 문자열로 변환 ex)65535 ==> 0xffff, 56506=>0xBCDA
	int n1 = num % 10;			//A(10): 문자가 아닌 숫자
	int n2 = (num / 10) % 10;	//B(11)
	int n3 = (num / 100) % 10;	//C(12)
	int n4 = num / 1000;		//D(13)
	
	arr[0] = digit[n1]; arr[1] = digit[n2]; arr[2] = digit[n3] + 0x80; arr[3] = digit[n4];
	
	if ( num<10 ){
		arr[3] = 0; arr[1] = 0; arr[2] = 0x80;
	}
	else if ( num<100 ){
		arr[2] = 0x80; arr[3] = 0;
	}
	else if ( num<1000 ){
		arr[3] = 0;
	}
	
	FND_4();
	return arr;
}

 
8. 전체 코드

/*
* test03.c
*
* Created: 2024-03-27 오후 12:26:02
* Author : jong9
*/
#define F_CPU 16000000L					//Board CLK 정보(16MHz)
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
#define OPTMAX 3
#define STATEMAX 3

uint8_t digit[] = {	0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x67, 0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71 };
char arr[5];		//세그먼트 이미지 정보를 담을 안전공간
volatile int opt = 0, state = 0;


void seg(int sel, uint8_t c){
	PORTC |= 0X0F;
	PORTC &= ~(1 << (3-sel));
	PORTD = c;	//숫자 데이터 출력
	_delay_ms(2);
}


void FND_4(char *inf){	//segment Image 배열
	for (int i = 0; i < 4; i++){
		seg(i, *(inf+i));

	}
}

//16진수 segment image 배열
char* Display(unsigned long num){	//10진 정수를 입력받아 16진수 문자열로 변환 ex)65535 ==> 0xffff, 56506=>0xBCDA
	int n1 = num % 10;			//A(10): 문자가 아닌 숫자
	int n2 = (num / 10) % 10;	//B(11)
	int n3 = (num / 100) % 10;	//C(12)
	int n4 = num / 1000;		//D(13)
	
	arr[0] = digit[n1]; arr[1] = digit[n2]; arr[2] = digit[n3] + 0x80; arr[3] = digit[n4];
	
	if ( num<10 ){
		arr[3] = 0; arr[1] = 0; arr[2] = 0x80;
	}
	else if ( num<100 ){
		arr[2] = 0x80; arr[3] = 0;
	}
	else if ( num<1000 ){
		arr[3] = 0;
	}
	
	FND_4(arr);
	return arr;
}



int main(void)
{
	//7-Segment 사용	: 4 Module - C type
	//	Pin assign	: PDx - Segment img, PCx - module sel
	//Interrupt 사용	: INT4~INT6 (External Interrupt)
	//	Pin assign	: PE4~PE6
	DDRD = 0xFF;
	DDRC = 0x0F;
	DDRE = 0x00;
	
	//인터럽트 설정
	EIMSK = 0x70;	//0111 0000	//INT 4~INT 6 활성화
	EICRB = 0x2a;	//4개의 B그룹(INT4~INT7)의 인터럽트 발생 시점 결정(00 10 10 10, 각 7 6 5 4에서의 INT발생 시점을 rising edge로 결정)
	SREG |= 0x80;	//status Register - 인터럽트 허용 상태 레지스터
	sei();			//set interrupt
	
	int t = 0;
	while (1)
	{
		switch(opt){
			case 0:	//reset단계
				t = 0; break;
			case 1:	//stopwatch start
				t++; break;
			case 2:	//stopwatch stop
				break;
			default:  break;
		}
		Display(t);	//값이 증가하며 Disp가 호출될 때 t가 증가하며 각 자릿수에 맞는 시간 표시		
		_delay_ms(2);
	}
}

ISR(INT4_vect){	//INT4 인터럽트 처리 루틴: sw1
	opt++;
	if (opt >= OPTMAX) opt = 0;
}

ISR(INT5_vect){	//INT5 인터럽트 처리 루틴: sw2
	state++;
	if (state >= STATEMAX) state = 0;
}

ISR(INT6_vect){	//INT6 인터럽트 처리 루틴: sw3
	
}

 
9. 작동 확인 - 오작동: 값을 할당하지 않은 SW1의 버튼 누르면 작동하는 현상 발생
이유: RC공진에 의한 오작동으로 파악 -> SW2의 저항을 제거하여 공진현상 방지


실습 2: 인터럽트 응용하여 주방 타이머 설계 - 16일차 진행