C 언어로 배우는 8051 마이크로프로세서
8051을 위한 C 언어 프로그래밍
학습목표 및 목차
프로그래밍 언어의 종류를 알고 특성을 이해한다.
8051에서 C 언어를 사용하는 방법을 익힌다.
실습에 필요한 주요 프로그래밍 기법을 익힌다.
01. 프로그래밍 언어
02. 8051을 위한 C 언어
03. 유용한 프로그래밍 기법
01. 프로그래밍 언어
1.1 기계어
프로그램(Program) : CPU가 순서적으로 처리해야 할 일을 만드는 것
명령어(Instruction) : CPU가 처리해야 할 일
기계어(Machine Language) : CPU가 이해할 수 있는 언어
프로그래밍 언어의 종류
고급 언어 (High level language) : FORTRAN, COBOL, C 언어 등
저급 언어 (Low level language) : 어셈블리어
01. 프로그래밍 언어
프로그래밍 언어의 변환 과정
소스 프로그램(Source Program) : 어셈블리어나 고급 언어로 작성한 원본
프로그램이며, 원시 프로그램이라고도 함
목적 프로그램(Object Program) : 변환된 기계어 프로그램
베이직 C 자바 C++ 어셈블리어 인터프리터 컴파일러 어셈블러 프로그래밍 언어 기계어 프로그램 01011010 1111000소스 프로그램
번역기
목적 프로그램
01. 프로그래밍 언어
1.2 어셈블리어
어셈블리어
기계어의 비트 형식을 니모닉 코드(mnemonic code)로 나타낸 것 - 니모닉 코드(Mnemonic Code) : 사람이 이해하기 쉽도록 기호 또는 문자를 압축해서 만든 코드MOV A, #03H
어셈블리어로 프로그래밍을 하는 경우
컴퓨터 하드웨어의 구성 요소를 직접 액세스할 때 컴파일러를 설계하거나 시스템 프로그램을 작성할 때 빠른 수행이 필요한 프로그램을 작성할 때 기억장소를 적게 차지하거나 입출력 장치를 효율적으로 사용할 때 8051 어셈블리어 예
02. 8051을 위한 C 언어
2.1 8051용 C 언어와 컴파일러
C 언어 vs. 어셈블리어
8051을 동작시키기 위한 언어 : 어셈블리어 또는 C 언어가 사용 어셈블리어는 수행속도와 메모리 측면에서 유리하지만 마이크로컨트롤러마다 다 르다는 단점이 있다. C 언어는 보다 쉽고 간편하게 프로그램을 작성할 수 있지만 정확한 시간산출이 어렵고 메모리 측면에서는 불리하다는 단점이 있다. 8051을 위한 상용 컴파일러 : Keil, IAR, Raisonance社에서 만든 컴파일러 - 상용 버전은 고가이지만 사용에 제한이 있는 데모 버전을 무료로 배포
02. 8051을 위한 C 언어
2.2 상수의 표현
정수 상수
진수 설명 예 8진수 0으로 시작되는 숫자 012 10진수 +, -, 0 이외의 숫자로 시작되는 숫자 123, -10 16진수 0x, 0X로 시작되는 숫자 0x1c, 0X1c
참고) 2진, 16진 및 10진 변환
▶ 10진법의 2진법 변환 (Ex) 45
▶ 16진법의 10진법 변환 (Ex) F2D
02. 8051을 위한 C 언어
C-프로그램 구조
전처리기 영역(#사용), 전역변수 영역, 함수(function) 영역의 3 부분 구성 - 전처리기 (include) : 항상 #으로 시작, 파일로 주어짐 - 함수: Main() 함수는 반드시 필요, 시작과 끝 표시{ ~ }, 변수 값 설정 - 변수: 프로그램에서 사용되는 자료(정수, 실수, 문자) 등을 선언(전역, 지역변수) - 주석 : 프로그램과 수행무관, 여러줄 주석 : /* */, 한줄 주석 : // Main() 함수는 반드시 필요
02. 8051을 위한 C 언어
2.2 상수의 표현
실수 상수
고정 소수점 방식 : 소수점을 찍어 정수부와 실수부로 나누어 표시 부동 소수점 방식 : 가수부(mantissa)와 지수부(exponent)로 나누어 표시
53.18765
.345
// 소수점 앞의 0을 생략하여 표현
-25.
// 소수점 뒤의 0을 생략하여 표현
예
5318e5
// 5318x10
5을 표현
.145e2
// 0.145x10
2을 표현
-2.4e-4
// -2.4x10
-4을 표현
예
02. 8051을 위한 C 언어
문자 상수
하나의 문자로 이루어진 상수이며, 작은 따옴표(‘’)사이에 하나의 문자로 표현 문자상수들은 아스키(ASCII)값들을 가진다. 문자열 상수
큰 따옴표(“ ”)사이에 문자열을 넣어서 사용한다. #define
어떤 특정한 값을 미리 정해주는 역할을 한다.‘A’의 경우 10진수 65, 16진수 0x41 값을 갖는다.
예
“Hi C”는 4개의 문자와 문자열의 끝을 알려주는 0(null 문자)을 포함하므로
실제 크기는 5바이트이다.
예
#define pi 3.14
// pi=3.14를 의미한다.
#define count 100
// count=100을 의미한다.
02. 8051을 위한 C 언어
2.3 연산자
산술연산
증가/감소연산
사용 예
설명
a+b a와 b를 더함 a-b a에서 b를 뺌 a*b a와 b를 곱함 a/b a를 b로 나눔 a%b a를 b로 나눈 나머지를 구함사용 예
설명
a++, (++a) a=a+1 또는 a+=1, a를 1 증가 a--, (--a) a=a-1 또는 a-=1, a를 1 감소
02. 8051을 위한 C 언어
대입연산
관계연산
사용 예
설명
a*=b a=a*b, a와 b를 곱해서 a에 대입
a /= b a=a/b, a를 b로 나누어서 몫을 a에 대입
a %= b a=a%b, a를 b로 나누어서 나머지를 a에 대입 a += b a=a+b, a와 b를 더해서 a에 대입
a -= b a=a-b, a에서 b를 빼서 a에 대입
사용 예
설명
a > b a가 b보다 크면 참 a >= b a가 b 이상이면 참 a < b a가 b보다 작으면 참 a <= b a가 b 이하이면 참 a == b a와 b가 같으면 참 a != b a와 b가 같지 않으면 참02. 8051을 위한 C 언어
논리연산
비트연산
사용 예
설명
!(a식) (a식)이 거짓(논리0)이면 참(논리1), 참이면 거짓이다. (a식) && (b식) (a식)과 (b식)이 모두 참(논리1)이면 참이다.
(a식) || (b식) (a식) 또는 (b식)이 참(논리1)이면 참이다.
사용 예
설명
~a a=~a (a를 비트 단위로 반전)
a <<= 2 a=a<<2 (a의 각 비트를 왼쪽으로 2회 시프트) a >>= 2 a=a>>2 (a의 각 비트를 오른쪽으로 2회 시프트)
a &= b a=a&b (a와 b를 각 비트 단위로 AND) a ^= b a=a^b (a와 b를 각 비트 단위로 XOR)
02. 8051을 위한 C 언어
2.4 데이터 형
데이터 형 내용 기억범위 크기 bit 비트 0 또는 1 1비트 char(signed char) 부호 있는 정수 -128 ~ +127 1바이트 unsigned char 부호 없는 정수 0 ~ 255 1바이트 int(signed int) 부호 있는 정수 -32,768 ~ +32,767 2바이트 unsigned int 부호 없는 정수 0 ~ 65,535 2바이트 short(signed short) 부호 있는 단정도 정수 -32,768 ~ +32,767 2바이트 unsigned short 부호 없는 단정도 정수 0 ~ 65,535 2바이트 long(signed long) 부호 있는 배정도 정수 -2,147,483,648 ~ +2,147,483,647 4바이트 unsigned long 부호 없는 배정도 정수 0 ~ 4,294,697,295 4바이트 float 실수 1.175494E-38 ~ 3.402823E+38 4바이트sfr 8비트 SFR 어드레스 0x00 ~ 0xff 1바이트
02. 8051을 위한 C 언어
bit 형
비트 단위의 독립적인 변수를 정의하는 것 sbit 형
bdata로 선언된 독립적인 변수 내부에서 몇 번째 비트를 액세스하기 위한 비트 지정 방법 8051 내부 RAM 중에 비트 단위로 처리 가능한 영역(0x20~0x2f)과 특수기능 레 지스터 영역(0x80~0xff)에서 비트 지정이 가능한 영역에 변수를 지정할 때 사용bit day = 0, night = 1;
예
sbit LED_L =P1^0;
// P1.0을 LED_L로 지정
sbit LED_C =P1^1;
// P1.1을 LED_C로 지정
sbit LED_R =P1^2;
// P1.2를 LED_R로 지정
예
02. 8051을 위한 C 언어
sfr 형
특수기능 레지스터를 선언하는데 사용 SFR 영역의 특수기능 레지스터 이름들은 헤더파일(8051용으로는 reg51.h, 8052 용으로는 reg52.h)에 있기 때문에 따로 지정할 필요는 없음sfr P0=0x80;
예
02. 8051을 위한 C 언어
2.5 메모리 형과 메모리 모델
메모리 형
프로그램 메모리 영역(ROM)의 상수 데이터
데이터 메모리 영역(RAM)의 상수 데이터
메모리 형 포인터 값 설명 idata 1 간접 액세스할 수 있는 내부 데이터 메모리(128바이트) xdata 2 외부 데이터메모리(64K바이트) pdata 3 페이지화 된 외부 데이터 메모리(256바이트) data 4 직접 액세스할 수 있는 내부 데이터 메모리(128바이트) code 5 프로그램 메모리(64K바이트)code unsigned char sensor=0x05;
code unsigned char table[7]={0x40,0x79,0x24,0x30,0x19,0x12,0x02};
예
02. 8051을 위한 C 언어
2.6 제어 구조와 루프(loop)문
if 문과 if~else 문
사용법 의미 if 문 if (조건식) { statement(s) 1; } statement(s) 2; 조건식이 참이면, statement(s) 1을 실행하고, 거짓이면 if 다음에 있는 statement(s) 2를 실행 한다. If~else 문 if (조건식) { statement(s) 1; } else { statement(s) 2; } 조건식이 참이면 statement(s) 1을 실행하고, 거 짓이면 statement(s) 2를 실행한다.02. 8051을 위한 C 언어
P03_01.c
#include <reg51.h> void main(void)
{
unsigned char a=2, b=0; // 변수 선언과 변수 초기화
if (a) P1=0x04; // a≠0이므로 조건식은 참이 되고 포트 1에 0x04 출력 if (a<b) { // 조건식이 거짓임으로 else 다음 { } 부분 실행 P1=0x00; // 포트 1에 0x00 출력 P2=0x00; // 포트 2에 0x00 출력 } else { P1=0xb3; // 포트 1에 0xb3 출력 P2=0xe5; // 포트 2에 0xe5 출력 } }
02. 8051을 위한 C 언어
while 문과 do~while 문
사용법 의 미 while 문 while (조건식) { statement(s); } 조건식이 참일 동안 { } 사이의 문장들인 statement(s)을 반복하며 실행한다. 조건식이 거짓 이 되면 { } 부분을 더 이상 실행하지 않고 빠져 나온다 . do~while 문 do { statement(s); } while (조건식) ; 조건식에 관계 없이 일단 { } 사이의 문장들인 statement(s)을 한번 실행한 후에 조건식을 검사한 다. 그리고 조건식이 참일 동안 { } 사이의 문장들을 반 복해서 실행한다. 조건식이 거짓이 되면 { } 부분을 더 이상 실행하지 않고 빠져 나온다.02. 8051을 위한 C 언어
P03_02.c
#include <reg51.h> void main(void)
{
unsigned char a=2, b=0; // 변수 선언과 초기화
while(b) P1=0xd2; // b=0이므로 P1=0xd2;를 실행하지 않음 do { P1=0x2e; // 포트 1에 0x2e 출력 P2=0x3c; // 포트 2에 0x3c 출력 } while(b); // b=0이므로 조건식은 거짓이지만 일단 한번은 실행 while(a-- > 0) { // a를 1씩 감소하면서 {} 안의 명령문 실행 // a=0이 되면 while 문을 빠져 나옴 P1=0x7f; // 포트 1에 0x7f 출력 P2=0xa3; // 포트 2에 0xa3 출력 }
02. 8051을 위한 C 언어
for 문
사용법 for (초기식; 조건식; 증감식) { statement(s); }P03_03.c
#include <reg51.h> void main(void) {unsigned char i, sum=0 ; // 변수 선언과 초기화
for (i=1; i<11; i++) { // i=1에서부터 10까지 {} 안의 명령문 실행 // i는 순환할 때마다 1씩 증가
sum=sum+i; // 1~10까지 숫자를 더함
P1=sum; // 포트1에 현재의 sum 값 출력 }
02. 8051을 위한 C 언어
2.7 포인터의 사용
포인터는 주소(address)를 나타내는 변수 C-언어 이외의 고급언어에서는 data 변수만 있음 8051용 C언어에서는 일반 포인터와 메모리 지정 포인터(memory specific pointer)의 2가지를 지원 일반 포인터 (generic pointer)
ANSI C에서 사용하는 것과 같은 기본적인 포인터로서 항상 3바이트를 사용하 여 변수를 가리킨다. 첫 번째 바이트는 메모리 형을 지정하는 것이고, 나머지는 메모리 어드레스를 나타내는 2바이트의 총 3바이트 크기다. 일반 포인터는 메모리 영역의 어느 위치에서라도 변수를 액세스할 수 있는 장 점이 있지만 처리 속도가 늦은 단점이 있다.char *strptr;
02. 8051을 위한 C 언어
메모리 지정 포인터
메모리 지정 포인터는 포인터 선언에 메모리 형을 함께 지정함으로써 항상 지정 된 메모리 영역을 참조하도록 하는 것
메모리 지정 포인터는 1바이트(idata 포인터) 또는 2바이트(code, xdata 포인터) 를 사용해서 저장
일반 포인터에 비해 메모리 효율적이며 처리속도가 빠르다.
char code *strptr; // code 영역에 char로 strptr 포인터를 선언
int idata *numptr
; // idata 영역에 int로 numptr 포인터를 선언
long xdata *varptr; // xdata 영역에 long으로 varptr 포인터를 선언
예
02. 8051을 위한 C 언어
배열과 포인터
포인터 선언 및 포인터 변수에 값을 저장하는 방법
unsigned char idata *ptr; // ptr : 메모리 주소(포인터)를 의미, *ptr : 메모리 데이터를 의미
unsigned char val; // &val : 메모리 주소 (포인터) 를 의미, val: 메모리 데이터를 의미
ptr = &val; // &는 메모리의 주소를 의미하는 포인터 연산자 // 포인터 변수 ptr에는 variable 변수가 저장되어 있는 // 메모리 주소가 들어간다 *ptr = 0x7f; // variable에 0x7f가 들어간다.
unsigned char *ptr;
ptr = &val;
*ptr = 0x7f;
val = 0x7f;
=
02. 8051을 위한 C 언어
배열의 이름은 배열의 첫 번째 요소가 있는 주소(포인터)이다. (배열이름+1)은 두 번째 요소가 있는 주소를, (배열이름+2)는 세 번째 요소가 있 는 곳의 주소를 나타낸다.char a[5]={11,22,33,44,55}; char *ptr; ptr=a; // ptr=&a가 아님 포인터 ptr ptr+1 ptr+2 ptr+3 ptr+4 주소 200 201 202 203 204 11 22 33 44 55
배열 요소 a[0] a[1] a[2] a[3] a[4]
a==&a[0];
// 배열 이름 a는 배열의 첫 번째 요소가 있는 주소(=200)
*a==a[0];
// *a는 배열의 첫 번째 요소의 값, a[0]=11
*(a+1)==a[1];
// *(a+1)는 배열의 두 번째 요소의 값, a[1]=22
*(a+2)==a[2];
// *(a+2)는 배열의 세 번째 요소의 값, a[2]=33
*(a+3)==a[3];
// *(a+3)은 배열의 네 번째 요소의 값, a[3]=44
예
02. 8051을 위한 C 언어
2.8 함수
일반 함수 형식
return_type : 함수의 리턴 값으로 데이터 형을 지정하지 않으면 int 형으로 간주 function_name : 함수 이름 arg_list는 : 함수 인수 interrupt n : 인터럽트 서비스 루틴 함수임을 나타냄 Ex_) 일반 함수02. 8051을 위한 C 언어
2.9 인터럽트 함수
인터럽트 처리 루틴도 함수의 일종이므로 8051 C에서 interrupt n 키워드를 추 가해 선언하는 것 외에는 일반 함수와 차이가 없다.return_type : 함수의 리턴 값으로 데이터 형을 지정하지 않으면 int 형으로 간주 function_name : 함수 이름 arg_list는 : 함수 인수 interrupt n : 인터럽트 서비스 루틴 함수임을 나타냄
인터럽트 종류와 시작 어드레스
벡터 어드레스 8051 C의 벡터 번호n
인터럽트 원인 0x0003 0 외부 인터럽트 0 0x000B 1 타이머/카운터 0 0x0013 2 외 인터럽트 1 0x001B 3 타이머/카운터 102. 8051을 위한 C 언어
인터럽트 함수 형식
extint0 : 인터럽트 처리함수 이름이며 사용자가 임의로 명명한다. interrupt : 인터럽트 처리루틴이라는 키워드이며 반드시 있어야 한다. 0 : 인터럽트 벡터번호
void
extint0(
void
) interrupt 0
{
// 인터럽트 처리
}
인수를 전달할 수 없으므로 인수를 반환할 수 없다.
인터럽트 처리 루틴에서 사용하는 레지스터는 인터럽트 처리 루틴으로
들어가기 전에 자동 저장된다.
인터럽트 처리 루틴을 만들 때 주의 사항
02. 8051을 위한 C 언어
2.10 헤더 파일
C 언어 프로그램에서 8051만의 하드웨어를 제어하기 위해서는 8051 헤더파일을
include 문으로 포함시킴으로써 가능하다.
/*--- REG51.H //8051.HHeader file for 8051.
---*/ /* BYTE Register */ sfr P0 = 0x80; sfr P1 = 0x90; sfr P2 = 0xA0; sfr P3 = 0xB0; sfr PSW = 0xD0; sfr ACC = 0xE0; sfr B = 0xF0; sfr SP = 0x81; sfr DPL = 0x82; sfr DPH = 0x83; sfr PCON = 0x87; sfr TCON = 0x88; sfr TMOD = 0x89; sfr TL0 = 0x8A; sfr TL1 = 0x8B; sfr TH0 = 0x8C; sfr TH1 = 0x8D; sfr IE = 0xA8; sfr IP = 0xB8; sfr SCON = 0x98; sfr SBUF = 0x99; /* BIT Register */ /* PSW */
sbit CY = 0xD7; sbit AC = 0xD6; sbit F0 = 0xD5;
02. 8051을 위한 C 언어
/* TCON */
sbit TF1 = 0x8F; sbit TR1 = 0x8E; sbit TF0 = 0x8D; sbit TR0 = 0x8C; sbit IE1 = 0x8B; sbit IT1 = 0x8A; sbit IE0 = 0x89; sbit IT0 = 0x88;
/* IE */
sbit EA = 0xAF; sbit ES = 0xAC; sbit ET1 = 0xAB;
sbit EX1 = 0xAA; sbit ET0 = 0xA9; sbit EX0 = 0xA8; /* IP */
sbit PS = 0xBC; sbit PT1 = 0xBB; sbit PX1 = 0xBA;
sbit PT0 = 0xB9; sbit PX0 = 0xB8; /* P3 */
sbit RD = 0xB7; sbit WR = 0xB6; sbit T1 = 0xB5;
sbit T0 = 0xB4; sbit INT1 = 0xB3; sbit INT0 = 0xB2; sbit TXD = 0xB1; sbit RXD = 0xB0;
/* SCON */
03. 유용한 프로그래밍 기법
3.1 시간 지연 루틴
소프트웨어 타이머
소프트웨어로 일정시간 지연하는 것
사용 형식
void delay(unsigned int i)
{
while(i--);
}
delay(0xb100);
//
약 0.5 sec 시간지연
delay(0x0700) 명령을 실행하면 delay 함수로 점프하여
i 값을 하나씩 감소시켜 1,792 (0700H)번 반복한다.
이때 i 값이 0이 되어 delay 함수로부터 리턴하기까지의
시간이 지연 시간이다.
delay(0x0700);
//
약 20 msec 시간지연03. 유용한 프로그래밍 기법
C 컴파일러에 의한 지연 루틴 계산
명령어 수행 횟수 사이클 수 사이클 총 수 MOV i,R6 MOV i+01H,R7 ?C0001: MOV A,i+01H DEC i+01H MOV R6,i JNZ ?C0104 DEC i ?C0104: MOV R7,A MOV A,R7 ORL A,R6 JNZ ?C0001 RET 1 1 256×R6+R7+1 256×R6+R7+1 256×R6+R7+1 256×R6+R7+1 R6+1 256×R6+R7+1 256×R6+R7+1 256×R6+R7+1 256×R6+R7+1 1 2 2 1 1 2 2 1 1 1 1 2 2 2 2 256×R6+R7+1 256×R6+R7+1 2(256×R6+R7+1) 2(256×R6+R7+1) 33 256×R6+R7+1 256×R6+R7+1 256×R6+R7+1 2(256×R6+R7+1) 2 합계 2817×R6+11×R7+1803. 유용한 프로그래밍 기법
3.2 무한 루프 사용
마이크로컨트롤러 시스템을 동작시키기 위한 사용자 프로그램은 무한 루프
구조로 작성하는 것이 일반적이다.
마이크로컨트롤러 시스템에서는 대부분 운영체제를 사용하지 않으므로 프로
그램 실행이 종료되면 되돌아 갈 곳이 없다.
방법 1
프로그램의 주요부를 1회만 실행하고, 의미 없는 하나의 명령을 반복하여 실행함으로
써 무한 루프에 빠지는 방법이다.
P03_08.c
#include <reg51.h> void main(void) { P1=0x55; // P1포트에 데이터 출력 P2=0xaa; // P2포트에 데이터 출력 while(1);03. 유용한 프로그래밍 기법
방법 2
프로그램의 주요부분을 무한히 반복하면서 무한 루프에 빠지도록 작성하는 방법이다.
P03_09.c
#include <reg51.h> sbit sw=P3^0;unsigned char led;
void delay(unsigned int i) { while(i--); } void main(void) { led=0xfe; do { P1=led; // 포트 1에 출력 led=(led<<1) | 0x01; // LED 상태 변경 if (led == 0xff) led=0xfe;
3.3 룩업 테이블 (Look-up table)
룩업 테이블은 여러 경우에 사용되며, 예를 들어 BCD값을 7-세그먼트에 디스 플레이하는 경우에 사용 (공통에노드) BCD 값 2진수 표시 16진수 표시 a b c d e f g dp 0 0 0 0 0 0 0 1 1 03H 1 1 0 0 1 1 1 1 1 9FH 2 0 0 1 0 0 1 0 1 25H 3 0 0 0 0 1 1 0 1 0DH 4 1 0 0 1 1 0 0 1 99H 5 0 1 0 0 1 0 0 1 49H 6 0 1 0 0 0 0 0 1 41H 7 0 0 0 1 1 1 1 1 1FH 8 0 0 0 0 0 0 0 1 01H03. 유용한 프로그래밍 기법
dp a b c d e f g7-세그먼트
03. 유용한 프로그래밍 기법
P03_10.c
#include <reg51.h> void main(void) {
unsigned char offset, segment;
unsigned char table[10]={0x03,0x9f,0x25,0x0d,0x99, 0x49,0x41,0x1f,0x01,0x09};
offset=0x02;
segment=table[offset]; }