[임베디드]Board-LED,SWITCH,BUZZER

공부기록·2023년 12월 2일
0
post-thumbnail
  • 6개의 8비트 I/O 포트
    • PA, PB, PC, PD, PE, PF
  • 1개의 5비트 I/O 포트
    • PG
  • DDRx : 입출력 설정( 1 - 출력, 0 - 입력)
  • PORTx : 출력될 값을 입력

🔊 LED (PA)

PA포트에 1을 출력하여야 LED가 켜짐
먼저 LED는 출력이므로 DDRA 레지스터의 해당 비트에 '1'을 write하여 출력상태로 설정하고 PORTA 레지스터의 해당 비트에 '1'을 write하여 LED가 켜지도록한다.

#include <avr/io.h>
void main()
{
	unsigned char value 128; 1000 0000 
    DDRA = 0xff; //포트A를 출력포트로 설정
    for(;;){
    	PORTA = value;
        _delay_ms(200);
        value >> 1;
        if(value == 0) value = 128;
}



🔊 FND(7-segment) (PC, PG)

C포트 - FND 출력설정, G포트 - 어떤 FND 선택할지 선택

업로드중..

  • 발광 다이오드
    • 두 개의 다리에 2.5V정도의 전압차르 걸면 빛을 방출한다.
    • Anode(+) : 핀의 길이가 긴 쪽으로 공통 단자에 VCC(+5V)에 연결하고 입력 단자에 0V를 인가하였을 때 해당하는 LED에 램프가 들어온다.
    • Cathode(-) : 핀의 길이가 짧은 쪽으로 공통단자에 접지(0V)를 연결하고 입력단자에 +5V를 인가하였을 때, 해당하는 LED 램프가 들어온다.
  • FND
    • 4개의 7-Segment의 숫자를 나타내는 부분의 신호를 모두 공유한다.
    • SEL 신호는 어느 부분에 숫자를 나타낼지 정하는 비트로 이는 오직 하나만 활성화(enable-high)상태가 가능한데 시간을 짧게하면 한번에 나오는 것처럼 할 수 있다.
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

unsigned char digit[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x7d, 0x27, 0x7f, 0x6f};
unsigned char fnd_sel[4] = {0x01, 0x02, 0x04, 0x08};
int main(){
	int i = 0;
    DDRC = 0xff; // C포트는 출력될 숫자는 모두 출력으로 사용
    DDRG = 0x0f; // G포트는 4개만 출력으로 사용
    
    while(1){
    	for (i = 0; i < 4; i++){
			PORTC = digit[4-i]; //데이터 신호
            PORTG = fnd_sel[i]; // FND 선택신호
            _delay_ms(2);
        }
    }
}



스위치를 이해하긴 위한 빌드업 : 인터럽트

🔈 Atmega 128 인터럽트

  • 35개의 인터럽트 벡터를 갖는데 외부핀을 통한 외부 인터럽트는 8개가 존재하며 타이며 관련 14개, URT관련 6개가 있다.
  • 인터럽트 벡터
    • 인터럽트 벡터 순으로 우선순위가 존재한다.
    • 우선순위 : 최하위주소 > 최상위주소
      • RESET : 최우선 1순위
      • INT0(External Interrupt Request 0) : 2순위
  • 외부 인터럽트의 트리거
    • edge trigger : 입력 신호가 변경되는 순간이 트리거
    • level trigger : 입력 신호가 일정시간동안 유지되면 트리거

인터럽트 설정 과정

🏷️ SREG : ALU 연산 후 상태와 결과를 표시하는 레지스터, 특정 인터럽트 허용하는 레지트로, 해당 인터럽트를 1로 세팅, 우리는 주로 7번째 비트만 1로 만듦

- Global Interrupt Enable : sei(), cli()
- SREG = 0x80;

🏷️ EIMSK(External Interrupt MASK register) : 외부 인터럽트의 개별적인 허용 제어 레지스터

- 우린 INT4(Swtich 1), INT5(Switch 2)를 쓸거라서 EIMSK는 0x30으로 설정

🏷️ EICR(External Interrup Control Register) : level 및 trigger 설정, 8개의 인터럽트가 존재하는데 두비트씩 필요해서 총 2바이트가 필요하다.

- 10이 하강 엣지에서의 인터럽트 발생
- EICRB = 0x0A(INT4, INT5쓴다고 가정)

ISR(Interrupt Service Routine)

ISR(인터럽트 이름){
	인터럽트 서비스 루틴
}

🔊 스위치 (PE)

  • 스위치가 눌리면 0, 눌리지않으면 1이며 PE 포트이면서 INT인 신호에 연결되어있다.

🖋️ FND와 스위치 이용 예제, 스위치를 누르면 FND에 나타나는 숫자가 1씩 증가하도록 설정

#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#include <util/delay.h>
unsigned char digit[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x7d, 0x27, 0x7f, 0x6f};
unsigned char fnd_sel[4] = {0x01, 0x02, 0x04, 0x08};
volatile int count = 0;

//switch1이 눌릴 때마다 count변수가 1씩 증가한다.
ISR(INT4_vect){
	count++;
    _delay_ms(100);
}

void display_fnd(int count){
	int i, fnd[4];
    fnd[3] = (count/1000)%10;
    fnd[2] = (count/100)%10;
    fnd[1] = (count/10)%10;
    fnd[0] = count%10;
    for(i = 0; i<4; i++){
		PORTC = digit[fnd[i]];
        PORTG = fnd_sel[i];
        _delay_ms(2);
    }
}
int main(){
	DDRC = 0xff;
    DDRG = 0x0f;
    DDRE = 0xef; 0b11101111, (switch1-PE4만 입력)
    EICRB = 0x02; //하강엣지 설정
    EIMSK = 0x10; //INT4 인터럽트 활성화
    SREG |= 1 << 7;
    while(1) display_fnd(count)l



🔊버저 (PB(4))

단순버저

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

int main(){
	DDRB = 0x10; //포트B의 4번째만 출력상태로 지정
    while(1){
    	PORTB = 0x10;
        _delay_ms(1);
        PORTB = 0x00;
        _delay_ms(1);
    }
}

음을 울리기위해서는 주파수를 이용해야한다. 여기서 쓰이는 도의 주파수는 1046.6으로 1초에 1046.6.번의 주파수가 필요하므로 주기는 1/1046.6이므로 955us이다. 그러면 신호 1인 상태를 478us 유지하고 0인 상태를 478초 유지해야한다.
Timer의 주기를 설정하고 정해진 시간(위의 예로는 478us) 후 인터럽트가 발생해야하며 인터럽트 핸들러에서 on/off를 수행해야한다.


🖇️ 타이머

  • 내부 클럭을 세는 장치로 일정시간 간격의 펄스를 만들거나 일정시간 경과 후에 인터럽트를 발생시킨다.

🖇️ 카운터

  • 외부에서 입력되는 클럭을 세는 장치로 외부핀으로 들어오는 펄스를 세어 특정 값이 되거나 Overflow가 생기면 인터럽트를 발생시킨다.

모두 4개의 타이머와 카운터를 보유하고 타이머 0,2는 8비트 타이머로 서로 기능이 유사하고, 타이머 1,3는 16비트 타이머로 서로 기능이 유사하다. ### 인터럽트 기능 - Overflow 인터럽트 : 카운터의 값이 오버플로우되면 발생한다. 8비트 타이머의 경우엔 0xff에서 0x00으로 넘어갈때 발생한다. - 출력비교 인터럽트 : 카운터 값이 출력비교 레지스터의 값과 같게되는 순간에 뱔생한다.

🔈 8비트 타이머/카운터의 특징

  • 0,2번을 사용하여 10비트의 프리스케일러(prescaler)를 가지고 있다.
    • 고속 클럭 사용하여 타이머를 동작하면 문제가 발생하므로 클럭을 분주하여 더 느린 타이머 클럭을 생성해낸다.
    • 프리스케이러를 1024로함녀 16MHz 클럭의 타이머 클럭은 주기가 62.5ns 1024 = 64us가 되고, 이 클럭을 256까지 센다면 64us 256 = 16.384ms 크기의 타이머를 만들 수 있다.
    • 16us counter \rarr 16.385ms counter
    • 62.5 ns \rarr 64 us
  • 내장 인터럽트
    • Normal Mode : 오버플로우 인터럽트
    • CTC mode : 출력 비교 인터럽트로 TCNTn과 OCR 레지스터 값을 비교한다.

      음을 내는 과정
      TCCRn (제어,Prescaler를 이용한 클럭 주기 설정) \rarr TCNTn (카운터 값 세팅) \rarr TIMSK (인터럽트, 오버플로우 인터럽트 활성화)



🏷️ TCCRn

  • 동작 모드 및 분주 설정
  • 0,1,2비트가 클럭 및 프리스케일러 세팅
210설명
01132분주
10064분주
101128분주

🏷️ TCNTn

  • read/write 기능이 있으며 Overflow 발생시 자동으로 0으로 클리어되고 분주된 주기마다 1씩 증가된다.

🏷️ TIMSK

  • 0번째 비트가 오버플로우 인터럽트를 활성화시킨다. (1은 출력비교 인터럽트)
  • 0x00000001 = 0x01

타이머로 원하는 시간 세팅하는 법 (16MHz클럭, prescaler = 32)
타이머 클럭의 주기 = (1/(161000000))32 = 2us
100us의 지연시간을 얻으려면 100/2 50클럭이 필요하므로
TCNT = 256 - 50 = 206
Overflow 인터럽트 활성화

  • 음계에 따른 TCNT0 값은?
    • TCNT0 = 256 - (((1/주파수) * 1000000) / 2(ON)) / 2(us - 타이머주기)
#include <avr/io.h>
#include <avr/interrupt.h>
#define ON 1
#define OFF 0
#define DO 17
volatile int state = OFF;

ISR(TIMER0_OVF_vect){
	if (state == ON){
    	PORTB = 0x00;
        state = OFF;
    }
    else{
    	PORTB = 0x10;
        state = ON;
    }
    TCNT0 = DO;
}
int main(){
	DDRB = 0x10;
    TCCR0 = 0x03; //32분주
    TIMSK = 0x01; //Overflow 인터럽트 활성화
    TCNT0 = DO;
    sei();
    while(1);
}

0개의 댓글

관련 채용 정보