본문 바로가기
Robot/MCU

Cortex-M3 Start Up Code 분석 3부

by lifeseed 2013. 9. 24.

 

※ 아래의 내용은 직접 작성한 내용이며, 경어를 사용하지 않았습니다.

읽으시는동안 불편하시더라도 이해 부탁드립니다.

그리고 테스트를 위한 보드는 http://oroca.org 에서 공동구매한 stm32 smart robot board를 사용하였습니다.

 

cf) 관련 글 목록 

 

 0. STM32F103CB 개발을 위한 ST Micro Resource 활용

 http://lifeseed.tistory.com/57

 1. Cortex-M3 Link Script 

 http://lifeseed.tistory.com/55

 2. Cortex-M3 Start Up Code 분석 1부 

 http://lifeseed.tistory.com/55

 3. Cortex-M3 Start Up Code 분석 2부 

 http://lifeseed.tistory.com/58

 

 

0. 시작하기 전에

앞서 설명한 Cortex-M3 Start Up Code 분석 1, 2부 에서 startup_xxx.S에 대한 설명을 하였다. startup_xxx.S의 마지막 과정인 SystemInit 함수 호출 후 main함수로 Jump함으로써 하나의 S/W가 구동할 준비가 된다.

본 장에서는 SystemInit함수에 대한 코드를 분석하려고 한다. SystemInit 함수에서 하는 일은 Clock 설정 및 Execption Handler Vector Table의 Base Address 등록 정도라고 이해하면 되겠다.

 

아래는 SystemInit 함수의 Body이다. 코드가 짧은 Vector Table 등록부분을 먼저 설명하고 클럭 설정부분을 다루도록 하겠다.


 1. Execption Handler Vector Table 등록

 

 우선 이 함수의 마지막 부분을 먼저 살펴보자. SCB->VTOR 에 __isr_vector 값을 설정한다.

__isr_vector 는 startup_xxx.S에 정의된 변수로, link scriptor에 정의된 .section .isr_vector의 가장 처음에 위치한다.  즉 코드상에 build되는 execption handler  vector table의 시작 주소를 나타낸다.

 

 

이전의 ARM Seriese는 설정에 따라 0x0 혹은 0xFFFF0000 번지만을 Vector Table로 사용할 수 있었으나,  Cortex-M Series에서는 Execption 이 발생하였을 때 ARM Core가 SCB->VCTOR 에 등록된 Address를 Execption Handler Vectore Table의 Base Address로 인식하도록 한다. 즉, 사용자가 원하는 Address에 Vector Table을 둘 수 있다는 얘기다.

 Reset시에는 0x0번지를 참조한다. 여러가지 이유가 있겠지만, 쉽게 설명하면 Reset 시 SCB->VCTOR 값이 0이기 때문에... 라고 해두자.

 

2. Clock 설정 초기화
 Clock은 내부 클럭 혹은 외부 클럭을 사용할 수 있으며, 여기서는 외부 클럭 8MHz를 이용하여 72MHz 의 Core Clock을 생성 시킨다.

SystemInit의 코드 앞부분은 내부 Clock를 Enable 하고 PLLSRC를 통하여 내부 클럭을 PLL로 설정하여 System Clock으로 사용되도록 설정되었으며, 외부 Clock관련 Register들을 초기화 한다.

이후 SetSysClock에서는 Define 문에 의해 SetSysClockTo72함수가 호출되며 여기서 외부 클럭을 이용하여 72MHz System Clock으로 설정된다.


 

3. 외부 Clock 설정

 

 

우선 CR Register의 HSEON을 이용하여 외부 Clock을 사용가능하도록 설정하고 HSERDY를 통하여 정상적으로 동작하는지 특정 시간동안 확인한다.

비정상적인 경우 더이상 아무런 설정이 없이 함수가 종료되므로, 앞서 설정한 대로 내부 Clock으로 코드가 동작하게 되고, 정상적으로 설정된 경우 72M 분주를 위한 세팅을 수행한다.


 

4. AHB, APB Prescale 설정

 

 

AHB Prescaler 및 APB high speed prescaler는 system clock을 그대로 사용하고, APB low speed prescaler는 HCLK의 1/2 clock을 사용하도록 설정한다.

 

 HCLK = SYSCLK

PCLK2 = HCLK

PCLK1 = HCLK/2

 

5. PLL 설정 

 

 

PLLSRC, PLLXTPRE, PLLMULL 비트를 0으로 초기화 하고 HSE, PLLMULL9 값을 사용하도록 한다.

즉 여기서 외부클럭 8MHz가 x9 되어 72MHz로 설정된다.


 

즉…

SYSCLK = 72MHz

HCLK = 72MHz

PCLK1 = 36MHz

PCLK2 = 72MHz

 

로 설정된다.


 마지막으로 PLL을 Enable시키고 PLL을 System Clock이 되도록 설정하면 클럭 설정이 완료된다.



 

cf) Clock 설정

 Clock설정 관련 RCC의 각 Register의 역할과 세부적인 내용은 Naver Cafe Oroca의 smartrobot 님이 작성한 글을 참조하자.

 [STM32]초기화 코드 분석 - 4. Vector table 처리 이후 (!오픈소스/하드웨어로 만들어가는 로봇 기술 공유 카페! 오로카!)

 

cf) 이하 첨부된 글은 링크의 본문입니다.

위의 그림은 STM32의 내부 클럭과 관련된 블록다이어 그램이다.


STM32의 RCC(Reset and Clock Control)관련 레지스터 메모리 맵을 표시하였다. 이를 통해서 STM32의 전반적인 HW 시스템에서 사용하는 System Clock 및 각 기능별로 제공되는 clock의 특성이 결정이 된다.


 

STM32에서 clock 설정은 세가지 방식에 의해서 설정되어진다.

1. 내부 clock을 사용하는 경우 - 내부에 8MHz의 clock source가 내장됨

2. 외부 clock을 사용하는 경우 - 모든 8MHz 또는 12MHz의 crystal을 사용한다.

3. 내부 또는 외부 clock을 중 하나를 선택한 후에 PLL 통하여 체배해서 사용하는 경우

내부나 외부 Clock을 바로 쓰는 경우에는 Clock 주파수가 그리 높지 않게 때문에 보통은 PLL을 사용하여 체배하여 사용하는 것이 일반적이다. 여기서 체배라함은 간단하게 곱셈을 한다 정도로 정의한다. PLL에 대하여 깊게 들어가려면 책한권의 분량이된다. 하지만 여기서 사용되는 용도는 입력되는 낮은 주파수를 배수가 되는 주파수로 올려주는 역할을 한다.


 

권고 대는 Cortex-M3 계열의 STM32 CPU clock의 최고치는 72MHz이므로 외부에 8MHz 그리고 9배 체배를 통하여 72MHz의 clock 주파수를 발생시킬 수 있으며 내부 clock을 사용할 경우에는 PLL로 입력되기 전에 2분주가 되어서 4MHz가 발생하며 이를 9배 체배하게 되면 36MHz Clock 주파수가 최대치가 되게 된다. 반면에 외부 clock을 사용할 경우 SmartRobot 보드의 경우는 8MHz의 crystal을 사용하게 때문에  9배 체배하게 되면 72MHz Clock 주파수가 발생한다.


 

RCC 레지스터


 


 

위 그림은 PLL을 사용한 Clock 설정 과정을 표시하였다. 일반적으로 외부 Crystal을 사용하는 경우에는 보드 마다 사용하는 crystal이 다르게 때문에 정상적으로 동작하지 않을 가능성이 있어서 내부 clock 소스를 사용하는 방식을 사용하여 STM32F103을 사용하는 CPU 들은 전부다 동작할 수 있도록 하였다. 향후에 보드의 종류에 따라 외부 clock을 사용할 수 있도록 소스에서 주석 처리를 하였다.


 

내부 clock 소스를 사용하는 경우에는 파란색으로 표시한 부분을 설정하면 36MHz로 동작할 수 있도록 하는 루틴이다.

1. HSION을 1로  설정하여 8MHz 내부 clock이 발진하도록 한다. 8MHz로 발진하지만 PLL로 들어가는 경로에는 2분주 회로가 있어서 4MHz로 된다.

2. PLLSRC를 1로 하여서 내부 clock이 선택되도록 한다.

3. PLLMUX를 9로 설정하여 36MHz로 동작하도록 한다.


 

외부 clock 소스를 사용하는 경우에는 빨간색으로 표시한 부분을 설정하면 72MHz로 동작할 수 있도록 하는 루틴이다.

1. HSEON을 1로  설정하여  8MHz 외부 clock이 발진하도록 한다. 어떤 보드들은 16MHz crystal을 사용하는 경우도 있다.

2. PLLXTPRE을 1로 설정하여 분주 없이 PLL에 전달되도록한다.

3. PLLSRC를 0로 하여서 외부 clock이 선택되도록 한다.

4. Crystal 이 8MHz인경우에는 PLLMUX를 9로 설정하여 72MHz로 동작하도록 한다. Crystal 이 12MHz인경우에는 PLLMUX를 6로 설정하여 72MHz로 동작하도록 한다.



 

위에 설명한 내용 들은 RCC 레지스터에 의해 제어되어서 PLL과 clock의 경로가 설정되어서 SYSTEM Clock이 설정된다.


////HSI(0x1) or HSE ON(0x1<<16)

*(volatile unsigned long *) 0x40021000 |= 0x1; //RCC_CR - HSION Set HSION bit

//*(volatile unsigned long *) 0x40021000 |= 0x1<<16; //RCC_CR CR_HSEON_Set

// 설정된 clock 이 설정 되었는지 확인

do

{

// HSEStatus = (*(volatile unsigned long *) 0x40021000 & 0x1<<17); // HSE 인경우

HSEStatus = (*(volatile unsigned long *) 0x40021000 & 0x1<<1); // HSI 인경우

StartUpCounter++;

} while((HSEStatus == 0) && (StartUpCounter != 0x0500));

위의 코드에서 주석이 되지않은 코드 부분이 내부 클럭을 사용한 경우이다. HSION을 1로 하고 HSIRDY가 1이 될 때까지 기다리는 루틴이다. 주석 처리된 코드가 외부 외부 클럭을 사용한 경우이다. HSEON을 1로하고 HSERDY가 1이 될 때까지 기다리는 루틴이다.

 


/////// PLL 설정 내부 일 경우 36MHz 외부 일 경우 72Mhz로 설정함

*(volatile unsigned long *) 0x40021004 &= ~(0xF<<18 | 0x1<<17 | 0x1<<16); //0x3F0000; //RCC_CFGR

*(volatile unsigned long *) 0x40021004 |= (0x7<<18); //RCC_CFGR //내부 4MHz

//*(volatile unsigned long *) 0x40021004 |= (0x7<<18 | 0x1<<16); //0x1D0000; //RCC_CFGR //8MHz

//*(volatile unsigned long *) 0x40021004 |= (0x4<<18 | 0x1<<16); //0x110000; //RCC_CFGR //12MHz

*(volatile unsigned long *) 0x40021000 |= 0x01 << 24/*0x1000000*/; //PLLON

while( ((*(volatile unsigned long *) 0x40021000) & 0x01 << 25/*0x2000000*/) == 0); //PLLRDY

*

내부 클럭인 경우는 PLLSRC = 0으로 설정이 되어서 내부 clock 을 사용하는 것으로 결정이 된다. 이때 POLLMUL[3:0]는 7값이 설정이되며 여기서 7은 9배 체배하라는 의미로 해석하면된다.


 

외부 클럭인 경우는 PLLSRC = 1로 설정이 되어서 외부 clock 을 사용하는 것으로 결정이 된다. 그리고 PLLXTPRE 는 으로 설정하여 2분주하지 않고 바로 PLL에 전달되도록 한다. POLLMUL[3:0]는 7값이 설정이되며 여기서 7은 9배 체배하라는 의미로 해석하면된다.


 

PLL입력 경로 및 체배값 등의 설정이 완료되면 PLL이 동작 되도록 PLLON을 1로 한 후에 상태 bit인 PLLRDY가 1이될 때까지 대기한다.

 


/////PLL을 선택한다.

/////Bits 1:0 SW : System clock switch

//00: HSI selected as system clock

//01: HSE selected as system clock

//10: PLL selected as system clock <----------------- 선택됨

//11: not allowed

*(volatile unsigned long *) 0x40021004 &= ~0x3;

*(volatile unsigned long *) 0x40021004 |= 0x2;

while( ((*(volatile unsigned long *) 0x40021004) & 0xC) != 0x08);



최종적으로 PLL과 system clock이 연결되록 SW(System clock switch)[1:0]을 0x02로 설정을하면된다.