Unable to receive more than two bytes of data through hardware UART

I'm using an Arduino Zero which has an ATSAMD21G18 microcontroller. I've interfaced it to a LIN Transceiver via the UART (TX -> PIN 1(SERCOM0.2); RX -> PIN 0(SERCOM0.3) ). The LIN transceiver follows LIN protocol 2.1. I'm able to send the LIN header frames to the transceiver via UART. The response of the transceiver is visible in an oscilloscope. The problem that I'm currently facing is the Arduino Zero is unable to read more than two bytes of data from UART. The LIN response frame from the Transceiver consists of 8 data bytes and checksum byte.

void loop()
{
//Transmit Header Frame
while(!SERCOM0->USART.INTFLAG.bit.DRE); //Wait for Data Register Empty
SERCOM0->USART.DATA.reg = (uint16_t)0x55; //Sync Byte
while(!SERCOM0->USART.INTFLAG.bit.DRE);
SERCOM0->USART.DATA.reg = (uint16_t)0x76; //LIN PID
//Begin LIN Response Frame
while(!SERCOM0->USART.INTFLAG.bit.RXC); //Wait for Receive Complete
rd_data[0] = SERCOM0->USART.DATA.reg; //receive 1st byte 
SerialUSB.println(rd_data[0], HEX);
while(!SERCOM0->USART.INTFLAG.bit.RXC); //Wait for Receive Complete
rd_data[1] = SERCOM0->USART.DATA.reg; //receive 2nd byte
SerialUSB.println(rd_data[1], HEX);
while(!SERCOM0->USART.INTFLAG.bit.RXC); //Wait for Receive Complete
rd_data[2] = SERCOM0->USART.DATA.reg; //receive 3rd byte
SerialUSB.println(rd_data[2], HEX);
while(!SERCOM0->USART.INTFLAG.bit.RXC); //Wait for Receive Complete
rd_data[3] = SERCOM0->USART.DATA.reg; //receive 4th byte
SerialUSB.println(rd_data[3], HEX);
delay(100);
}

The Serial Window just shows the same two bytes that were transmitted. i.e. 0x55 and 0x76. The Datasheet states that ATSAMD21G18 microcontroller shares same address for UART TX and RX DATA registers


Alternative: Use two different UARTS. SERCOM0 for TX and SERCOM2 for RX. This provides for two different DATA registers.

void loop()
{
//Transmit Header Frame
while(!SERCOM0->USART.INTFLAG.bit.DRE); //Wait for Data Register Empty
SERCOM0->USART.DATA.reg = (uint16_t)0x55; //Sync Byte
while(!SERCOM0->USART.INTFLAG.bit.DRE);
SERCOM0->USART.DATA.reg = (uint16_t)0x76; //LIN PID
//Begin LIN Response Frame - SERCOM2
while(!SERCOM2->USART.INTFLAG.bit.RXC); //Wait for Receive Complete
rd_data[0] = SERCOM2->USART.DATA.reg; //receive 1st byte 
SerialUSB.println(rd_data[0], HEX);
while(!SERCOM2->USART.INTFLAG.bit.RXC); //Wait for Receive Complete
rd_data[1] = SERCOM2->USART.DATA.reg; //receive 2nd byte
SerialUSB.println(rd_data[1], HEX);
SerialUSB.println(SERCOM2->USART.INTFLAG.bit.RXC); // To check whether RX is complete
while(!SERCOM2->USART.INTFLAG.bit.RXC); //Wait for Receive Complete
rd_data[2] = SERCOM2->USART.DATA.reg; //receive 3rd byte
SerialUSB.println(rd_data[2], HEX);
while(!SERCOM2->USART.INTFLAG.bit.RXC); //Wait for Receive Complete
rd_data[3] = SERCOM2->USART.DATA.reg; //receive 4th byte
SerialUSB.println(rd_data[3], HEX);
delay(100);
}

Upon modifying the code as above only The first two bytes of the LIN response frame were visible followed by a complete halt.

Looks like the device is unable to receive more than 2 bytes via UART.

Please do provide your valuable inputs.

  • Safeer
while(!SERCOM0->USART.INTFLAG.bit.RXC); //Wait for Receive Complete
rd_data[1] = SERCOM0->USART.DATA.reg; //receive 2nd byte
SerialUSB.println(rd_data[1], HEX);

You probably don't have time to so a SerialUSB.print() in the time that it takes the next character to arrive on the LIN port. Try receiving all the characters of the LIN message, and THEN printing them all. (perhaps include a timeout in case not all characters are there.)
The two characters you're receiving probably match up with the two bytes of buffering in most uart implementations.

Thank you @westfw for the suggestion. I’ve made necessary changes to the code as follows

SERCOM2->USART.CTRLA.bit.ENABLE = 0x1u;
  while (SERCOM2->USART.SYNCBUSY.bit.ENABLE); //wait for synchronization
  while (!SERCOM2->USART.INTFLAG.bit.RXC);
  val[0] = SERCOM2->USART.DATA.reg;
  while (!SERCOM2->USART.INTFLAG.bit.RXC);
  val[1] = SERCOM2->USART.DATA.reg;
  for (uint8_t i = 2; i < 11; i++)
  {
    while (!SERCOM2->USART.INTFLAG.bit.RXC);
    val[i] = SERCOM2->USART.DATA.reg;
  }

for (uint8_t i = 0; i < 11; i++)
  {
    SerialUSB.print(val[i], HEX);
    SerialUSB.print(" ");
  }
  SerialUSB.println(" ");

Now I’m able to view all data bytes and the final checksum byte. Now the problem is the serial communication wont begin unless I reset the device multiple times or disconnect and connect the Rx_Jumper several times. I don’t know if it’s related to the USART setup. The USART configuration is as shown below:

PORT->Group[0].PINCFG[10].bit.PMUXEN = 1;
  PORT->Group[0].PINCFG[14].bit.PMUXEN = 1;
  PORT->Group[0].PINCFG[14].bit.INEN = 1;
  PORT->Group[0].PINCFG[14].bit.PULLEN = 1;
  PORT->Group[0].PMUX[5].reg = 0x02;
  PORT->Group[0].PMUX[7].reg = 0x02;
  //SERCOM0.2 TX and SERCOM2.2 RX

  GCLK->GENCTRL.reg = (uint32_t) (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC(0x07u) | GCLK_GENCTRL_ID(0x03u));
  while (GCLK->STATUS.bit.SYNCBUSY); //wait for synchronisation
  GCLK->GENCTRL.reg = (uint32_t) (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC(0x07u) | GCLK_GENCTRL_ID(0x05u));
  while (GCLK->STATUS.bit.SYNCBUSY); //wait for synchronisation
  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK3 | GCLK_CLKCTRL_ID(0x14u));
  while (GCLK->STATUS.bit.SYNCBUSY); //wait for synchronisation
  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK5 | GCLK_CLKCTRL_ID(0x16u));
  while (GCLK->STATUS.bit.SYNCBUSY); //wait for synchronisation
  //Enable clock for BOOT_USART_MODULE
  PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0;
  PM->APBCMASK.reg |= PM_APBCMASK_SERCOM2;

  SERCOM0->USART.CTRLA.bit.ENABLE = 0x0u;
  while (SERCOM0->USART.SYNCBUSY.bit.ENABLE); //wait for synchronization
  SERCOM2->USART.CTRLA.bit.ENABLE = 0x0u;
  while (SERCOM2->USART.SYNCBUSY.bit.ENABLE); //wait for synchronization
  SERCOM0->USART.CTRLA.bit.SWRST = 0x1u;
  while (SERCOM0->USART.SYNCBUSY.bit.SWRST); //wait for synchronization
  SERCOM2->USART.CTRLA.bit.SWRST = 0x1u;
  while (SERCOM2->USART.SYNCBUSY.bit.SWRST); //wait for synchronization

  //  SERCOM0->USART.CTRLA.reg = SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_FORM(0x04u) | SERCOM_USART_CTRLA_TXPO(0x1u) | SERCOM_USART_CTRLA_SAMPR(0x1u) | SERCOM_USART_CTRLA_MODE(0x1u);
  //  SERCOM2->USART.CTRLA.reg = SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_FORM(0x04u) | SERCOM_USART_CTRLA_RXPO(0x2u) | SERCOM_USART_CTRLA_SAMPR(0x1u) | SERCOM_USART_CTRLA_MODE(0x1u);
  SERCOM0->USART.CTRLA.reg = SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_FORM(0x0u) | SERCOM_USART_CTRLA_TXPO(0x1u) | SERCOM_USART_CTRLA_MODE(0x1u);
  SERCOM2->USART.CTRLA.reg = SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_FORM(0x0u) | SERCOM_USART_CTRLA_RXPO(0x2u) | SERCOM_USART_CTRLA_MODE(0x1u);

  SERCOM0->USART.CTRLB.reg = SERCOM_USART_CTRLB_CHSIZE(0x0u) | SERCOM_USART_CTRLB_TXEN;
  while (SERCOM0->USART.SYNCBUSY.bit.CTRLB); //wait for synchronization
  SERCOM2->USART.CTRLB.reg = SERCOM_USART_CTRLB_CHSIZE(0x0u) | SERCOM_USART_CTRLB_RXEN;
  while (SERCOM2->USART.SYNCBUSY.bit.CTRLB); //wait for synchronization

  SERCOM0->USART.BAUD.reg = BAUD_RATE_19200;
  SERCOM2->USART.BAUD.reg = BAUD_RATE_19200;
  SERCOM0->USART.CTRLA.bit.ENABLE = 0x1u;
  while (SERCOM0->USART.SYNCBUSY.bit.ENABLE); //wait for synchronization
  SERCOM2->USART.CTRLA.bit.ENABLE = 0x1u;
  while (SERCOM2->USART.SYNCBUSY.bit.ENABLE); //wait for synchronization