Run-time configuration of a UART for ATSAME54P20A using ASF4

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

Hello all!

 

I am using the ATSAME54P20A (specifically the SAME54XPLAINED pro development board) and the ASF4 software architecture with Atmel Start. I have spent quite a bit of time working with this board, ASF4 and Atmel Start, but so far I have not been able to figure out how to configure a new serial port at run-time. Atmel Start makes it very easy to configure a new serial port and then use it at run time, but the configuration stuff is all implemented in #define statements, so if you want to set up an entirely new serial port, you have to build a new atmel start project and recompile the software, or once you understand how their preprocessor macros work, you can write the #define statements yourself, but still, all the configuration stuff is done at compile-time rather than run-time. I noticed that ASF4 implements some functions to allow baud rates to be changed at runtime, but as far as I can tell there is no way to configure an entirely new serial port at run-time using ASF4 short of reverse-engineering the ASF4 macros and implementing them in run-time code. I've been trying to hack the ASF4 drivers so that I can do that, but it's a big task, and I don't want to re-invent the wheel if I can avoid it. So do you guys know of a simpler way to configure a serial port at run-time, or do I have to continue digging into the ASF4 drivers and extend them to get this capability?

 

Thanks!

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

I just generated a UART project and it seems to me that the core of the baud rate setting is:

/**
 * \internal Set baud rate
 *
 * \param[in] device The pointer to USART device instance
 * \param[in] baud_rate A baud rate to set
 */
static void _usart_set_baud_rate(void *const hw, const uint32_t baud_rate)
{
	bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw);

	hri_sercomusart_clear_CTRLA_ENABLE_bit(hw);

	CRITICAL_SECTION_ENTER()
	hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE);
	hri_sercomusart_write_BAUD_reg(hw, baud_rate);
	CRITICAL_SECTION_LEAVE()

	hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled);
}

in turn that makes a call to:

static inline void hri_sercomusart_write_BAUD_reg(const void *const hw, hri_sercomusart_baud_reg_t data)
{
	SERCOM_CRITICAL_SECTION_ENTER();
	((Sercom *)hw)->USART.BAUD.reg = data;
	SERCOM_CRITICAL_SECTION_LEAVE();
}

So it's really nothing more than a write to the USART->USART.BAUD.reg register for the active SERCOM.

 

PS I don't "do" SAM so I'm just basing this on what I see in the generated Start code.

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

Hi clawson,

 

Thanks for the response! I am familiar with the _usart_set_baud_rate function and it does look pretty straightforward. However, I am not interested in just changing the baud rate at run time, I want to be able to set up an entirely new SERCOM object at run time. I will post some code to try and clarify what I am doing.

 

This is the first ~60 lines from the hpl_sercom_config.h file from an atmel start uart project.

 

#ifndef CONF_SERCOM_2_USART_ENABLE
#define CONF_SERCOM_2_USART_ENABLE 1
#endif

// <h> Basic Configuration

// <q> Receive buffer enable
// <i> Enable input buffer in SERCOM module
// <id> usart_rx_enable
#ifndef CONF_SERCOM_2_USART_RXEN
#define CONF_SERCOM_2_USART_RXEN 1
#endif

// <q> Transmitt buffer enable
// <i> Enable output buffer in SERCOM module
// <id> usart_tx_enable
#ifndef CONF_SERCOM_2_USART_TXEN
#define CONF_SERCOM_2_USART_TXEN 1
#endif

// <o> Frame parity
// <0x0=>No parity
// <0x1=>Even parity
// <0x2=>Odd parity
// <i> Parity bit mode for USART frame
// <id> usart_parity
#ifndef CONF_SERCOM_2_USART_PARITY
#define CONF_SERCOM_2_USART_PARITY 0x0
#endif

// <o> Character Size
// <0x0=>8 bits
// <0x1=>9 bits
// <0x5=>5 bits
// <0x6=>6 bits
// <0x7=>7 bits
// <i> Data character size in USART frame
// <id> usart_character_size
#ifndef CONF_SERCOM_2_USART_CHSIZE
#define CONF_SERCOM_2_USART_CHSIZE 0x0
#endif

// <o> Stop Bit
// <0=>One stop bit
// <1=>Two stop bits
// <i> Number of stop bits in USART frame
// <id> usart_stop_bit
#ifndef CONF_SERCOM_2_USART_SBMODE
#define CONF_SERCOM_2_USART_SBMODE 0
#endif

// <o> Baud rate <1-6250000>
// <i> USART baud rate setting
// <id> usart_baud_rate
#ifndef CONF_SERCOM_2_USART_BAUD
#define CONF_SERCOM_2_USART_BAUD 9600
#endif

You can see that this config file uses #define statements to set up all the SERCOM 2 configuration parameters, like baud rate, number of stop bits, etc. This is basically all the information I entered on atmel start. When the code is compiled, there's a macro in hpl_sercom.c (given below) that grabs all of these parameters from the #define statements for each usart that has been defined (with #defines) and builds a configuration struct that gets stored in the _usarts[] array.

#ifndef CONF_SERCOM_0_USART_ENABLE
#define CONF_SERCOM_0_USART_ENABLE 0
#endif
#ifndef CONF_SERCOM_1_USART_ENABLE
#define CONF_SERCOM_1_USART_ENABLE 0
#endif
#ifndef CONF_SERCOM_2_USART_ENABLE
#define CONF_SERCOM_2_USART_ENABLE 0
#endif
#ifndef CONF_SERCOM_3_USART_ENABLE
#define CONF_SERCOM_3_USART_ENABLE 0
#endif
#ifndef CONF_SERCOM_4_USART_ENABLE
#define CONF_SERCOM_4_USART_ENABLE 0
#endif
#ifndef CONF_SERCOM_5_USART_ENABLE
#define CONF_SERCOM_5_USART_ENABLE 0
#endif
#ifndef CONF_SERCOM_6_USART_ENABLE
#define CONF_SERCOM_6_USART_ENABLE 0
#endif
#ifndef CONF_SERCOM_7_USART_ENABLE
#define CONF_SERCOM_7_USART_ENABLE 0
#endif

/** Amount of SERCOM that is used as USART. */
#define SERCOM_USART_AMOUNT                                                                                            \
	(CONF_SERCOM_0_USART_ENABLE + CONF_SERCOM_1_USART_ENABLE + CONF_SERCOM_2_USART_ENABLE + CONF_SERCOM_3_USART_ENABLE \
	 + CONF_SERCOM_4_USART_ENABLE + CONF_SERCOM_5_USART_ENABLE + CONF_SERCOM_6_USART_ENABLE                            \
	 + CONF_SERCOM_7_USART_ENABLE)

/**
 * \brief Macro is used to fill usart configuration structure based on
 * its number
 *
 * \param[in] n The number of structures
 */
#define SERCOM_CONFIGURATION(n)                                                                                        \
	{                                                                                                                  \
		n,                                                                                                             \
		    SERCOM_USART_CTRLA_MODE(CONF_SERCOM_##n##_USART_MODE)                                                      \
		        | (CONF_SERCOM_##n##_USART_RUNSTDBY << SERCOM_USART_CTRLA_RUNSTDBY_Pos)                                \
		        | (CONF_SERCOM_##n##_USART_IBON << SERCOM_USART_CTRLA_IBON_Pos)                                        \
		        | (CONF_SERCOM_##n##_USART_TXINV << SERCOM_USART_CTRLA_TXINV_Pos)                                      \
		        | (CONF_SERCOM_##n##_USART_RXINV << SERCOM_USART_CTRLA_RXINV_Pos)                                      \
		        | SERCOM_USART_CTRLA_SAMPR(CONF_SERCOM_##n##_USART_SAMPR)                                              \
		        | SERCOM_USART_CTRLA_TXPO(CONF_SERCOM_##n##_USART_TXPO)                                                \
		        | SERCOM_USART_CTRLA_RXPO(CONF_SERCOM_##n##_USART_RXPO)                                                \
		        | SERCOM_USART_CTRLA_SAMPA(CONF_SERCOM_##n##_USART_SAMPA)                                              \
		        | SERCOM_USART_CTRLA_FORM(CONF_SERCOM_##n##_USART_FORM)                                                \
		        | (CONF_SERCOM_##n##_USART_CMODE << SERCOM_USART_CTRLA_CMODE_Pos)                                      \
		        | (CONF_SERCOM_##n##_USART_CPOL << SERCOM_USART_CTRLA_CPOL_Pos)                                        \
		        | (CONF_SERCOM_##n##_USART_DORD << SERCOM_USART_CTRLA_DORD_Pos),                                       \
		    SERCOM_USART_CTRLB_CHSIZE(CONF_SERCOM_##n##_USART_CHSIZE)                                                  \
		        | (CONF_SERCOM_##n##_USART_SBMODE << SERCOM_USART_CTRLB_SBMODE_Pos)                                    \
		        | (CONF_SERCOM_##n##_USART_CLODEN << SERCOM_USART_CTRLB_COLDEN_Pos)                                    \
		        | (CONF_SERCOM_##n##_USART_SFDE << SERCOM_USART_CTRLB_SFDE_Pos)                                        \
		        | (CONF_SERCOM_##n##_USART_ENC << SERCOM_USART_CTRLB_ENC_Pos)                                          \
		        | (CONF_SERCOM_##n##_USART_PMODE << SERCOM_USART_CTRLB_PMODE_Pos)                                      \
		        | (CONF_SERCOM_##n##_USART_TXEN << SERCOM_USART_CTRLB_TXEN_Pos)                                        \
		        | (CONF_SERCOM_##n##_USART_RXEN << SERCOM_USART_CTRLB_RXEN_Pos),                                       \
		    SERCOM_USART_CTRLC_GTIME(CONF_SERCOM_##n##_USART_GTIME)                                                    \
		        | (CONF_SERCOM_##n##_USART_DSNACK << SERCOM_USART_CTRLC_DSNACK_Pos)                                    \
		        | (CONF_SERCOM_##n##_USART_INACK << SERCOM_USART_CTRLC_INACK_Pos)                                      \
		        | SERCOM_USART_CTRLC_MAXITER(CONF_SERCOM_##n##_USART_MAXITER),                                         \
		    (uint16_t)(CONF_SERCOM_##n##_USART_BAUD_RATE), CONF_SERCOM_##n##_USART_FRACTIONAL,                         \
		    CONF_SERCOM_##n##_USART_RECEIVE_PULSE_LENGTH, CONF_SERCOM_##n##_USART_DEBUG_STOP_MODE,                     \
	}

/**
 * \brief SERCOM USART configuration type
 */
struct usart_configuration {
	uint8_t                       number;
	hri_sercomusart_ctrla_reg_t   ctrl_a;
	hri_sercomusart_ctrlb_reg_t   ctrl_b;
	hri_sercomusart_ctrlc_reg_t   ctrl_c;
	hri_sercomusart_baud_reg_t    baud;
	uint8_t                       fractional;
	hri_sercomusart_rxpl_reg_t    rxpl;
	hri_sercomusart_dbgctrl_reg_t debug_ctrl;
};

#if SERCOM_USART_AMOUNT < 1
/** Dummy array to pass compiling. */
static struct usart_configuration _usarts[1] = {{0}};
#else
/**
 * \brief Array of SERCOM USART configurations
 */

volatile struct usart_configuration _usarts[8] = {
#if CONF_SERCOM_0_USART_ENABLE == 1
    SERCOM_CONFIGURATION(0),
#else
	{0},
#endif
#if CONF_SERCOM_1_USART_ENABLE == 1
    SERCOM_CONFIGURATION(1),
#else
	{0},
#endif
#if CONF_SERCOM_2_USART_ENABLE == 1
    SERCOM_CONFIGURATION(2),
#else
	{0},
#endif
#if CONF_SERCOM_3_USART_ENABLE == 1
    SERCOM_CONFIGURATION(3),
#else
	{0},
#endif
#if CONF_SERCOM_4_USART_ENABLE == 1
    SERCOM_CONFIGURATION(4),
#else
	{0},
#endif
#if CONF_SERCOM_5_USART_ENABLE == 1
    SERCOM_CONFIGURATION(5),
#else
	{0},
#endif
#if CONF_SERCOM_6_USART_ENABLE == 1
    SERCOM_CONFIGURATION(6),
#else
	{0},
#endif
#if CONF_SERCOM_7_USART_ENABLE == 1
    SERCOM_CONFIGURATION(7),
#else
	{0},
#endif
};
#endif

Later on, when you call the usart_async_init function (defined in hal_usart_async.c) from the driver_init.c file, eventually it gets to the _usart_init function (in hpl_sercom.c) where the data from the configuration structs gets written to the actual registers on the uart port (code given below).

/**
 * \internal Initialize SERCOM USART
 *
 * \param[in] hw The pointer to hardware instance
 *
 * \return The status of initialization
 */
static int32_t _usart_init(void *const hw)
{
	volatile uint8_t i = _get_sercom_index(hw);
	volatile struct usart_configuration test = _usarts[i];

	if (!hri_sercomusart_is_syncing(hw, SERCOM_USART_SYNCBUSY_SWRST)) {
		uint32_t mode = _usarts[i].ctrl_a & SERCOM_USART_CTRLA_MODE_Msk;
		if (hri_sercomusart_get_CTRLA_reg(hw, SERCOM_USART_CTRLA_ENABLE)) {
			hri_sercomusart_clear_CTRLA_ENABLE_bit(hw);
			hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE);
		}
		hri_sercomusart_write_CTRLA_reg(hw, SERCOM_USART_CTRLA_SWRST | mode);
	}
	hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_SWRST);

	volatile SercomUsart test2 = ((Sercom *)hw)->USART;
	hri_sercomusart_write_CTRLA_reg(hw, _usarts[i].ctrl_a);
	hri_sercomusart_write_CTRLB_reg(hw, _usarts[i].ctrl_b);
	hri_sercomusart_write_CTRLC_reg(hw, _usarts[i].ctrl_c);
	volatile SercomUsart test3 = ((Sercom *)hw)->USART;
	if ((_usarts[i].ctrl_a & SERCOM_USART_CTRLA_SAMPR(0x1)) || (_usarts[i].ctrl_a & SERCOM_USART_CTRLA_SAMPR(0x3))) {
		((Sercom *)hw)->USART.BAUD.FRAC.BAUD = _usarts[i].baud;
		((Sercom *)hw)->USART.BAUD.FRAC.FP   = _usarts[i].fractional;
	} else {
		hri_sercomusart_write_BAUD_reg(hw, _usarts[i].baud);
	}

	hri_sercomusart_write_RXPL_reg(hw, _usarts[i].rxpl);
	hri_sercomusart_write_DBGCTRL_reg(hw, _usarts[i].debug_ctrl);

	return ERR_NONE;
}

So, it seems like if one were to write a function to manually create this configuration struct and call _usart_init on it, then you would be able to initialize a new SERCOM at run time, instead of having to add a bunch more #defines and recompile the code to initialize a new port. I wrote a new function to do this, given below, which initializes the new port with some standard values for testing purposes.

int32_t _usart_async_add_port(void *const hw, uint8_t sercom_num, hri_sercomusart_baud_reg_t baud)
{
	volatile struct usart_configuration config;
	config.number = sercom_num;
	config.ctrl_a = 1074790404;
	config.ctrl_b = 196608;
	config.ctrl_c = 7340034;
	config.baud = baud;
	config.fractional = 0;
	config.rxpl = 0;
	config.debug_ctrl = 0;
	_usarts[sercom_num] = config;
	return sercom_num;
}

If I run this, and then run the uart_async_init function, then usart_async_enable, and finally io_write, I am able to follow the debugger all the way through to the completion of the io_write function. I have also checked the SERCOM object and all of the registers are identical to a test project I have debugged that was generated by atmel start. So I don't believe that there is anything wrong with my serial port configuration process. But, after finishing the io_write call, I don't get any data out when I look at the pins with my oscope - I just get a small "blip" where the port pulls the tx line low briefly, but then releases it after failing to send any data. So it seems like there is something else that needs to be configured in order to use an asynchronous port. Maybe something with an interrupt register? I am not sure where to go next, so I thought I would see if any of you guys are more familiar with how ASF4 does asynchronous serial port configuration and could point me in the right direction. Or maybe there is already some code that allows run-time setup of new SERCOMs so I don't have to write the code for it.

 

Thanks!