Trouble setting LIN baudrate (on SAMD10, no ASF)

Go To Last Post
2 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm having trouble setting up the UART/USART on the D10 (Xmini). The goal is to configure the UART as a LIN slave at 19.2kbps, on pins 24+25.

 

When I change the value for the .BAUD register (or sub-registers) bit-times never exceed 20us and can range into less than a microsecond.

 

This occurs in both arithmetic and fractional modes.

 

Everything else except the baudrate seems to work fine. I have seen many examples and don't see what's different. But code-blindness is always a possibility.

 

This is the code using:

void SERCOM1_sync()
{
    while (SERCOM1->USART.SYNCBUSY.reg);     // for any of the bits
}     

void SERCOM1_setup()
{
    // switch on peripheral
    REG_PM_APBCMASK |= PM_APBCMASK_SERCOM1;
    connect_to_GCLK0(SERCOM1_GCLK_ID_CORE);

    // reset peripheral
    SERCOM1->USART.CTRLA.bit.SWRST = 1;
    SERCOM1_sync();

    // configure CTRLA
    SERCOM_USART_CTRLA_Type cfga =
    {
        .bit.CMODE   = 1,       // 1=asynchronous mode
        .bit.CPOL    = 0,       // XCLK polarity, rising->TX, falling->RX (sync mode)
        .bit.DORD    = 1,       // 1=LSB sent first (for LIN)
        .bit.ENABLE  = 0,       // enable (disable before settings), later
        .bit.FORM    = 4,       // 0=UART 1=+PARITY 4=AUTOBAUD 5=+PARITY, 4 has break-detection & auto-baud (for LIN), see STATUS.ISF
        .bit.IBON    = 0,       // immediate STATUS.BUFOVF
        .bit.MODE    = 1,       // 0=UART+XCLK, 1=UART, 2=SPI-slave, 3=SPI-master, 4=I2C-slave, 5=I2C-master
        .bit.RUNSTDBY= 1,       // run in standby
        .bit.RXPO    = 0,       // PAD[x], 0=>PIN11
        .bit.TXPO    = 3,       // PAD[x], 3=>PIN10
        .bit.SAMPA   = 0,       // SAMPle Adjustment to the right, 0=7/8/9, 1=+2, 2=+4, 3=+6
        .bit.SAMPR   = 1,       // baudrate & oversampling: 0=A16x, 1=F16x, 2=A8x, 3=F8x, 4=A3x, 5=F3x (A=Arithmetic, F=Fractional(=For LIN))
        .bit.SWRST   = 0,       // reset, other bits are ignored
    };
    SERCOM1->USART.CTRLA = cfga;
    SERCOM1_sync();

    // configure CTRLB
    SERCOM_USART_CTRLB_Type cfgb =
    {
        .bit.CHSIZE=7&   8,     // character size: 5..9 bits
        .bit.COLDEN    = 0,     // enable collision detection (requires RX & TX share line)
        .bit.ENC       = 0,     // enable IrDA encoding
        .bit.PMODE     = 0,     // 1=odd parity (requires CTRLA.FORM=1) => STATUS.PERR=parity error
        .bit.RXEN      = 1,     // enable RX, see SYNCBUSY.CTRLB
        .bit.TXEN      = 1,     // enable TX, see SYNCBUSY.CTRLB
        .bit.SBMODE    = 0,     // enable 2 stopbits
        .bit.SFDE      = 0,     // enables wakeup on RXS (RX Start) or RXC (RX Complete) interrupts
    };
    SERCOM1->USART.CTRLB = cfgb;
    SERCOM1_sync();

    // configure baudrate (using fractional divider, for LIN)
    // see https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/SERCOM.cpp
    #define LIN_BAUDRATE
    #define BAUDCONST8    ((MAIN_CLOCK_HZ * 8) / (16 * LIN_BAUDRATE))
    SERCOM1->USART.BAUD.FRAC.FP   = (BAUDCONST8 % 8);
    SERCOM1->USART.BAUD.FRAC.BAUD = (BAUDCONST8 / 8);
    SERCOM1_sync();

    // no interrupts
    //SERCOM1->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC;
    //NVIC_EnableIRQ(SERCOM1_IRQn);

    // use "PORT Function Multiplexing" to C pads
    PortA->PMUX[2].bit.PMUXE = PORT_PMUX_PMUXE_C_Val;
    PortA->PMUX[2].bit.PMUXO = PORT_PMUX_PMUXO_C_Val;
    // connect SERCOM1 to C pads
    mux(PIN_PA24, MUX_PA24C_SERCOM1_PAD2);  // TX
    mux(PIN_PA25, MUX_PA25C_SERCOM1_PAD3);  // RX

    // enable peripheral
    SERCOM1->USART.CTRLA.bit.ENABLE = 1;
    SERCOM1_sync();
}

 

I also tried .bit.FORM=0 (to get regular UART mode, instead of the =4 (for LIN) above). Same result.

 

The main clock is in the range of 30MHz. Not 200MHz as the results would suggest ;)

 

Thanks.

This topic has a solution.

Last Edited: Wed. Feb 24, 2016 - 06:32 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

The below code is behaving as expected, and is noteworthy because it is universal: no need to study the datasheet!

 

// just define other pins and it should work ootbox
// change SERCOM1 to any other SERCOM and it should work (on D09/D10/D11)
#define  PIN_RX          PIN_PA17
#define  PIN_TX          PIN_PA16
void select_peripheral_function(u32 pin, u32 pf)
{
    PortA->PINCFG[pin].bit.PMUXEN = 1;
    if (pin & 1)
      PortA->PMUX[pin / 2].bit.PMUXO = pf;  // odd pins
    else
      PortA->PMUX[pin / 2].bit.PMUXE = pf;  // even pins
}

// normal use (for SERCOM1): .RXP = sercom_use_pin(1, PIN_RX);
i32 sercom_use_pin(u32 sercom, u32 pin)
{
    // activate Peripheral Funcion C (2) on pin
    select_peripheral_function(pin, 2);
    // remainder normally gets optimized out
    if ((   "////00001100//0011////1111////11"[pin]-'0') != sercom) while(1);
    //       01234567890123456789012345678901
    // retornar o PAD[0..3] usado
    return ("////23012323//0123////0123////01"[pin]-'0');
    //  pin: 01234567890123456789012345678901
    // now connect RXPO and TXPO to the value returned
}

void SERCOM1_sync()  { while (SERCOM1->USART.SYNCBUSY.reg); }    // any bit

// 0=use arithmetic prescaler, 1=fractional baudrate
#define  FBRP               0           

#define  BHF                (rdiv(MAIN_CLOCK_HZ,  0x100 * 100))
#define  BAUD(baudrate)     ((0x100 * (BHF*0x100 - (16 *2 *baudrate)/100)) /BHF)  // arithmetic
#define  BAUDFP(baudrate)   (MAIN_CLOCK_HZ / (16 * baudrate))                     // fractional

void UART_init()
{
    // setup RX/TX pin state elsewhere

    // correct PAD number to txpo bits below
    if (txpo & 1) while(1);     // invalid or unsupported, see table 6.1 in D11 manual
    txpo = txpo >> 1;

    // power & clock peripheral
    REG_PM_APBCMASK |= PM_APBCMASK_SERCOM1;

    // reset peripheral
    SERCOM1->USART.CTRLA.bit.SWRST = 1;
    SERCOM1_sync();

    //below is same as: REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_ID(GCLK_CLKCTRL_ID_SERCOM1_CORE)| GCLK_CLKCTRL_GEN(0);
    connect_to_sysclock(SERCOM1_GCLK_ID_CORE);

    SERCOM_USART_CTRLA_Type cfga =
    {
        .bit.CMODE   = 0,       // 1=Synchronous mode
        .bit.CPOL    = 0,       // XCLK polarity, 1=(rising->TX, falling->RX) (sync mode)
        .bit.DORD    = 1,       // 1=LSB sent first (for LIN)
        .bit.ENABLE  = 0,       // enable (disable before settings), later
        .bit.FORM    = 0,       // 0=UART 1=+PARITY 4=AUTOBAUD 5=+PARITY; 4) has break-detection & auto-baud (for LIN), see STATUS.ISF
        .bit.IBON    = 0,       // enable immediate STATUS.BUFOVF
        .bit.MODE    = 1,       // 0=UART+XCLK, 1=UART, 2=SPI-slave, 3=SPI-master, 4=I2C-slave, 5=I2C-master
        .bit.RUNSTDBY= 1,       // run in standby
        .bit.RXPO    = rxpo,    // PAD[0..3], see Datasheet 6.1
        .bit.TXPO    = txpo,    // normally PAD[2], see Datasheet 6.1
        .bit.SAMPA   = 0,       // SAMPle Adjustment to the right, 0=7/8/9, 1=+2, 2=+4, 3=+6
        .bit.SAMPR   = FBRP,    // baudrate & oversampling: 0=A16x, 1=F16x, 2=A8x, 3=F8x, 4=A3x, 5=F3x (A=arithmetic, F=fractional (=For LIN))
        .bit.SWRST   = 0,       // reset, other bits are ignored
    };
    SERCOM1->USART.CTRLA = cfga;
    SERCOM1_sync();

    // configure CTRLB
    SERCOM_USART_CTRLB_Type cfgb =
    {
        .bit.CHSIZE=7&   8,     // CHaracter SIZE: 5..9 bits
        .bit.COLDEN    = 0,     // enable COLlision Detection (requires RX & TX share line)
        .bit.ENC       = 0,     // enable IrDA ENCoding
        .bit.PMODE     = 0,     // 1=odd Parity (requires CTRLA.FORM=1) => STATUS.PERR=parity error
        .bit.RXEN      = 1,     // enable RX, see SYNCBUSY.CTRLB
        .bit.TXEN      = 1,     // enable TX, see SYNCBUSY.CTRLB
        .bit.SBMODE    = 0,     // enable 2 stopbits
        .bit.SFDE      = 0,     // enable wakeup on RXS (RX Start) or RXC (RX Complete) interrupts
    };
    SERCOM1->USART.CTRLB = cfgb;
    SERCOM1_sync();

    // set baudrate
    SERCOM1->USART.BAUD.reg = (u16) (FBRP) ?BAUDFP(LIN_BAUDRATE) :BAUD(LIN_BAUDRATE);
    SERCOM1_sync();

    // enable peripheral
    REG_SERCOM1_USART_CTRLA |= SERCOM_USART_CTRLA_ENABLE;
    SERCOM1_sync();
}

 

 

Last Edited: Wed. Feb 24, 2016 - 06:40 PM