programming help with USART

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

Hi, I need to use all three usarts on my 4809 40 pin dip. I picked up the following code from the Microchip help files

 

void USART1_sendChar(char c)
{
    while (!(USART1.STATUS & USART_DREIF_bm))
    {
        ;    
    }
    USART1.TXDATAL = c;
}

which is very helpful, I can send chars, and strings of chars to my PC serial monitor. I undersatand the code.

 

However, I need to reuse this code depending upon which USART I'm writing to, so I'd like to pass the USART to this function as an argument. My problem is I don't know how to type the parameter in the function definition.

 

Could someone help me with that please?

 

thanks

Paul (not a very good programmer at present...) or more positively (still learning programming) :)

Paul

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

You can do it this way.
 

void USARTx_sendChar(USART_t* usart_x, char c)
{
    while (!( usart_x->STATUS & USART_DREIF_bm))
    {
        ;
    }
    usart_x->TXDATAL = c;
}

 

Call it like this.

USARTx_sendChar(&USART0, 'a');
USARTx_sendChar(&USART1, 'b');
USARTx_sendChar(&USART2, 'c');

 

 

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

^ what he said.  Here is sample of code I use:

STATIC void usart_putchar(USART_t *usart, char c) {
  while ((usart->STATUS & USART_DREIF_bm) == 0);
  usart->TXDATAL = c;
}


STATIC uint8_t usart_char_ready(USART_t *usart) {
  return (usart->STATUS & USART_RXCIF_bm)? 1: 0;
}

STATIC char usart_getchar(USART_t *usart) {
  while ((usart->STATUS & USART_RXCIF_bm) == 0);
  return usart->RXDATAL;
}

STATIC void usart_putstring(USART_t *usart, char *p) {
  while (*p != '\0') {
    usart_putchar(usart, *p++);
  }
}

 

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

thanks very much for replies. It's my first use of pointers in C++, so a deep end I guess. I get how it works though.

 

Just one question about use of usart->STATUS, where I haven't seen -> used before. It looks to be the same result as usart.STATUS would be. Presumably the two are interchangeable?

 

thanks again,

Paul

Paul

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

O.K, I've answered my question about interchangeable by trying usart_x.STATUS and it doesn't like that.

 

Paul

Paul

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

It wasn't clear you are targeting C++ vs C.  The responses are C.  I guess for C++ you could use references and replace -> with .

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

paulskirk53 wrote:
I need to reuse this code depending upon which USART I'm writing to, so I'd like to pass the USART to this function as an argument.

See the discussion here: https://www.avrfreaks.net/forum/if-you-are-supporting-two-usarts-do-you-clone-code-them-or - it's the same question

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thanks for the link

 

I agree that

 

'the fiddling around effort'

 

probably isn't worth it for 2 USARTS. My application uses three and if I have to fiddle with it longer than today, I'll probably clone them. The code's easier to understand and maintain too.

 

The -> operator dereferences the pointer:

https://docs.microsoft.com/en-us...

 

The level of difficulty takes me back to writing assembler on an ICL1900 :) (that's only 45 years ago) Good fun though.

thanks for help

Paul

Paul

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

paulskirk53 wrote:
Just one question about use of usart->STATUS, where I haven't seen -> used before. It looks to be the same result as usart.STATUS would be. Presumably the two are interchangeable?

Most definitely not. Simple struct use is:

struct {
    int n;
    char c;
} mystruct;

int main(void) {
    mystruct.n = 1234;
    mystruct.c = 'A';
}

The struct definition creates an "object" in RAM memory that is an int and a char positioned together. The "members" of the object are accessed using the member access operator ('.') . That says "find the 'mystruct' object in memory and set the 'n' or 'c' field to these values". Behind he scenes the compiler/linker know the address of "mystruct" and of the "n" and "c" fields within it so code can be generated to put the 12345 and 'A' values in the right locations. But you, as a user here, don't really know what the address of "mystruct" is. Simply that "the compiler will have found a home for it somewhere".

 

But now consider if I want to put my initialisation code into a separate function so I can call some kind of struct_init() function. First consider how you might do that for a simple variable like:

int n;

void var_init(int var) {
    // ???
}

int main(void) {
    var_init(n);
}

now how can this var_init() function actually write to the variable n. If I simply invoke the function from main() as:

    var_init(n);

then what gets passed to the function is the "value currently held in n". So say it holds 0 (actually the rules of C guarantee this as it is an uninitialised global) then this function call is no more than:

    var_init(0);

but that does not help in creating a function that can actually update n. What is going on in this function call is something called "pass by value". When the function call is made it's what is "IN" the variable that is passed. But what is actually required is the address of "n" so the called function will know what to update. So in this case you use pointers and the C "address of" operator. The code becomes:

void var_init(int * var) {
    * var = 1234;
}

int main(void) {
    var_init(&n);
}

So now when var_init() is called it's not what is IN "n" that is passed but the actual address of "n" that is passed to the function. On the receiving side the function has to be ready to receive not an "int" value but "the address of an int variable". That is what "*" ("pointer"/"dereference") means in:

void var_init(int * var) {

this is a function that takes the address of an int variable (IOW a "pointer" to an int variable). Within this function "var" is not that actual value in "n" but the address of some int variable (the address of "n" in fact). When it comes time to write. You don't want to say "change "var"" you want to say "change the thing at the address that is held in var". That again is what "*" is doing in:

   * var = 1234;

which means "go to the location currently in var and write the value 1234 to that location. Often you might add a "p" at the start of a variable name like "var" to make it clear it's a "pointer to var". that is:

void var_init(int * pVar) {
    *pVar = 1234;
}

 

The way in which this function was called, being passed the address rather that the content of a variable is "pass by reference" rather than "pass by value". (reference is just another way of saying "address of").

 

So now finally back to the struct example:

struct {
    int n;
    char c;
} mystruct;

void struct_init(???) {
    something.n = 1234;
    something.c = 'A';
}

int main(void) {
    struct_init(&mysrtuct);
}

So in main() it's clear we will be passing "address of "mystruct"" so that the code in struct_init() can write to it. But how will the code in that function look? Well, just like the simple "n" case we need the function to receive the address of a struct rather than the actual content of the struct (pass by reference not by value). To simplify the way we could write this it is easiest to introduce another C concept which is typedef which allows you to create "new C types" based on building up things that have already been defined. So one can write:

typedef struct {
    int n;
    char c;
} struct_type;

which adds a new type to C called "struct_type" that is a variable type made up of a struct that contains an int and a char. Our actual variable ("mystruct") is now simply a variable of this type:

struct_type mysstruct;

and now the interface to the struct_init() function becomes easy to write:

void struct_init(struct_type * addrOfStruct) {

A function that takes the address of a struct. But how do we now write the init code in the middle? It's not as simple as:

    addrOfStruct.n = 1234;
    addrOfStruct.c = 'A';

because addrOfStruct is not a struct itself it cannot simply be "." because that means "this field within this struct". 

 

addrOfStruct is the ADDRESS OF a struct. So we need some way to say "take this address and at that location find within it the field called "n" or "c" and then write to that location". The C operator that achieve this are the two characters "->" if you screw your eyes up you could almost say this was an arrow "pointing" to something. That is handy because what we have in addrOfStruct is an address of a struct - a "pointer". So addrOfStruct->c really means "take the base address of the struct and knowing how far on from that address the "c" member will be located actually access that location".

 

The complete solution then is:

typedef struct {
    int n;
    char c;
} struct_type;

struct_type mysstruct;

void struct_init(struct_type * addrOfStruct) {
    addrOfStruct->n = 1234;
    addrOfStruct->c = 'A';
}

int main(void) {
    struct_init(&mysrtuct);
}

Hopefully it is now clear that "." and "->" are not interchangeable. One of them involves a "pointer dereference" (ie an "address lookup").

 

Last Edited: Mon. Feb 22, 2021 - 10:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

paulskirk53 wrote:
probably isn't worth it for 2 USARTS. My application uses three

As I said in the other thread, I think it's probably not worth it for 3 - four is where I'd start thinking it might be worth it...

 

Especially where it's fairly simple UARTs - like AVR

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thanks for all the detailed help, I appreciate it.

Paul

Paul