[ATmega128 Setting]
Microchip Studio 실행 전, ATmega128 board가 컴퓨터와 정상적으로 연결되었는지 체크해야한다.
포트(COM & LPT)에 뜨거나, 기타 항목에서 우클릭해서 해당 드라이버를 업데이트 해주면 제대로 인식된다.
LED
우리가 사용할 LED는 극성을 가지고 있으며, 해당하는 극과 반대로 연결한 경우 터질 수 있다.
대개 wire가 긴 쪽이 (+), 짧은 쪽이 (-) 이고, 허용전류는 약 250mA이다.
여담으로 Red, Green, White, Blue 순서로 개발되었고 RGB가 모두 개발되어 Full color 구현이 가능해졌다.
프로젝트 생성
솔루션과 프로젝트라는 개념이 있는데, "프로젝트 ⊂ 솔루션" 이라고 생각하면 된다.
[프로젝트 : 하나의 실행 파일(exe)을 만들어 내기 위해서 필요한 여러 개의 소스 파일과 헤더 파일 등을 하나로 묶어 놓은 것]
[솔루션 : 여러 개의 프로젝트가 모인 것]
전원이 인가되는 순간, Boot loader 영역을 가장 먼저 찾는데 해당 영역에 아무 것도 없기 때문에 main 함수가 실행되며
현재 main 함수의 while(1)에 의해 blank가 무한 반복되고 있다.
Build solution
: 소스코드 파일을 실행가능한 소프트웨어 산출물로 만드는 일련의 과정
Build는 Compile과 유사한 기능을 하고 컴파일은 빌드의 부분집합으로 볼 수 있다.
Programming
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
- DDRx 레지스터의 초기값이 0이므로, 최초 설정은 모두 입력
- 입력인 상황에서 PORTx 레지스터의 초기값은 0
- PINx 은 풀업 저항이 연결되지 않은 Z(High-Impedance)인 입력 핀
- PINx 에 인가되는 초기 전압은 예측할 수 없어, 초기값은 N/A
[LED_1EA blink]
#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 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_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)
<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개 컨트롤
2. LED 8개 컨트롤
3. LED 8개 컨트롤 활용
'# Semiconductor > - Semicon Academy' 카테고리의 다른 글
[Harman 세미콘 아카데미] 18일차 - Interrupt, Timer/Counter 이론 (0) | 2023.07.12 |
---|---|
[Harman 세미콘 아카데미] 17일차 - ATmege128 실습(LED shift, Tactile switch 활용), FSM의 개념 (0) | 2023.07.11 |
[Harman 세미콘 아카데미] 16일차 - ATmege128의 개요 (0) | 2023.07.10 |
[Harman 세미콘 아카데미] 15일차 - C언어(함수~구조체 및 표준함수) (0) | 2023.07.07 |
[Harman 세미콘 아카데미] 14일차 - 강의 일정 Summary(ARM Architecture 이해 및 RTOS 활용), C언어 review (0) | 2023.07.06 |