본문 바로가기

stm32 레지스터 직접 접근 (6. Timer/Counter [코드 Part 01])

반응형

관련글


stm32 레지스터 직접 접근 (6. Timer/Counter [함수 설명 Part 02]) (tistory.com)

stm32 레지스터 직접 접근 (6. Timer/Counter [Registers Part 03]) (tistory.com)


 

더보기

 


서론


우리는 시간의 흐름 속에 살고 있습니다. 팔을 1초 동안 움직여 올리고 다시 1초 동안 움직여 내리고, 정확한 1초는 아니지만 그래도 우리는 우리가 느끼는 1초라는 시간 동안 팔을 올리고 내릴 수 있죠. 이는 우리가 마음 속으로 1초, 2초, 3초 이렇게 일정 시간 간격 (1초)마다 숫자를 카운트하기 때문이죠. 쉽게 말해서 일정 시간마다 카운트한다는 거죠.

MCU에서도 이런 시간을 카운트하는 장치가 들어 있습니다. 바로 Timer 또는 Counter(통칭 TC)라고 불리우는 것입니다. TC는 "서보모터를 1초동안 0 -> 180도로 움직이게" 하는 동작이나, "1초동안 프로그램을 멈춰라" 즉, delay(1000);과 같은 기능을 수행할 수 있게 합니다. TC는 MCU에 들어 있는 모든 장치들 중에서 가장 시간적으로 정확한 장치라고 할 수 있기에 MCU에서 시간을 측정을 필요로하는, 또는 일정주기마다 실행되어야하는 그런 기능은 무조건 TC를 사용해야한다고 생각하셔야합니다.

아두이노에서 사용했던 PWM도 TC의 기능을 활용한 것이고, 서보모터의 각도를 제어하는데도 TC를 사용하죠.일반적으로 이러한 TC는 한 MCU 안에 여러 개 들어 있습니다. STM32F103ZET6에는 14개의 TC가 들어 있습니다. TIM1 ~ TIM14가 있다는 뜻이죠. 이 중에서 2, 3, 4, 5, 6, 7, 12, 13, 14는 APB1이라는 Bus(또는 Line)에 연결 되어 있어 최대 36 MHz의 주기를 가질 수 있고, 나머지 1, 8, 9, 10, 11은 APB2에 연결 되어 있어 최대 72 MHz의 주기로 동작합니다. (Reference manual 93 page)

RCC를 어떻게 설정하느냐에 다르지만, 일반적으로 TIM1, 8, 9, 10, 11이 더 빠른 주기를 생성할 수 있다는 것입니다.모든 TCs에는 각 TCs에 설정된 일정 기간이 지나면 Interrupt를 발생시킬 수도 있는 기능이 있어 "1초마다 LED를 키고 끈다"라는 동작을 가능케합니다. 더보기란에 제가 생각한 TC의 활용을 몇가지만 정리해 뒀습니다.

더보기
1. 일정 주기마다 LED를 키고 끄기
2. 서보모터의 각도 조절하기 (서보모터의 각도는 High 신호와 Low 신호의 시간 비율을 기준으로 각도를 지정합니다.)
3. 스텝모터 제어하기
4. 속도 측정
5. RPM 측정
6. 일정 주기마다 인터넷 자료 다운로드
7. 아두이노의 SoftwareSerial
8. PWM (하드웨어적으로 구현되어 있습니다.
9. 스피커

이밖에도 시간 / 일정 주기 / 일정 시간과 관련된 모든 것은 TC를 사용해야합니다. 

STM32는 TC와 관련된 Register들이 많습니다. 시간과 관련되어서 설정해야할 것들이 많고, 그만큼 많은 기능을 제공하기 때문입니다.우리가 사용할 TC는 TIM2로 APB1에 연결 되어 있어 최대 36 MHz를 지원하기에 이번 장에서는 36 MHz로 구동되도록 할 것입니다.

 

 

 

이번 시리즈에서는 Registers를 알아보기 전, 간단하게 Interrupt를 사용하여 LED를 끄고 키는 예제에서, 추후 작성할 Registers의 기능을 알아보도록 하겠습니다.


설정

코드

Interrupt 코드

최종 코드


참고자료


STM32F103ZET6 Reference Manual

 

STM32F10xxx/20xxx/21xxx/L1xxxx Cortex®-M3 programming manual

 

stm32 레지스터 직접 접근 (Interrupt 및 Register 관련 설정) (tistory.com)

 

STM32_Examples/Ex_07_Timer_easy at master · KwangryeolPark/STM32_Examples (github.com)

설정


TC는 Interrupt를 사용하기 때문에 반드시 다음 사이트를 따라하셔서 startup_~~~.s파일과 Preprocessing, 그리고 stm32폴더와 cortex_m3폴더를 추가하셔야합니다.

stm32 레지스터 직접 접근 (Interrupt 및 Register 관련 설정) (tistory.com)

 

코드


이번 포스트에서는 간단하게 만들 함수에 대한 설명과 전체 코드를 미리 보겠습니다.

 

initRCC()

기본 RCC관련 설정을 하는 함수입니다.

1. TIM2는 APB1에 연결 되어 있기 때문에 APB1의 주파수를 설정하며 18 MHz로 설정합니다.

2. TIM2에 Clock을 인가합니다.

void initRCC(void) {
/*

	TIM2를 사용할 것이기 때문에 TIM2가 연결 되어 있는 APB1의 주파수를 설정합니다.
    
    여기서는 36 MHz로 설정했습니다.

*/
  RCC -> CFGR = (1 << PLLXTPRE) | (1 << PLLSRC) | (7 << PLLMUL) | (2 << SW) | (4 << HPRE);      // APB1에 18 MHz를 공급합니다.
  RCC -> CR = (1 << PLLON) | (1 << HSEON) | (16 << HSITRIM);            // PLL, HSE를 ON합니다.
  RCC -> APB1ENR = 0x00000001;  // TIM2 활성화
}

 

 

initTIM2()

TIM2의 주기를 0.5s로 설정합니다.

1. TIM2에 공급되는 클럭의 주파수는 1,000 Hz로 Prescaling합니다.

2. TIM2의 주기를 0.5s로 설정합니다.

3. TIM2의 초깃값을 0으로 설정합니다.

4. TIM2와 관련된 모든 상태를 초기화합니다.

void initTIM2(void) {
/*
   TIM2를 0.5s 주기로 설정합니다.

  0.5s마다 TIM2_Handler를 발생시키는 것과 동일한 의미입니다.
*/
  TIM2 -> PSC = 36000 - 1;      // 36000000 / (36000 - 1 + 1) -> 1000  Hz
  TIM2 -> ARR = 500 - 1;        // (500 - 1 + 1) / 1000 -> 0.5 sec
  TIM2 -> CNT = 0;              // TIM2 초기값을 초기화합니다.
  TIM2 -> SR = 0;               // TIM2 관련 모든 상태를 초기화합니다.
}

 

 

initLED

LED 관련 설정들을 초기화합니다.

void initLED(void) {
  RCC -> APB2ENR = (1 << IOPFEN);               // GPIOF에 클럭을 인가합니다.
  GPIOF -> CRL = (1 << MODE6);          // PF6를 Push-pull mode로 설정합니다.
}

 

 

enableTIM2

TIM2를 활성화합니다.

1. TIM2 Interrupt를 활성화 준비를 합니다.

2. NVIC에 연결하여 TIM2를 활성화합니다.

3. Registers가 update되었다는 것을 알려줍니다.

4. TIM2의 counter를 활성화합니다. (-> 최종 활성화)

void enableTIM2(void) {
  TIM2 -> DIER = (1 << 0);      //  TIM2  interrupt를 활성화합니다.
  NVIC_ISER0 = (1 << 28);       // NVIC에 TIM2를 연결합니다.
  
  TIM2 -> EGR = (1 << 0);       // 레지스터가 update 되었다는 것을 알려줍니다. (자세한 것은 더 알아봐야합니다...)
  TIM2 -> CR1 = (1 << 0);       // TIM2 시작
}

 

 

 

Interrupt 코드


TIM2는 TIM2_IRQHandler라는 Handler를 호출합니다.

initTIM2에서 설정한 것처럼, 이 Interrupt는 0.5s마다 호출됩니다.

SR register의 0번째 비트를 기준으로 NVIC는 handler를 호출하기 때문에 Handler가 한 번 호출되면 SR의 0번째 비트를 0으로 다시 초기화 시켜줘서 다음 0.5s 후에 실행되도록 해야합니다.

SR의 0번째 비트를 0으로 초기화하지 않으면 TIM2_IRQHandler가 정상적(의도한데로)으로 동작하지 않습니다.

void TIM2_IRQHandler(void) {    // TIM2에 대한 Handler입니다.
  TIM2 -> SR = 0;       // TIM2가 SR의 0번째 비트를 기준으로 1이면 인터럽트를 발생시키기 때문에 다시 0으로 초기화합니다.(수동으로 초기화 해야함)
  GPIOF -> ODR ^= (1 << 6);     // PF6를 Toggle합니다.
}

 

 

 

최종 코드


최종 코드

/*      기본 정보

  * Ex_07_Timer_easy
  * 2021-02-11
  * 박광렬
  * @copyright Copyright (c) Park Kwangryeol All right reserved.


*/
/*      설명

  타이머를 이용하여 0.5초 주기로 LED를 ON/OFF하는 프로그램입니다.

*/

#include "inc.h"

void initRCC(void);
void initTIM2(void);
void initLED(void);
void enableTIM2(void);


void TIM2_IRQHandler(void) {    // TIM2에 대한 Handler입니다.
  TIM2 -> SR = 0;       // TIM2가 SR의 0번째 비트를 기준으로 1이면 인터럽트를 발생시키기 때문에 다시 0으로 초기화합니다.(수동으로 초기화 해야함)
  GPIOF -> ODR ^= (1 << 6);     // PF6를 Toggle합니다.
}

int main(void) {
  initRCC();    // 기초 RCC 관련 설정을 끝냅니다.
  initTIM2();    // TIM2의 주기를 0.5s로 설정합니다.
  initLED();    // LED 관련 초기화
  enableTIM2(); // TIM2 활성화
  
  while(1);
  
  return 0;
}



void initRCC(void) {
/*

	TIM2를 사용할 것이기 때문에 TIM2가 연결 되어 있는 APB1의 주파수를 설정합니다.
    
    여기서는 18 MHz로 설정했습니다.

*/
  RCC -> CFGR = (1 << PLLXTPRE) | (1 << PLLSRC) | (7 << PLLMUL) | (2 << SW) | (4 << HPRE);      // APB1에 18 MHz를 공급합니다.
  RCC -> CR = (1 << PLLON) | (1 << HSEON) | (16 << HSITRIM);            // PLL, HSE를 ON합니다.
  RCC -> APB1ENR = 0x00000001;  // TIM2 활성화
}

void initTIM2(void) {
/*
   TIM2를 0.5s 주기로 설정합니다.

  0.5s마다 TIM2_Handler를 발생시키는 것과 동일한 의미입니다.
*/
  TIM2 -> PSC = 36000 - 1;      // 36000000 / (36000 - 1 + 1) -> 1000  Hz
  TIM2 -> ARR = 500 - 1;        // (500 - 1 + 1) / 1000 -> 0.5 sec
  TIM2 -> CNT = 0;              // TIM2 초기값을 초기화합니다.
  TIM2 -> SR = 0;               // TIM2 관련 모든 상태를 초기화합니다.
}

void initLED(void) {
  RCC -> APB2ENR = (1 << IOPFEN);               // GPIOF에 클럭을 인가합니다.
  GPIOF -> CRL = (1 << MODE6);          // PF6를 Push-pull mode로 설정합니다.
}

void enableTIM2(void) {
  TIM2 -> DIER = (1 << 0);      //  TIM2  interrupt를 활성화합니다.
  NVIC_ISER0 = (1 << 28);       // NVIC에 TIM2를 연결합니다.
  
  TIM2 -> EGR = (1 << 0);       // 레지스터가 update 되었다는 것을 알려줍니다. (자세한 것은 더 알아봐야합니다...)
  TIM2 -> CR1 = (1 << 0);       // TIM2 시작
}

 

 

 

 

 

 


Continue

 


 

반응형