Skip to content

PWM using EMIOS (Enhanced Modular Input Output Subsystem)#

PWM Example (PWM Duty Cycle에 따른 LED 밝기 변화)#

  1. 새로운 예제를 위한 프로젝트를 생성한다.
  2. 원하는 동작을 위해 레지스터와 메모리에 직접 접근해서 값을 써야한다.
  3. Board Schematic을 통해 해당 보드의 PWM 출력에 대한 정보를 파악한다.
  4. PWM 신호 생성을 위해 사용할 FTM 모듈의 동작 원리를 파악하고 메모리 맵을 분석한다.
  5. 분석 결과를 활용해 임베디드 프로그래밍을 한다.

1. Datasheet 분석#

Easy Module Shield V1의 RGB LED 중 RED를 이용하기 위해서 PTC10을 사용한다.

해당 예제에는 PTC10에 해당하는 EMIOS_0_CH6를 이용할 것이다.

1-1. MSCR#

MSCR(Multiplexed Signal Configuration Register)의 SSS(Source Signal Select) 비트를 0001b로 설정하면 EMIOS_Channel로 동작한다.

MSCR의 OBE 비트를 1로 설정하면 출력으로 동작한다.

PTCm을 제어하기 위한 MSCRn 번호는 \(\rm n=(m+64)\)로 구할 수 있다.
MSCRn의 주소는 \(\rm SIUL2\ base + MSCR\ offset+ (n \times 4)\)로 구할 수 있다.
MSCR74 주소: 4029_0368h (4029_0000h + 240h + 128h)

1-2. IMCR#

S32K312는 EMIOS 모듈을 사용할 때 IMCRn의 SSS 비트를 따로 설정할 필요가 있다. PTC10의 경우 IMCR566의 SSS 비트를 0100b로 설정해야 한다.

하지만 IMCR566이라는 레지스터는 없다. 512의 offset이 적용된 상태이므로 실제로는 \(566-512=54\)이므로 IMCR54에 접근하면 된다.

IMCRn의 주소는 \(\rm SIUL2\ base + IMCR\ offset+ (n \times 4)\)로 구할 수 있다.
IMCR54 주소: 4029_0B18h (4029_0000h + A40h + D8h)

IMCR54의 SSS 비트에 앞서 확인한 값(0100b)을 쓰면 된다.

1-3. EMIOS#



EMIOS는 S32K3보드에서 제공하는 다목적 모듈로 PWM 생성과 관련된 기능도 제공한다.
우리가 사용할 eMIOS_0은 16비트 타이머이며 클락은 CORE_CLK으로부터 공급된다.


EMIOS 에서는 PWM 신호 생성과 관련해 4가지 모드를 제공하는데, 우리는 Duty Cycle만 변경하면 되므로 OPWMB 모드를 사용할 것이다.


OPWMB 모드에서는 AS1과 BS1 값을 조정하여 Duty Cycle을 지정할 수 있다.

1-4. EMIOS 레지스터#


다음은 출력 모드 설정을 위해 채널을 초기화하는 기본 단계이다. 해당 순서에 맞게 설정을 해주어야 정상 작동한다.

설정에 필요한 레지스터 주소 오프셋은 위와 같다.

1-5. EMIOS 레지스터 설정#

  • MCR

이름 설명
GTBE(Global Timebase Enable) GTBE_OUT이 하나 이상의 eMIOS 인스턴스의 GTBE_IN 입력에 연결된 경우, 신호를 활성화하면 해당 eMIOS 인스턴스의 내부 카운터가 동시에 활성화된다. 내부 카운터를 활성화하기 위해 1b로 설정한다.
GPREN(Global Prescaler Enable) 글로벌 프리스케일러 카운터를 활성화하거나 비활성화한다. 프리스케일러를 활성화하여 글로벌 프리스케일러 카운터가 동작하고 클럭 신호를 처리한다. 이를 위해 1b로 설정한다.
GPRE(Global Prescaler) 글로벌 클럭 프리스케일러의 분주 비율을 설정한다. 원하는 클럭 분주 비율에서 1을 뺀 값을 이 필드에 설정해야 한다. 프리스케일러 값은 1부터 256까지 설정 가능하다. 해당 예제에서는 분주비를 8로 설정하기 위해 111b로 설정한다.

MCR의 주소는 \(\rm EMIOS0\ base + IMCR\ offset\) 로 구할 수 있다.
MCR 주소: 4008_8000 (4008_8000h + 0h)

  • UC Control

이름 설명
UCPREN(Prescaler Enable) 프리스케일러를 활성화한다.
BSL(Bus Select) eMIOS의 각 채널이 사용할 타이머 버스 또는 내부 카운터를 선택한다. 해당 예제에서는 단일 채널을 사용하므로 11b로 설정하여 내부 카운터를 이용하게끔 한다.
MODE(Mode Selection) UC 모드를 선택한다. OPWMB 모드를 사용하기 위해 1100000b로 설정해준다.
EDPOL(Edge Polarity) 내부 카운터, 입력 캡처, 또는 입력 캡처 플래그를 트리거하는 신호의 엣지를 선택한다. 해당 예제에서는 상승 엣지 트리거를 사용하기 위해 1b로 설정해준다.
0b: Falling Edge (하강 엣지)에서 트리거
1b: Rising Edge (상승 엣지)에서 트리거

UC Control n의 주소는 \(\rm EMIOS0\ base + UC \ Control \ offset+ (n \times 20)\)로 구할 수 있다.
UC Control 6 주소: 4008_80EC (4008_8000h + 2Ch + C0h)

  • UC A

이름 설명
A 레지스터에 AS1 값을 설정하여 PWM 신호의 첫 번째 엣지 위치를 정의한다.

UC A n의 주소는 \(\rm EMIOS0\ base + UC \ A \ offset+ (n \times 20)\)로 구할 수 있다.
UC A 6 주소: 4008_80E0 (4008_8000h + 20h + C0h)

  • UC B

이름 설명
B 레지스터에 BS1 값을 설정하여 PWM 신호의 두 번째 엣지 위치를 정의한다.

UC B n의 주소는 \(\rm EMIOS0\ base + UC \ B \ offset+ (n \times 20)\)로 구할 수 있다.
UC B 6 주소: 4008_80E4 (4008_8000h + 24h + C0h)

AS1과 BS1에 적절한 값을 대입하여 PWM 신호의 Duty Cycle을 설정해준다.
* 16비트 타이머이므로 0~65535 사이의 값으로 정해준다.

예로들어 A 레지스터에 1000을 설정하면 카운터가 1000에 도달할 때 첫 번째 엣지가 발생하고, B 레지스터에 2000을 설정하면 카운터가 2000에 도달할 때 두 번째 엣지가 발생하여 PWM 신호가 완성된다.

1-6. CLOCK 설정#

세부 설정은 이전 강의에서 진행한 것과 동일하나 REQ34에 대한 PRTN0_COFB1_CLKEN 레지스터 설정을 해주어 eMIOS0 모듈에 클럭 공급을 활성화시켜준다.

2. 프로그래밍#

2-1. 매크로 정의#

#include "S32K312.h"
#include <stdio.h>

// SIUL2 Registers - PTC10 (10+64=74)
#define SIUL2_BASE          (0x40290000UL)
#define SIUL2_MSCR_BASE     (SIUL2_BASE + 0x240)
#define SIUL2_MSCR74        (*(volatile unsigned *)(SIUL2_MSCR_BASE + 0x128))
#define SIUL2_IMCR_BASE     (SIUL2_BASE + 0xA40)
#define SIUL2_IMCR54        (*(volatile unsigned *)(SIUL2_IMCR_BASE + 0xD8)) // 566 - 512

// MSCR & IMCR Bits
#define OBE_BIT             21
#define SSS_BITS            0



// MC_ME Registers
#define MC_ME_BASE                  (0x402DC000UL)
#define MC_ME_PRTN0_PCONF           (*(volatile uint32_t *)(MC_ME_BASE + 0x100))
#define MC_ME_PRTN0_PUPD            (*(volatile uint32_t *)(MC_ME_BASE + 0x104))
#define MC_ME_PRTN0_COFB1_CLKEN     (*(volatile uint32_t *)(MC_ME_BASE + 0x134))
#define MC_ME_MCTL                  (*(volatile uint32_t *)(MC_ME_BASE + 0x000))
#define MC_ME_KEY                   0x5AF0
#define MC_ME_KEY_INV               0xA50F



// EMIOS0 Registers - CH6_G
#define EMIOS0_BASE         (0x40088000UL)
#define EMIOS0_MCR          (*(volatile unsigned *)(EMIOS0_BASE + 0x0))
#define EMIOS0_UC_A6        (*(volatile unsigned *)(EMIOS0_BASE + 0x20 + 0xC0))   // 6x32=192->C0
#define EMIOS0_UC_B6        (*(volatile unsigned *)(EMIOS0_BASE + 0x24 + 0xC0))
#define EMIOS0_UC_Control6  (*(volatile unsigned *)(EMIOS0_BASE + 0x2C + 0xC0))

// MCR Bits
#define GPRE_BIT            8
#define UCPREN_BIT          25
#define GPREN_BIT           26
#define GTBE_BIT            28

// Control Bits
#define MODE_BIT            0
#define EDPOL_BIT           7
#define BSL_BIT             9

2-2. SIUL2_init()#

  • 포트 설정을 위해 SIUL2_init() 함수를 구현한다.
    void SIUL2_Init()
    {
        SIUL2_MSCR74 &= ~(0b1111 << SSS_BITS);
        SIUL2_MSCR74 |= (0b0001 << SSS_BITS);
        SIUL2_MSCR74 |= (1 << OBE_BIT);
    
        SIUL2_IMCR54 &= ~(0b1111 << SSS_BITS);
        SIUL2_IMCR54 |= (0b0100 << SSS_BITS);
    }
    
    PTC10(MSCR74)는 출력으로, PTD2(MSCR98 & IMCR26)은 입력 및 EIRQ로 설정한다.

2-3. EMIOS0_Clock()#

  • 클럭 활성화를 위한 함수를 구현한다.
    void EMIOS0_Clock(void)
    {
        MC_ME_PRTN0_PCONF |= 0x1;
        MC_ME_PRTN0_PUPD |= 0x1;
    
        MC_ME_PRTN0_COFB1_CLKEN |= (1 << 2);
    
        MC_ME_MCTL = MC_ME_KEY | (0x4 << 16);
        MC_ME_MCTL = MC_ME_KEY_INV | (0x4 << 16);
    }
    

2-4. EMIOS0_CH6()#

  • EMIOS의 PWM을 사용하기 위한 함수를 구현한다.
    void EMIOS0_CH6()
    {
        EMIOS0_MCR &= ~(1 << GPREN_BIT);
    
        EMIOS0_UC_Control6 &= ~(1 << UCPREN_BIT);
    
        EMIOS0_UC_A6 = 0;
        EMIOS0_UC_B6 = 1000;
    
        EMIOS0_UC_Control6 |= (0b11 << BSL_BIT);
        EMIOS0_UC_Control6 |= (0b1100000 << MODE_BIT);
        EMIOS0_UC_Control6 |= (1 << EDPOL_BIT);
    
        EMIOS0_UC_Control6 |= (1 << UCPREN_BIT);
    
        EMIOS0_MCR |= (7 << GPRE_BIT);
        EMIOS0_MCR |= (1 << GPREN_BIT);
        EMIOS0_MCR |= (1 << GTBE_BIT);
    }
    

2-5. main#

  • 위에서 작성한 함수들을 호출한다.
    int main(void)
    {
        SIUL2_Init();
        EMIOS0_Clock();
        EMIOS0_CH6();
    
        for (;;)
        {
            ;
        }
    
        return 0;
    }
    

3. 동작 모습#

빌드, 디버그 후 동작시켰을 때 Duty Cycle에 따라 LED의 밝기가 변하는 것을 확인할 수 있다.

4. 실습 예제 (가변 저항에 걸리는 전압 값에 따라 LED 밝기 조절하기)#

  1. 지난 강의 시간에 실습한 ADC 예제를 활용한다.
  2. ADC로 읽은 값을 이용해 PWM Duty Cycle을 조절한다.
  3. 가변 저항을 돌렸을 때 LED의 밝기가 바뀌는 것을 확인한다.