sudo apt-get install manpages-dev glibc-doc
맨페이지 설치
http://linux.die.net/man/
영문 manpage 사이트
FND count 99 with DK128
main.c
#include "port.h"
#include "delay_m.h"
int main(void)
{
unsigned char fnd1 = 0x00;
unsigned char fnd2 = 0x00;
int flag = 1;
int check = 0;
DDRF = 0xff; // 포트 초기화
PORTF = 0x00;
DDRC = 0x01;
PORTC = 0x00;
while(1)
{
if(PINC & 0x02) // 출력 신호를 받았을 때
{
flag = 1; // 출력 flag set
}
if(flag)
{
PORTF = fnd2 | fnd1;
fnd1++;
if(fnd1 >= 0x0A) // 출력이 끝난 시점
{
fnd1 = 0x00;
PORTC = 0x01; // 상대편 FND 출력 시작
flag = 0; // 출력 flag off
check = 1; // C PORT 초기화를 위한 check
if(fnd2 >= 0x80) // 10자리 처리
{
if(fnd2 == 0x80) // 10자리가 8일 때
{
fnd2 = 0x00;
}
else // 10자리가 9일 때
{
fnd2 = 0x10;
}
}
else // 10자리를 2씩 증가
{
fnd2 += 0x20;
}
}
}
else // 출력 안할 때
{
PORTF = 0x00;
}
delay(600);
if(check) // C PORT 초기화
{
PORTC = 0x00;
check = 0;
}
}
return 0;
}
// DK128 두 대를 연결해서 99까지 카운트 하는 코드이다. 일단 A DK128에서 0에서 9까지 카운트하고, 그 다음 B DK128에서 10부터 19까지 카운트 하는 방식으로 99까지 카운트한다.
// C PORT에 핀 2개를 이용해서 서로 카운트 동기화를 한다.
fopen(), fclose(), fprinf(), fscanf()
main.c
#include <stdio.h>
int main()
{
int i;
fprintf(stdout, "Enter the number: ");
fscanf(stdin, "%d", &i);
fprintf(stdout, "i = %d\n", i);
return 0;
}
// fprintf(), fscanf() 함수들을 사용법을 알아보았다. 이 함수들은 기본적으로 입출력 스트림을 지정 할 수 있다. 위 코드를 보면 fprintf는 stdout(모니터) 표준출력으로 출력하고 있고, fscanf는 stdin(키보드) 표준입력에서 입력받는다. 즉, 각각 printf, scanf와 같은 동작을 한다고 보면 된다.
main1.c
#include <stdio.h>
int main()
{
fprintf(stdout, "one\n");
fprintf(stderr, "two\n");
fprintf(stdout, "three\n");
return 0;
}
// stdout은 표준출력, stderr은 표준에러로 둘 다 모니터로 출력된다. 기본적으로 둘 다 모니터로 출력은 되지만 엄밀히 따지면 서로 다른 스트림이다.
// 리다이렉션(>)명령을 통해 이를 확인 할 수 있었다.
p5-9.c
#include <stdio.h>
int main()
{
int score;
char name[10];
FILE *fpin;
FILE *fpout;
fpin = fopen("d0327.in", "r");
fpout = fopen("d0327.out", "w");
while(!(feof(fpin)))
{
fscanf(fpin, "%s %d", name, &score);
fprintf(stdout, "%s %d\n", name, score);
fprintf(fpout, "%s\t%d\n", name, score);
}
fclose(fpin);
fclose(fpout);
return 0;
}
// fopen(), fclose() 함수를 이용하여 직접 파일에서 데이터를 읽어오고 데이터를 써보는 코드이다. 기본적으로 파일 입출력을 위해서 FILE 구조체 포인터가 필요하며, 파일을 이 구조체에 연결시켜주는 함수가 fopen()이고 파일을 다 쓴 후 연결을 끈어주는 함수가 fclose()이다. 함수 원형들은 http://www.winapi.co.kr 사이트에 가보면 잘 알 수 있다.
// 이 프로그램은 d0327.in 파일에서 데이터를 읽어와서 모니터와 d0327.out으로 출력시키는 프로그램이다.
p5-10.c
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
char ch;
int count;
if((fp = fopen("d0328.dat", "r")) == NULL)
{
printf("File open error!!\n");
exit(1);
}
count = fscanf(fp, "%c", &ch);
while(count != EOF)
{
putchar(ch);
count = fscanf(fp, "%c", &ch);
}
fclose(fp);
return 0;
}
// 이 프로그램은 d0328.dat에서 데이터를 문자 단위로 읽어와서 화면에 출력해주는 프로그램이다.
더블클릭
main.c
#include "port.h"
#define LED_ON(led) PORTF &= (~(0x01 << led))
#define LED_OFF(led) PORTF |= (0x01 << led)
int main(void)
{
unsigned int us = 0;
unsigned int ms = 0;
char click_count = 0; // 클릭 횟수 카운트
char start_flag = 0; // 더블 클릭 시간감지 플래그
char action = 0; // 클릭에 대한 액션을 결정
DDRF = 0xff; // 포트 최기화
PORTF = 0xff;
DDRC = 0x00;
PORTC = 0x00;
while(1)
{
if(ms >= 500) // 시간 카운트 종료
{
start_flag = 0;
ms = 0;
action = click_count;
click_count = 0;
}
else if((PINC & 0x01) == 0) // 버튼을 눌렀을 때
{
if(click_count == 0 || (click_count % 2) == 0)
{
start_flag = 1;
click_count++; // 버튼을 눌렀을 때 카운트
}
}
else if((PINC & 0x01) && start_flag) // 버튼 땠을 때
{
if((click_count % 2) == 1)
{
click_count++; // 버튼을 땠을 때 카운트
}
}
us++;
if(us >= 1000)
{
if(start_flag) // 시간 카운트 시작
{
ms++;
}
us = 0;
}
// 클릭에 대한 동작 코드
if(action)
{
LED_OFF(0);
LED_OFF(1);
LED_OFF(2);
switch(action)
{
case 1:
case 2:
LED_ON(0); // 원 클릭 했을 때
break;
case 3:
case 4:
LED_ON(1); // 더블 클릭 했을 때
break;
case 5:
case 6:
LED_ON(2); // 트리플 클릭 했을 때
break;
defalut :
break;
}
action = 0;
}
}
return 0;
}
// DK128에서 스위치 1번을 한번 클릭 했을 때 첫 번째 LED를 ON시키고, 더블 클릭 했을 때 두 번째 LED를 ON, 트리플 클릭 했을 때 세 번째 LED를 ON 시키는 코드이다.
// 스위치를 눌렀을 때 카운트를 하나씩 올리고 땔 때 다시 카운트를 올린다. 즉 카운트 값이 1이나2이면 한번 클릭을 한것이다.
FND 제어
delay_m.h
void delay(unsigned int ms);
void delay(unsigned int ms)
{
unsigned int u = 0;
unsigned int m = 0;
while(1)
{
u++;
if(u >= 1000)
{
m++;
u=0;
}
if(m >= ms)
{
break;
}
}
}
main.c
#include "port.h"
#include "delay_m.h"
int main(void)
{
unsigned char fnd1 = 0x00; // 1자리
unsigned char fnd2 = 0x00; // 10자리
DDRF = 0xff; // 핀 초기화
PORTF = 0x00;
DDRC = 0x00;
PORTC = 0x00;
while(1)
{
if((PINC & 0x01) == 0) // 스위치를 눌렀을 때
{
PORTF = fnd2 | fnd1;
}
else // 스위치가 눌러지지 않았을 때
{
PORTF = fnd2 | fnd1;
fnd1++;
if(fnd1 >= 0x0A) // 1자리가 9가 되면
{
fnd1 = 0x00;
if(fnd2 == 0x90) // 10자리가 9가 되면
{
fnd2 = 0x00;
}
else
{
fnd2 += 0x10;
}
}
}
delay(600);
}
return 0;
}
// 7-segment를 1부터 99까지 카운트하는 코드이다. 스위치를 누르면 카운트를 중지한다.
// 각 자리 수를 변수 2개를 OR연산해서 구현하였다.
포인터 연산
P5-4.c
#include <stdio.h>
int main()
{
int inum;
int *ip;
short *sp;
unsigned char *c;
int i;
inum = 0x0F5A0B43;
ip = &inum;
c = (unsigned char *)ip;
printf("inum: %x *ip: %p \n", inum, ip);
sp = (short *)ip;
printf("sp: %p *sp: %x \n", sp, *sp);
sp++; // short형이라 증감연산시 2씩 증가 한다
printf("sp: %p *sp: %x \n", sp, *sp);
for(i = 0 ; i < 4 ; i++) // Little Endian을 확인하는 코드
{
printf("addr:%p *c: %02x \n", c, *c);
c++; // char형이라 증감연산시 1씩 증가 한다
}
return 0;
}
// 포인터 연산은 덧셈과 뺄셈만 허용된다. 덧셈과 뺄셈 연산 시 가리키는 자료형의 크기만큼 변한다.
// 인텔 x86 cpu들은 Little Endian 방식을 사용한다. 이를 확인하기 위해서 char형 포인터를 이용하여 1바이트씩 출력하여 확인할 수 있었다.
Debugger
Windows Visual Studio
Debugging 이란 프로그램의 논리적 오류를 찾아내서 수정하는 일이며, 이 작업을 도와 주는 툴을 Debugger라고 한다. 즉 프로그램의 논리적 오류를 검출하는데 사용하는 프로그램이 Debugger이다. 윈도우 환경에서 우리가 설치한 Visual Studio 6에는 Debugger가 포함되어 있는데, 간단한 사용법을 알아 보았다.
1. 일단 Debugging 해볼 간단한 코드를 작성해 보았다.
Linux Debugger GDB
Linux에도 강력한 Debugger GDB가 있다. GDB는 텍스트 기반 Debugger로 많은 명령어들이 있다
l(list) |
소스 보기 |
b |
브레이크 포인트 |
info breakpoints |
브레이크 포인트 정보(info b) |
cl |
브레이크 포인트 삭제 |
r(run) |
프로그램 수행 |
s(step) |
프로그램을 한 행씩 실행(함수 호출 시 내부로 들어감) |
n(next) |
프로그램을 한 행씩 실행(함수 호출 시 내부로 들어가지 않음) |
c(continue) |
브레이크 포인트를 만날 때까지 실행 |
u |
현재 루프를 빠져 나감 |
finsh |
현재 함수를 수행하고 빠져나감 |
return |
현재 함수를 수행하지 않고 빠져나감 |
si |
인스트럭션 단위로 s동작 실행 |
ni |
인스트럭션 단위로 n동작 실행 |
watch |
변수 값이 변할 때마다 알려줌 |
info locals |
현재 상태에서 지역 변수들을 보여줌 |
info variable |
현재 상태에서 전역 변수들을 보여줌 |
p(print) |
해당 변수나 함수 값을 보여준다(밑에 표 참조) |
p 변수=설정값 |
변수 값 설정 |
display |
변수 값을 계속 보여줌(밑에 표 참조) |
info frame |
현재 함수의 스택 프레임을 보여줌 |
x |
메모리 상태 검사(x/[범위][출력 형식] [범위의 단위]) |
disas |
어셈블리 코드 보기 |
call |
함수를 임의대로 호출 할 수 있다 |
signal |
프로세스에게 시그널을 보낸다 |
set |
메모리 특정 영역에 값을 설정(set {타입}[주소] = [값]) |
p [변수명] |
변수 값을 출력 |
p [함수명] |
함수의 주소를 출력 |
p/[출력 형식] [변수명] |
변수 값을 출력 형식으로 출력 |
p '[파일명]'::[변수명] |
파일명에 있는 전역 변수의 값을 출력 |
p [함수명]::[변수명] |
함수에 있는 변수 값을 출력 |
p [변수명]@[배열 크기] |
변수의 내용을 변수 배열의 크기 형태로 출력 |
display [변수명] |
변수 값을 매번 화면에 디스플레이 한다 |
display/[출력 형식] [변수명] |
변수 값을 출력 형식으로 디스플레이 한다 |
undisplay [디스플레이 번호] |
디스플레이 설정을 없앤다 |
disable display [디스플레이 번호] |
디스플레이를 일시 중단한다 |
enable display [디스플레이 번호] |
디스플레이를 다시 활성화한다 |
t |
2진수로 출력 |
o |
8진수로 출력 |
d |
부호가 있는 10진수로 출력(int) |
u |
부호가 없는 10진수로 출력(unsigned int) |
x |
16진수로 출력 |
c |
최소 1바이트 값을 문자형으로 출력 |
f |
부동 소수점 값 형식으로 출력 |
a |
가장 가까운 심볼의 오프셋을 출력 |
기본적인 명령어는 다음과 같다. 위와 같은 코드를 리눅스에서 GDB를 이용하여 Debugging 해보았다.
1. GDB에서 Debugging을 하기 위해서는 일단 컴파일 할 때 옵션 -g를 주어야 한다. 이 옵션은 Debugging정보를 넣고, 모든 최적화 flag를 turn off 한다.
2. "$ gdb 실행파일명" 으로 GDB를 실행한다.
LED ON/OFF 일반화
main.c
#include
"port.h"
#define LED_ON(led) PORTF &= (~(0x01 << led)) // led번째LED ON
#define LED_OFF(led) PORTF |= (0x01 << led) // led번째LED OFF
int main(void)
{
unsigned
int us = 0;
unsigned
int ms = 0;
unsigned
char led = 0x00;
DDRF = 0xff;
PORTF = 0x00;
while(1)
{
us++;
if(us >= 1000)
{
ms++;
us = 0;
}
if(ms >= 500)
{
if(led == 8)
{
led = 0;
PORTF = 0x00;
}
LED_OFF(led);
// LED_ON(led);
led++;
ms = 0;
}
}
return 0;
}
#define LED_ON(led) PORTF &= (~(0x01 << led))
LED를 ON시키려면 Low 신호를 주면 된다. 어떤 수든 상관없이 Low를 만들려면 0을 AND연산하면 되므로 다음과 같이 원하는 비트만큼 0x01를 <<연산한 다음에 보수를 취해서 AND연산을 하면 ON시킬 수 있다.
#define LED_OFF(led) PORTF |= (0x01 << led)
LED를 OFF 시키려면 High 신호를 주면 된다. 어떤 수든 상관없이 High를 만들려면 1을 OR연산하면 되므로 다음과 같이 원하는 비트만큼 0x01를 <<연산한 다음 OR연산을 하면 OFF 시킬 수 있다.
소수를 구하는 프로그램
main.c
#include
<stdio.h>
int main()
{
int i, j;
printf("2\n");
for(i = 3 ; i <= 100 ; i += 2)
{
for(j = 3 ; j <= i ; j += 2)
{
if(i % j == 0)
{
break;
}
}
if(j == i)
{
printf("%d\n", i);
}
}
return 0;
}
DK128간 통신으로 LED 켜기
main.c
#include
"port.h"
int main(void)
{
unsigned
char led;
DDRE = 0x00;
PORTE = 0x00;
DDRF = 0xFF;
PORTF = 0xFF;
DDRC = 0x00;
PORTC = 0x00;
while(1)
{
led = PINC;
led = 0xFF - led;
switch(led)
{
case 0x01 :
DDRF = 0x01;
break;
case 0x02 :
DDRF = 0x03;
break;
case 0x04 :
DDRF = 0x07;
break;
case 0x08 :
DDRF = 0x0F;
break;
case 0x10 :
DDRF = 0x1F;
break;
case 0x20 :
DDRF = 0x3F;
break;
case 0x40 :
DDRF = 0x7F;
break;
case 0x80 :
DDRF = 0xFF;
break;
default :
DDRF = 0x00;
}
if(PINE & 0x01)
{
DDRF = 0xFF;
}
else
{
DDRF = 0x00;
}
}
return 0;
}
// DK128보드 2개를 이용해서 스위치를 누를 때마다 각각 LED ON 개수를 늘려가는 코드이다. 예전 실습 시간에 작성 했던 코드에서 조금만 수정해서 작성 할 수 있었다. 일단 한쪽에선 스위치가 눌려 졌다는 신호를 받아서 LED 8개 모두를 ON 시키고, 다른 한쪽에서는 키가 눌려 졌을 때 눌러 졌다는 신호를 다른쪽 DK128한테 보내기만 하면 구현이 가능했다.
// 이 코드는 신호를 받아서 LED 모두를 ON 시키는 코드이다.
DK128간 통신으로 LED 켜기
DK128간 서로 통신하여 한 번씩 LED를 ON 시키는 코드를 작성해 보았다. 즉 A DK128에서 8개의 LED를 차례되로 ON 시킨 후 B DK128에 신호를 준다. 그러면 B DK128이 8개의 LED를 차례되로 ON 시킨 후 다시 A DK128에 신호를 주면 A DK128이 다시 LED ON 동작을 반복하는 코드이다.
main.c
#include
"port.h" // 각핀들에대한정의가선언되어있다
int main()
{
int flag = 1; //입출력여부확인flag
unsigned
int us = 0; //시간딜레이를위한변수
unsigned
int ms = 0;
unsigned
char led = 0x01;
unsigned
char check = 0;
DDRF = 0xff;
PORTF = 0xff - led; //portf 초기값led 하나만켜지는상태
DDRC = 0x01; //0번은출력1번은입력으로사용
PORTC = 0x00;
while(1)
{
us++;
if(us >= 1000)
{
ms++;
us = 0;
}
if(PINC & 0x02) // 1번핀으로입력이들어오면
{
flag = 1;
if(led == 0x00)
{
led = 0x01;
PORTF = 0xff - led;
}
}
if(ms >= 500)
{
if(check) // 출력을하고있을때
{
PORTC = 0x00; // C핀출력을리셋
check = 0;
}
if(flag)
{
led = led << 1;
PORTF = 0xff - led;
if(led == 0x00) // led이동이끝난후
{
flag = 0;
PORTC = 0x01; // 0번핀으로High 신호출력
check = 1;
}
}
ms = 0;
}
}
return 0;
}
// F핀은 LED 출력으로 사용하고, C핀은 DK128간 통신을 하는데 사용하고 있다. A DK128은 C0핀을 입력으로 C1핀을 출력으로 B DK128은 C1핀을 입력으로 C0핀을 출력으로 사용한다.
// 코드를 보면 입력을 받으면 LED 출력 여부를 판별하는 flag를 1로 셋팅해주는 코드가 있는데 이 코드가 us 카운트 밑에 있다. 이는 두 DK128간 통신 동기화를 위해서 us 카운트하는 코드 존재한다.
C언어 for 문
for문은 반복문으로 while문과 비교해서 동작에는 차이가 없다. 다만 초기식, 조건식, 증감식 양식만 조금 다르다고 생각하면 된다
for(초기식 ; 조건식 ; 증감식)
{
명령문
}
p3-21.c
#include
<stdio.h>
int main()
{
int i;
// i = 1;
// while(i <= 5)
for(i = 1; i <= 5 ; i++)
{
printf("%d\t", i);
// i++;
}
putchar('\n');
return 0;
}
// 1부터 5까지의 정수를 화면에 출력하는 프로그램이다. for문으로 작성된 이 코드는 while문으로 변경이 가능하다 위의 코드에서 현재 주석처리 된 부분을 해제하고 for문 한 줄만 주석처리 하면 완전한 동일한 동작을 하는 코드가 된다.
p3-23.c
#include
<stdio.h>
#include
<conio.h>
int main()
{
char ch;
int i;
printf("Please enter any character: ");
for(i = 0 ; (ch = getche()) != '$' ; i++)
{
printf("\nYou typed : %c\n", ch);
printf("Please enter any character: ");
}
putchar('\n');
return 0;
}
main.c
#include
<stdio.h>
int main()
{
int i, j;
for(j = 1 ; 10 > j ; j++)
{
for(i = 3 ; 9 > i ; i++)
{
printf("%d * %d = %d\t", i, j, i * j);
}
putchar('\n');
}
return 0;
}
전자회로실험
스위치를 누름에 따라 LED ON 개수를 하나씩 늘려가는 코드
main.c
#include
"port.h"
int main(void)
{
unsigned
char led;
DDRF = 0x00; //F핀 초기화
PORTF = 0x00;
DDRC = 0x00; //C핀 초기화
PORTC = 0x00;
while(1)
{
led = PINC; // C핀으로 입력된 값을 보수를 취하기 위한 코드
led = 0xFF - led;
switch(led)
{
case 0x01 : //첫번째 스위치가 눌려지면
DDRF = 0x01;
break;
case 0x02 : //두번째 스위치가 눌려지면
DDRF = 0x03;
break;
case 0x04 : //세번째 스위치가 눌려지면
DDRF = 0x07;
break;
case 0x08 : //네번째 스위치가 눌려지면
DDRF = 0x0F;
break;
case 0x10 : //다섯번째 스위치가 눌려지면
DDRF = 0x1F;
break;
case 0x20 : //여섯번째 스위치가 눌려지면
DDRF = 0x3F;
break;
case 0x40 : //일곱번째 스위치가 눌려지면
DDRF = 0x7F;
break;
case 0x80 : //여덟번째 스위치가 눌려지면
DDRF = 0xFF;
break;
default : //아무것도 눌러지지 않으면
DDRF = 0x00;
}
}
return 0;
}
// 회로도를 보면 스위치는 안눌러진 상태에서 High 신호를 보내다가 스위치를 누르게 되면 Low신호가 입력된다. 그러므로 이 값을 보수를 취해서 각각을 비교하면 쉽게 어느 스위치가 눌렀는지 알 수 있다.
// PORTF가 0x00으로 초기화 되어 있고 DDRF방향설정으로 LED ON 개수를 조절 하고 있다.
위의 코드를 간략화한 코드
main.c
#include
"port.h"
int main(void)
{
unsigned
char in = 0x01;
unsigned
char out = 0xFE;
unsigned
int count = 0;
DDRC = 0x00; //switch input
PORTC = 0x00;
DDRF = 0xFF; //LED output
PORTF = 0xFF;
while(1)
{
if((~PINC) & (in << count)) // 어느 스위치가 눌러졌는지 확인
{
PORTF = out << count; // LED ON
}
else // 아무 스위치도 눌러 지지 않았을 때
{
PORTF = 0xFF;
}
count++;
if(count >= 8)
{
count = 0;
}
}
return 0;
}
// 시프트연산자를 이용하여 처음 작성한 코드를 간단히 만들어 보았다. 처음 코드를 보면 스위치를 확인 할 때의 숫자들을 자세히 살펴보면 결국 0x01로 시작해서 왼쪽으로 1개씩 시프트 연산한 것과 같다. 이를 변수로 만들어서 if문 하나에서 전부 확인하고 있다.
// LED ON 또한 Low 신호에 반응하기 때문에 시프트연산을 이용하면 쉽게 출력을 만들어 낼 수 있다.
C언어
swtich 문
p3-9.c
#include
<stdio.h>
int main()
{
char ch;
printf("Please enter a number between 0 to 9 : ");
scanf("%c", &ch);
switch(ch)
{
case
'0' :
printf("Input Number is Zero.\n");
break;
case
'1' :
printf("Input Number is One.\n");
break;
case
'2' :
printf("Input Number is Two.\n");
break;
case
'3' :
printf("Input Number is Three.\n");
break;
case
'4' :
printf("Input Number is Four.\n");
break;
case
'5' :
printf("Input Number is Five.\n");
break;
case
'6' :
printf("Input Number is Six.\n");
break;
case
'7' :
printf("Input Number is Seven.\n");
break;
case
'8' :
printf("Input Number is Eight.\n");
break;
case
'9' :
printf("Input Number is Nine.\n");
break;
default :
printf("wrong number.\n");
}
return 0;
}
// switch문은 ch 안의 변수와 매칭되는 case 문이 실행 된다. 조건이 맞아 떨어지는 case문부터 그 밑으로 쭉다 실행하기 때문에 break;는 필수적으로 사용해야 한다.
문자열 입출력 함수
p3-10.c
#include
<stdio.h>
int main()
{
char ch;
printf("Please enter any character: ");
ch = getchar();
putchar(ch);
printf(" is a letter you typed.\n");
//fflush(stdin);
__fpurge(stdin);
printf("Please enter any character: ");
scanf("%c", &ch);
printf("%c is a letter you typed.\n", ch);
return 0;
}
// fflush(stdin)은 입출력 버퍼에 남아 있는 내용을 강제로 쓰는 명령이다. 이로 인해 입출력 버퍼가 비워지는데 윈도우 시스템에서는 이 코드가 원하는데로 잘 동작하나 리눅스시스템에서는 우리가 원하는데로 버퍼가 비워지지 않았다. 리눅스에서는 __fpurge(stdin)으로 입력버퍼의 내용을 지울 수 있었다. 이 함수들을 사용하는 이유는 앞선 입력에서 입력버퍼를 사용하고 버퍼에 불필요한 쓰레기 값을 남기게 되는데 이 값이 다음 입력에 영향을 주기 때문에 입력 버퍼를 비워주어야만 다음 입력이 제대로 동작한다.
//getchar(), putchar()함수는 각각 하나의 문자를 입력, 출력 하는 함수 이다. getchar()함수는 키보드로부터 하나의 문자를 입력받아서 이를 리턴해주고, putchar()함수는 아스키값을 넘겨주면 이를 문자로 화면에 출력해준다.
반복문
반복문은 같은 명령문을 여러 번 반복해서 수행해야 될 경우 사용한다. 반복문을 사용할 때는 다음의 3가지 조건이 필요하다.
1. 초기조건 // 2. 반복되는 일 // 3. 종료조건
p3-12.c
#include
<stdio.h>
int main()
{
int num;
num = 0; // 1.초기조건
while(num <= 5) // 3.종료조건
{
printf("%d\t", num); // 2.반복되는 일
num = num + 1;
}
printf("\n");
return 0;
}
// while(논리식) { 명령문 } while문은 논리식이 참(0이아닌값)일 동안 계속해서 명령문을 실행한다.
전자회로실험
LED 속도 변화
어제 실험에서는 주기적인 시간에 따라 LED를 On/Off시키는 코드를 작성해 보았다. 오늘은 이 LED On/Off를 시간을 동적으로 바꾸어 보는 코드를 작성했다.
port.h파일
#define PINA (*(volatile unsigned int *)0x39)
#define DDRA (*(volatile unsigned int *)0x3A)
#define PORTA (*(volatile unsigned int *)0x3B)
#define PINB (*(volatile unsigned int *)0x36)
#define DDRB (*(volatile unsigned int *)0x37)
#define PORTB (*(volatile unsigned int *)0x38)
#define PINC (*(volatile unsigned int *)0x33)
#define DDRC (*(volatile unsigned int *)0x34)
#define PORTC (*(volatile unsigned int *)0x35)
#define PIND (*(volatile unsigned int *)0x30)
#define DDRD (*(volatile unsigned int *)0x31)
#define PORTD (*(volatile unsigned int *)0x32)
#define PINE (*(volatile unsigned int *)0x21)
#define DDRE (*(volatile unsigned int *)0x22)
#define PORTE (*(volatile unsigned int *)0x23)
#define PINF (*(volatile unsigned int *)0x20)
#define DDRF (*(volatile unsigned int *)0x61)
#define PORTF (*(volatile unsigned int *)0x62)
main.c파일
#include "port.h"
int main(void)
{
unsigned int us = 0; // 10^-6
unsigned int ms = 0; // 10^-3
int flag = 0;
int t = 2000; // 시간 변화를 주기 위한 변수
DDRC = 0xFF;
while(1)
{
us++;
if(us >= 1000)
{
ms++;
us = 0;
}
if(ms >= t)
{
if(flag)
{
PORTC = 0x00;
flag = 0;
}
else
{
PORTC = 0xFF;
flag = 1;
}
ms = 0;
t -= 100;
if(t == 0)
{
t = 2000;
}
}
}
return 0;
}
// 변수 t를
이용하여 LED가 on/off 할
때
마다 t값을
줄여줌으로써 on/off 주기가
점점
짧아진다. 변수 t가 0이
되면
다시 2000으로
초기화하고
다시
반복한다.
PINx
PINx 레지스터는 I/O핀이 입력으로 사용될 경우 핀에 입력된 데이터를 읽어 오는 레지스터이다. 이 PINx를 직접 사용해보았다.
main.c 파일
#include "port.h"
int main(void)
{
DDRC = 0x01; // C핀 01번을 출력으로 설정
PORTC = 0x01; // C핀 01번에 High 출력
DDRD = 0x00; // D핀을 입력으로 설정
PORTD = 0x00; // D핀 초기화
PORTF = 0x00; // F핀 초기화
while(1)
{
if(PIND & 0x01) // D핀 01번에 High신호가 들어오면
{
DDRF = 0xff; // F핀에 High 신호 출력
}
else
{
DDRF = 0x00; // F핀에 Low 신호 출력
}
}
return 0;
}
// F핀에는 LED를
연결시켜놓고, C핀 01번을 D핀 01번으로
연결시키면 F에서
출력이
나오므로 LED에
불이
들어오는
것을
알
수
있다.
switch누름에 따라 LED on개수변화
#include "port.h"
int main(void)
{
DDRC = 0x00; //switch input
PORTC = 0x00;
DDRF = 0xFF; //LED output
PORTF = 0xFF;
while(1)
{
if((~PINC) & 0x01) // 1번 스위치가 눌러 졌을 때
{
PORTF = ~(0x01);
}
else if((~PINC) & 0x02) // 2번 스위치가 눌러 졌을 때
{
PORTF = ~(0x03);
}
else if((~PINC) & 0x04) // 3번 스위치가 눌러 졌을 때
{
PORTF = ~(0x07);
}
else if((~PINC) & 0x08) // 4번 스위치가 눌러 졌을 때
{
PORTF = ~(0x0F);
}
else if((~PINC) & 0x10) // 5번 스위치가 눌러 졌을 때
{
PORTF = ~(0x1F);
}
else if((~PINC) & 0x20) // 6번 스위치가 눌러 졌을 때
{
PORTF = ~(0x3F);
}
else if((~PINC) & 0x40) // 7번 스위치가 눌러 졌을 때
{
PORTF = ~(0x7F);
}
else if((~PINC) & 0x80) // 8번 스위치가 눌러 졌을 때
{
PORTF = ~(0xFF);
}
else
{
PORTF = 0xFF;
}
}
return 0;
}
// switch 입력
받은핀 : PORTC // LED 출력핀 : PORTF
// switch 회로도를
보면
스위치가
눌려
졌을
때 Low 신호가
입력
된다. 그러므로 C핀
입력을
보수
연산을해서
어느
핀이
눌러
졌는가
확인하고, 이에
따라 LED On의
개수를
정하고 F핀으로
출력
하였다.
C언어
Big Endian & Little Endian
정수 0x12345678가 실제 메모리에 저장 되는 방식에는 Big-Endian과 Little-Endian방식이 있다. Big-Endian의 의미는 정수의 끝부분 메모리 주소가 크다는 의미이고, Little-Endian은 정수의 끝부분의 메모리 주소가 작다는 의미다.
일반적인 인텔x86계열 cpu들은 Little-Endian방식을 채택하고 있는데, Little-Endian 방식은 메모리 첫 부분에 바로 정수의 끝자리가 오기 때문에 산술연산이 간단해지는 장점이 있다. 반면 끝부분이 앞에 오기 때문에 대수비교에 약점이 있다.
main.c파일
#include <stdio.h>
int main()
{
int iNum = 0x12345678;
int *iP = &iNum;
printf("iNum address: %08x\n", &iNum);
printf("iP address: %08x\n", &iP);
printf("iNum value: %08x\n", iNum);
*iP = 0x77777777;
printf("iNum value: %08x\n", iNum);
*((int *)0x12ff7c) = 0x99;
printf("iNum value: %08x\n", iNum);
/* little endian을 확인하는 코드 */
*((char *)0x12ff7c) = 0x01;
*((char *)0x12ff7d) = 0x02;
*((char *)0x12ff7e) = 0x03;
*((char *)0x12ff7f) = 0x04;
printf("iNum value: %08x\n", iNum);
return 0;
}
Volatile 키워드
c컴파일러는 불필요한 코드를 자동적으로 최적화 시켜준다. 하지만 특수 역할을 하는 레지스터를 엑세스 해야 되는 경우 이 최적화가 문제를 일으킨다. 이를 방지하기 위해서 volatile키워드를 사용한다.
전자회로실험
Atmega128
Atmega128 칩은 설정에 따라 변경 가능한 8비트 I/O포트 6개(Port A~F)와 5비트 I/O 포트 1개(Port G)를 가지고 있다. 즉 설정에 따라 출력으로 혹은 입력으로 사용할 수 있는 I/O 라인이 53개이다.
각 I/O 핀들은 DDR(Data Direction Register), PORT(Data Register), PIN(Port Input Pins Register)과 같은 레지스터에 의해 제어된다.
DDRx은 핀이 입력용으로 사용할지 출력용으로 사용할지와 같은 핀의 신호 방향을 결정하는 레지스터 이고, PORTx는 I/O핀이 출력용으로 사용될 경우에 출력할 데이터를 저장하는 레지스터이며, PINx은 I/O핀이 입력용으로 사용될 경우에 핀을 입력된 데이터를 저장하는 레지스터이다.
오늘 실험에서는 C핀을 출력으로 설정하고, LED를 제어하는 실험을 해보았다.
기본 컴파일러의 라이브러리를 이용하지 않고, 각 핀 주소를 #define을 사용하여 제어하였다.
LED를 일정 시간 단위로 불을 켰다 끄는 것을 반복하는 코드를 작성하였는데 이를 위해 딜레이를 주기위한 코드를 작성하였다.
port.h파일
#define DDRA (*(volatile unsigned int *)0x3A)
#define PORTA (*(volatile unsigned int *)0x3B)
#define DDRB (*(volatile unsigned int *)0x37)
#define PORTB (*(volatile unsigned int *)0x38)
#define DDRC (*(volatile unsigned int *)0x34)
#define PORTC (*(volatile unsigned int *)0x35)
#define DDRD (*(volatile unsigned int *)0x31)
#define PORTD (*(volatile unsigned int *)0x32)
#define DDRE (*(volatile unsigned int *)0x22)
#define PORTE (*(volatile unsigned int *)0x23)
#define DDRF (*(volatile unsigned int *)0x61)
#define PORTF (*(volatile unsigned int *)0x62)
// 기존
컴파일러에서
제공하는
헤더
파일을
이용하지
않고, atmega128 datasheet에
나와
있는
각
핀별
주소를
직접 #define 했다.
main.c파일
#include "port.h"
int main(void)
{
unsigned int us = 0; // 10^-6
unsigned int ms = 0; // 10^-3
int flag = 0;
DDRC = 0xFF;
while(1)
{
us++;
if(us >= 1000)
{
ms++;
us = 0;
}
if(ms >= 500)
{
if(flag)
{
PORTC = 0x00;
flag = 0;
}
else
{
PORTC = 0xFF;
flag = 1;
}
ms = 0;
}
}
return 0;
}
// atmega128의 동작 클럭은 16MHz로 1초에 16000000번의 마이크로연산이 가능하다. 그러므로 일반적인 사람의 눈으로 LED 불을 on/off 시키는 것을 보기 위해서는 불을 켠 후 어느정도 딜레이를 주고 다시 불을 끄는 동작을 해야 한다. 이 딜레이를 덧셈 연산의 반복을 이용하여 구현한다.
c언어 실습
다중 if 문
#include
<stdio.h>
int main()
{
int score;
char grade = 'X';
printf("Please enter examination score: ");
scanf("%d", &score);
if((score <= 100) && (score >= 90))
{
grade = 'A';
}
else
if((score < 90) && (score >= 80))
{
grade = 'B';
}
else
if((score < 80) && (score >= 70))
{
grade = 'C';
}
else
if((score < 70) && (score >= 60))
{
grade = 'D';
}
else
if((score < 60) && (score >= 0))
{
grade = 'F';
}
else
{
printf("wrong score\n");
}
printf("The grade is %c\n", grade);
return 0;
}
포인터
포인터는 메모리의 주소를 의미하고, 포인터 변수는 이 메모리 주소들을 저장할 수 있는 변수를 뜻한다.
#include
<stdio.h>
int main()
{
int a = 100; // int형 변수 a선언 & 100으로 초기화
int *p; // int형 포인터 변수 p 선언
p = &a; // 포인터 변수p에 a변수의 주소를 대입한다
*p = 0x99; // 포인터 변수p에 저장된 메모리 주소의 메모리에 0x99를 대입(즉 변수a에 0x99를 대입)
*((int *)0x18FF38) = 0x77; // 변수a의 메모리 주소를 int형 포인터 변수로 캐스팅 연산한 후, 이 주소의 메모리에 0x77을 대입
printf("a value : %x\n", a);
printf("a address(16) : %x\n", &a);
printf("p address(16) : %x\n", p);
return 0;
}