OpenNuvoton / M2351BSP

M2351 BSP
15 stars 9 forks source link

Possible to drive UART from LIRC32? #13

Open travisby opened 3 years ago

travisby commented 3 years ago

Hello,

I noticed that the valid clock sources for UART0 are:

depending on the value placed into UART0SEL.

Since LIRC/LIRC32 is not an option listed there, I looked at how LXT can work: image

in the technical reference manual RTC section it shows that LXT can actually be sourced from either an external crystal OR LIRC32 depending on RTC_LXTCTL[7] (C32KS)

So I'm attempting to set C32KS and then use LXT as the clock source for UART0. Unfortunately I seem to get one non-printable character over serial instead of the expected Hello, World!\r\n

In the BSP I found an example to use HIRC as the HXT:

void SYS_Init(void) {
    CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);

    /* Wait for HIRC clock ready */
    CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);

    /* Select HCLK clock source as HIRC and HCLK clock divider as 1 */
    CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1));

    /* Enable HXT clock */
    CLK_EnableXtalRC(CLK_PWRCTL_HXTEN_Msk);

    /* Wait for HXT clock ready */
    CLK_WaitClockReady(CLK_STATUS_HXTSTB_Msk);

    /* Enable PLL */
    CLK->PLLCTL = CLK_PLLCTL_128MHz_HIRC;

    /* Waiting for PLL stable */
    CLK_WaitClockReady(CLK_STATUS_PLLSTB_Msk);

    /* Select HCLK clock source as PLL and HCLK source divider as 2 */
    CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL, CLK_CLKDIV0_HCLK(2));

    /* Enable UART module clock */
    CLK_EnableModuleClock(UART0_MODULE);

    /* Select UART module clock source as HXT and UART module clock divider as 1 */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_HXT, CLK_CLKDIV0_UART0(1));

    /*---------------------------------------------------------------------------------------------------------*/
    /* Init I/O Multi-function                                                                                 */
    /*---------------------------------------------------------------------------------------------------------*/

    /* Set multi-function pins for UART0 RXD and TXD */
    SYS->GPB_MFPH = (SYS->GPB_MFPH & (~(UART0_RXD_PB12_Msk | UART0_TXD_PB13_Msk))) | UART0_RXD_PB12 | UART0_TXD_PB13;
}

This example with UART_Open(UART0, 9600) works beautifully.

I convert the example to: enable RTC + use LXT for uart with:

void SYS_Init(void) {
    /* Enable the LIRC32K clock as the source for RTC */
    CLK_EnableModuleClock(RTC_MODULE);
    RTC->LXTCTL |= RTC_LXTCTL_LIRC32KEN_Msk | RTC_LXTCTL_C32KS_Msk;

    /* Wait for LIRC32 clock ready */
    assert(1 == CLK_WaitClockReady(CLK_STATUS_LIRC32STB_Msk));

    /* Enable LXT clock */
    CLK_EnableXtalRC(CLK_PWRCTL_LXTEN_Msk);

    /* Wait for LXT clock ready */
    assert(1 == CLK_WaitClockReady(CLK_STATUS_LXTSTB_Msk));

    /* Enable UART module clock */
    CLK_EnableModuleClock(UART0_MODULE);

    /* Select UART module clock source as LXT and UART module clock divider as 1 */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_LXT, CLK_CLKDIV0_UART0(1));

    /*---------------------------------------------------------------------------------------------------------*/
    /* Init I/O Multi-function                                                                                 */
    /*---------------------------------------------------------------------------------------------------------*/

    /* Set multi-function pins for UART0 RXD and TXD */
    SYS->GPB_MFPH = (SYS->GPB_MFPH & (~(UART0_RXD_PB12_Msk | UART0_TXD_PB13_Msk))) | UART0_RXD_PB12 | UART0_TXD_PB13;
}

and get just one non-printable byte on the serial console.

Am I doing something wrong, or is there something innate that prevents LIRC32 being the LXT for UART0 clock? Or is there a different value I need to place in CLK_CLKDIV0_UART0? (So far this appears to be "magic" for me, I haven't figured uot how the clkdiv0_uart0 works)

travisby commented 3 years ago

It looks like I needed to RTFM some more (sorry, I'm very new to embedded development so this is a bit out of my grasp / understanding).

I don't know why, but setting

UART0->BRCOMP = 0xA5; after UART_Open fixed this for me.

This came from example 1 of the UART section page 915 of the TRM. I don't understand how it's calculated but I'm glad my usecase was part of the examples ^.^

So for those who are looking to do something similar, here was my total solution:

#include <M2351.h>

void SYS_Init(void) {
    /* Enable LIRC32K clock */
    // This can only be done if the RTC module clock is enabled
    CLK_EnableModuleClock(RTC_MODULE);
    RTC->LXTCTL |= RTC_LXTCTL_LIRC32KEN_Msk;

    /* Wait for LIRC32 clock ready */
    CLK_WaitClockReady(CLK_STATUS_LIRC32STB_Msk);

    /* Use LIRC32K as LXT */
    RTC->LXTCTL |= RTC_LXTCTL_C32KS_Msk;

    /* Wait for LXT clock ready */
    CLK_WaitClockReady(CLK_STATUS_LXTSTB_Msk);

    /* Enable UART module clock */
    CLK_EnableModuleClock(UART0_MODULE);

    /* Select UART module clock source as LXT and UART module clock divider as 1 */
    // divider 1 feels like a magic number, but there's an example in the TRM with it set to 1 for our clock & baud rate
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_LXT, CLK_CLKDIV0_UART0(1));

    /*---------------------------------------------------------------------------------------------------------*/
    /* Init I/O Multi-function                                                                                 */
    /*---------------------------------------------------------------------------------------------------------*/

    /* Set multi-function pins for UART0 RXD and TXD */
    SYS->GPB_MFPH = (SYS->GPB_MFPH & (~(UART0_RXD_PB12_Msk | UART0_TXD_PB13_Msk))) | UART0_RXD_PB12 | UART0_TXD_PB13;
}

void UART0_Init(void) {
  SYS_ResetModule(UART0_RST);
  UART_Open(UART0, 9600);
  // This 0xA5 comes from an example in TRM 6.18-7 where they talk about the calculation of BRCOMP w/
  // a 32k crystal, divrate of1, and 9600 baud
  UART0->BRCOMP = 0xA5;
}

int main(void) {
  SYS_UnlockReg();
  SYS_Init();
  SYS_LockReg();

  UART0_Init();

  printf("Hello, World!\n");

  while (1) { }
}