PSoC 6 듀얼 코어로 Hello World (3)

이것은 PSoC Advent Calendar 2017의 17 일째에 밀린 기사입니다.

마지막 회의



마지막 기사 에서는, 메세지를 송신하는 Cortex-M4측의 프로그램을 멀티 태스크풍으로 변경해, 복수의 요구가 엉망에 도착하는 모습을 보았습니다.
이번에는, 이러한 태스크가 발하는 메세지를 하드웨어 타이머의 타이밍으로 송신하도록(듯이) 변경해 갑니다.

하드웨어



이 프로젝트에서는 타이머로 MCWDT를 추가합니다.



또 하나 잊어서는 안되는 것이, 인터럽트를 수신하는 CPU의 지정입니다.



CYDWR 파일의 Interrupts 탭에서 Int_WDT 인터럽트가 Cortex-M4로 수신되도록 설정합니다.

MCWDT



Multi-Counter WatchDog Timer(MCWDT)에는 3개의 카운터가 들어 있으며, 각각을 3개의 독립된 타이머로 사용하거나, 복수의 카운터를 조합하여 주기적인 긴 타이머를 구성할 수 있습니다.
또, 이 블록은 Cortex-M0+와 Cortex-M4의 어느쪽에도 하나씩 탑재되고 있으므로, 초기화에 사용한 CPU에 탑재되고 있는 블록이 사용됩니다. 이 프로젝트에서는 메시지를 보내는 Cortex-M4 측의 타이밍을 만들므로 당연히 Cortex-M4 측의 프로그램으로 초기화를 합니다.



본 프로젝트에서는 위와 같은 설정을 하고 있습니다.
  • 3개의 카운터를 독립한 3개의 주기 타이머로서 사용하므로, 카운터끼리를 연결하는 Cascade 기능은 사용하지 않습니다.
  • 카운터 0(C0)은 32kHz 클럭을 12288회 곱하여 인터럽트를 발생합니다. 주기는 384ms입니다.
  • 카운터 1(C1)은 32kHz 클럭을 16384회 곱하여 인터럽트를 발생합니다. 주기는 512ms입니다.
  • 카운터 2(C2)는 32kHz 클럭을 바이너리 카운터로 세어 카운터의 15비트째를 인터럽트 요인으로 사용하고 있습니다. 인터럽트 주기는 32768 클럭이고 주기는 1.02초입니다.

  • 이 세 가지 요인을 하나의 인터럽트로 취급합니다.

    Cortex-M0+ 측 프로그램



    Cortex-M0+ 측의 프로그램은 이번에도 변경 부분은 제로입니다.

    Cortex-M4 측 프로그램



    Cortex-M4측의 프로그램에서는, 우선, MCWDT를 이용하기 위한 인터럽트 서비스 루틴(ISR)과 초기화 루틴을 정의하고 있습니다.

    main_cm4.c
    /* WDT を使った周期割り込み */
    bool Int_WDT_flag0 = false;
    bool Int_WDT_flag1 = false;
    bool Int_WDT_flag2 = false;
    
    void Int_WDT_isr(void) {
        uint32_t status = Cy_MCWDT_GetInterruptStatus(MCWDT_HW);
        if (status & CY_MCWDT_CTR0) {
            Cy_MCWDT_ClearInterrupt(MCWDT_HW, CY_MCWDT_CTR0);
            Int_WDT_flag0 = true;
        }  
        if (status & CY_MCWDT_CTR1) {
            Cy_MCWDT_ClearInterrupt(MCWDT_HW, CY_MCWDT_CTR1);
            Int_WDT_flag1 = true;
        }  
        if (status & CY_MCWDT_CTR2) {
            Cy_MCWDT_ClearInterrupt(MCWDT_HW, CY_MCWDT_CTR2);
            Int_WDT_flag2 = true;
        }  
    }
    
    void Int_WDT_Start(void) {
        /* 割り込みコンポーネントを初期化しイネーブル */
        Cy_SysInt_Init(&Int_WDT_cfg, Int_WDT_isr);
        NVIC_ClearPendingIRQ((IRQn_Type) Int_WDT_cfg.intrSrc);
        NVIC_EnableIRQ((IRQn_Type) Int_WDT_cfg.intrSrc);
    
        /* MCWDT コンポーネントを起動する */
        Cy_MCWDT_Init(MCWDT_HW, &MCWDT_config);
        Cy_MCWDT_Enable(MCWDT_HW, MCWDT_ENABLED_CTRS_MASK, 0u);
    
        /* MCWDT コンポーネントの割り込みマスクを設定する */
        Cy_MCWDT_SetInterruptMask(MCWDT_HW,
            CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2
        );
    }
    

    ISR 내에서 MCWDT 블록의 세 가지 요소에 대해 각각 플래그가 지정됩니다. 메인 루프 안에 기술된 메세지 송신 프로그램이, 이러한 플래그를 이용해 타이밍을 제어하고 있습니다.

    MCWDT 설정 방법은 CE219339 – PSoC 6 MCU – MCWDT and RTC Interrupts (Dual Core)을 참조하십시오.

    main_cm4.c
    struct Task1Context {
        enum Task1State state;  /* ステートマシンの状態 */
        bool *flag;  /* 割り込みフラグ */
        uint32_t count;  /* メッセージ番号 */
        uint32_t side;  /* 使用中バッファ面 */
        const char_t *name;  /* タスクの名前 */
        char_t buffer[2][128]; /* ダブルバッファ */
    };
    
    void task1_init(struct Task1Context *context, const char_t *name, bool *flag) {
        /* タスクの初期化 */
        context->name = name;
        context->count = 0;
        context->side = 0;
        context->flag = flag;
        context->state = ST_CREATE;
    }
    
    void task1_dispatch(struct Task1Context *context) {
        switch (context->state) {
            case ST_CREATE:
                /* 送信するメッセージを作成する */
                (void)sprintf(
                    context->buffer[context->side],
                    "%s: HELLO WORLD %lu\r\n",
                    context->name, context->count++
                );
                context->state = ST_SEND;
                break;
            case ST_SEND:
                /* フラグが立っている時のみ実行 */
                if (*(context->flag)) {
                    /* メッセージを送る */
                    if (
                        Cy_IPC_Drv_SendMsgPtr(
                            ipcHandle,
                            CY_IPC_NO_NOTIFICATION,
                            context->buffer[context->side]
                        ) == CY_IPC_DRV_SUCCESS
                    ) {
                        /* バッファを切り替える */
                        context->side = (context->side)?(0):(1);
                        context->state = ST_CREATE;
                        /* 割り込みフラグを折る */
                        *(context->flag) = false;
                    }
                }
                break;
            default:
                CY_ASSERT(false);
        }
    }
    

    Task1에 약간의 변경이 있습니다.
  • Task1Context 구조체에 플래그에 대한 포인터를 추가했습니다.
  • 디스패처에서 플래그가 서 있다는 조건을 메시지를 보내는 조건에 추가했습니다.
  • 메시지를 보낼 수있는 경우에만 플래그를 지정합니다.

  • 실행해보면



    실행을 시작하면 세 가지 작업이 처리되는 방식을 알 수 있습니다.



    각 태스크의 실행 빈도는 MCWDT에 지정된 주기에 따라 다릅니다. 태스크의 실행 주기가 관리되기 때문에 한 태스크만 실행되는 상황이 사라졌습니다.



    관련 문헌



    AN215656 – PSoC 6 MCU Dual-Core CPU System Design
    AN217666 - PSoC 6 MCU 인터럽트
    CE216795 - PSoC(R) 6 MCU Dual-Core Basics
    CE219339 – PSoC 6 MCU – MCWDT and RTC Interrupts (Dual Core)
    PSoC 6 MCU: PSoC 63 with BLE Architecture Technical Reference Manual

    관련 기사



    PSoC 6 듀얼 코어 L 치카 (1)
    PSoC 6 듀얼 코어 L 치카 (2)
    PSoC 6 듀얼 코어 L 치카 (3)
    PSoC 6 듀얼 코어 L 치카 (4)
    PSoC 6 듀얼 코어 L 치카 (5)
    PSoC 6 듀얼 코어 L 치카 (6)
    PSoC 6 듀얼 코어 L 치카 (7)
    PSoC 6 듀얼 코어로 Hello World (1)
    PSoC 6 듀얼 코어에서 Hello World (2)

    좋은 웹페이지 즐겨찾기