__________

Designing the Future with Circuits

반도체 회로설계 취준기

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

하만(Harman) 세미콘 아카데미 10일차 - SW 구조설계(ATmega128, Atmel Studio 설치, Microchip Studio 설치)

semicon_circuitdesigner 2024. 3. 20. 12:02

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


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


마이크로 컨트롤러 & CISC / RISC


  • 마이크로 컨트롤러(칩 위의 컴퓨터)
    • ex) ATmega128, 아두이노 등
    • 컴퓨터의 메인보드+메
    • 마이크로 프로세서+메모리+입출력 인터페이스
    • 컴퓨터 메인보드와 하드디스크 기능을 하나의 IC칩으로 집적시켜 만든 반도체 소자
    • 특수 목적용 낮은 사양의 컴퓨터 / 작고 간단한 제어장치 제작에 사용
    • 임베디드 시스팀: 다른 시스템의 일부로 사용
    • 마이크로컨트롤러 VS. 데스크톱 컴퓨터
      항목 마이크로컨트롤러 데스크톱 컴퓨터
      CPU ATmega128 인텔 Core i7
      비트 8 64
      메모리 128KB 8GB
      클록 16MHz 3.4GHz(Quad Core)
  • 마이크로 프로세서
    • 중앙처리장치(CPU)를 하나의 IC칩으로 집적시킨 반도체 소자
  • 직렬 통신
    • 동기식(Sync): 여러 번에 걸쳐 전송되는 데이터 비트 구분을 위해 동기화 클록 사용(SPI, I2C 방식 사용)
    • 비동기식(Async): 전송 속도를 약속하여 사용하고 별도의 클록을 사용하지 않음. 동일 속도로 송수신하지 않으면 잘못된 데이터 전달 (UART 방식 사용)
  • 교차 개발 환경
    • 교차 컴파일러(아트멜 스튜디오)에서 개발된 기계어 파일을 ISP/시리얼 방식으로 프로그램 다운/업로드
  • CISC VS. RISC: 하드웨어 수준에서 지원되는 명령어의 개수로 구분
    • CISC(Complex Instruction Set Computer)
       - 복잡한 명령 한 번에 처리 가능하나 하드웨어가 복잡
       - 명령어에 따라 필요한 클록 수가 다름
    • RISC (Reduced Instruction Set Computer)
       - 하드웨어가 간단하지만 복잡한 명령을 여러번 간단한 명령으로 처리
       - 명령어가 동일한 수의 클록을 필요로 하므로 파이프라인 도입 간단
    • CISC와 RISC 비교
      구분 CISC RISC
      명령어 개수 많음(1000개 내외) 적음(100개 내외)
      프로그램 크기 작음
      하드웨어 복잡도 높음 낮음
      소프트웨어(컴파일러) 복잡도 낮음 높음
      명령어당 클록 수 가변 고정
      전력소모 많음 적음
      호환성 높음 낮음
      대표적 CPU 제조사 인텔 ARM

ATmega 128 소개


1. ATmega128: 고성능, 저전력 마이크로 컨트롤러

2. ATmega128 핀

  • 전원 핀 7개: GND, VCC(디지털 전원) / AVCC(아날로그 전원), AREF(아날로그 기준 전압)
  • 크리스털 연결 핀 2개(XTALn)
  • 리셋 핀 1개
  • PEN 핀 1개
  • 데이터 입출력 핀 53개: 포트(A~G)단위로 8개 핀(8Bti 표현)을 묶어서 관리. G포트만 5개 핀 할당
  • 주로 하나의 핀이 두 개 이상의 기능
  •  - ex) 40번 핀은 PC5(Port C의 5번 핀)과 A13(Analog 데이터 입력을 위한 13번 핀) 두 개의 이름을 가짐

3. 입출력 레지스터

  • 8비트의 메모리
  • 실행 명령어, 피연산자, 계산 결과 등의 임시 저장 공간

4. 메모리 주소

  • 플래시메모리, SRAM, EEPROM은 각각 별도의 메모리 주소를 가짐

5. 플래시메모리

  • 128KB의 메모리에 2바이트 단위로 명령어 저장
  • 16비트의 0X0000~0XFFFF주소 사용
  • 애플리케이션 프로그램 영역+부트로더 영역

6. 시스템 클록

  • 클록: 디지털 시스템에서 동작의 기준이 되는 신호

7. 명령어 실행

출처ㅣ ATmega128로 배우는 마이크로컨트롤러 프로그래밍, 허경용 지음

  • 명령어 읽기&명령어 실행의 2단계가 파이프라인에 의해 중첩되어 실행(1클록에 1개 명령 실행 가능, RISC구조이므로 가능)
  • 파이프 라인: 한 데이터 처리 단계의 출력이 다음 단계의 입력으로 이어지는 형태로 연결된 구조

개발 환경 설정[Atmel Studio(=Microchip Studio) 설치]


1. 아트멜 스튜디오 설치

  • 아트멜 스튜디오(AVR Studio): ATmega128개발사 아트멜에서 제공하는 무료 통합 개발환경
  • 다운로드 위치: https://www.microchip.com/en-us/tools-resources/develop/microchip-studio
  • 상단 카테고리 - Tools and Resources - Develop - Microchip Studio for AVR and SAM Devices
    스크롤을 내려 Microchip Studio for AVR and SAM Devices- Web Installer 설치

2. ATmega128을 브레드보드에 연결하고, 전원 선 연결

 
3. 장치관리자 - 포트 확인

포트 추가적인 설치 작업 필요

 
4.sillabs.com 사이트 - Downloads - CP210x_Windows_Drivers.zip 다운로드

 

CP210x USB to UART Bridge VCP Drivers - Silicon Labs

The CP210x USB to UART Bridge Virtual COM Port (VCP) drivers are required for device operation as a Virtual COM Port to facilitate host communication with CP210x products. These devices can also interface to a host using the direct access driver.

www.silabs.com

 
5. 압축 해제 후 컴퓨터 비트에 맞게 설치

컴퓨터 비트 확인: 내 PC 우클릭 - 속성
장치 사양 - 시스템 종류의 비트 확인
비트 크기에 맞는 설치 파일 실행 (32비트는 x86 설치)

 
6. ATmega128 컴퓨터에 연결 후 장치관리자의 포트 재확인

Silicon Labs CP210x USB to UART Bridge(COM4) 확인_COM번호는 개인마다 다름

 
7. 1번 과정에서 설치한 Microchip Studio 실행

 
8. Menu 탭 - File - New - Project

 
9. New Project 창에서 GCC C Executable Project 선택 - 이름 설정(test01)

 
10. Location은 C:\Users\SYSTEM-00\source\repos\Work에 ATmega 폴더 생성 후 선택

 
11. Device Selection: ATmega128 선택

 
12. Visual Studio와 유사한 프로젝트가 생성됨

 
13, 상단 메뉴 - Projects - test01 properties - 아래 내용 입력

 
14. 상단 메뉴 - Tools - Add Target

 
15. 다음과 같이 설정(COM 포트는 장치관리자에서 확인한 포트 설정) - Apply

 
16. Tools - Device Programming - Tool에 STK500, Device에 ATmega128, Interface에 ISP 선택 후 Apply

 

에러 발생

 
17. 에러 발생 시 보드의 파란색 스위치 이동(Interface를 ISP로 바꾸는 과정) 후 재시도

Read를 누르면 장비의 ID 표시

 
18. 코드 작성 후 실행 시 Output에 Build succeeded.와 같은 문구가 나오면 test01.elf파일 생성

 
 
19. Tools - Device Programming - Memories 탭에서 Program을 클릭하면 ATmega128에 프로그래밍

 

  • Embeded Program에서는 무한 Loop가 기본(동작을 반복 실행해야 하므로)

20. 코드 작성 후 상단 메뉴 - Build - Rebuild Solution을 통해 elf파일 재생성

 
21. 파일 생성 후 Device Programming - Apply - Read - Read한 후 좌측 Memories 탭에서 프로그램 클릭

메시지 창에 OK 확인

  • LED 점멸 코드 예시
    #define F_CPU 16000000L
    #include <avr/io.h>
    #include <util/delay.h>
    
    int main(void){
    	DDRA |= 0x01; 				//PA0(Port A의 0번째 비트) 핀을 출력으로 설정. DDR뒤의 A가 포트를 뜻함. 
        
        while(1){
        	PORTA |= 0x01;			// PA0핀에 연결된 LED 켜기(HIGH 신호 전달)
            
            _delay_ms(500);			//0.5초 대기, ms(milli second)는 1/1000초
            PORTA &= ~0x01;			//PA0핀에 연결된 LED 끄기(LOW신호 전달)
           
            _delay_ms(500);
        }
        
        return 0;
    }​​
    • DDRA : 입출력 레지스터. 값이 0x01이면 출력모드, 0x00이면 입력모드
    • PORTA : 포트 레지스터
    •  ~0x01에서 ~은 BitNOT을 의미 => ~0x01: 최하위 비트를 0으로 만들어라
      0x01은 0000 0001이므로, ~0x01은 1111 1110을 의미.

ATmega128 실습 1: 점등 LED 작동


[ 사용 소자 ]

저항은 위부터 200 / 20K / 330 / 1K


1. 회로 구성


2. 코드 입력 후 컴파일

 
3. Tools - Device Programming - Memories - program

 
4. 작동 확인

delay가 1초보다 짧게 진행

 
5, delay_ms 시간의 오차를 수정하기 위해 코드의 가장 윗줄에 #define F_CPU 16000000L 정의

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


int main(void)
{
	DDRA = 0x01; //A port의 0번째 비트(PA0)를 출력용(1)으로 사용, 나머지는 입력용(0)
	int i = 0;
    while (1) 
    {
		PORTA = 0x01; //Port A의 최하위비트(0번째 비트)를 1(HIGH)로 설정
		_delay_ms(1000);
		PORTA = 0x00; //Port A의 최하위비트(0번째 비트)를 0(LOW)로 설정
		_delay_ms(1000);
    }
}

 
6. Build - Rebuild solution

 
7. Tools - Device Programming - Memories에서 Program

 
8. 정상 작동 확인

 
9. Delay 시간을 0.5초로 수정

#define F_CPU 16000000L

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



int main(void)
{
	DDRA = 0x01; //A port의 0번째 비트(PA0)를 출력용(1)으로 사용, 나머지는 입력용(0)
	int i = 0;
    while (1) 
    {
		PORTA = 0x01; //Port A의 최하위비트(0번째 비트)를 1(HIGH)로 설정
		_delay_ms(500);
		PORTA = 0x00; //Port A의 최하위비트(0번째 비트)를 0(LOW)로 설정
		_delay_ms(500);
    }
}

 
 

  • 코드가 다음과 같이 =이 아닌 |=인 경우 의미: 특정 비트만 조작하기 위함(특정 비트를 1과 OR 연산 -> 무조건 1이 됨)
    int main(void)
    {
    DDRA |= 0x01; //A port의 0번째 비트(PA0)를 출력용(1)으로 사용, 나머지는 입력용(0)
    int i = 0;
        while (1) 
        {
    PORTA |= 0x01; //Port A의 최하위비트(0번째 비트)를 1(HIGH)로 설정
    _delay_ms(500);
    PORTA  |=  0x00; //Port A의 최하위비트(0번째 비트)를 0(LOW)로 설정
    _delay_ms(500);
        }
    }
  • 특정 비트를 0으로 만둘어주기 위해서는 0과 AND 연산 진행(나머지를 그대로 유지하기 위해 나머지 비트는 1과 AND 연산) -> 복잡하므로 다음 과정으로 진행
  • 특정 비트를 0으로 만들기 위해 비트 NOT 연산자 사용
  • [example] xxxx xxxx비트에서
    • 세번째 비트(0x04)를 출력으로 바꿀 때: 0000 0100과 OR 연산 수행(PORTA |= 0x04)
    • 세번째 비트를 입력으로 바꿀 때
       - 1111 1011과 AND연산 수행
       - PORTA &= ~0x04 연산 수행(위 과정과 동일한 결과)

ATmega128 실습 2: LED 2개 동시 작동


코드에 DDRA |= 0x02;를 추가하여 A포트의 1번째 비트를 출력용으로 사용, 나머지는 입력용
다음과 같이 main 함수 변경

int main(void)
{
	DDRA |= 0x01; //A port의 0번째 비트(PA0)를 출력용(1)으로 사용, 나머지는 입력용(0)
	DDRA |= 0X02;
	int i = 0;
    while (1) 
    {
		PORTA |= 0x01; //Port A의 최하위비트(0번째 비트)를 1(HIGH)로 설정
		PORTA |= 0x02; //Port A의 1번째 비트를 1(HIGH)로 설정
		_delay_ms(500);
		PORTA &= ~0x02; //Port A의 최하위비트(0번째 비트)를 0(LOW)로 설정
		PORTA &= ~0x01; //Port A의 1번째 비트를 0(LOW)로 설정
		_delay_ms(500);
    }
}

 
1. LED와 저항 추가

 
2. 컴파일 후 실행 확인

 
3. 위 코드에서 각 비트를 출력으로 선언한 것을 DDRA |= 0x03으로 동시에 출력 선언할 수 있다.
 
4. 마찬가지로 PORTA 출력도 동시에 PORTA |= 0x03으로 선언할 수 있다.


ATmega128 실습 3: LED 여러개 순차적으로 작동


1. 코드 수정

#define F_CPU 16000000L

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



int main(void)
{
	DDRA |= 0x03; //A port의 0번째 비트(PA0)와 1번째 비트를 출력용(1)으로 사용(0000 0011), 나머지는 입력용(0)
	int i = 0;
    while (1) 
    {
		PORTA |= 0x01; //Port A의 최하위비트(0번째 비트)와 1번째 비트를 1(HIGH)로 설정
		_delay_ms(500);
		PORTA |= 0x02; //Port A의 1번째 비트를 1(HIGH)로 설정
		_delay_ms(500);
		PORTA &= ~0x01; //Port A의 최하위비트(0번째 비트)와 1번째 비트를 0(LOW)로 설정
		_delay_ms(500);
		PORTA &= ~0x02; //Port A의 최하위비트(0번째 비트)와 1번째 비트를 0(LOW)로 설정
		_delay_ms(500);
    }
}

 
2. Device Programming 수행 후 작동 확인

 
3. LED와 저항 추가 후 코딩

#define F_CPU 16000000L

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



int main(void)
{
	DDRA |= 0x07; //A port의 0번째 비트(PA0), 1번째 비트, 2번째 비트를 출력용(1)으로 사용(0000 0011), 나머지는 입력용(0)
	int i = 0;
    while (1) 
    {
		PORTA |= 0x01; //Port A의 최하위비트(0번째 비트)와 1번째 비트를 1(HIGH)로 설정
		_delay_ms(500);
		PORTA &= ~0x01; //Port A의 최하위비트(0번째 비트)를 0(LOW)로 설정
		PORTA |= 0x02; //Port A의 1번째 비트를 1(HIGH)로 설정
		_delay_ms(500);
		PORTA &= ~0x02; //Port A의 1번째 비트를 0(LOW)로 설정
		PORTA |= 0x04; //Port A의 1번째 비트를 1(HIGH)로 설정
		_delay_ms(500);
		PORTA &= ~0x04; //Port A의 1번째 비트를 0(LOW)로 설정
    }
}

 
4. 작동 확인


ATmega128 실습 4: 스위치를 통해 Input 작동


  • Pull-up 저항
    • 플로팅 현상: 스위치 사용 시 스위치가 열려있는 경우, 핀이 0과 1 사이에서 불안정 상태로 변동
    • 플로팅 현상을 방지하기 위해 Pull-up 또는 Pull-down 저항을 사용하여 핀을 고전압 상태 또는 저전압 상태로 유지
    • Pull-up 저항을 Vcc와 스위치 사이에 위치시켜 플로팅 현상을 방지할 수 있음

1. Switch 및 Pull-up 저항 설치


2. LED를 점멸하는 함수 정의

  • char b = 1 << n; //n번만큼 비트를 왼쪽으로 이동하라 (n이 1이면 10이 되어 2, n이 2이면 100이 되어 4 표현)
  •  
  • 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); }

3. main 함수 변경

int main(void)
{
	DDRA |= 0x07;					//A port의 0번째 비트(PA0), 1번째 비트, 2번째 비트를 출력용(1)으로 사용(0000 0011), 나머지는 입력용(0)
	int i = 0;
    while (1) 
    {
		togglepinA(0);				//0번째 비트(LED)를 점멸
		togglepinA(1);				//1번째 비트(LED)를 점멸
		togglepinA(2);				//2번째 비트(LED)를 점멸
    }
}

 
4. 입력을 받는 코드 작성


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)를 점멸
		}
    }
}

 
5. 입력이 있을 때 실행하는 코드를 while문 내에 작성

  • PIN~ : 입력 레지스터
  • 전체 코드
    #define F_CPU 16000000L
    
    #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이라면(스위치가 눌리면)
    			//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)를 점멸
    		}
        }
    }

6. 풀업저항에 의해 스위치를 누르면 Port B에 입력되는 값이 0이 됨 -> 조건이 pinb가 1이 아니면 실행되도록 설정 및 실행