'공부합시다'에 해당되는 글 70건

  1. 2009.04.24 [ATmega1128] ATmega128은 Little endian일까 Big endian일까 2
  2. 2009.04.23 Volatile 한정자
  3. 2009.04.23 [AVR] DDRA, PORTA를 찾아보자. 1
  4. 2009.04.22 [DK-128] LED 순차적으로 점멸하기
  5. 2009.04.22 [avr] 확장형 데이터 타입
  6. 2009.04.20 [C언어]좀 더 복잡한 함수 포인터
  7. 2009.04.16 [프로그래밍 일반] 부동소수점의 저장 방식
  8. 2009.04.16 [프로그래밍 일반]실수형 10진수 2진수로 변환하기
  9. 2009.04.16 [C언어]Little Endian
  10. 2009.04.16 [C언어]함수 주소를 직접 입력하여 호출하기
2009. 4. 24. 17:50

[ATmega1128] ATmega128은 Little endian일까 Big endian일까

이를 확인하기 위해 다음과 같은 프로그램을 작성하였다.



 소스 코드를 보면 2바이트 int형 데이터를 선언하고(atmega128에서 int형은 2바이트),
1바이트 포인터로 주소값을 저장하였다.

 만약, Big endian이라면 0x12가 먼저 출력되고 3초뒤에 0x34가 출력될테고,
Little endian이라면 0x34가 먼저 출력되고 3초뒤에 0x12가 출력될 것 이다.

 키트를 연결시킨후 hex파일을 전송 시켜보았다.





 여기서 켜지는 것이 0이고, 불이 들어오지 않은 것이 1이다.
위의 사진에서 00110100으로 출력되고 3초뒤의 모습인 아래 사진에서 00010010으로 출력되는 것을 알 수 있다.
즉, 0x34 -> 3초뒤 ->0x12로 출력되는 것이다.

 따라서, 0x1234가 34, 12 의 순으로 역순으로 저장되어있기 때문에 Little endian방식으로 메모리에 저장하는 것을 알 수 있다.
2009. 4. 23. 12:03

Volatile 한정자

1. No-Cache Mode로 사용하기 위해서이다.
   보통 프로그램 실행시 속도를 위해 CPU에서 데이터를 메모리에서 직접 읽어오지 않고, 캐시(Cache)를 통해서 읽어오게 된다. 하지만, 하드웨어에 의해서 변경되는 값들은 캐시에서 바로 반영되지 않기 때문에 캐시를 통해 읽어오지말고, 직접 메모리에서 읽어와야한다.(속도는 조금 늦춰지긴 한다.)
    - 하드웨어가 사용하는 메모리는 volatile로 선언해 줘야 한다.

ex)
      #define rTEMP1     (*(volatile unsigned char *)0x30000028)
      #define rTEMP2     (*(volatile unsigned char *)0x30000032)


2. 컴파일러 최적화가 임의로 코드를 변경하는 것을 방지한다.
    단계별 최적화 옵션을 사용하게 되면 컴파일러에 따라 전체 코드상 사용되지 않는 DeadCode등은 아예 삭제되거나 무시되는 경우가 있다.  이러한 점은 프로그래머의 의도와는 다르게 기계적 해석에 따라 결과물이 나올 수도 있게 된다. 이러한 경우 프로그래머의 의도가 명확한 경우 volatile을 사용하여 컴파일 최적화를 하지 않게 지정할 수 있다.

출처: No Day But Today
2009. 4. 23. 11:47

[AVR] DDRA, PORTA를 찾아보자.

DDRA, PORTA는 도대체 어디에 정의되어 있을까?
헤더파일에서 직접 찾아보자.

소스코드를 보면 <stdio.h>처럼 많이 사용하는 io함수들을 사용하기 위해 아래와 같이 include한다.

#include <avr/io.h>

따라서 io.h를 살펴보면,

#elif defined (__AVR_ATmega128__)
#include <avr/iom128.h>

이라고 정의된 부분을 볼 수있다.

iom128.h를 찾아보자.(다른 MCU라면 해당 헤더파일을 찾으면 된다.)

imo128.h를 보면, (ctrl+f로 찾자)

/* Input Pins, Port A */
#define PINA      _SFR_IO8(0x19)

/* Data Direction Register, Port A */
#define DDRA      _SFR_IO8(0x1A)

/* Data Register, Port A */
#define PORTA     _SFR_IO8(0x1B)

이렇게 정의된 부분이 있다.

따라서 PORTA = 0xFF 라는 문장은,
_SFR_IO8(0x1B) = 0xFF 라고도 쓸수있다.

그렇다면, _SFR_IO8는 무엇일까.

아까 io.h를 다시 살펴보면,
#include <avr/sfr_defs.h> 라고 된 부분이 있다.
특수기능 레지스터에 대한 헤더파일인데, 이것을 열어보자.

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)

이라고 된 부분을 보자.
이것은 매크로 함수인데, 뒷부분의 _MMIO...부분을 수행하는 것이다.

따라서,
PORTA = 0xFF;
_SFR_IO8(0x1B) = 0xFF;
_MMIO_BYTE((0x1B) + __SFR_OFFSET) = 0xFF;
세문장은 모두 같은 문장이 된다.

그렇다면 _MMIO_BYTE와 __SFR_OFFSET는 무엇일까.

sfr_defs.h에서 _MMIO_BYTE를 다시 찾아보면,

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

라고 정의된 부분을 찾을 수 있다.

따라서,
PORTA = 0xFF;
_SFR_IO8(0x1B) = 0xFF;
_MMIO_BYTE((0x1B) + __SFR_OFFSET) = 0xFF;
(*(volatile uint8_t *)((0x1B) + __SFR_OFFSET)) = 0xFF;
는 모두 같은 문장이 된다.

volatile에 대해서는 여기를 참조하도록하자. <volatile 한정자 보기>
uint8_t는 unsigned 타입의 정수형(int) 8bit의 타입이라는 것이다.(이 부분이 정의된 부분은 아직 못찾았다.)
즉, unsigned char의 포인터형이란 뜻이고, 괄호가 되있으므로 캐스팅(형변환)을 하겠다는 뜻이다.

__SFR_OFFSET을 찾아보자.
마찬가지로 sfr_defs.h에 정의되어있다.

#ifndef __SFR_OFFSET
/* Define as 0 before including this file for compatibility with old asm
   sources that don't subtract __SFR_OFFSET from symbolic I/O addresses.  */
#  if __AVR_ARCH__ >= 100
#    define __SFR_OFFSET 0x00
#  else
#    define __SFR_OFFSET 0x20
#  endif
#endif

조건은 호환모드인가 노멀모드인가를 판별하는 것이고
그에 따른 값을 정의한다. 따라서, 노멀모드이므로 0x20이 된다.

따라서,
PORTA = 0xFF;
_SFR_IO8(0x1B) = 0xFF;
_MMIO_BYTE((0x1B) + __SFR_OFFSET) = 0xFF;
(*(volatile uint8_t *)((0x1B) + __SFR_OFFSET)) = 0xFF;
(*(volatile uint8_t *)((0x1B) + 0x20)) = 0xFF;
(*(volatile uint8_t *)0x3B) = 0xFF;

즉, 0x3B주소지에서 1바이트 공간에 0xFF를 쓰겠다는 뜻이다.

그렇다면 0x3B는 무엇인가.
이는 atmega128의 스펙을 살펴봐야한다.
스펙을 살펴보면, PORTA 레지스터와 연결되어있음을 알수있다.

즉 0x3B에 데이터를 쓰게 되면 PORTA 레지스터에 데이터를 쓰는 것과 같은 것이다.

이를 활용하여 헤더파일을 작성할 수 있다.


 


2009. 4. 22. 18:24

[DK-128] LED 순차적으로 점멸하기

소스코드




2009. 4. 22. 18:16

[avr] 확장형 데이터 타입

      AVR 컴파일러의 확장 데이터형

가)    sfrb, srfw

     특수기능 레지스터(SFR)을 나타내는 지시어로 sfrb는 바이트 단위 레지스터를 sfrw는 워드단위의 레지스터를 나타낸다.

     srfb

#define sfrb_(x,x_) \

extern "C" volatile unsigned char x asm(#x_) 로 정의 되어있다.

     srfw

#define sfrw_(x,x_) \

extern "C" volatile unsigned int x asm(#x_) 로 정의 되어있다.

     사용법은 다음과 같다.

sfrb 심볼1 = IO레지스터주소값; sfrw 심볼2 = IO레지스터주소값;

나)    sfr 데이터형들의 용도

     헤더 파일에 sfr 데이터형들로 정의된 I/O 레지스터 이름들을 사용하여, 레지스터에 직접 값을 대입할 수 있다.

다)    헤더 파일에 정의된 레지스터

     DDR

데이터의 방향성을 지정해주는 레지스터. 해당되는 포트를 1로 정해주면 출력이 되고 0으로 지정해주면 입력이 된다. 기본적으로는 input으로 설정되어있다

) DDRA=0xff;  DDRF=0x00;

     PORT

       출력을 담당한다. 해당되는 핀에 1이나 0을 써주면 그 값이 핀으로 나가는 것이 PORT의 역할이다.

       ) PORTA = 0x01;   PORTF = 0xf0;

     PIN

       DDR 0으로 설정되었을 때 해당되는 핀의 값을 받아들이는 역할을 한다.

2009. 4. 20. 10:40

[C언어]좀 더 복잡한 함수 포인터


2009. 4. 16. 20:35

[프로그래밍 일반] 부동소수점의 저장 방식

부동 소수점의 저장 방식에 대하여

대부분의 CPU는 실수를 저장하는 방식인 부동 소수점을 표현하기 위해 IEEE 754 규약을 따르고 있다.

IEEE 754에 따르면 실수형 데이터타입의 대표적인 float형의 저장 방식을 다음과 같이 표현하고 있다.

float형의 데이터의 메모리 내 저장 방식

부호비트

지 수 부

가 수 부

1 bit

8 bit

23 bit

  32비트의 크기를 가진 float형의 각 비트의 의미는 다음과 같다.

1.     부호비트: 수의 부호를 표시해주는 비트로 0이면 양수 1이면 양수임을 나타낸다.

2.     지수부: 지수승을 나타내기 위한 비트이다.

3.     가수부: 첫 번째 비트는 2 -1승 두 번째 비트는 2 -2승을 나타내며 총 23비트이다.

 

 실제 예를 들어 설명하면, 3.2f를 저장한다고 하면, 먼저 3.2f는 이진수로 11.001100110011……이다. 여기서 지수승으로 표현하기 위해 소수점을 일의 자리 하나 남게 이동한다. 따라서 3.2(10) = 1.1001100110011…x21(2)이 된다.

 여기서 부호비트는 양수이므로 당연히 0이 된다.

 그리고, 지수부는 IEEE 754의 규약에 따르면 대수연산의 편의를 위해 2 n승은 127+n으로 표시한다. 위에서는 2 1승이므로 127+1 = 128이 된다. 이를 2진수로 표현하면 10000000

 또한, 가수부에서는 정수 부분의 1은 항상 존재하는 것이기 때문에 생략을 하고 그 이하 소수점 부분만을 저장한다. 따라서 가수부는 소수점 이하 23자리까지 표현하며 24번째 자리에서는 CPU의 특성에 따라 올리거나 버리게 된다. Intel CPU에서는 올리기 때문에 가수부에 저장되는 값은 10011001100110011001101이 된다.

 이를 모두 합쳐서 표현하면 0 1000000 10011001100110011001101이 된다.

 

 


 위의 실행결과 화면은 3.2f가 실제 메모리에 저장된 순서대로 16진수와 2진수로 출력한 것이다.

Intel CPU에서는 Little Endian방식으로 데이터가 저장되므로, 역순으로 나타내면 404CCCCD이며, 2진수로 나타내면,

0100 0000 0100 1100 1100 1100 1100 1101 로 위의 수와 같게 저장됨을 알 수 있다.
2009. 4. 16. 12:33

[프로그래밍 일반]실수형 10진수 2진수로 변환하기

65.75를 2진수로 바꾸어보자

65.75 = 65 + 0.75 로 나타낼 수 있다.

먼저 65를 2진수로 변환

  2│65       
   └─────
  2│ 32  ...1           △
   └─────      │
  2│ 16  ...0           │
   └─────      │
  2│  8  ...0            │
   └─────      │
  2│  4  ...0           △
   └─────      │
  2│  2  ...0           │
   └─────      │
      1  ...0              │
    ─────▷─┘
  
  따라서 65 = 1000001(2) 가 된다.
 
  이젠 0.75를 2진수로 변환해보자.
 
    0.75
  ×    2
 ─────  
    ①.50         ▽
                     ▽
    0.50           ▽
  ×    2          
 ─────   ▽
    ①.00         ▽
 
  화살표대로 읽어나가면 0.75 = 0.11(2) 가된다.
   
    0.11(2) = 1×2^(-1) + 1×2^(-2) 이다.
            = 0.5 + 0.25
            = 0.75
           
 결과적으로 65.75=1000001.11(2) 이다.

2009. 4. 16. 11:43

[C언어]Little Endian

Intel Cpu는 Little Endian 방식으로 데이터를 저장한다.



소스코드 에서 iNum 에 실제로 저장된 데이터

78

56

34

12

Little                                                                                  Big
End                                                                                 Start

little endian의 장점 산술연산
big endian의 장점 대수비교
2009. 4. 16. 11:15

[C언어]함수 주소를 직접 입력하여 호출하기

소스코드