Pointing to a USART module using a variable

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

**Edit: I had made another error related to the bootloading. It seems to be working for now. I feel terribly silly. 

**Edit 2: Still looking for better ways to do it, so any inputs are appreciated.

 

 

 

I'm working on a project (mega3208) where I'm using going to use both USART1 and USART2, at different times. 

I have a library of usart functions written - the usual stuff, usart_read(), usart_init() and so on, which I want to reuse for both USARTs. For example, one function is:

#define USART_NUM USART1

void usart_write(const uint8_t data)
{
	while (!(USART_NUM.STATUS & USART_DREIF_bm))
		;
	USART_NUM.TXDATAL = data;
}

While I was still dealing with only one USART, I could define USART_NUM and it worked fine. Now I'd like to switch between two USARTs, so I'd have to make USART_NUM a variable and set it to USART1 or USART2 as needed. I really don't want to have a copy of every USART handling function for each USART module, because then I'd also need to have a copy of all functions making calls to these USART handling functions (I hope I'm still making sense). It'd be super easy to just set USART_NUM = USART2; wherever I need, so the USART functions automatically do their job on the USART2 registers etc.

Problem is, I have no idea what type of variable to use. The .h file defines USART1 as a pointer to a USART_t struct casted on a fixed address (at least, that's my understanding of it): 

#define USART1 (*(USART_t *)0x0820)
#define USART2 (*(USART_t *)0x0840)

 

I tried the following, but it doesn't seem to work  this seems to work:

#define USART_1 0x0820
#define USART_2 0x0840

#define USART_NUM (*(USART_t *)USART_ADDRESS)

volatile uint16_t USART_ADDRESS = USART_1;      

void usart_write(const uint8_t data)
{
	while (!(USART_NUM.STATUS & USART_DREIF_bm))
		;
	USART_NUM.TXDATAL = data;
}

Any help with how I can get this done would be much appreciated. Thanks.

-Sam

Last Edited: Wed. Sep 9, 2020 - 08:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Method without switching USART.

 

#define USART_NUM USART1

void usart_write(const uint8_t data)
{
    while (!(USART_NUM.STATUS & USART_DREIF_bm))
    ;
    USART_NUM.TXDATAL = data;
}

 

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

Why don't you do this properly rather than with kludgey workarounds. The whole point about Xmega and then AVR-0/AVR-1/AVR-DA is that Atmel did programmers a favour by (a) making multiple UARTs that are homogenous (each has the same set of registers at the same offsets in the control block and (b) defining structures for those control blocks.

 

So to differentiate UARTs you just need to pass the base address of a given control structure. So a routine such as:

void usart_write(const uint8_t data)
{
	while (!(USART_NUM.STATUS & USART_DREIF_bm))
		;
	USART_NUM.TXDATAL = data;
}

would become:

void usart_write(USART_t * uart, const uint8_t data)
{
	while (!(uart->STATUS & USART_DREIF_bm))
		;
	uart->TXDATAL = data;
}

then simply:

usart_write(USART0, 'A');
usart_write(USART2, 'Z');
etc.

Or you could delve into the realms of C++. Have a UARTn choice passed into a constructor (and remembered) so you can do stuff like:

USARTclass gps(USART1);
USARTclass barcode_reader(USART2);

...

  gps.write('A');
  barcode_reader.write('Z');

 

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

How to specify USART

 

void usart_write(const uint8_t data, USART_t *usart_adr)
{
    while (!(usart_adr->STATUS & USART_DREIF_bm))
    ;
    usart_adr->TXDATAL = data;
}

//  Call like this
//  usart_write(1, &USART1);

 

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

Why don't you do this properly rather than with kludgey workarounds.

I would if I knew how, hence my post.

Yes, passing the USART_t would be one way to do it, but that would still mean I have to modify every call that is made to the usart_write etc.  

My wanting to use a variable to point to the USART with a variable would mean I can simply set the value of that variable to USART1 or USART2 in my main file and the rest of it would work as it is.

The workaround seems to work for now (the problem was elsewhere), but I'd love to know if there's a better way to do it without all the hoopla. What would be the type of the variable I would use to do this? 

-Sam

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

But your #define thing that fixes code to a specific UART at compile time means that if you want to support two uarts you have to have two copies of each function, one built for UART0 and one for UART1. By passing it at run time (or construction time) you can have one copy of the code functions that just adapt to one UART or the other. I personally think the C++ way is the "nicest". But in part that is because this is almost exactly what C++ was developed to do - using a single set of code but having multiple "instances" of it where the only "cost" is the items held in memory that differentiate one version from the other (such as the base address of the register block).

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

I'm not fixing the USART using #define:

#define USART_1 0x0820
#define USART_2 0x0840

#define USART_NUM (*(USART_t *)USART_ADDRESS)

volatile uint16_t USART_ADDRESS = USART_1;      

void usart_write(const uint8_t data)
{
	while (!(USART_NUM.STATUS & USART_DREIF_bm))
		;
	USART_NUM.TXDATAL = data;
}

I can switch USART_ADDRESS to USART_2 in my main function and have the usart_write get redirected accordingly.

-Sam

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

C version, uses inline code-

https://godbolt.org/z/4s3csb

 

C++ version, 'normal' class-

https://godbolt.org/z/E8e48M

 

C++ version, static class-

https://godbolt.org/z/71zKx6

 

C++ version, class template (basically same result as C version)-

https://godbolt.org/z/fbhMEo

 

 

I think I have said this before, getting all the usarts (or any peripheral) to use the same functions is not really the goal as there is little to no benefit, but having only one set of functions to create and update is the real goal. With the inlined C version, you are basically getting a C++ class template version, its just that C syntax is more cumbersome to use.

Last Edited: Wed. Sep 9, 2020 - 07:20 PM