1. Programming

 

이번에 공부해볼 알고리즘은 Dynamic Programming, 즉 우리말로 동적계획법이다.

여기서 Programming 이란 단어는 흔히 생각하는 Computer language를 이용한 programming이 아닌, 

'계획법' 혹은 '제한 속에서의 조건부 최적화'을 의미.

동적 계획법이란 용어를 처음 사용한 수학자 리처드 벨만은 '시가변적이며 다단계적인' 특성을 나타내기 위해서 Dynamic 이란 용어를 채택해 이름을 붙였다고 한다.

 

이러한 Programming에는 Linear Programming, Quadratic Programming, Integer Programming 등의 여러 기법이 존재한다.  특징으로는 모두 제한된 조건속에서의 최적화를 다룬다.

 

Linear Programming

 

Quadratic Programming

 

Integer Programming

 


2. Dynamic Programming

 

동적 계획법은 복잡한 문제를 간단한 여러 개의 문제로 나누어 푸는 방법을 말한다. 

이때 과거의 해를 활용하여 현재의 문제해결을 한다는 원리를 가진다.

사실 이 동적계획법은 알고리즘시간에 가장먼저 등장하는 개념이긴 하지만, 내 생각으론

단순 Algorithm 이라기 보다는, 일종의 기술을 만드는 기술인 metatechnique 이다.

한마디로 기술 구현이나 문제해결을 보다 쉽게 해결할 수 있도록 도와주는 역할을 한다.

 

이러한 동적계획법을 설몋알때 가장 기본적으로 등장하는것이 피보나치 수열이다.

 

피보나치

다음의 코드를 보면 재귀적으로 수행되는 피보나치 수열코드를 볼수있다.

이러한 코드는 fib(n-1)과 fib(n-2) 작업에서 겹치는 작업을 할 수 있다는 문제점이 발생하는데

다음과 같이 fib(5)는 각각의 fib(4) 와 fib(3)으로 나누어지지만, fib(4)내의 재귀 호출의 과정에서 fib(3)을 다시호출해 비효율적인 계산 과정을 거친다. 이때 각 계산과정에서 계산결과를 저장해놓기만 해도, 다음호출에서 계산수행을 할필요없이결과만 가져오면 되기에 효율적인 연산을 할 수 있다는 것이다.

그렇다면 다음 두개의 코드를 비교해보자.

 

Memorization ( Top-Down )
Tabulation ( Bottom-Up )

 

두 코드의 차이는 일단 재귀연산을 하되 계산값들을 저장하고 다음과정을 진행한다는 공통점이 있긴 하다.

하지만 엄연히 방식은 다르다. Memorization 기법은 가장먼저 구하고자하는 n번째의 피보나치수열의 배열값을 구하고자 재귀적으로 내려가는 Top-Down 방식을 취하고 아래의 Tabulation은 구하고자하는 n번째 피보나치수열 값까지 for문을 통해 하나하나 채워나가는 Bottom-Up방식을 취한다.

이렇듯 두 가지 경우모두 이전과정을 이용한다는 동적 계획법이다. 단지 코드 및 구현의 차이일뿐이다.


3. Longest Common Subsequence (LCS)

 

Dynammic Programming을 통해 해결할 수 있는 잘 알려진 문제 LCS 알고리즘까지 다뤄보고,

또다른 문제인 배낭문제, Knapsack Algoritm이라고도 하는 문제는 다음 포스팅에서 다루어 보도록 하겠다.

 

LCS 문제는 말그래도 가장 긴 공통된 부분수열을 찾는 문제이다.

 

Longest Common Subsequence

 

다음과 같은 그림에서, X문자열과 Y문자열의 가장 긴 부분문자열은 BCBA가 될 것이다. 물론 경우에 따라 답이 2개이상 존재하기도 하지만, 그것은 프로그래밍 기법에따라 차이가 있을수 있다.

결국 이 문제는 동적프로그래밍 기법으로 해결 가능하다. 이러한 DP, 즉 동적프로그래밍 문제들의 공통점은 표를 만들면 쉽게 생각이 가능한데, 앞선 결과들을 이용하는 과정을 가장 잘 표현할 수 있기 때문이다.

 

 

여기서 예시로 드는 문자열은 

 

X = ABCB

Y = BDCAB 

이다.

위와 같이 빈 표를 그리고, 다음과같이 x,y축에 각각 문자열들을 배치한다. 이때 각각의 0행과 0열은 0으로 채워준다.

 

다음과정은 이제 오른쪽-> 방향으로 가며 문자열의 일치를 확인할 것이다. 이 표안의 수는 이때까지 일치한 문자열, 즉부분수열의 최대 길이인 lcs 가 될 것이다. (1,1) 까지는 문자열이 일치한 경우가 없으니 0

 

계속 따라가면 처음으로 Y문자열의 'A'와 X문자열의 'A' 가 일치한다. 따라서 1로 채워주면 된다.

 

이때 다음 문자가 일치한다면, lcs는 현재 2가되겠지만, 다르므로 아직 lcs는 1이다.

 

위의 경우를 보면 두번째로 일치하는 문자 'B'가 나왔다.

이때 앞선 lcs 1값에다가 1을 추가해 2로 만들어주면 되는데, 이때는 현재 index의 이전대각방향의 값에 1을 더해주면된다. 결론적으로 문자가 일치않으면 위 혹은 왼쪽값과 비교해 큰값을 가져오고, 문자가 일치하면 왼쪽 대각값에 1을더한값을 넣어주면된다.

 

일반화하면 다음과 같다.

if(Xi == Yj) c[i , j] = c[i-1 , j-1] + 1
else c[i , j] = max( c[i-1 ,  j], c[i ,  j-1] )

 

위의 결과가 나왔다면 성공이다.

그렇다면 코드구현을 보자.

'Algorithm' 카테고리의 다른 글

Quick Sort, Merge Sort, and Locality  (0) 2021.02.02

학교 수업시간들에서 흔하게 배울수 있는 Sorting 에는 Selection, Bubble, Insertion 등의 n² 의 시간복잡도를 가지는 정렬과 Quick sort와 Merge sort같이 nlogn의 시간복잡도를 가지는 대표적인 정렬 방법이 있다.

 

인터넷 블로그들을 떠돌다가, 일반적으로 Quick sort가 Merge sort보다 크다는 정보를 접하였다.

하지만 대게 알려지기로는 Quick sort는 피봇의 선정에 따라 최악의 경우 n² 의 시간복잡도를 보여줄 여지도 있다.

이러한 Quick sort가 Merge sort보다 대게 성능이 좋은 이유는 Locality와 관련이 있다고 한다. Locality의 개념을 알아보고 왜 Quick sort가 더 빠른지 알아보도록 하자.

 


1. Quick Sort and Merge Sort

 

int Partition(int arr[], int left, int right)
{
    int pivot = arr[left]; 
    int low = left + 1;
    int high = right;
 
    while (low <= high) 
    {
        while (low <= right && pivot >= arr[low])
        {
            low++; 
        }
        while (high >= (left+1)  && pivot <= arr[high]) 
        {
            high--;
        }
        if (low <= high)
        {
            Swap(arr, low, high);
        }
    }
    Swap(arr, left, high);
    return high; 
 
}
 
void QuickSort(int arr[], int left, int right)
{
    if (left <= right)
    {
        int pivot = Partition(arr, left, right);
        QuickSort(arr, left, pivot - 1); 
        QuickSort(arr, pivot + 1, right); 
    }
}

 

위는 일반적인 Quick Sort 코드이다. 

이 퀵정렬에서는 다른 정렬법과는 다르게 pivot을 사용한다.

코드상에서 while문을 통해 pivot값을 기준으로 큰값과 작은값을 좌우로 나누는것을 알수있다. 그후 재귀적으로 다시 나뉜 부분또한 pivot을 설정해 정렬을 수행한다.

퀵정렬의 느낌은 한마디로  큰 그룹에서부터 작은 그룹으로 연산이 진행되는 Top-Down 방식이라 할 수 있다.

눈여겨 보아야 할 것은 별도의 메모리가 필요하지 않다. 위코드 내에서도 단지 arr배열 내에서 정렬이 수행된다.

 

void mergeSort(int data[], int p, int r) {
    int q;
    if(p<r) {
        q = (p+r)/2;
        mergeSort(data, p , q);
        mergeSort(data, q+1, r);
        merge(data, p, q, r);
    }
}
void merge(int data[], int p, int q, int r) {
    int i = p, j = q+1, k = p;
    int tmp[8]; 
    while(i<=q && j<=r) {
        if(data[i] <= data[j]) tmp[k++] = data[i++];
        else tmp[k++] = data[j++];
    }
    while(i<=q) tmp[k++] = data[i++];
    while(j<=r) tmp[k++] = data[j++];
    for(int a = p; a<=r; a++) data[a] = tmp[a];
}

Merge Sort

다음으로 위는 Merge Sort이다.

이 합병 정렬의 경우 일단 각각으로 다 쪼개어 작은 그룹을 정렬한 후 더 큰 그룹으로 합쳐나가는 Bottom-Up 방식이다.

이러한 과정에서 데이터들을 임시적으로 저장하는 temp배열이 필요하다.

 

 


2. 참조의 지역성(Locality of Refrence)과 메모리

 

학부 운영체제 수업을 듣다보면 지역성의 원리라는 단어에 대해 듣게 된다.

이러한 지역성(Locality)은 CPU가 짧은 시간 범위 내에 일정 구간의 메모리 영역을 반복적으로 엑세스하는 경향을 말한다. 메모리 내의 정보를 균일하게 엑세스 하는게 아닌, 짧은 시간내에 특정 부분을 집중적으로 참조하는 특성이다.

 

Memory hierachy

 

이러한 지역성의 원리를 설명할때 가장 자주등장하는게 메모리 계층인데, 결론적으로 가장 속도가 빠른 레지스터는 

하위계층들까지의 접근을 대신해 데이터를 가지게 되는데, 이 작업을 캐싱(Caching)이라 하고, 이전에 사용한 데이터가

다시 사용될것을 예측하고 시기적절하게 상위 계층 메모리에 갖다놓는것이다.

이때 다음 아래의 두가지 원리를 통해 해결할 수 있다.

 

시간적 지역성(Temporal Locality) : 최근 접근된 내용이 또 다시 접근될 확률이 높다.
공간적 지역성(Spatial Locality) : 한 지역의 내용이 접근되었다면, 그 주위의 내용도 접근될 확률이 높다.

 

그렇다면 왜 Locality가 정렬 알고리즘의 평가 기준이 될 수 있을까?

 

 


3. Quick Sort, Merge Sort, and Locality of Refrence

 

정렬하려고 하는 데이터들이 다른 페이지로 이동하는 것 없이, 자신의 페이지에서 계속 있는게 좋다. 다른 캐쉬에 없는 페이지로 이동하면 page cache hit 비율이 떨어지게 된다.

그래서 결국 physical memory로 접근을 해야 하기 때문에 시간이 상대적으로 오래 걸린다.

만약 자신의 페이지에 계속 있는다면, cache에서 반복적으로 접근하기 때문에 시간이 덜 걸린다. 결국 데이터가 이동하지 않을 수록 좋다.

 

위 코드에서 알수 있듯이 Quick Sort는 Merge Sort에 비해 pivot에 의한 분할은 했지만 데이터가 존재하는 위치는 변하지 않는다. 따라서 제자리정렬이라 할수있으며 지역성의 원리에 따라 더 빠른 성능을 보이기도 한다는 것이다.

 

 

 

Merge Sort

Merge Sort

 

 

Quick Sort

Quick Sort


#Reference

https://medium.com/pocs/locality%EC%9D%98-%EA%B4%80%EC%A0%90%EC%97%90%EC%84%9C-quick-sort%EA%B0%80-merge-sort%EB%B3%B4%EB%8B%A4-%EB%B9%A0%EB%A5%B8-%EC%9D%B4%EC%9C%A0-824798181693

https://velog.io/@agugu95/%EC%B0%B8%EC%A1%B0%EC%9D%98-%EC%A7%80%EC%97%AD%EC%84%B1%EA%B3%BC-Quicksort-vs-Mergesort

 

'Algorithm' 카테고리의 다른 글

Dynamic Programming(1)  (0) 2021.02.02

Who is he?

 

암달의 법칙(Amdahl's law)은 미국의 컴퓨터 공학자이자 기업가였던 진 암달(Gene Amdahl)이 만든 법칙으로 암달의 저주(Amdahl's curse), 암달의 인수(Amdahl's argument)라고도 불린다.


진 암달(Gene Amdahl, 1922년 11월 16일 ~ 2015년 11월 10일)은 위스콘신 대학교 매디슨에서 이론물리학 석박사를 취득했고, 52년에 IBM으로 입사해 704 메인프레임, 시스템 360 메인프레임 등의 개발을 총괄하기도 했다. 암달의 법칙은 1965년 옮긴 어드밴스드컴퓨팅시스템랩에서 만든 것으로 컴퓨터 성능 최적화의 한계점을 측정하기 위해 만든 법칙이다.

Gene Amdahl

 


Amdahl's Law?

 

컴퓨터 좀 알아본 사람이면 듀얼코어, 쿼드코어 같은 말을 들어본적이 있을것이다.

이러한 코어는 결국 CPU를 의미하는데, 인텔의 공동설립자인 고든 무어는 1960년대 당시 18개월에서 24개월마다 CPU속도는 2배향상될 것이라고 말했다. 이것이 유명한 무어의 법칙.

 

무어의 법칙은 끝인가?

이 법칙은 나름 2000년대 초반까지 맞아들어갔고, 파란색 그래프, Transistors per chip은 향상되나 speed는 더이상 증가하지 않는다고도 볼수있는 시점에 왔다. 따라서 이때부터 CPU 의 개수를 늘리는방법을 고안해냈다.

하지만 컴퓨터의 두뇌라고 할수있는 CPU가 많아진다고 해서 단순히 속도가 빨라질수 있을까?

무어의 법칙에 따르면 아니라는 것이다. 

물론 아닐꺼라고 짐작은 가능하다. 하지만 왜일까?

 

 

Amdahl's law

프로그램에는 여러개의 코어로 병렬처리할수 있는 부분이 있고, 없는 부분이 있을것이다.

당연스럽게도, 코어개수를 늘리면 병렬처리 가능한 부분은 개선이 되지만 그것은 일부일 뿐이고 나머지 부분에는 개선이 없다는 것이다. 

위그림에서보면 보라색부분은 3개의 코어로 병렬처리된다고 가정하면, 주황색부분은 병럴처리 불가능하기 때문에

단순히 3배의 성능향상이라고는 볼 수 없다는 것이다.

 

 

Amdahl's law

수식을 딱히 좋아하진 않지만, 간단히 살펴보면 1-P는 병렬처리가 불가능한 부분이고, P는 병럴처리가 가능한 부분이라고 하고 S를 프로세서 개수라고 할때 100개의 코어로된 시스템으로 90프로가 병럴처리가능한 프로그램을 처리한다면,

 1 / (1-(0.9) + 0.9/100) = 대충 10에 수렴한다. 따라서 이 프로그램의 최대 성능향상은 최대 10배이다.

 

 

결론적으로 병렬처리프로그램에서 차례로 수행되어야하는 비교적 적은 수의 명령문들이,프로세서의 수를 추가하더라도 그 프로그램의 실행을 더 빠르게할 수 없도록 속도향상을 제한하는 요소를 갖고 있다는 것이다.

 

마지막 프로젝트로 7segment를 이용한 간단한 전자시계를 만들었다.

사용된 기능은 GPIO, Timer interrupt, Switch interrupt, ADC 기능이다.

 

NXP S32K144 PinMap

 

7segment schematic

 

 

먼저 7segment 핀 이용을 위한 정의이다.

#define DIG1 1
#define DIG2 2
#define DIG3 6
#define DIG4 8

#define SEG1 14
#define SEG2 4
#define SEG3 13
#define SEG4 3
#define SEG5 5
#define SEG6 11
#define SEG7 10
#define SEG8 7 //dot

#define DOT 17

#define PTC12 12
#define PTC13 13
#define PTD15 15
#define PTD16 16
#define PTD0 0

volatile int timer = 0;
volatile int setmode = 0;
int digpin[4] = {DIG1,DIG2,DIG3,DIG4};
int segpin[8] = {SEG1,SEG2,SEG3,SEG4,SEG5,SEG6,SEG7,SEG8};

 

 

가장 기본적인 port초기화에 대한 내용이다.

사용하고자 하는 pin들을 모두 GPIO설정 해주었다.

void PORT_init(void){

   PCC -> PCCn[PCC_PORTC_INDEX] = PCC_PCCn_CGC_MASK;
   PORTC ->PCR[12] = PORT_PCR_MUX(1);
   PTC ->PDDR &= ~(1<<PTC12);
   PORTC->PCR[12] |= (9<<16);

   PCC->PCCn[PCC_PORTD_INDEX] |=PCC_PCCn_CGC_MASK;

	for(int i=0;i<4;i++){
			PORTD->PCR[digpin[i]] &= ~PORT_PCR_MUX_MASK;
			PORTD->PCR[digpin[i]]|= PORT_PCR_MUX(1);
			PTD->PDDR |= 1<<digpin[i];
		}

	for(int i=0;i<8;i++){
			PORTD->PCR[segpin[i]] &= ~PORT_PCR_MUX_MASK;
			PORTD->PCR[segpin[i]]|= PORT_PCR_MUX(1);
			PTD->PDDR |= 1<<segpin[i];
		}

		PORTD->PCR[17] &= ~PORT_PCR_MUX_MASK;
		PORTD->PCR[17]|= PORT_PCR_MUX(1);
		PTD->PDDR |= 1<<DOT;

		PORTD->PCR[0] &= ~PORT_PCR_MUX_MASK;
		PORTD->PCR[0] |= PORT_PCR_MUX(1);
		PTD->PDDR |= 1<<PTD0; /////Blue
}

 

 

setTime 함수는 앞으로 타이머 인터럽트가 들어올때마다 계속해서 호출될것이다.

7segment의 원리상 4개의 digit을 가지고있고, 이를 모두 동시에 켤순는 없다.

자리수마다 한개씩 매우 빠르게 번갈아 숫자를 갱신하는 원리이므로, setTime은 그때마다 호출되게된다.

void setTime(int dig_num, int n){
	for(int i=0;i<8;i++){
		PTD->PSOR |= 1<<segpin[i]; //pin clear
	}

	PTD-> PSOR |= 1<<digpin[dig_num];

	switch(n){
	case 0:
		PTD->PCOR |= 1<<SEG1;
		PTD->PCOR |= 1<<SEG2;
		PTD->PCOR |= 1<<SEG3;
		PTD->PCOR |= 1<<SEG4;
		PTD->PCOR |= 1<<SEG5;
		PTD->PCOR |= 1<<SEG6;
		break;
	case 1:
		PTD->PCOR |= 1<<SEG2;
		PTD->PCOR |= 1<<SEG3;
		break;
	case 2:
		PTD->PCOR |= 1<<SEG1;
		PTD->PCOR |= 1<<SEG2;
		PTD->PCOR |= 1<<SEG4;
		PTD->PCOR |= 1<<SEG5;
		PTD->PCOR |= 1<<SEG7;
		break;
	case 3:
		PTD->PCOR |= 1<<SEG1;
		PTD->PCOR |= 1<<SEG2;
		PTD->PCOR |= 1<<SEG3;
		PTD->PCOR |= 1<<SEG4;
		PTD->PCOR |= 1<<SEG7;
		break;
	case 4:
		PTD->PCOR |= 1<<SEG2;
		PTD->PCOR |= 1<<SEG3;
		PTD->PCOR |= 1<<SEG6;
		PTD->PCOR |= 1<<SEG7;
		break;
	case 5:
		PTD->PCOR |= 1<<SEG1;
		PTD->PCOR |= 1<<SEG3;
		PTD->PCOR |= 1<<SEG4;
		PTD->PCOR |= 1<<SEG6;
		PTD->PCOR |= 1<<SEG7;
		break;
	case 6:
		PTD->PCOR |= 1<<SEG1;
		PTD->PCOR |= 1<<SEG3;
		PTD->PCOR |= 1<<SEG4;
		PTD->PCOR |= 1<<SEG5;
		PTD->PCOR |= 1<<SEG6;
		PTD->PCOR |= 1<<SEG7;
		break;
	case 7:
		PTD->PCOR |= 1<<SEG1;
		PTD->PCOR |= 1<<SEG2;
		PTD->PCOR |= 1<<SEG3;
		PTD->PCOR |= 1<<SEG6;
		break;
	case 8:
		PTD->PCOR |= 1<<SEG1;
		PTD->PCOR |= 1<<SEG2;
		PTD->PCOR |= 1<<SEG3;
		PTD->PCOR |= 1<<SEG4;
		PTD->PCOR |= 1<<SEG5;
		PTD->PCOR |= 1<<SEG6;
		PTD->PCOR |= 1<<SEG7;
		break;
	case 9:
		PTD->PCOR |= 1<<SEG1;
		PTD->PCOR |= 1<<SEG2;
		PTD->PCOR |= 1<<SEG3;
		PTD->PCOR |= 1<<SEG4;
		PTD->PCOR |= 1<<SEG6;
		PTD->PCOR |= 1<<SEG7;
		break;
	}
	PTD-> PCOR |= 1<<digpin[dig_num];
}

 

 

main에서는 기본적으로 무한루프내에서 시간의 최대, 즉 24:00을 넘어가게 되면 0으로 초기화되게 된다.

초로 환산하면 86400초이다.

또한 ADC기능을 추가한부분, timer가 홀수와 짝수일때로 나누어 시계의 : 부분이 깜빡이도록 하는 부분까지 확인할 수 있다.

int main(void){
	PORT_init();
	SOSC_init_8MHz();
	SPLL_init_160MHz();
	NormalRUNmode_80MHz();
	NVIC_init_IRQs();
	ADC_init();
	LPIT0_init();
	int hour, min, sec, adc_num;
		for(int i=0;i<8;i++){
			PTD->PSOR |= 1<<segpin[i]; //pin clear
		}

		for(;;){


		convertAdcChan(12);
		if(setmode){
			while(adc_complete()==0){}
			timer = read_adc_chx()*(17.28);
		}
		if(timer == 86400)
			timer = 0;

		if(((timer >=0) && (timer <5) )|| ((timer >= 43200) && (timer < 43205)))
			PTD-> PCOR |= 1<<PTD0;
		else PTD-> PSOR |= 1<<PTD0;

		if((timer % 2) == 0)
			PTD->PCOR |= 1<<DOT;
		else PTD->PSOR |= 1<<DOT;


		hour = timer/3600;
		min = (timer%3600)/60;
		sec = ((timer%3600)%60)%60;


		setTime(0,hour/10);
		setTime(1,hour%10);
		setTime(2,min/10);
		setTime(3,min%10);
		}


}

 

 

실행 영상

 

 

 

 

 

포스팅내 모든 소스코드는 아래사이트에서 확인할 수 있습니다.

github.com/MinkiJo/NXP-S32DesignStudio-Project/tree/main

 

MinkiJo/NXP-S32DesignStudio-Project

마이크로프로세서 설계실험 NXP보드를 이용한 시계만들기입니다. Contribute to MinkiJo/NXP-S32DesignStudio-Project development by creating an account on GitHub.

github.com

 

'NXP&S32DesignStudio' 카테고리의 다른 글

PWM  (0) 2021.01.17
ADC  (0) 2021.01.17
Timer Interrupt  (0) 2021.01.17
GPIO & Switch  (0) 2021.01.17

PWM

 

 

클럭생성 및 클럭설정, 시스템이 동작하기 위한 System Clock/Bus Clock/Flash Clock을 생성하는 부분이다.

void SOSC_init_8Mhz(void){
   SCG->SOSCDIV = SCG_SOSCDIV_SOSCDIV1(1)|SCG_SOSCDIV_SOSCDIV2(1);

   SCG->SOSCCFG = SCG_SOSCCFG_RANGE(2)|SCG_SOSCCFG_EREFS_MASK;

   while(SCG->SOSCCSR & SCG_SOSCCSR_LK_MASK);
   SCG->SOSCCSR = SCG_SOSCCSR_SOSCEN_MASK;

   while(!(SCG->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK));
}

void SPLL_init_160Mhz(void){
   while(SCG->SPLLCSR & SCG_SPLLCSR_LK_MASK);
   SCG->SPLLCSR &= ~SCG_SPLLCSR_SPLLEN_MASK;

   SCG->SPLLDIV |= SCG_SPLLDIV_SPLLDIV1(2)|SCG_SPLLDIV_SPLLDIV2(3);
   SCG->SPLLCFG = SCG_SPLLCFG_MULT(24);

   while(SCG->SPLLCSR & SCG_SPLLCSR_LK_MASK);
   SCG->SPLLCSR |= SCG_SPLLCSR_SPLLEN_MASK;

   while(!(SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK));
}

void NormalRUNMode_80Mhz(void){
   SCG->SIRCDIV = SCG_SIRCDIV_SIRCDIV1(1)|SCG_SIRCDIV_SIRCDIV2(1);

   SCG->RCCR = SCG_RCCR_SCS(6)
         |SCG_RCCR_DIVCORE(0b01)
         |SCG_RCCR_DIVBUS(0b01)
         |SCG_RCCR_DIVSLOW(0b10);

   while(((SCG->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT) != 6){}
}

 

 

 

FTM의 PWM을 사용하기 위한 설정을 한다. 

void FTM0_CH1_PWM(void){
	PCC->PCCn[PCC_FTM0_INDEX] &= ~PCC_PCCn_CGC_MASK;
	PCC->PCCn[PCC_FTM0_INDEX] |= PCC_PCCn_PCS(0b010)
							| PCC_PCCn_CGC_MASK;

	FTM0->SC = FTM_SC_PWMEN1_MASK
			|FTM_SC_PS(1);

	FTM0->MOD = 16000 - 1;

	FTM0->CNTIN = FTM_CNTIN_INIT(0);

	FTM0->CONTROLS[1].CnSC |= FTM_CnSC_MSB_MASK;
	FTM0->CONTROLS[1].CnSC |= FTM_CnSC_ELSA_MASK;

	FTM0->CONTROLS[1].CnV = 2048;
	FTM0->SC |= FTM_SC_CLKS(3);
}

 

 

 

실행결과

 

'NXP&S32DesignStudio' 카테고리의 다른 글

전자시계 만들기  (0) 2021.01.17
ADC  (0) 2021.01.17
Timer Interrupt  (0) 2021.01.17
GPIO & Switch  (0) 2021.01.17

보드의 ADC기능을 확인하기위해 가변저항을 사용해 변화되는 전압값에따른 LED의 밝기 차이를 확인할 것이다.

 

 

먼저 타이머 코드에서 사용한 클럭설정과 관련된 함수들을 가져오자.

void SOSC_init_8MHz(void)
{
	SCG->SOSCDIV = SCG_SOSCDIV_SOSCDIV1(1) |
			SCG_SOSCDIV_SOSCDIV2(1);
	SCG->SOSCCFG = SCG_SOSCCFG_RANGE(2)|SCG_SOSCCFG_EREFS_MASK;

	while(SCG->SOSCCSR & SCG_SOSCCSR_LK_MASK);
	SCG->SOSCCSR = SCG_SOSCCSR_SOSCEN_MASK;




	while(!(SCG->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK));
}
void SPLL_init_160MHz(void){
	while(SCG->SPLLCSR & SCG_SPLLCSR_LK_MASK);
	SCG->SPLLCSR &= ~SCG_SPLLCSR_SPLLEN_MASK;
	SCG->SPLLDIV |= SCG_SPLLDIV_SPLLDIV1(2)|
					SCG_SPLLDIV_SPLLDIV2(3);

	SCG->SPLLCFG = SCG_SPLLCFG_MULT(24);

	while(SCG->SPLLCSR & SCG_SPLLCSR_LK_MASK);
	SCG->SPLLCSR &= ~SCG_SPLLCSR_SPLLEN_MASK;

	while(!(SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK));
}
void NormalRUNmode_80MHz(void){
	SCG->SIRCDIV = SCG_SIRCDIV_SIRCDIV1(1) | SCG_SIRCDIV_SIRCDIV2(1);


	SCG->RCCR = SCG_RCCR_SCS(6)
			|SCG_RCCR_DIVCORE(0b01)
			|SCG_RCCR_DIVBUS(0b01)
			|SCG_RCCR_DIVSLOW(0b10);

	while(((SCG->CSR & SCG_CSR_SCS_MASK)>> SCG_CSR_SCS_SHIFT) != 6) {}
}

 

 

 

포트 설정에 관한 부분에서는

ADC 출력 값에 따라 LED 색이 바뀌는 예제이므로 Blue, Red, Green LED 모두를 사용한다.

void PORT_init(void){
   PCC->PCCn[PCC_PORTD_INDEX] |= PCC_PCCn_CGC_MASK;
   PORTD->PCR[PTD0] = PORT_PCR_MUX(1);
   PORTD->PCR[PTD15] = PORT_PCR_MUX(1);
   PORTD->PCR[PTD16] = PORT_PCR_MUX(1);

   PTD->PDDR |= 1<<PTD0
            |1<<PTD15
            |1<<PTD16;
}

 

 

본 ADC구현과 관련된 함수들이다.

더보기

ADC초기설정과 관련된 순서는 다음과 같다.

 

① PCC_ADC0 레지스터를 통해 ADC의 클럭을 선택하고 Enable한다.

② SC1[0] 레지스터를 통해 모듈의 conversion과 interrupt를 Disable한다.

③ CFG1 레지스터를 통해 ADC 입력 클럭과 해상도를 설정한다.

④ CFG2 레지스터를 통해 ADC의 sample time을 설정한다.

⑤ SW Trigger이고, Compare function은 사용하지 않으므로 SC2 레지스터를 0으로 세팅한다.

⑥ Calibration을 하지 않고 One conversion 모드이므로 SC3 레지스터를 0으로 세팅한다.

void ADC_init(void){
   PCC->PCCn[PCC_ADC0_INDEX] &= ~PCC_PCCn_CGC_MASK;
   PCC->PCCn[PCC_ADC0_INDEX] |= PCC_PCCn_PCS(1);
   PCC->PCCn[PCC_ADC0_INDEX] |= PCC_PCCn_CGC_MASK;

   ADC0->SC1[0] |= ADC_SC1_ADCH_MASK;

   ADC0->CFG1 &= ~ADC_CFG1_ADIV_MASK;

   ADC0->CFG1 |= ADC_CFG1_MODE(1);

   ADC0->CFG2 = ADC_CFG2_SMPLTS(12);

   ADC0->SC2 = 0x00000000;
   ADC0->SC3 = 0x00000000;
}

void convertAdcChan(uint16_t adcChan){
   ADC0->SC1[0] &= ~ADC_SC1_ADCH_MASK;
   ADC0->SC1[0] |= ADC_SC1_ADCH(adcChan);
}

uint8_t adc_complete(void){
   return ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) >>ADC_SC1_COCO_SHIFT);
}

uint32_t read_adc_chx(void){
   uint16_t adc_result = 0;
   adc_result = ADC0->R[0];

   return (uint32_t)((5000*adc_result)/0xFFF);
}

 

 

 

가변저항값에 따른 LED색의 변화확인

 

'NXP&S32DesignStudio' 카테고리의 다른 글

전자시계 만들기  (0) 2021.01.17
PWM  (0) 2021.01.17
Timer Interrupt  (0) 2021.01.17
GPIO & Switch  (0) 2021.01.17