__________

Designing the Future with Circuits

반도체 회로설계 취준기

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

하만(Harman) 세미콘 아카데미 11일차 - SW 구조설계( 데이터 입출력, 버튼 LED 점멸, 7세그먼트 출력 )

semicon_circuitdesigner 2024. 3. 21. 11:13

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


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


디지털 데이터 입출력


1. 디지털 데이터 출력

  • PORT 레지스터 (출력 레지스터)
    • 포트: 8개의 핀을 하나로 묶어 관리하는 것
    • PORTA, PRTAB, ... PORTG까지의 레지스터
    • PORTA~PORTF에는 0~7까지 8개의 비트가 존재
       - 비트 표현: PORTA1, PORTA2, ... PORTXn으료 표시 가능(X는 포트 종류, n은 비트)
    • PORTG에는 5개의 비트만 존재하므로 상위 3개의 비트 사용 불가
    • 특정 비트를 1로 설정 시: 1<<PORTA4사용
  • DDR(Data Direction Register): 핀의 입출력 방향 선택
    • 데이터 핀을 사용하기 전에 입력 또는 출력 중 사용 용도를 결정하는 레지스터
    • Bit OR( | )로 세팅 가능
       - ex) DDRA의 0번째 비트를 입력으로 사용하기 위해 => DDRA |= 0x01;
    • DDRx(x = A, B, ..., G) = 0 or 1로 설정(0은 입력상태, 1은 출력상태)
    • 하나의 포트를 하나의 용도로 사용 권장

2. 디지털 데이터 입력

  • DDR 레지스터
    • 포트를 입력상태로 설정 시 0을 설정 -> 해당 포트 중 입력으로 사용하는 비트만 0으로 설정 필요
    • 특정 비트만 입력상태 0으로 만들기 위해 해당 부분만 1, 나머지는 0인 비트에서 bitNOT과 AND를 통해 구현 가능
       - ex) DDRA의 0번째 비트를 출력으로 사용하기 위해 => DDRB &= ~0x01;
  • PIN 레지스터(입력 레지스터)
    • PINA, PINB, ..., PING까지의 레지스터
    • PINx 레지스터는 DDR, PORT 레지스터와 다르게 읽기전용, 초깃값이 N/A(Not Available)
  • 풀업 / 풀다운 저항
    • 스위치가 open된 경우, GPIO 핀에는 아무런 회로가 연결되지 않은 것과 같은 상태
    • 플로팅 상태: 개방된 핀의 입력에 무작위의 값이 가해질 수 있는 상태
    • 풀업 / 풀다운 저항을 통해 플로팅 상태 해결 가능
    • 풀업 저항: 저항을 Vdd쪽 회로에 설치 -> 스위치 ON(0) / OFF(1)
    • 풀다운 저항: 저항을 GND쪽 회로에 설치 -> 스위치 ON(1) / OFF(0)
  • 디바운스
    • 바운스 현상(채터링): 기계적인 현상으로, 버튼을 누르면 내부 스프링의 진동에 의해 버튼을 여러 번 누른 효과가 나타나는 현상
      채터링 현상 / 스위치를 누르고 뗄 때 탄성에 의해 상태가 빠르게 변화
    • 디바운스: 바운스 현상의 제거
       - 소프트웨어 이용: 시간지연을 통해 버튼의 빠른 상태 변화 무시
       - 하드웨어 이용: 캐퍼시터를 통해 버튼의 빠른 상태 변화 무시(간단하고 효율적)

실습 1: 버튼이 눌리면 LED 순차 점멸


 - 사용한 메인함수(스위치가 안눌리면 LED 순차 점멸, 눌리면 정지)

int main(void)
{
	DDRA |= 0x07;					//A port의 0번째 비트(PA0), 1번째 비트, 2번째 비트를 출력용(1)으로 사용(0000 0011), 나머지는 입력용(0)
	DDRB &= ~0x01;					//B port의 0번째 비트는 입력용(0)으로, 나머지는 출력용(1)로 설정 (1111 1110 = NOT 0000 0001)
	int i = 0;
    while (1) 
    {
		if(PINB & 0x01 == 1){		//B port의 0번 비트의 값이 0이라면(스위치가 눌리면)
			//togglepinA(0);			//0번째 비트(LED)를 점멸
			//togglepinA(1);			//1번째 비트(LED)를 점멸
			//togglepinA(2);			//2번째 비트(LED)를 점멸
		}
		else {		//B port의 0번 비트의 값이 0이라면(스위치가 눌리면)
			togglepinA(0);			//0번째 비트(LED)를 점멸
			togglepinA(1);			//1번째 비트(LED)를 점멸
			togglepinA(2);			//2번째 비트(LED)를 점멸
		}
    }
}
  • if(PINB & 0x01 == 1): 스위치가 눌리지 않을 때가 1, 눌리면 0인 상태(Pullup 저항에 의해)
  • 이 조건을 0으로 하여 스위치를 눌렸을 때 점멸 실행되게 해도, 정상 작동 X (N/A 상태이므로)
  • -> 전체에 대하여 NOT 연산으로 수행 가능
  • 수정한 메인함수(스위치가 눌리면 작동하는 코드)
int main(void)
{
	DDRA |= 0x07;					//A port의 0번째 비트(PA0), 1번째 비트, 2번째 비트를 출력용(1)으로 사용(0000 0011), 나머지는 입력용(0)
	DDRB &= ~0x01;					//B port의 0번째 비트는 입력용(0)으로, 나머지는 출력용(1)로 설정 (1111 1110 = NOT 0000 0001)
	int i = 0;
    while (1) 
    {
    	//if(PINB & 0x01 == 1) 			스위치가 안눌리면 TRUE
		if(!(PINB & 0x01 == 1)){		//스위치가 눌리면 TRUE
			togglepinA(0);			//0번째 비트(LED)를 점멸
			togglepinA(1);			//1번째 비트(LED)를 점멸
			togglepinA(2);			//2번째 비트(LED)를 점멸
		}
    }
}

 


실습 2: 버튼이 눌릴 때 다음 LED 점멸


  • 수정한 메인함수(스위치가 눌릴 때 다음 LED를 점멸하는 코드)
int main(void)
{
	DDRA |= 0x07;					//A port의 0번째 비트(PA0), 1번째 비트, 2번째 비트를 출력용(1)으로 사용(0000 0011), 나머지는 입력용(0)
	DDRB &= ~0x01;					//B port의 0번째 비트는 입력용(0)으로, 나머지는 출력용(1)로 설정 (1111 1110 = NOT 0000 0001)
	int i = 0;
    while (1) 
    {
		if(!(PINB & 0x01 == 1)){		//B port의 0번 비트의 값이 0이라면(스위치가 눌리면)
			int on = 1;
			while(on == 1){
				togglepinA(i);			//0번째 비트(LED)를 점멸
				if(!(PINB & 0x01 == 1)) on = 0;
			}
			i++;
			if (i>2) i=0;			//2번째 LED 점멸 후 다시 0번째부터 실행해야 하므로 i 초기화
		}
    }
}
  • 실행 확인
누를 때 다음 LED가 1회만 점멸

 

  • 위치를 누르지 않으면 해당 LED가 계속 점멸하도록  코드 수정
#define F_CPU 16000000L				//Board CLK 정보(16MHz)

#include <avr/io.h>
#include <avr/delay.h>				//delay를 실행하기 위한 헤더파일

void togglepinA(int n){				//n: n번째 비트, dl: delay in mili-second
	char b = 1 << n;				//n번만큼 비트를 왼쪽으로 이동하라 (n이 1이면 10이 되어 2, n이 2이면 100이 되어 4 표현)
	PORTA |= b;						//위의 식을 따라 b번째 비트를 HIGH로 출력
	_delay_ms(500);
	PORTA &= ~b;					//Port A의 최하위비트(0번째 비트)를 0(LOW)로 설정}
	_delay_ms(500);
}

int main(void)
{
	DDRA |= 0x07;					//A port의 0번째 비트(PA0), 1번째 비트, 2번째 비트를 출력용(1)으로 사용(0000 0011), 나머지는 입력용(0)
	DDRB &= ~0x01;					//B port의 0번째 비트는 입력용(0)으로, 나머지는 출력용(1)로 설정 (1111 1110 = NOT 0000 0001)
	int i = 0;
    while (1) 
    {
		if(!(PINB & 0x01 == 1)){		//B port의 0번 비트의 값이 0이라면(스위치가 눌리면)
			int on = 1;
			while(on == 1){
				togglepinA(i);			//0번째 비트(LED)를 점멸
				if(!(PINB & 0x01 == 1)) on = 0;
			}
			i++;
			if (i>2) i=0;			//2번째 LED 점멸 후 다시 0번째부터 실행해야 하므로 i 초기화
		}
    }
}
  • 작동 확인

 


실습 3: 세그먼트 출력 실습


1. 7세그먼트 표시장치

  • 2개의 공통핀과 8개의 세그먼트 제어핀으로 구성
  •  - 공통핀에 Vcc/GND를 가하고 제어핀에 GND/Vcc를 가하면 해당 세그먼트가 켜지는 공통 양극/음극 방식으로 나눔
출처:디바이스마트
글자가 써 있는 부분이 아래쪽

 

출처:코코아팹
a~g에 7개의 digit 존재 / 네 자리 7세그먼트 표시장치 연결 회로도

 
2. 7세그먼트 숫자 표현

숫자세그먼트제어값
DPGFEDCBA2진수 값16진수 값
0001111110011 11113F
1000001100000 011006
2010110110101 10115B
3010011110100 11114F
4011001100110 011066
5011011010110 11016D
6011111010111 11017D
7001001110010 011127
8011111110111 11117F
9011001110110 011167

 
3. 실습에 사용할 부품

200옴 저항 / 7세그먼트

 
4. 회로 연결

 
5. Solution Explorer의 Solution 우클릭- New - Project

 
6. 이름 입력 후 생성

 
7. ATmega 128 선택 후 OK

 
8. Soluteion 탐색기에 test02-7seg 생성 확인

 
9. while loop을 통해 네자리 중 한 자리에 숫자를 번갈아가며 출력하는 코드 작성

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

int main(void)
{
	uint8_t numbers[] = {
			0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x67
	};
	int i, j;		
	DDRD = 0xFF;						//세그먼트 제어 핀 8개를 출력으로 설정
	DDRC = 0x0F;						//자릿수 선택 핀 4개를 출력으로 설정
			
    
    while (1)							//한 번의 while loop 내에서 네자리 중 한 자리에 숫자를 번갈아가며 출력하는 코드
    {
		for (i = 0; i < 4; i++){		//해당 자리에만 0(GND)를 출력하고 나머지에는 1을 출력하여 자리 선택
			PORTC |= 0X0f;
			PORTC &= ~(1 << i);			//
			for (j = 0; j < 10; j++){
				PORTD = numbers[j];
				_delay_ms(500);
			}
		}
    }
}

 
주의사항! test02를 현재의 active project로 바꿔야 하므로, 프로젝트 우클릭 - Set as a Startup Project 선택

 
10. 컴파일을 누르면 나오는 경고창에서 확인 후, 뒤 내용 입력

 
11. 상단의 Tool - Rebuild solution 뒤, Add target

 
12. Device programming

 
13. 코드 작성: 한 번의 while loop 내에서 네자리 중 한 자리에 숫자를 번갈아가며 출력하는 코드

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


int main(void)
{
	uint8_t numbers[] = {
		0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x67
	};
	int i, j;
	DDRD = 0xFF;						//세그먼트 제어 핀 8개를 출력으로 설정
	DDRC = 0x0F;						//자릿수 선택 핀 4개를 출력으로 설정
			
    
    while (1)							//한 번의 while loop 내에서 네자리 중 한 자리에 숫자를 번갈아가며 출력하는 코드
    {
		for (i = 0; i < 4; i++){		//해당 자리에만 0(GND)를 출력하고 나머지에는 1을 출력하여 자리 선택
			PORTC |= 0X0f;
			PORTC &= ~(1 << i);			//
			
			for (j = 0; j < 10; j++){
				PORTD = numbers[j];
				
				_delay_ms(500);
			}
		}
		
    }
}

 
14. 작동 확인

 
15. 세그먼트 네자리에 1234를 표시하는 코드 작성

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


int main(void)
{
	uint8_t numbers[] = {
		0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x67
	};
	int i, j;
	DDRD = 0xFF;						//세그먼트 제어 핀 8개를 출력으로 설정
	DDRC = 0x0F;						//자릿수 선택 핀 4개를 출력으로 설정

void display_digit(int position, int number){
	PORTC |= 0x0F;
	PORTC &= ~(1 << (position - 1));
	
	PORTD = numbers[number];
}

	while(1){
		display_digit(1, 1);
		_delay_ms(5);
		display_digit(2, 2);
		_delay_ms(5);
		display_digit(3, 3);
		_delay_ms(5);
		display_digit(4, 4);
		_delay_ms(5);
		
	}
}

 
16. 작동 확인