[2024.03.15.금] 인천인력개발원 하만 세미콘 아카데미
임베디드 시스템을 위한 SW 구조설계
ATmega128
수업 시작에 앞서 ATmega128 기본 모듈을 지급받았다.
다음주부터는 이 모듈을 이용하여 실습을 진행할 예정인데, 그에 앞서 ATmega128 모듈에 대해 알아보고자 한다.

- AVR 마이크로컨트롤러
- 고성능, 저전력 AVR 8비트 마이크로컨트롤러 사용
- 산업 제어, 스마트 홈, 자동화 시스템 등에 활용
- 주요 특징
- CPU 성능: 최대 16MIPS(Million Instructions Per Second)의 처리속도와 16MHz의 클록 속도
- 메모리: 128kb의 플래시 메모리를 사용하여 대용량의 코드 저장 가능. 또한 4kb의 SRAM과 4kb의 EEPROM을 내장하여 데이터 저장과 접근에 용이
- 입출력(I/O) 포트: 다수의 디지털 입출력 포트를 제공하며 일부는 PWM출력, 외부 인터럽트 지원. 다양한 센서, 모터 등의 주변 장치 제어 가능
- 통신 인터페이스: UART, SPI, I2C 등 여러 종류의 시리얼 통신 지원. 이를 통해 다른 마이크로컨트롤러, 컴퓨터, 다양한 통신 모듈과의 데이터 교환 가능
- 전원: 저전력 설계->배터리로 구동하는 시스템에 적합
[ 용어 정리 ]
- 마이크로컨트롤러: 중앙처리장치(CPU), 메모리(RAM, ROM), 입출력 I/O포트 등이 하나의 칩 위에 통합된 소형 컴퓨터로, IC내에 구현되어 제어장치나 임베디드 시스템에 널리 사용
- MIPS: 프로세서의 성능을 나타내는 단위. 매초 처리할 수 있는 백만개의 명령어 수를 의미
- 클록 속도: 마이크로 컨트롤러, 디지털 시스템의 프로세서가 명령을 처리하는 속도. 1MHz는 1초동안 1백만 사이클을 의미
- 플래시 메모리: 전원이 끊겨도 데이터를 보존하는 반도체 메모리
- 비휘발성: 전원 공급이 중단되어도 데이터 유지
- 재프로그램 가능: 데이터를 저장하기 전에 기존의 데이터를 지우고 새로운 데이터를 여러번 쓰기 가능
- 빠른 접근 속도: HDD보다 데이터 접근 속도가 빠르며 SSD나 USB메모리와 같은 저장 장치에서 유용
- SRAM(Static Random Access Memory, 정적 램): 데이터 저장에 사용되는 반도체 메모리의 한 종류
- 빠른 접근 속도: DRAM에 비해 접근 속도가 빨라 CPU의 캐시 메모리 등 고속으로 데이터에 접근할 때 사용
- 저전력 소모: 데디터를 유지하기 위한 새로고침이 불필요하므로 DRAM에 비해 저전력 소모
- 단순 구조: 한 비트를 저장하기 위해 4~6개의 트랜지스터를 사용하여 구조적으로 단순하며 이를 통해 고속 데이터 접근이 가능
- EEPROM(Electrically Erasable Programmable Read-Only Memory)
- 비휘발성: 전원 공급이 중단되어도 데이터 유지
- 재프로그래밍 가능: 데이터를 전기적으로 지우고 다시 쓸 수 있어 데이터 수정이 용이
- 단위별 데이터 조작: 데이터를 바이트 단위로 읽고쓰기가 가능하며, 개별적인 데이터 수정이 가능
- PWM(Pulse Width Modulation 출력, 펄스 폭 변조): 전기 신호 폭의 변조를 통해 전력 양을 제어하는 방식
- 일정한 주파수를 갖는 사각파 형태의 펄스로 구성. 각 펄스의 "Duty Cycle"이라 불리는 '켜짐'상태의 지속 시간을 조절하여 출력 전력 제어(Duty Cycle은 펄스의 전체 주기 대비 켜진 상태가 차지하는 비율료 표현)
- 고효율: 전력 손실을 최소화하는 방식으로 전력을 제어하여 에너지 효율이 매우 높음
- 정밀 제어: 모터의 속도, LED의 밝기 등을 정밀하게 제어 가능
- 유연성: 주파수와 Duty Cycle을 조절하여 다양한 출력 요구 사항 만족 가능
<함수 선언 및 메인함수 형성>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <math.h>
//함수 선언 모음
void work01();
void work02(); //ASCII 코드표 출력
void work03(); //swap 함수 실습
void work04(); //printf 실습
void work05(); //최대공약수 출력
void work06(); //수학 함수 사용
void work07();
//사용자 정의 함수
void swap(int *a, int *b);
int Factors(int a);
int cont(void); //m을 누르면 cont값을 1을 반환하여 메뉴로 돌아오고, 그 외의 값을 누르면 프로그램 재실행
int main()
{
int n;
void* pf[] = { work01, work02, work03, work04, work05, work06, work07 };//type이 정해지지 않은 포인터 배열, 내부의 배열요소는 함수의 주소
void (*pfunc)(); //return값이 없고 인자도 필요 없는 포인터 함수 선언
while (1) {
printf("\n<실행 기능 선택>\n");
printf("1. scanf 함수 실습\n ");
printf("2. ASCII 코드표 출력\n");
printf("3. swap 함수 실습\n");
printf("4. 입력한 숫자만큼 * 출력)\n");
printf("5. 문자를 입력받아 개수를 줄여가며 출력)\n");
printf("6. 최대공약수 출력\n");
printf("7. 수학 함수 사용\n");
printf("0. 종료\n");
printf("=====================================\n > ");
scanf("%d", &n);
printf("\n");
if (n == 0) return; //0 입력 시 함수 종료
pfunc = pf[n - 1]; //배열은 0부터 시작하므로 1을 빼줌
pfunc(); //위에서 지정한 함수를 호출
}
}
<가장 아랫부분에 반복 실행 여부 확인하는 cont함수 본체 정의>
int cont(void) {
printf("\n\n[실행이 끝났습니다. 메뉴로 돌아가려면 \"m\", 계속하려면 다른 키를 입력하세요.]\n>");
char select = _getch();
printf("select = %c\n", select);
if (select == 109) {
printf("\n[메뉴로 돌아갑니다.]\n\n"); return 1;
}
else {
printf("\n[다시 실행합니다.]\n\n"); return 0;
}
}
과제 1. scanf함수 실습
- 첫 번째 줄: scanf함수를 이용하여 문자열을 입력받고 한 문자씩 띄어서 출력(char 배열 이용)
- 두 번째 줄: 첫 번째 줄에서 대소문자가 혼합된 경우 문자열을 모두 대문자로 변환하여 출력
- 코드 작성
void work01() { while (1) { char string[100]; //입력받을 문자열 메모리공간 char blank[200]; //띄어쓰기 수행할 메모리공간 printf("\n 문자열을 입력하세요.\n>"); scanf("%s", &string); //문자열을 입력받아 string에 저장 printf("\n입력된 문자열은 다음과 같습니다: %s\n", string); int length; for (length = 0; length < sizeof(string); length++) //입력한 문자열의 길이 확인 { if (*(string + length) == NULL) break; } printf("\n문자열의 한 칸씩 띄워 출력합니다.\n"); for (int i = 0; i < length * 2; i++) { //띄어쓰기 포함 시 원래 문자열 길이의 두 배의 공간이 필요하므로 그에 해당하는 수만큼 반복실행 *(blank + i) = 32; //모든 요소에 빈 칸(아스키코드 32) 입력 if (i % 2 == 0) //짝수번째에는 글자 입력 { *(blank + i) = *(string + (i / 2)); } *(blank + i + 1) = NULL; //마지막 종료 반복 시 끝에 null을 붙여 끝점 지정 } printf(">%s", blank); printf("\n\n모든 문자를 대문자로 변환합니다.\n>"); for (int j = 0; j < length * 2; j += 2) { if (*(blank + j) > 96 && *(blank + j) < 123) { //입력된 문자가 소문자이면 *(blank + j) = *(blank + j) - 32; } } printf("%s", blank); if (cont() == 1) break; } }
- 실행 화면
과제 2. ASCII 코드표 출력 - 미완성
- 다음과 같은 형식으로 출력(가로 3열)
- [10진수] [16진수] 문자


- 코드 작성
void work02() { while (1) { printf(" Dec Hx Char\n"); for (int i = 1; i < 128; i++) { printf("[%4d] [%4X] %4c ", i, i, i); if ((i) % 3 == 0) printf("\n"); } if (cont() == 1) break; } }
- 실행 화면
과제 3. swap 함수 실습
- 두 개의 수를 입력받아 각각의 변수에 저장하여 출력하고, 두 변수의 값을 교환하여 다시 저장하고 출력
- 코드작성
void work03() { while (1) { char buf[100]; int* pbuf; int num1; int num2; printf("[두 숫자를 입력받아 변수에 저장하고, 이를 교환하여 출력합니다.]\n"); printf("num1에 저장할 숫자를 입력하세요.\n>"); scanf("%d", &num1); //printf("num1의 주소값은 %08x\n", &num1); printf("\nnum2에 저장할 숫자를 입력하세요.\n>"); scanf("%d", &num2); printf("\nnum1: %d, num2: %d\n\n", num1, num2); printf("두 변수의 값을 교환하여 출력합니다.\n\n"); //printf("num2의 주소값은 %08x \n", &num2); int* a = &num1; int* b = &num2; //printf("a의 주소값은 %08x\n", a); //printf("b의 주소값은 %08x\n", b); swap(a, b); //printf("a의 주소값은 %08x\n", a); //printf("b의 주소값은 %08x\n", b); printf("교환된 값 num1: %d, num2: %d\n", *a, *b); if (cont() == 1) break; } } //사용자 정의 함수 void swap(int* a, int* b) { int* pBuf; int temp[500]; //printf("a 값 보기: %x\n", a); pBuf = temp; //printf("\n%x가 pBuf의 주소, %x가 a의 주소, %x가 b의 주소!!!\n\n", &pBuf, &a, &b); *pBuf = *b; //printf("pBuf의 주소 %08x, b의 주소 %08x, a의 주소 %08x\n", pBuf, b, a); //printf("pBuf의 값 %d, b의 값 %d\n", *pBuf, *b); *b = *a; //printf("pBuf의 주소 %08x, b의 주소 %08x, a의 주소 %08x\n", pBuf, b, a); *a = *pBuf; //printf("pBuf의 주소 %08x, b의 주소 %08x, a의 주소 %08x\n", pBuf, b, a); }
- 실행 화면
과제 4. 홀수를 입력받아 가장 아랫줄이 입력받은 홀수개의 *로 이루어진 피라미드 출력
- 홀수를 입력받아 가장 아랫줄이 입력받은 홀수개의 *로 이루어진 피라미드 출력
- 예) 숫자(홀수)를 입력하시오 > 5
*
***
*****
- 예) 숫자(홀수)를 입력하시오 > 5
- 코드 작성
void work04() { while (1) { int n; printf("숫자(홀수)를 입력하시오 >"); scanf("%d", &n ); printf("\n"); for (int repeat = 0; repeat < (n - 1) / 2 + 1; repeat++) { for (int i = (n - 1) / 2 - repeat; i > 0; i--) { printf(" "); } for (int j = 1; j < (repeat+1) * 2; j++) { printf("*"); } printf("\n"); } if (cont() == 1) break; } }
- 실행 화면
과제 5. 문자를 입력받아 다음의 형태로 출력
- 예) 문자를 입력하시오 > E
AAAAA
BBBB
CCC
DD
E - 코드작성
void work05() { while (1) { int character = 0; printf("문자를 입력하시오 >"); scanf(" %c", &character); int A = 65; for (int i = 64; i < character; i++) { for (int blank = 65; blank < A; blank++) { printf(" "); } for (int wr = A - 1; wr < character; wr++) { printf("%c", A); } printf("\n"); A++; } if (cont() == 1) break; } }
- 실행 화면
과제 6. 최대공약수 출력
- 두 수를 입력받아 각 수의 약수와 최대공약수를 출력
- 예) > 6 15
6: 1, 2, 3, 6
15: 1, 3, 5, 15
6과 15의 최대공약수는 3입니다.
- 예) > 6 15
- 코드 작성(미완성)
void work06() { while (1) { int num1; int num2; printf("두 개의 숫자를 한 칸 띄어서 입력하세요.\n>"); scanf("%d %d", &num1, &num2); //num1의 공약수 출력 Factors(num1); Factors(num2); //최대공약수 출력 printf("%d와 %d의 최대공약수는 %d입니다.\n", num1, num2, gcd(num1, num2)); if (cont() == 1) break; } } int Factors(int a) { int count = 0; printf("%d의 약수: ", a); for (int i = 1; i < a; i++) { if (a % i == 0) { printf("%d, ", i); count++; } } printf("%d입니다.\n", a); return count + 1; } int gcd(num1, num2) { int gcd; for (int i = 1; i <= num1 && i <= num2; ++i) { if (num1 % i == 0 && num2 % i == 0) gcd = i; } return gcd; }
- 실행 화면
과제 7. 수학 함수 사용-미완성
- sin, cos, sqrt 함수 이용 (<math.h> 헤더파일 포함)
- 공백을 이용하여 출력 위치를 변경하여 함수 곡선을 출력
- sin, cos, tan곡선 출력
- 원 출력
- 원 내부에 sin곡선을 그려 태극마크 출력
1. pi값을 사용하기 위해 위줄에 PI 선언
#define PI 3.141592
과제 8. 헤더파일 생성
1. 헤더파일 생성을 위해 솔루션 탐색기의 헤더파일 우클릭 -> 추가 -> 새 항목 -> 헤더파일 선택



2. 생성한 headerfile.h에 함수 선언
#include <stdio.h>
#include <conio.h>
#include <string.h>
int Factors(int a);
int gcd(int num1, int num2);
int cont(void);
void swap(int* a, int* b);
3. 소스파일에 headerfile.c 생성하고 headerfile에 들어갈 함수 내용 입력
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "headerfile.h"
int Factors(int a) {
int count = 0;
printf("%d의 약수: ", a);
for (int i = 1; i < a; i++)
{
if (a % i == 0) {
printf("%d, ", i);
count++;
}
}
printf("%d입니다.\n", a);
return count + 1;
}
int gcd(int num1, int num2) {
int gcd;
for (int i = 1; i <= num1 && i <= num2; ++i)
{
if (num1 % i == 0 && num2 % i == 0)
gcd = i;
}
return gcd;
}
int cont(void) {
printf("\n\n[실행이 끝났습니다. 메뉴로 돌아가려면 \"m\", 계속하려면 다른 키를 입력하세요.]\n>");
char select = _getch();
printf("select = %c\n", select);
if (select == 109) {
printf("\n[메뉴로 돌아갑니다.]\n\n"); return 1;
}
else {
printf("\n[다시 실행합니다.]\n\n"); return 0;
}
}
void swap(int* a, int* b) {
int* pBuf;
int temp[500];
//printf("a 값 보기: %x\n", a);
pBuf = temp;
//printf("\n%x가 pBuf의 주소, %x가 a의 주소, %x가 b의 주소!!!\n\n", &pBuf, &a, &b);
*pBuf = *b;
//printf("pBuf의 주소 %08x, b의 주소 %08x, a의 주소 %08x\n", pBuf, b, a);
//printf("pBuf의 값 %d, b의 값 %d\n", *pBuf, *b);
*b = *a;
//printf("pBuf의 주소 %08x, b의 주소 %08x, a의 주소 %08x\n", pBuf, b, a);
*a = *pBuf;
//printf("pBuf의 주소 %08x, b의 주소 %08x, a의 주소 %08x\n", pBuf, b, a);
}
4. 형성한 헤더파일을 과제 파일에 포함(자체 제작 헤더파일은 #include "헤더파일.h"로 포함)
#include "headerfile.h"
5. 7일차 과제.c 파일 내부의 사용자 정의 함수를 모두 주석처리 후 실행 확인
