본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 16일차 - ATmege128 setting & 실습(LED-on/off, shift, structure)

by Graffitio 2023. 7. 10.
[Harman 세미콘 아카데미] 16일차 - ATmege128 setting & 실습(LED-on/off, shift, structure)
728x90
반응형
[ATmega128 Setting]

 

Microchip Studio 실행 전, ATmega128 board가 컴퓨터와 정상적으로 연결되었는지 체크해야한다.

 

시작 메뉴 우클릭 → 작업관리자
작업관리자

포트(COM & LPT)에 뜨거나, 기타 항목에서 우클릭해서 해당 드라이버를 업데이트 해주면 제대로 인식된다.

 

 

LED

 

LED

우리가 사용할 LED는 극성을 가지고 있으며, 해당하는 극과 반대로 연결한 경우 터질 수 있다.

대개 wire가 긴 쪽이 (+), 짧은 쪽이 (-) 이고, 허용전류는 약 250mA이다.

여담으로 Red, Green, White, Blue 순서로 개발되었고 RGB가 모두 개발되어 Full color 구현이 가능해졌다.

 

 

프로젝트 생성

 

New project 클릭 시, 해당 화면이 출력된다. 파일명과 솔루션명 기입 후, next 클릭
ATmega128A 지정 후, Ok 클릭
프로젝트 생성 완료

솔루션과 프로젝트라는 개념이 있는데, "프로젝트 ⊂ 솔루션" 이라고 생각하면 된다.

[프로젝트 : 하나의 실행 파일(exe)을 만들어 내기 위해서 필요한 여러 개의 소스 파일과 헤더 파일 등을 하나로 묶어 놓은 것]

[솔루션 : 여러 개의 프로젝트가 모인 것]

전원이 인가되는 순간, Boot loader 영역을 가장 먼저 찾는데 해당 영역에 아무 것도 없기 때문에 main 함수가 실행되며

현재 main 함수의 while(1)에 의해 blank가 무한 반복되고 있다.

 

 

Build solution

 

: 소스코드 파일을 실행가능한 소프트웨어 산출물로 만드는 일련의 과정

Build는 Compile과 유사한 기능을 하고 컴파일은 빌드의 부분집합으로 볼 수 있다.

 

Build solution 실행하여 output file 생성

 

 

Programming

 

ATmege128A 에 apply
ATmega128A Memory에 프로그래밍
프로그래밍 완료

 

ISP/UART select switch

해당 스위치는 좌측으로 토글되어 있어야 한다.      (다운로드 가능)

: 프로그램 다운로드 시, 반드시 ISP의 위치에 있어야 다운로드가 가능하다.

  { 좌측 : ISP  /  우측 : UART }

 

일련의 과정을 요약하면,

Source를 코딩
Solution을 Build(F7)
Board에 Programming(Ctrl + Shift + P)
보드의 출력 핀을 통하여 출력

 


 

[I/O port Register에 대한 이해]

 

PORTx 레지스터

- 포트에 대한 데이터 레지스터(Data Register)

- 초기값은 0(Low 설정)

- 출력 방향일때, 논리값을 디지털 전압 레벨로 출력

- 0이 되어 핀이 입력 방향일 때, PORTx값이 1로 설정되면 ATmega128 내부에서 Pinx에 풀업저항 연결

   ex) C포트의 3번 핀을 high로 출력

         ▶ PORTC = 0x08; // 0x08 = 0000_1000

 

DDRx 레지스터

- 데이터 방향 레지스터(Data Direction Register)

- 초기값은 0(입력 모드)

- 포트에 포함된 핀의 입출력 방향 결정

   ex) B 포트의 2번 핀만 출력 설정하고 싶다.

         ▶ DDRB = 0x04; // 0x04 = 0000 0010

 

PINx 레지스터

- 입력 핀 레지스터

- 읽기(Read)만 가능

- 핀이 입력 방향일 때, 핀의 디지털 전압 레벨을 논리값으로 읽음

   ex) B 포트의 2번, 3번 핀에 high가 들어오고 있을 때,

         char a = PINB;

         ▶ a의 값에 0x0C가 들어온다. // 0x0C = 0000 1100

 

PORTx, DDRx, PINx Register

PORTx, DDRx, PINx Register

- DDRx 레지스터의 초기값이 0이므로, 최초 설정은 모두 입력

- 입력인 상황에서 PORTx 레지스터의 초기값은 0

- PINx 은 풀업 저항이 연결되지 않은 Z(High-Impedance)인 입력 핀

- PINx 에 인가되는 초기 전압은 예측할 수 없어, 초기값은 N/A

 

 

 

[LED_1EA blink]

 

LED의 허용전류로 인해 220Ω 저항 설치(+, - 위치 상관없이 설치 가능)

#define F_CPU 16000000UL
                    // CPU 클럭 속도를 16MHz로 설정,
                    // 16MHz 이므로(1초에 1,600만 회의 펄스 발생시킴)
#include <avr/io.h> // AVR 입출력 라이브러리 
                    // AVR 사용 시, 필수인 헤더파일(ATmege128의 모든 정보가 들어 있다.)
#include <util/delay.h> // 딜레이 함수를 사용하기 위한 라이브러리
#include <stdio.h> // 표준 입출력에 대한 헤더파일
#include <stdint.h> // int 자료형의 크기 명시

int main(void)
{
    //DDRC = 0xff; // DDR 포트들의 bit들을 전부 1로 만들어 줌(0b1111_1111) -> 출력으로 설정
                   // 포트C 전체를 출력으로 설정
	DDRC0 = 0x01;  // 이렇게 해야됨
	               // 우리가 사용하는 포트 레지스터는 C-0, 해당 포트만 출력으로 변경시켜줘도 됨.
                       // 나머지는 default 또는 initial value로 가져가겠다.(대부분의 initial value는 0)
                       // DDRC0 = 0x01 == DDRC 0x01
    while (1) 
    {
		PORTC = 0xff;   // 포트C를 High로 설정
		_delay_ms(500); // 0.5sec 대기
		PORTC = 0x00;   // 포트C를 Low로 설정
		_delay_ms(500); // 0.5sec 대기
    }
}

 

<앞으로 자주 사용할 것들>

#define F_CPU 16000000UL

  : CPU 클럭 속도를 16MHz로 설정, 16MHz 이므로(1초에 1,600만 회의 펄스 발생시킴)

    UL : ultra long 타입을 의미

 

#include <avr/io.h> 

  : AVR 입출력 라이브러리 
    AVR 사용 시, 필수인 헤더파일(ATmege128의 모든 정보가 들어 있다.)

 

#include <util/delay.h>

  : 딜레이 함수를 사용하기 위한 라이브러리


#include <stdio.h>

  : 표준 입출력에 대한 헤더파일


#include <stdint.h>

  : int 자료형의 크기 명시

 

LED_1EA_blink

 


 

[LED Shift]

 

코드 작성 시, 소스 파일 하나에 다 때려 박아서 작성해도 되지만,

그렇게 하면 가독성도 떨어지고 지저분해진다.

 

함수명을 미리 선언한 헤더파일(LED.h)함수 원형들이 선언된 C file(LED.c), 메인 함수가 작성된 main.c로 구분하여 작성할 것이다.

 

<LED.h>

/*
 * LED.h
 *
 * Created: 2023-07-10 오후 2:39:45
 *  Author: USER
 */ 


#ifndef LED_H_ // 만약 얘가 선언되어있지 않다면, 
#define LED_H_ // 얘를 선언하겠다.

#include <avr/io.h>
#include <stdio.h>

#define LED_PORT	PORTD // 
#define LED_DDR		DDRD // DDR : 방향 결정

void ledInit(); // led를 이니셜하는 함수
void GPIO_output(uint8_t data); // General perform input/output
void ledLeftShift(uint8_t *data); // LED를 좌시프트, 얘는 포인터로 받음
void ledRightShift(uint8_t *data);// LED를 우시프트




#endif /* LED_H_ */ // if로 열었기 때문에 endif로 닫아준다.

 

<LED.c>

/*
 * LED.c
 *
 * Created: 2023-07-10 오후 2:40:12
 *  Author: USER
 */ 

#include "LED.h" // 함수의 원형이 여기 선언되어있으니, LED.h를 include해줘야 한다.

void ledInit() // led를 이니셜하는 함수
{
	LED_DDR = 0xff; // Art+G 누르면 LED_DDR가 선언된 곳으로 이동함.
}

void GPIO_output(uint8_t data) // general purpose input/output을 의미하는데,
                               // 여기서는 그냥 함수명(PORTD에 data 입력됨)
{
	LED_PORT = data; // 0x01
}

void ledLeftShift(uint8_t *data) // LED를 좌시프트, 얘는 포인터로 받음
							      // * : 참조연산자
{
	*data = (*data >> 7) | (*data << 1); // 비트마스킹
    // 0000 0001 | 0000 0000 -> 0000 0010 | 0000 0000 .... 1000 0000 | 0000 0000 
	GPIO_output(*data); // (*data) : 연산자 우선순위때문에 묶음
}

void ledRightShift(uint8_t *data) // LED를 우시프트
{
	*data = (*data << 7) | (*data >> 1);
    // 0000 0000 | 0000 0001 -> 0000 0000 | 0000 0010 .... 0000 0000 | 1000 0000
	GPIO_output(*data);
}

 

<main.c>

/*
 * LED_shift.c
 *
 * Created: 2023-07-10 오후 2:32:19
 * Author : USER
 */ 
#define F_CPU 16000000UL
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h> // delay함수써야되니까~
#include "LED.h" // 얘를 빼먹으면 지멋대로 움직임


int main(void)
{
   ledInit();
   
   uint8_t ledData = 0x01;  // unsigned int 8bit 0~255까지
   
    while (1) 
    {
		for(int i = 0 ; i < 7 ; i++)
		{
			ledLeftShift(&ledData); // 주소값으로 던져줘야 함수에서 포인터 변수로 받음
			_delay_ms(200); 
		}
		for (int i = 0 ; i < 7 ; i++)
		{
			ledRightShift(&ledData);
			_delay_ms(200);
		}
    }
}

 

#include " " 

   : 유저의 헤더파일을 include할 때 사용

#include < >

   : 시스템의 헤더파일을 include할 때 사용

 

<Result>

LED_shift

 

[LED_structure]

 

 

위 사진과 같이 port와 pin들이 연속적으로 할당되어 있으므로, 구조체를 사용하여 만들어줄 수 있다.

 

<LED.h>

#include <stdio.h>

#ifndef LED_H_
#define LED_H_

#define LED_COUNT	8 

typedef struct _led{ // 구조체 선언
	volatile uint8_t *port; // LED가 연결된 포트,
			// volatile : 레지스터 변수가 손을 대는 것을은 다 이걸 적어줘야 됨.
			// 컴파일러가 지 멋대로 최적화시켜버리면, 예상치못한 일이 벌어질 '수도' 있다.
			// 너 맘대로 최적화하지 마! 라는 의미
			// 따라서 레지스터 변수에는 반드시 volatile을 붙여줄 것.
	uint8_t pin; // LED가 연결된 핀 번호
	}LED;

void ledInit(LED *led);
void ledOn(LED *led); // LED에 대한 주소를 받음
void ledOff(LED *led); // 인자를 다 주소값으로 받음



#endif /* LED_H_ */

 

<LED.c>

/*
 * LED.c
 *
 * Created: 2023-07-10 오후 3:36:16
 *  Author: USER
 */ 

#include "LED.h"

void ledInit(LED *led) // led의 주소를 받을 거야
{
    // Bitmask 관련 설명 참조 //
	*(led->port -1) |=(1 << led->pin); // initial value = 0
        // led->pin = (*led).pin
	// = *(led->port -1) = *(led->pin-1) | (1<<led->pin));
	// 포트에 해당하는 핀을 출력으로 설정(0번 핀)
	// port-1을 하면 DDR로 간다
	// Datasheet상, 포트보다 주소값이 한 단계 아래인 것을 활용
	// *(led->port -1)를 이용해서 port에서 DDR로 접근
	// |=(1 << led->pin) OR연산을 통해 led->pin으로 지정된 포트를 출력으로 설정
	// 여기서 1은 0x01

}

void ledOn(LED *led)
{
	// 해당 핀을 high로 설정
	*(led->port) |= (1<<led->pin);
}

void ledOff(LED *led)
{
	// 해당 핀을 low로 설정
	*(led->port) &= ~(1<<led->pin);
}

 

위 사진에서 보이는 것처럼,

Data sheet 상 DDR의 주소값이 PORT보다 하나(4byte) 적은 것을 알 수 있다.

 

 

<Bitmask에 대한 설명 참조>

(하단 Link → C언어 문법 → 12.Bitmask)

 

https://rangvest.tistory.com/entry/Harman-%EC%84%B8%EB%AF%B8%EC%BD%98-%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8-11%EC%9D%BC%EC%B0%A8-%EB%B0%98%EB%8F%84%EC%B2%B4-%EA%B0%9C%EC%9A%94-C%EC%96%B8%EC%96%B4-%EA%B0%9C%EC%9A%94-%EB%B0%8F-%EB%AC%B8%EB%B2%95

 

[Harman 세미콘 아카데미] 11일차 - 반도체 개요, C언어 개요 및 문법

[반도체 개요] 반도체란? https://rangvest.tistory.com/entry/%EB%B0%98%EB%8F%84%EC%B2%B4%EB%9E%80-%EB%B0%98%EB%8F%84%EC%B2%B4%EC%9D%98-%EC%A0%95%EC%9D%98-%EB%B0%8F-%ED%8A%B9%EC%A7%95-%EC%86%8C%EC%9E%90-%EB%B0%98%EB%8F%84%EC%B2%B4-%EC%A2%85%EB%A5%98

rangvest.tistory.com

 

<main.c>

/*
 * LED_structure.c
 *
 * Created: 2023-07-10 오후 3:35:50
 * Author : USER
 */ 
#define  F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include "LED.h"

// LED 1개 컨트롤 //
//int main(void)
//{
	//LED led; // 데이터형, 변수 선언 // LED라는 데이터타입의 led
	//led.port = &PORTD;
	//led.pin = 0;
	//ledInit(&led);
	 //
    //while (1) 
    //{
		//ledOn(&led);
		//_delay_ms(200);
		//ledOff(&led);
		//_delay_ms(200);
    //}
//}

// LED 8개 컨트롤 //
int main()
{
	LED leds[LED_COUNT] = {
		{&PORTD, 0}, // (주소값, 핀번호)
		{&PORTD, 1},
		{&PORTD, 2},
		{&PORTD, 3},
		{&PORTD, 4},
		{&PORTD, 5},
		{&PORTD, 6},
		{&PORTD, 7},
	};
	// 초기화
	for (int i = 0 ; i < LED_COUNT ; i++)
	{
		ledInit(&leds[i]); // i번째 배열의 주소값을 넘겨 줌
	}
/*
 *		while (1)
 *	{
 *		for (int i=0 ; i<LED_COUNT ; i++)
 *		{
 *			ledOn(&leds[i]);
 *			_delay_ms(200);
 *			ledOff(&leds[i]);
 *			_delay_ms(200);
 *		}
 *		for (int i=6 ; i>0 ; i--)
 *		{
 *		    ledOn(&leds[i]);
 *			_delay_ms(200);
 *			ledOff(&leds[i]);
 *			_delay_ms(200);
 *		}
 *	}
*/
	while (1)
	{
		for (int i=0 ; i<LED_COUNT ; i++)
		{
			ledOn(&leds[i]);
			_delay_ms(200);
			//ledOff(&leds[i]);
			//_delay_ms(200);
		}
		for (int i=7 ; i>=0 ; i--) // 왜 i=6부터 시작하는가?
		{
			//ledOn(&leds[i]);
			//_delay_ms(200);
			ledOff(&leds[i]);
			_delay_ms(200);
		}
	}
}

 

<Result>

 

1. LED 1개 컨트롤

 

LED_Control_1EA

 

2. LED 8개 컨트롤

 

LEC_Control_8EA

 

3. LED 8개 컨트롤 활용

 

 

728x90
반응형