ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [nuvoton] MCU 120N Firmware & RTOS
    SW/Embedded 2018. 2. 15. 15:04

    사용보드

    • MCU : nuvoton

    • 컴파일러 : keil

    • NANO120SE3BN MCU사용

    • Nu-Link 디버거usb

    • CMSIS-core

    • Device-startup

    • Device-driver-CLK

    • Device-driver-SYS

    내용

    • startup은 전기가 들어가면 처음으로 실행되는 부분으로 최종적으로 메인을 실행한다.
    • (노모톤 MCU는 )0x00번지에 처음으로 접근한다.
    • 마이크로프로세서는 리턴을 하지 않는다.(운영체제가 없어서 리턴을 받아줄때가 없다)
    int main(void)
    {
    	while(1)
    	{
    
    	}
    	while(1); // 리턴하지말고 할거없으면 쉬어.
    }
    
    • 디버거 설정 option for target클릭 => 디버거 : 오른쪽 use 를NUlink debuger로 설정 후 세팅에서 칩타입(나노100) => 유틸리티의 세팅 : 리셋엔런체크

    메모리

    데이터와어드레스를 같은 핀으로 전달. 메모리 맵 - 인텔, 모토롤라

    • 인텔 : 메모리영역(빠름)과 IO영역(느림)으로 나누었다.

    • 모토롤라 : 하나에 다!!

    메모리 영역확인하기(데이트시트). 베이스 영역 - 4바이트씩(32비트), 0x00부터… 특정 레지스터를 쓸때는 락과 언락을 사용하여 오작동 방지.

    c 코드 락&언락

    void System Unlock()
    {
    	* ((volatile unsigned int * )(0x50000100))=(0x59);
    	* ((volatile unsigned int * )(0x50000100))=(0x16);
    	* ((volatile unsigned int * )(0x50000100))=(0x88);
    }
    void System_Lock()
    {
    	* ((volatile unsigned int * )(0x50000100))=(0x00);
    }
    int main(void)
    {
    	* ((volatile unsigned int * )(0x50004000+0x88))=(0x00);
    }
    
    
    • CMSIS는 arm에서 공통으로 표준을 정함.

    • Nano100Series.h 헤더에서 메모리내용을 구조체로 정의 하였다.

    int id;
    id = SYS->PDID;
    

    인터럽트

    • 일을 하던중에 갑자기 브레이크가걸리면(중요한요청) 먼저 처리후 복귀한다.

    • 인터럽트에 들어오는 이벤트가 오래걸리면 프로세서로 간다.(인터럽트에는 오래 있으면 안됨 [인터럽트 처리 루틴에 인터럽트금지가잇어서])

    • 인터럽트 발생 -> 팬딩 레지스터로 어떤 인터럽트가 발생한지 확인 후 인터럽트 벡터값(내가 갈곳이 정리되있음)-> 처리루틴(끝났으면 clear pending 작업 후) -> 복귀

    ARM 인터럽트 enable

    1. IP의 인터럽트 설정

    2. IP의 인터럽트 활성화

    3. 인터럽트 컨트롤러 활성화

    4. ARM코어 인터럽트(FIQ/IRQ) 활성화

    NVIC

    • MCU가 하는 인터럽트를 간소화!!

    클럭

    • 가장기본단위, 펌웨어 초기단계에 클럭을 설정한다.

    • 데이터 시트 => 어느 순간에 어느 동작이 하는지 알 수 있다.

    • 보드(x1,x2가 클럭)

    • 클럭을 나누어 각각의 I/O 디바이스가 자신만의 클럭을 제공 받는다.

    • AHBCLK => 빠른 버스(빠른 클럭) 디바이스에게 보냄, 초기 : 0x0000_0035

    • APHCLK => 느린 클런 디바이스에게 보냄, 초기 : 0x0000_0001

    • PWRCON => 초기값 : 0x0000_031x, 외부에서 들어오기위해서 HXT_EN을 1로

    프로그램 작성시

    1. 시스템 초기화 필수!!!
    // 헤더 선언
    void SYS_init(void)
    {
    	SYS_UnlockReg();	//쓰기 위해서 필수적으로 선언
    
    	//Clock setting
    	CLK_EnableXtalRC(CLK_PWRCTL_HXT_EN_Msk);	//HXT clock on 외부 클럭을 사용하기(12MHZ)
    	CLK_EnableXtalRC(CLK_PWRCTL_LXT_EN_Msk);	//LXT clock on 외부 클럭을 사용하기(32.768KHz)
    	SystemCoreClockUpdate();	//wait for stable(?)
    
    	SYS_LockReg();	//쓰기 막기!!!!!
    }
    int main(void)
    {
    		SYS_init();
    
    		while(1);
    }
    

    real time clock (RTC)

    • 시간을 체크 한다.(32.768KHz를 계속 주서 독립적으로 작업 수행),알람 수행.

    • 자체 전원 필요 하다.

    • 사람이 보기 편하도록 시간을 저장한다.(cpu에서 계산하지 않기 때문에 바로 사람 이 볼수 있도록)

    • 코드의 동작을 확인하기 위해 -> uart 통신(밖으로 보여주기위해 다리 하나를 사용해야된다. )

    • printf 는 보통 많은 곳에서 지원해 준다. uart0 으로 되어있으면 retarget에서 tartget option -> c/c++ 에서 정의해 DEBUG_PORT=uart1로 준다.

    TIMER

    • Timer 동작방식

    One-Shot : 1번 인터럽트 발생 후 종료

    Periodic : 설정된 값에 따라서 주기적으로 인터럽트 발생

    Toggle : Timer에 할당된 GPIO 핀으로 주기적으로 Output Signal 생성

    Continuous Counting : 인터럽트 발생 후 타이머가 초기화 되지 않고 Counting 을 계속

    // 타이머 만들기
    timer.c 추가 한 후
    
    init()함수에 아래 추가
    //Enable Timer0 clock and select Timer0 clock source
     CLK_EnableModuleClock(TMR0_MODULE);
     CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0_S_HXT, 0);
    
    
     uint32_t volatile sec = 0;
     uint32_t volatile min = 0;
      void TMR0_IRQHandler(void)
      {
    
         if(sec == 60)
     		{
     			sec=0;
     			min+=1;
     		}
     		else
     			sec++;
    
         // clear Timer0 interrupt flag
         TIMER_ClearIntFlag(TIMER0);
     	 printf("Time : %d min %d sec\n" , min,sec);
    
      }
      void initTimer()
      {
      	//Initial Timer0 to periodic mode with 2Hz
      	TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 1);
      	//Enable Timer0 interrupt
      	TIMER_EnableInt(TIMER0);
      	NVIC_EnableIRQ(TMR0_IRQn);
      }
    //main 에
    initTimer();
    TIMER_Start(TIMER0);
    

    클럭

    클럭은 메인클럭 , RTC, 타이머, watch dog 가 있다.

    watch dog TIMER

    • 정상적일 때는 수행이 안된다! 왜냐하면 시피유를 리셋 한다는 것이기 때문이다.

    • 오류가 생기게 된다면 복구 하기위한 보험같은 존재이다…

    • 독립적으로 카운터를 하지만 정밀하게 카운트 할필요는 없다.

    • 코드에 반복하는 부분이 한바퀴가 10 초걸린다고 가정하고 10초가 초과(시간을 넉넉히 정함)되면 와치독타이머가 오류로 판단하고 발생한다.!!!

    • 개발 할때는 잘 사용안하고 마지막에 보완을 할 때 사용한다.

    • clear watchdog timer 를 꼭 한번 해줘야 초기화가 되서 다시 시작한다.

    drive -> WDT 체크
    
    

    GPIO

    클럭은 안씀,

    • 핀은 input/output/openDrain모드로 활용할 수 있다.

    • open-drain : 전류를 많이 먹는 것들을 제어하기 위해서 사용???

    • 레지스터

      1. GPIOx_DMASK : 어떤 값을 쓰던지 변하지 않게 하기 위해 사용한다. 변경원치 않을 때 사용한다.(1:막기 0:쓸수있음)

      2. GPIOx_PIN : 들어오는 시그널이 1 인지 0 인지?

      3. GPIOx_DOUT : 신호 출력

    Px_L/H_MFR( 지피아이오기능을 쓸수있게 해준다.)

    void LEDPortInit()
    {
      // GPIO Settings
      SYS->PC_L_MFP  &=  
        ~(SYS_PC_L_MFP_PC6_MFP_Msk| SYS_PC_L_MFP_PC7_MFP_Msk);
      SYS->PC_L_MFP  |=  
         (SYS_PC_L_MFP_PC6_MFP_GPC6 | SYS_PC_L_MFP_PC7_MFP_GPC7);
                 //6번핀             |      7번핀
      PC->PMD = (GPIO_PMD_OUTPUT << 14) | (GPIO_PMD_OUTPUT << 12);
    
    }
    
    LEDPortInit();
    
    PC->DOUT = 0x00;
    

    라시아룰렛게임 코드

    • 사진 클릭

    Video Label

    
    void SYS_Init(void)
    {
    
        SYS_UnlockReg();
    
        /* Enable External XTAL (4~24 MHz) */
        CLK_EnableXtalRC(CLK_PWRCTL_HXT_EN_Msk);
    
        /* Waiting for 12MHz clock ready */
        CLK_WaitClockReady( CLK_CLKSTATUS_HXT_STB_Msk);
    
    
        /* Switch HCLK clock source to HXT */
        CLK_SetHCLK(CLK_CLKSEL0_HCLK_S_HXT,CLK_HCLK_CLK_DIVIDER(1));
    
        /* Enable IP clock */
        CLK_EnableModuleClock(UART1_MODULE);
    
        /* Select IP clock source */
        CLK_SetModuleClock(UART1_MODULE, CLK_CLKSEL1_UART_S_HXT, CLK_UART_CLK_DIVIDER(1));
    
        /* Update System Core Clock */
        /* User can use SystemCoreClockUpdate() to calculate SystemCoreClock. */
        SystemCoreClockUpdate();
    
        // UART 1 Port Settings
    	SYS->PC_H_MFP &= ~( SYS_PC_H_MFP_PC10_MFP_Msk | SYS_PC_H_MFP_PC11_MFP_Msk);
        SYS->PC_H_MFP |= (SYS_PC_H_MFP_PC10_MFP_UART1_RX|SYS_PC_H_MFP_PC11_MFP_UART1_TX);
    
        /* Lock protected registers * /
        SYS_LockReg();
    }
    void ButtonPortInit()
    {
      // GPIO Settings  Input
      SYS->PB_H_MFP  &=
        ~(SYS_PB_H_MFP_PB8_MFP_Msk | SYS_PB_H_MFP_PB15_MFP_Msk);
      SYS->PB_H_MFP  |=   
         (SYS_PB_H_MFP_PB8_MFP_GPB8 | SYS_PB_H_MFP_PB15_MFP_GPB15);
      PB->PMD = (GPIO_PMD_INPUT << 30) | (GPIO_PMD_INPUT << 16);
    
    }
    
    
    void LEDPortInit()
    {
      // GPIO Settings
      SYS->PC_L_MFP  &=  
        ~(SYS_PC_L_MFP_PC6_MFP_Msk| SYS_PC_L_MFP_PC7_MFP_Msk);
      SYS->PC_L_MFP  |=  
         (SYS_PC_L_MFP_PC6_MFP_GPC6 | SYS_PC_L_MFP_PC7_MFP_GPC7);
    
      PC->PMD = (GPIO_PMD_OUTPUT << 14) | (GPIO_PMD_OUTPUT << 12);
    
    }
    void nap(void)
    {
    	int a,b;
    	for(a=0;a<1000;a++)
    	{
    		for(b=0;b<100;b++){}
    	}
    }
    
    int main()
    {
    	int id,count=0,result=(int)(rand()%5)+1; //1~6
    	int flag=0;
    	SYS_Init();
    	// Init UART to 115200-8n1 for print message
    	UART_Open(UART1, 115200);
    	id = * ((volatile unsigned int * )(0x50000000 + 0x00));
    	printf("Hello World 0x%x 0x%x\n ", id , SYS->PDID);
    
    	LEDPortInit();
    	ButtonPortInit();
    
    	PC->DOUT =0x80;
    	printf("result: %d count: %d\n",result,count);
    	while(1){
    
    		PC->DOUT |=(0x3 << 6);	//0b11000000  
    		  nap();
    			PC->DOUT=0x00;  //0b00000000 ±×¸° on
    			nap();
    
    	// on / off
    		if(PB->PIN & (0x1 << 8)){		
    		PC->DOUT &= ~(0x80);			
    	} else {
    		PC->DOUT =0x80;
    	}
    	if(PB->PIN & (0x1 << 15)){		
    		PC->DOUT |=0x40;			
    	} else {
    		PC->DOUT &= ~(0x40);
    	}   
    
    		if(PB->PIN & (0x1 << 8))  //기본 안눌렸을 때 1
    		{
    			flag=0;
    			continue;
    		}
    		else
    		{
    			if(flag==0){   //스위치가 빠빡 눌리는걸 방지
    				printf("click %d   %d\n",count,result);
    				flag=1;
    				count++;
    			}
    		}
    		if(count==result)
    		{
    			printf("count:%d\n",count);
    			while(count<7)
    			{
    				PC->DOUT |=0x40;
    				nap();
    				PC->DOUT &= ~(0x40);
    				nap();
    				PC->DOUT |=0x80;
    				nap();
    				PC->DOUT &= ~(0x80);
    				nap();
    				count++;
    			}
    			PC->DOUT =0x80;
    			count=0;
    			result=(int)(rand()%5)+1;
    			printf("start again\n");
    		}
    
    	}
    
    }
    
    

    인터럽트 gpio

    • GPIO는 각 포트별로 인터럽트를 설정할 수 있다.

    • 인트럽트 발생시 status Register를 확인하여 어떤 인터럽트가 발생하는지 확인한다.

    • EINT0,1

    void GPIOInterruptInit()
    {
      printf("\n  GPB5 and GPE5 are used to test interrupt\n");    
    
      // GPIO Port Clear
      SYS->PB_L_MFP &= ~( SYS_PB_L_MFP_PB5_MFP_Msk);
      SYS->PE_L_MFP &= ~( SYS_PE_L_MFP_PE5_MFP_Msk);
    
      // Set GPIO Interrupt
      SYS->PB_L_MFP |= (SYS_PB_L_MFP_PB5_MFP_GPB5);
      SYS->PE_L_MFP |= (SYS_PE_L_MFP_PE5_MFP_GPE5);
    
    }
    void init_GPIO_int()
    {
      GPIO_SetMode(PB, BIT5, GPIO_PMD_INPUT);
      GPIO_EnableInt(PB, 5, GPIO_INT_BOTH_EDGE);
      NVIC_EnableIRQ(GPABC_IRQn);
    
      GPIO_SetMode(PE, BIT5, GPIO_PMD_INPUT);
      GPIO_EnableInt(PE, 5, GPIO_INT_BOTH_EDGE);
      NVIC_EnableIRQ(GPDEF_IRQn);
    
      GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_HCLK,
       GPIO_DBCLKSEL_1);
    
      GPIO_ENABLE_DEBOUNCE(PB, BIT5);
      GPIO_ENABLE_DEBOUNCE(PE, BIT5);
    }
    void GPABC_IRQHandler()
    {
    	PB->ISRC=(0x1<<5);
    	printf("GPABC,count:%d\n",count);
    	count++;
    }
    
    void GPDEF_IRQHandler()
    {
    	PE->ISRC=(0x1<<5);
    	printf("GPDEF %d\n",PE->ISRC);
    }
    
    

    UART

    • -12v ~ 12v(레벨 컴버터 필요!)

    • Rx 받는거, Tx주는 것

    • UART 레지스터

      1. UARTx_IER :

      2. UARTx_ISR : (RDA_IS)-읽기만 가능, 자동으로 초기화

    • UART설정

    1. 클럭설정한다.

    2. 핀을 설정한다.

    3. Baud rate설정 한다.

    • 입력이 들어오면 FIFO를 사용한다!!!

    PWM

    • 핸드폰 밝기 조절 할 때 사용됨.

    • 데드존(+ + 펑) : 한쪽에 딜레이를 줘서 해결.

    • pwm레지스터 : pwm1,pwm2 두개 있다. 듀티1 만 건들면 된다. (인터럽트는 거의안쓰고 타이머처럼 사용), 아우풋인에이블 : 외부에 시그널을 보내고 싶은때 사용한다.

    AD converter

    • 아날로그 신호를 디지털로(테이블로) 변환시키는 기능이다!!

    • sampling rate : 일정한 시간으로 아날로그신호를 읽음.(빠를수록 정확!!)

    Flash Meomory

    1. RAM
    • SRAM : 내부 복잡, 제어 쉽고, 고속 가능, 큰 데이터 불가

    • DRAM : 단순, 제어 복잡, 정기적으로 데이터를 리플레시 해야됨

    1. ROM
    • 임베디드-부팅에 필요한 정보를 넣는다.
    1. Flash Memory
    • 전기적으로 데이터를 지우고 다시 기록할 수 있는 저장 매체.

    • NOR flash : 바이오스에 들어감, SRAM과 비슷하다. 많은 용량을 줄 수 없다.

    • NAND flash : 구조 간단, 집적도 높음. 단점 : 입출을 할 때 불럭 단위로 줘야됨. (외부의 컨트롤러가 있어야 사용하기 편하다. 롬이나 nor플레시 역활을 할 수 없다. )

    • slc : 0,1

    • mlc : 0~3 (2비트)

    • tlc : 3비트

    • 저장 공간을 늘려간다.

    I2C

    • 선 두개만을 사용하여 시리얼 전송하는 프로트콜

    • uart경우 클럭을 줄수 없어서 미리 정의하고 하지만 i2c는 데이터를 주는 쪽에서 클럭을 설정하여 보내면 받는 쪽에서 맞춘다.

    • 마스터에서 -> 슬레이브로(1:N) 통신한다.

    • 프로토콜 구조

      1. 처음에는 클락과 데이터가 하이인데 데이터가 로우로 떨어지면 전송을 시작한다.

      2. 슬레이브의 어드레스로 호출한다.

      3. 슬레이브 대답한다.(ack-응답 -> 데이터를 보낸다.)

      4. 데이터 보낸다. (여러개 가능 - 설정)

      5. 데이터가 다시 하이로 되면 클럭과 데이터가 하이 이므로 스톱한다.

    • 쓰기 구조

      시작 - 어드레스 - 모드(쓰기0/읽기1) - A(슬레이브->마스터에게) - 데이터 - A(슬레이브->마스터) - A/~A - 끝

    • 읽기 구조

      시작 - 어드레스 - 모드 - A(슬->마) - 데이터(슬->마) - A(마->슬) - 데이터 - A/~A(마->슬) - 끝(마스터->슬)

    • 레지스터

      1. I2CCON : ack,start,stop

      2. I2CSTATUS : 확인을 하면서 다루어야된다.

      3. I2CDATA : 내가 보내고 싶은 것을 집어넣는다.(어드레스 혹은 데이터)

      4. I2CSADDRx : 슬레이브 어드레스.

      5. I2CAMSKx : 확장해서 더 많은 데이터를 받을 떄(?)사용한다.

    • 통신 할 모듈의 데이터시트를 보고 슬레이브어드레스와 통신구조를 본다!

    USB

    • 하나의 호스트와 다수의 디바이스로 동작한다.

    • 디바이스(클라이언트)는 자신의 종류와 아이디 등을 저장하고 있으며 초기화 시 호스트에게 알린다.

    • 호스트에서 받은 정보를 디바이스드라이버 테이블에서 찾아 실행시킨다.

    마이크로 프로세서 응용

    • 펌웨어 -> 한가지기능, os-> 여러가지 일을 멀티적으로 실행 시킨다.

    OS역활

    1. 시스템 초기화 (부팅)

    2. 리소스 관리 (cpu,ram등)

    3. 디바이스 관리 (드라이버)

    4. 인풋/아웃풋 제어

    5. 어플리케이션 관리 (실행, 실행 죽이기)

    커널

    1. 메모리 관리 (메모리 호보등) (가상과 물리 mem 매칭)

    2. CPU, 디바이스 관리

    RTOS

    • 실시간(정해진 시간) OS 이다.

    • 사용할 수 있는 라이브러리를 잘 찾아야 한다.!!!

    • 보드에서 돌아가는 라이브러리인지 잘 알아봐야한다.!!! 또한 수정!!!

    • (os 라기보단 멀티 테스팅등 을 사용할수있게해주는 라이브러리같은 느낌)

    • 케일 컴파일이 제공하는 rtos사용할것(셋팅)

    • 전에 main의 while에서 했던 것을 Thread의 while에 작성한다.

    쓰레드

    Thread 정의

    • Thread 를 정의 하기 위해서는 아래와 같은 철차가 필요하다.

    1.Thread ID Handle 선언

    • osThreadId thread1_id; //thread handle

    2.Thread 함수 원형 선언

    • void thread1 (void const * argument);

    3.Thread 생성

    • thread1_id = osThreadCreate(osThread(Thread1), NULL);

    4.Thread 구조 선언

    • osThreadDef(thread1, osPriorityNormal, 1, 0);

    • (name, priority, instances, stacksz)

    • osPriorityNormal : 우선순위 정하기

    5.Thread 죽이기

    • osThreadTerminate(쓰레드 id)

    우선순위

    쓰레드 인자

    • void * vp;

    • 강제 형변환으로 출력해야한다.

    스케줄링

    • 요청등 빨리 처리해줘야할때 기달릴수 없으니깐 순서를 정하기 위해 사용한다.

    • 라운드로빈방식 : 시간할당량만 한다!

    • 케일에서 재공해주는 RTOS에서는 우선순위를 변경하는 함수를 제공해 준다.

    IPC (inter process communication)

    • 프로세스 간의 정보 교환 방식

      1. Signal

      이벤트(인터럽트, 익셉션등)가 발생할떄 Task에게 전달하는 소프트웨어 인터럽트.

      소프트웨어적으로 인터럽트를 발생시키는 방법이다.

    semaphore

    우선순위 역전

    • 세마포어를 얻은 쓰레드가 우선순위가 높은애보다 먼저 실행되는 현상

    Mutex

    상호 배제, 동시 프르그램이 불가능한 자원을 동시 사용을 피하기 위해(독점)

    virtual timer & tick

    os는 기본으로 타이머가 돌아가며 인터럽트를 발생시킨다.

    펌웨어처럼 레지스터를 키고 할 필요 없이 가상으로 타이머를 만든다.

    callback() 함수 : 정해진 조건이되면 자동으로 불러와서 실행한다.

    Memory pool

    메모리를 커다란 공간을 만들어서 내가 쓸것을 넣어 둔다. 요청이 오면 풀에이는 메모리를 사용자가 사용하고 끝날시 다시 반납한다. (사용할 만큼의 메모리를 미리 만들어 놓는다.)

    할당,해제 중요!!!

    Mail Queue

    메시지 큐 + 메모리 풀

    • 순서

      1. 데이터 구조 만들기(struct)

      2. 메시지 큐 생성

      3. 메모리 블록 할당

      4. 생성된 큐에다가 데이터 넣기

      5. 받아서 꺼내기

      6. 메모리 프리

    결론

    함수같은 경우는 제공해주는 컴파일러마다 다르므로 데이터시트와 회로도를 보는 것이 가장 중요하다. 그리고 제공해주는 샘플코드를 통해 어떻게 사용하는지 파악하고 코딩할 수 있는 능력이 중요한 것 같다.

    댓글

Designed by Tistory.