SAML21 USART implementation

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

Hello, I've tried to implement UART at 9600 baud on a saml21g17b, but I don't believe it is working. I'm trying to verify that it's working using an arduino mega to capture the character 'h' and reprint it to the serial console in the arduino IDE. The bits are set and I'm convinced that the micro-controller is transmitting because there are symbols showing up on the console. Additionally, the bits are being set correctly according to the debugger (segger studios) and the DRE flag changes when I push characters to the data register. I think it's a timing issue, but I'm not certain. I'll be checking semi-frequently and will provide info if more is needed. Thanks for reading!

 

you can find the data sheet here

My code for making sure mclk, gclk are active as well as making sure we're using the internal 16MHz oscillator

// set the pins for the outlet controller
int initClocks(){

  OSCCTRL->OSC16MCTRL.reg = OSCCTRL_OSC16MCTRL_ENABLE;

  MCLK->APBAMASK.reg |= MCLK_APBAMASK_PM | MCLK_APBAMASK_GCLK | MCLK_APBAMASK_MCLK;

  return 1;
}

Body of code that initiates SERCOM2 and sets up USART

void main(void) {

    initClocks();
    if(initPins() == -1)return;

    // make sure slow core is enabled
    GCLK->PCHCTRL[UART_SERCOM_GCLK_SLOW].reg &= ~GCLK_PCHCTRL_CHEN;
    GCLK->PCHCTRL[UART_SERCOM_GCLK_SLOW].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(0); // enable PERIPHERAL clock for sercom SLOW. Used by sercom 0 - 4
    // make sure core is enabled
    GCLK->PCHCTRL[UART_SERCOM2_GCLK_CORE].reg &= ~GCLK_PCHCTRL_CHEN;
    GCLK->PCHCTRL[UART_SERCOM2_GCLK_CORE].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(0); // enable PERIPHERAL clock for sercom CORE
    // make sure apbc for sercom2 is enabled
    MCLK->APBCMASK.reg |= MCLK_APBCMASK_SERCOM2;
    // debugging mode enabled
    SERCOM2->USART.DBGCTRL.reg |= SERCOM_USART_DBGCTRL_DBGSTOP;

    // --------    BAUD VALUE CALCULATION     --------
    uint16_t baudValue = 65536 * (1 - (16 * (9600 / (double)FCPU)));// set this to 9600 baud

    SERCOM2->USART.BAUD.reg |= SERCOM_USART_BAUD_BAUD(baudValue);

    // set output format to 8 bit data no parity one stop bit and pads 2(RX) and 0 (TX)
    SERCOM2->USART.CTRLA.reg |= SERCOM_USART_CTRLA_MODE(0x1) /*internal clock*/ | SERCOM_USART_CTRLA_TXPO(0) | SERCOM_USART_CTRLA_RXPO(2);
    SERCOM2->USART.CTRLB.reg |= SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN;
    SERCOM2->USART.CTRLA.reg |=SERCOM_USART_CTRLA_ENABLE;

    int count = 0;
    int k;
    do{
        //sendUSART(2,"hi",2);
       setHigh(led[1]);
       while(SERCOM_USART_INTFLAG_DRE == 0){
        k =  SERCOM_USART_INTFLAG_DRE;
       }
       SERCOM2->USART.DATA.reg = 'h';

       setLow(led[1]);

    }while(1);

}

Arduino sketch where "Serial" prints to serial console

void setup() {
  // initialize both serial ports:
  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() {
  // read from port 1, send to port 0:
  if (Serial1.available()) {
    int inByte = Serial1.read();
    Serial.write(inByte);
    Serial.write("\n");
  }

  // read from port 0, send to port 1:
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial1.write(inByte);
  }
}

Output from the serial console


00:20:11.117 -> ⸮
00:20:11.117 -> x
⸮
00:20:11.152 -> x
00:20:11.152 -> ⸮
00:20:11.152 -> x
00:20:11.152 -> ⸮
00:20:11.152 -> x
00:20:11.152 -> ⸮
00:20:11.152 -> x
00:20:11.152 -> ⸮
00:20:11.152 -> x
00:20:11.152 -> ⸮
00:20:11.152 -> x
00:20:11.152 -> ⸮
00:20:11.152 -> x
00:20:11.152 -> ⸮
00:20:11.152 -> x
00:20:11.152 -> ⸮
00:20:11.152 -> x
00:20:11.186 -> ⸮
00:20:11.186 -> x
00:20:11.186 -> ⸮
00:20:11.186 -> x
00:20:11.186 -> ⸮
00:20:11.186 -> x
00:20:11.186 -> ⸮
00:20:11.186 -> x
00:20:11.186 -> ⸮
00:20:11.186 -> x
00:20:11.186 -> ⸮
00:20:11.186 -> x
00:20:11.186 -> ⸮
00:20:11.186 -> x
00:20:11.186 -> ⸮
00:20:11.186 -> x
00:20:11.220 -> ⸮
00:20:11.220 -> x
00:20:11.220 -> ⸮
00:20:11.220 -> x
00:20:11.220 -> ⸮
00:20:11.220 -> x
00:20:11.220 -> ⸮
00:20:11.220 -> x
00:20:11.220 -> ⸮
00:20:11.220 -> x
00:20:11.220 -> ⸮
00:20:11.220 -> x
00:20:11.220 -> ⸮
00:20:11.220 -> x
00:20:11.220 -> ⸮
x
00:20:11.253 -> ⸮
00:20:11.253 -> x

here is a picture of the equation to calculate the baud value for asynchronous arithmetic where S is the sample rate (16 in my case)

baud value calculation

This topic has a solution.

Avi

Last Edited: Sun. Jan 20, 2019 - 03:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This may be a wrong condition in a loop waiting for end of sending of char. The SERCOM_USART_INTFLAG_DRE is wrote by You macro that define code for returning DRE flag from INTFLAG register for SERCOM2 or it is mask for bit in this register? In the second case the condition never is true and next char is put in register without waiting to end of previous.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks!. I'll changed that now. I have a similar output after the change. converting the text to binary yields
⸮ = 0010111000101110

x = 01111000

h = 01101000

how is that possible if a USART data Frame is only 8 bits?

 

update: It looks like SERCOM2 is throwing a frame error. Could be due to my lack of pull up resistors

    do{

       while(!SERCOM2->USART.INTFLAG.bit.DRE){

       }
       SERCOM2->USART.DATA.reg = 'h';

    }while(1);
⸮
09:50:45.088 -> x

 

Avi

Last Edited: Tue. Nov 20, 2018 - 09:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are not setting FSEL in OSC16M, the default is 4MHz, is this your FCPU?

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I changed

OSCCTRL->OSC16MCTRL.reg = OSCCTRL_OSC16MCTRL_ENABLE;

to

 

OSCCTRL->OSC16MCTRL.reg = OSCCTRL_OSC16MCTRL_ENABLE | OSCCTRL_OSC16MCTRL_FSEL_16;

I thought I had set the frequency to 2MHz with divider, I changed it to 16MHz now with no divider

 

I am now getting buffer overflow and Frame error flags.

On occasion, frame error is thrown prior to data being put into the register.

 

frame error occurs first and then buffer overflow one loop later

 

here is the serial console from arduino

 

Avi

Last Edited: Tue. Nov 20, 2018 - 02:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You should set DORD --> LSB First

Is your pin configuration correct? (PINMUX enabled and set to the correct function)

Is your sercom mapping correct?

I would suggest to calculate the baudrate not at runtime.

DRE bit is read only.

CTRLA & CTRLB are write sync.

 

Hope this helps

 

Flo1991

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Flo1991, I was able to get my hands on a usb to serial device and resolve some issues. This is what I have now:
 

    /* - - - - CLOCK ENABLES - - - - */
    GCLK->PCHCTRL[UART_SERCOM2_GCLK_CORE].reg &= ~GCLK_PCHCTRL_CHEN; // switch off
    GCLK->PCHCTRL[UART_SERCOM2_GCLK_CORE].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(0x1); // enable PERIPHERAL clock for sercom CORE
    /* - - - - - - - - - - - - - - - */

    // get fractional and integer parts of the baud
    uint8_t fp = fractionalPart(&baudValue,baud);
    SERCOM2->USART.CTRLA.bit.ENABLE = 0;
    SERCOM2->USART.CTRLA.bit.MODE = 1; // Internal oscillator
    SERCOM2->USART.CTRLA.bit.CMODE = 0; //asynchronous
    SERCOM2->USART.CTRLA.bit.RXPO = 2; // rx is pad 2
    SERCOM2->USART.CTRLA.bit.TXPO = 0; // TX is pad 0
    SERCOM2->USART.CTRLB.bit.CHSIZE = 0; // 8 bit char size
    SERCOM2->USART.CTRLB.bit.SBMODE = 0; // 1 stop bit
    SERCOM2->USART.CTRLA.bit.DORD = 1; // lsb
    SERCOM2->USART.CTRLA.bit.SAMPR = 3; // 8x sampling using fractional (0x1 for 16x oversample)
    SERCOM2->USART.CTRLA.bit.FORM = form; // frame, frame w/ parity , autobaud , autobaud with parity
    SERCOM2->USART.CTRLB.bit.PMODE = 0; // parity is even
    SERCOM2->USART.BAUD.reg = SERCOM_USART_BAUD_FRACFP_BAUD(baudValue) | SERCOM_USART_BAUD_FRACFP_FP(fp) ;
    SERCOM2->USART.CTRLB.bit.RXEN = 1; // enables
    SERCOM2->USART.CTRLB.bit.TXEN = 1;
    SERCOM2->USART.CTRLA.bit.ENABLE = 1;

 

Here is my method which sends a string one character at a time:

          for(i = 0 ; i < length ; i++){
            while(SERCOM2->USART.INTFLAG.bit.DRE == 0 && SERCOM2->USART.INTFLAG.bit.TXC){;}
            SERCOM2->USART.DATA.reg = SERCOM_USART_DATA_DATA(d[i]);
          }

Receiving data from the micro controller is like this when sending the string "hi":

OUTPUT: ihihhhhihhhhihihhhhihhhhih

This problem does not persist if I am in debug mode and run the send message command once.

Is there an issue with how I send my messages?

 

Any thoughts?

 

EDIT: The problem was solved by changing the while loop condition to !SERCOM2->USART.INTFLAG.bit.DRE

Although if someone has an explanation as to why it wouldn't have worked the other way I'd appreciate that very much.

 

The full project and code is available on my github here

Avi

Last Edited: Sun. Jan 20, 2019 - 03:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I cannot exactly tell you what is happening, but the following thing is important and may cause misbehaviour:

-TXC is default 0, so the while loop cannot be true

-TXC is not cleared automaticly, so if it is true once, it will always be true

 

and what is this:

 

SERCOM2->USART.DATA.reg = SERCOM_USART_DATA_DATA(d[i]);

?!??!?!

 

Flo1991

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interesting, I missed that while going through the data sheet. That second part is a CMSIS macro to position the data in the correct place in the register and uses a bitmask (0x1ff) to keep the data at 9 bits maximum. Since I'm using an array of characters I removed that part.

Avi

Last Edited: Sun. Jan 20, 2019 - 05:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ok, I have never used this macro so far...