아 머리 빠개지는군. 그냥 변수에 포인터 강제형 변환을 수행하면 알기 쉬운데 숫자에 바로 포인터 강제형 변환을 수행하니깐 헷갈린다.

(*(volatile unsigned *))0x40021018 의미를 정리하기 전에 먼저 포인터에 대해서 다시 한번 정리하자.




포인터


한마디로 포인터(pointer)는 메모리의 주소를 의미한다.

int *pTemp; 이라고 했을 때, 정확히 말하면 pTemp는 포인터가 아니라 포인터를 저장할 수 있는 변수명을 말한다. 다음 예를 보자.


int temp;

int *pTemp;


printf("%p \n", (char *)temp);

printf("%p \n", &pTemp);

printf("%p \n", pTemp);


 위에서 보면 (%p 는 주소값을 출력하고 기본 자료형은 void * 이다) temp 변수를 강제로 char 형 포인터로 형변환을 시켰다. 따라서 이것은 주소값을 의미하고 바로 %p 서식자에 의해서 출력이 된다. 반면 pTemp 는 int 형 포인터 변수를 의미하므로  pTemp 변수 자체의 주소값이 존재하고 &pTemp 연산을 통해서 그 주소를 확인할 수 있다. 세번째 출력에는 pTemp 변수가 담고 있는 주소값을 출력하게 된다. 지금 당장은 쓰레기값이 입력 되었으니 이상한 주소가 출력 될 것이다.




(*(volatile unsigned *))0x40021018 


(*(volatile unsigned *)0x40021018) |= 0x8; 


'의미 자체는 0x40021018 주소에 찾아가서 0x8을 입력시켜라'이다.  0x40021018 자체는 단순한 숫자에 불과하지만 포인터로 강제형 변환을 하게 되면 주소를 나타내는 의미를 갖게 된다. 과정을 보면 (volatile unsigned *)0x40021018 에서 ()는 형변환 연산,  volatile unsigned * 형으로 숫자 0x40021018를 주소값으로 변환.




 포인터 변수나 직접 위처럼 주소값을 나타내는 정수 앞에 붙어있는 '*'연산자의 정의 : 해당 주소의 메모리 공간으로 접근해라.


char num = 0;

char *pNum;


pNum = #

*pNum = 5;


 위와 같은 문장이 있을 때 *pNum 을 좀더 자세히 분석하면 pNum 포인터 변수는 num 의 주소를 저장하고 있다. 그러므로 *연산이 실행되기 전에 pNum 은 num 의 주소값을 반환된다. 그리고 * 연산에 의해서 반환된 주소값의 메모리 공간으로 접근한다.

'MCU > ARM' 카테고리의 다른 글

ADC independent DMA (2)  (0) 2016.08.14
ADC main feature(1)  (0) 2016.08.13
SPI 통신 (3)  (0) 2016.08.11
SPI 통신 (2)  (0) 2016.08.11
SPI 통신 (1)  (0) 2016.08.11
Posted by 나무길 :

[AVR] 데이터 손실

2013. 12. 12. 12:26 from MCU/AVR

 요즘 ATmega128 책을 속독으로 보고 있는데 신기한 개념을 알게 되었다. ATmega128 이 데이터를 8 bit 로 처리하므로 인해서 발생하는 데이터 손실 문제이다. 다음과 같은 예제를 보자


unsigned short icount, temp;


while(1)

{

  icount = (unsigned short) (ts * CPU_CLOCK_KHZ  / PRESCALE);

  timer = 0xFFFF - icount + 1;


 timer = temp;

}


ISR (TIMER1_OVF_vect) // 타이머 1 오버플로 인터럽트 서비스루틴

{

  TCNT1 = timer;

}

<예제 1>


 원래 LED 를 주기적으로 패턴 이동시키면서 뭐 하는 예제인데 일부만 빼왔다. 위 과정을 보면 멀쩡하게 TCNT1 레지스트에 timer 값이 잘 들어갈 것 같지만! 함정이 있다. 


 CPU 가 연산하는 과정을 한번 뜯어보자. ATmega128 는 8 bit CPU 이므로 항상 1Byte 단위로 연산을 수행한다. 그러므로 temp 변수 0x1234 의 값이 timer 변수 0x5678 자리에 대입 될 때 


1. timer = temp     <==>     0x5634 = 0x1234  // 0x1234 중 34를 먼저 연산하고 아직 12 는 연산하지 못한 결과

2. timer = temp     <==>     0x1234 = 0x1234  // 0x1234 우 12까지 전부 연산한 결과

 <예제2>


 요렇게 두번의 과정을 거쳐서 대입된다. 그래서 <예제1>을 보면 인터럽트가 발생을 하는데 이 인터럽트가 <예제2> 의 과정을 다 마치고 실행 된다면 다행히도 TCNT1 에는 프로그래밍 한 사람이 원하는 0x1234 값이 들어가게 된다. (0x1234 는 temp 값이 timer 로 대입된 것) 하지만 인터럽트가 일정 시간이 되면 무작위로 발생하기 때문에 <예제2> 의 괴정 중간에 발생하게  될 수도 있다. 이렇게 된다면! TCNT1 값은 0x5634 가 대입 되게 된다.이런.. 그래서 제대로 원하는 값을 넣으려면 timer = temp 연산을 하는 중간에 인터럽트가 발생하지 않도록 해야한다. 다음 예제가 제대로 된 예제.


unsigned short icount, temp;


while(1)

{

  icount = (unsigned short) (ts * CPU_CLOCK_KHZ  / PRESCALE);

  timer = 0xFFFF - icount + 1;


  cli ();        // 전역 인터럽트 금지

  timer = temp;

  sei ();        // 전역 인터럽트 허용

}


ISR (TIMER1_OVF_vect) // 타이머 1 오버플로 인터럽트 서비스루틴

{

  TCNT1 = timer;

}


'MCU > AVR' 카테고리의 다른 글

펌웨어와 임베디드  (0) 2015.05.29
인터럽트 한개로 다수의 RC 서보 제어  (0) 2013.12.17
[AVR] 카운트 계산법. TCNTn  (0) 2013.12.12
Datasheet 레지스터 보는 방법  (0) 2013.12.03
[AVR] 인터럽트  (0) 2013.12.03
Posted by 나무길 :

[AVR] 카운트 계산법. TCNTn

2013. 12. 12. 07:34 from MCU/AVR

프리스케일러(분주비) 값이 N 이면 내부클럭 N 개 마다 TCNTn 값이 1씩 증가한다. 따라서TCNTn 값이 1 증가하는 데 필요한 최소시간은


Tt = ( 1/fclk_I/O) * N


* 추가 설명

 - fclk_I/O : 진동수. 내부 클럭이다.

 - 1 / fclk_I/O : 한개 진동하는 데 걸리는 시간



 (1 / fclk_I/O) * N : 여기서 N 은 내부 클럭 N 개를 뭉쳤다고 생각하면 쉽다.  진동을 N 번 하는 데 걸리는 시간. 진동을 N 번 하면 TCNTn 이 1 증가. 따라서 카운트(=TCNTn) 10번을 하면


(1/fclk_I/O) * N * 10 의 시간이 걸린다.


'MCU > AVR' 카테고리의 다른 글

인터럽트 한개로 다수의 RC 서보 제어  (0) 2013.12.17
[AVR] 데이터 손실  (0) 2013.12.12
Datasheet 레지스터 보는 방법  (0) 2013.12.03
[AVR] 인터럽트  (0) 2013.12.03
[AVR] 시작하기 전에 알아야 할 것들  (0) 2013.12.02
Posted by 나무길 :