AVR USART Baudrates [Solved]

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

Dear Freaks,

 

I'm developing an avr-powered device that needs to communicate with both a linux device (Beaglebone Black) and a XpressNet network.

The XpressNet implementation dictates to use 62500Baud, which is fine when using a 10MHz clock for the AVR (3.3V) and using 9 as UBRR value.

On the other side (Linux), AFAIK only the traditional baudrates (9600, 19200, ... , 115200 etc) are available.  These rates are not exactly producible when using a 10MHz crystal.

 

I cannot change the XpressNet parameters (devices on the market).  I don't know if the BBB can use non-standard baudrates.

 

How can I generate traditional baudrates using a 10MHz crystal?

 

Thanks.

This topic has a solution.

"As simple as possible, but not simpler"

GUI Framework for Atmel Xplained Pro

Last Edited: Tue. Oct 10, 2017 - 08:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Use 9600 as your target. The actual number will come out as 9615 which is only 0.16% too high which is well within spec.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

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

You'd have to do a little poking about with a spreadsheet, or AVRcalc or KAVRcalc utilities.

 

If you want to stick with 10MHz, then as you know 62500 is fat and happy.

 

115200 is -1.4% if U2X is used.  Not ideal, but not too bad.  Same with e.g. 57600.  Slow rates 9600 & 19200 are less than 0.2% off.

 

With e.g. 14.7456MHz magic crystal, 62500 is about 1.7% off.  18.432 and U2X is about 0.4% off.

 

11.0592 is pretty good, too.  Do some noodling like above...

 

 

 

 

 

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Tue. Oct 10, 2017 - 03:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PaulVdBergh wrote:
On the other side (Linux), AFAIK only the traditional baudrates
Tell us more about the "AFAIK" in this - on what do you base your understanding?

 

Going back to original IBM PCs that had the 8051 UART and later the 16550 UART as far as I know that hardware was very like AVR UARTs in fact that there is just an integer register to set baud so, yes, because the UART was clocked form a "baud rate friendly" crystal that only lends itself to specifc baud rates I don't think that in turn limits it to just the "well known" ones like 300, 2400, 9600, 19200, etc

 

Or where you referring to termios ?

 

http://man7.org/linux/man-pages/...

 

It's true that says:

 

But I have a feeling that you can probably program the UART with other "unusual" values though you may need to modify the ioctl() support for the UART in the kernel to achieve this.

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

Actually, grubbing about in the core of the Linux kernel one arrives at:

 

https://elixir.free-electrons.co...

 

which is where values passed in from termios are decoded. From there you arrive at:

 

https://elixir.free-electrons.co...

 

/*
 * Routine which returns the baud rate of the tty
 *
 * Note that the baud_table needs to be kept in sync with the
 * include/asm/termbits.h file.
 */
static const speed_t baud_table[] = {
	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
	9600, 19200, 38400, 57600, 115200, 230400, 460800,
#ifdef __sparc__
	76800, 153600, 307200, 614400, 921600
#else
	500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
	2500000, 3000000, 3500000, 4000000
#endif
};

#ifndef __sparc__
static const tcflag_t baud_bits[] = {
	B0, B50, B75, B110, B134, B150, B200, B300, B600,
	B1200, B1800, B2400, B4800, B9600, B19200, B38400,
	B57600, B115200, B230400, B460800, B500000, B576000,
	B921600, B1000000, B1152000, B1500000, B2000000, B2500000,
	B3000000, B3500000, B4000000
};
#else
static const tcflag_t baud_bits[] = {
	B0, B50, B75, B110, B134, B150, B200, B300, B600,
	B1200, B1800, B2400, B4800, B9600, B19200, B38400,
	B57600, B115200, B230400, B460800, B76800, B153600,
	B307200, B614400, B921600
};
#endif

 

Last Edited: Tue. Oct 10, 2017 - 03:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could always drop your main crystal to 8MHz which would open up 19,200 and 38,400 as viable rates running at X1.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

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

@Cliff:

I include<termois.h> in my sourcecode, which points to /usr/arm-linux-gnueabihf/include/termios.h.  the later redirects to <bits/termios.h> (I'm cross-compiling on Debian stretch 64 bits).

 

/usr/arm-linux-gnueabihf/include/bits/termios.h defines (among others...)

/* c_cflag bit meaning */
#ifdef __USE_MISC
# define CBAUD	0010017
#endif
#define  B0	0000000		/* hang up */
#define  B50	0000001
#define  B75	0000002
#define  B110	0000003
#define  B134	0000004
#define  B150	0000005
#define  B200	0000006
#define  B300	0000007
#define  B600	0000010
#define  B1200	0000011
#define  B1800	0000012
#define  B2400	0000013
#define  B4800	0000014
#define  B9600	0000015
#define  B19200	0000016
#define  B38400	0000017
#ifdef __USE_MISC
# define EXTA B19200
# define EXTB B38400
#endif
#define CSIZE	0000060
#define   CS5	0000000
#define   CS6	0000020
#define   CS7	0000040
#define   CS8	0000060
#define CSTOPB	0000100
#define CREAD	0000200
#define PARENB	0000400
#define PARODD	0001000
#define HUPCL	0002000
#define CLOCAL	0004000
#ifdef __USE_MISC
# define CBAUDEX 0010000
#endif
#define  B57600   0010001
#define  B115200  0010002
#define  B230400  0010003
#define  B460800  0010004
#define  B500000  0010005
#define  B576000  0010006
#define  B921600  0010007
#define  B1000000 0010010
#define  B1152000 0010011
#define  B1500000 0010012
#define  B2000000 0010013
#define  B2500000 0010014
#define  B3000000 0010015
#define  B3500000 0010016
#define  B4000000 0010017
#define __MAX_BAUD B4000000
#ifdef __USE_MISC
# define CIBAUD	  002003600000		/* input baud rate (not used) */
# define CMSPAR   010000000000		/* mark or space (stick) parity */
# define CRTSCTS  020000000000		/* flow control */
#endif

This made me thinking that no other possibilities were appropriate and no other baudrates could be possible on the BBB.

 

I'l have a look at your suggestion and investigate on the ioctl() capabilities.

 

Thanks.
 

"As simple as possible, but not simpler"

GUI Framework for Atmel Xplained Pro

Last Edited: Tue. Oct 10, 2017 - 07:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I say you can read Linux source at "LXR" (links above) so you can see (if you haven't pulled it locally) what the source of your kernel looks like (LXR has all issued versions to pick from).

 

There does seem to be a discrete mapping from the Bnnnn values in the Termios to actual hardware values. But I'm saying that maybe you could pull and modify your kernel source to allow access to more "unusual" divider values. You'd need to do this in unison with the datasheet for the TI chip on the BBB.

 

But it probably still does not help as others have pointed out - you need a baud that is a common divisor of both clock sources.

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

As this is a forum dedicated to Atmel devices, more specific AVR processors, maybe this is a little off-topic, but nevertheless...

 

In regard to Cliff's suggestion to use the ioctl() function and googling around I came up with the following code:

 

/*
 * XpressNetInterface.cpp
 *
 *  Created on: Oct 9, 2017
 *      Author: paul
 */

#include "XpressNetInterface.h"

#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

//#include <termios.h>
#include <asm/termbits.h>

namespace DCC_V3
{
.
.
.
	XpressNetInterface::XpressNetInterface(const char* pDevice)
			: m_pDevice(pDevice)
	{
		struct termios2 settings;

		m_fdSerial = open(m_pDevice.c_str(), O_RDWR | O_NOCTTY);
		if(m_fdSerial < 0)
		{
			//	TODO Daemon has no terminals, use syslog
			perror("open");
		}

		int r = ioctl(m_fdSerial, TCGETS2, &settings);
		if(r)
		{
			perror("ioctl");
		}

		settings.c_ispeed = settings.c_ospeed = 62500;
		settings.c_cflag &= ~CBAUD;
		settings.c_cflag |= BOTHER;

		r = ioctl(m_fdSerial, TCSETS2, &settings);
		if(r)
		{
			perror("ioctl");
		}

		m_fdStop = eventfd(0, 0);
		m_thread = thread([this]
		{
			threadFunc();
		});

	}
.
.
.

This code compiles without errors, however before I can test it on the BBB I have to dig in the never ending device tree overlay story...

 

edit : typos

"As simple as possible, but not simpler"

GUI Framework for Atmel Xplained Pro

Last Edited: Tue. Oct 10, 2017 - 05:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AM335x UART also has max 0.16% error for mutliple bauds (48MHz clock, 13x or 16x oversampling)

 

http://www.ti.com/product/AM3358/datasheet/peripheral_information_and_timings#lit23802305 (UART)

open the Technical Reference Manual (23MB) and go to page 4344 for

Table 19-25. UART Baud Rate Settings (48-MHz Clock)


https://elinux.org/Beagleboard:BeagleBone_Black_Serial 

 

"Dare to be naïve." - Buckminster Fuller

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

PaulVdBergh wrote:
How can I generate traditional baudrates using a 10MHz crystal?
By an fractional baud rate generator (FBRG)

XMEGA have USART FBRG with adjustable clock(s)

Could attach to the AVR a UART with a FBRG.

 

http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8331-8-and-16-bit-AVR-Microcontroller-XMEGA-AU_Manual.pdf

(page 289)

23.9 Fractional Baud Rate Generation

https://www.exar.com/products/interface/uarts/i2c-spi-uarts

 

"Dare to be naïve." - Buckminster Fuller

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

clawson wrote:
8051 UART

I think you mean 8250https://en.wikipedia.org/wiki/8250_UART ?

 

But I agree that it (and its successors) should be able to do "arbitrary" baud rates - not just limited to those which the OS provides as "easy options" ...

 

IIRC, this is how some PCs did MIDI ...

 

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

According to the same page in the TRM:

 

• UART 16x mode: Divisor value = Operating frequency/(16x baud rate)

So, if I set DLH and DLL to a divisor value of 48, I get exactly 62500 baud?  But how can I set these registers from within Linux userspace?

 

"As simple as possible, but not simpler"

GUI Framework for Atmel Xplained Pro

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

@ Cliff & awneil:

 

the UART is 16C750 compatible according the TRM.  (I think that's a chip used in older systems and is now implemented/emulated inside the TI Soc)

 

 

"As simple as possible, but not simpler"

GUI Framework for Atmel Xplained Pro

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

Yes.

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

Hi folks,

 

Finally, I succeeded in resolving this problem:

 

		struct termios2 settings;

		m_fdSerial = open(m_pDevice.c_str(), O_RDWR | O_NOCTTY);
		if(m_fdSerial < 0)
		{
			//	TODO Daemon has no terminals, use syslog
			perror("open");
		}

		int r = ioctl(m_fdSerial, TCGETS2, &settings);
		if(r)
		{
			perror("ioctl");
		}

		settings.c_ispeed = settings.c_ospeed = 62500;
		settings.c_cflag &= ~CBAUD;
		settings.c_cflag |= BOTHER;

		r = ioctl(m_fdSerial, TCSETS2, &settings);
		if(r)
		{
			perror("ioctl");
		}

		while(1)
		{
			write(m_fdSerial, "U", 1);
		}

Running this code on the BBB gives this output:

 

 

This is exactly what was needed.  smiley

 

Thanks to everybody helping me out.

"As simple as possible, but not simpler"

GUI Framework for Atmel Xplained Pro

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

Just to make the story complete:

If one will use the serial port for binary communication, like I will do, one has to add extra flags:

 


        struct termios2 settings;

        m_fdSerial = open(m_pDevice.c_str(), O_RDWR | O_NOCTTY);
        if (m_fdSerial < 0)
        {
            //    TODO Daemon has no terminals, use syslog
            perror("open");
        }

        int r = ioctl(m_fdSerial, TCGETS2, &settings);
        if (r)
        {
            perror("ioctl");
        }

        settings.c_ispeed = settings.c_ospeed = 62500;
        settings.c_cflag &= ~CBAUD;
        settings.c_cflag |= BOTHER;

        settings.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
        settings.c_oflag &= ~OPOST;
        settings.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
        settings.c_cflag &= ~(CSIZE | PARENB);
        settings.c_cflag |= CS8;

        r = ioctl(m_fdSerial, TCSETS2, &settings);
        if (r)
        {
            perror("ioctl");
        }


These flags sets the serial port in non-canonical mode, so that no filtering (CR/LF etc.) happens and data is send byte per byte instead of line per line.

More info can be found in "The Linux Programming Interface" (highly recommended literature).

 

Paul.

"As simple as possible, but not simpler"

GUI Framework for Atmel Xplained Pro