## Help with USART Atmega328

28 posts / 0 new
Author
Message

Hi,

I have been trying to run the USART program on Atmega328P. I have a few questions regarding the same.

1.) What type of character is sent by the computer terminal to the microcontroller using terminal assuming (ASCII)?

2.) My code is receiving a byte into uint_8t? Converting ascii to an actual number value is done by subtracting 48 from it? (only able to convert 0-9)

3.) What is the way of receiving 3 digit number from the terminal?

https://github.com/hexagon5un/AVR-Programming/blob/master/Chapter10_Pulse-Width-Modulation/pwmTimers/pwmTimers.c

USART.C:

https://github.com/hexagon5un/AVR-Programming/blob/master/AVR-Programming-Library/USART.c

The getnumber function in USART.C is not responding correctly.

Thanks in advance

This topic has a solution.

Mav

Last Edited: Tue. Apr 9, 2019 - 10:54 PM
Total votes: 0

Mav1985 wrote:

1.) What type of character is sent by the computer terminal to the microcontroller using terminal assuming (ASCII)?

Yes, ASCII codes. It's easy to find a table of these

using an Internet search.

Decimal digits have values in HEX of 0x30 through

0x39, which is 48 through 57 in decimal.

Uppercase characters are in the range 0x41 to 0x5A

and lowercase 0x61 to 0x7A (there is just one extra

bit set in the lowercase version).

Quote:

2.) My code is receiving a byte into uint_8t? Converting ascii to an actual number value is done by subtracting 48 from it? (only able to convert 0-9)

Yes, a uint8_t is an 8-bit quantity.  Since you want

to use the received byte as a character, you can put

it in a char after receiving it.

First you need to check that the received byte is

actually a digit.  Make sure it's at least 48 and also

not bigger than 57.  Otherwise the character is not a

digit.  Once you are sure it's a digit character, the

value can be determined by subtracting 48 from it.

Quote:

3.) What is the way of receiving 3 digit number from the terminal?

Each character is sent one at a time, so as above

make sure each of them is a digit character or an

error occurred. Once you have the three digit values,

multiply by 100, 10 or 1 and add them together.

The result could be as large as 999 which won't fit in

a uint8_t, so put the result in at least 16 bits.

--Mike

Total votes: 0

I'd just receive the 3 characters to a buffer then use atoi() or sscanf() on it. No point reinventing the ASCII to binary thing when C already has all the conversion routines implemented as standard.

Total votes: 0

Can you please have look at code :

static inline uint8_t getNumber16(void) {
// Gets a PWM value from the serial port.
// Reads in characters, turns them into a number
char thousands = '0';
char hundreds = '0';
char tens = '0';
char ones = '0';
char thisChar = '0';

do {
thousands = hundreds;                        /* shift numbers over */
hundreds = tens;
tens = ones;
ones = thisChar;
thisChar = receiveByte();                   /* get a new character */
transmitByte(thisChar);                                    /* echo */
} while (thisChar != '\r');

transmitByte('\n');                                       /* newline */
return (1000 * (thousands - '0') + 100 * (hundreds - '0') +
10 * (tens - '0') + ones - '0');
}

void transmitByte(uint8_t data) {
/* Wait for empty transmit buffer */
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = data;                                            /* send data */
}

uint8_t receiveByte(void) {
loop_until_bit_is_set(UCSR0A, RXC0);       /* Wait for incoming data */
return UDR0;                                /* return register value */
}

void printByte(uint8_t byte) {
/* Converts a byte to a string of decimal text, sends it */
transmitByte('0' + (byte / 100));                        /* Hundreds */
transmitByte('0' + ((byte / 10) % 10));                      /* Tens */
transmitByte('0' + (byte % 10));                             /* Ones */
}

'This print byte function is printing 218 unnecessary for example I transmit 256 it transmits 02 05 06 218. is there any issue with getnumber() function?

Also can you please suggest where I can learn about data types and more avr programming.

Thank you for response

Mav

Total votes: 0

getNumber16 needs to return a uint16_t.

--Mike

Total votes: 0

Mav1985, the first thing to learn about data communication is that as much you can program the sender to transmit what you want to receive, the better, but it is not always like that.

In some cases, yes, for you to receive the decimal number 485, the sender (if working with ASCII chars/numbers) will send hex 0x34, 0x38, 0x35, if working with IBM EBCDIC will be 0x74, 0x78, 0x75.  But, if you instruct the sender to send raw binary, it would send 0x04, 0x08, 0x05, or, if you instruct it to send BCD, it may send 0x04, 0x85.  Anyway, there is nothing wrong to always do a little "translation" are the receiver, if you want to pack the decimal 485 sent by the transmitter, into two AVR registers, as a 16 bits integer variable, it will need to be converted to 0x01E6, and that is a job to be done by your receiver software.  In all cases, you, the programmer, must know exactly how to translate, knowing what the sender is transmitting and what the receiver wants.

Sometimes the sender packs the data into a small envelope, containing some extra bytes before and after the real data, that is something else that your software needs to know to strip the envelope away, revealing just the desired data. Sometimes it inserts a 0x0a, 0x0d at the end, as linefeed and carriage return.

Sometimes this envelope is even more packed into a communication protocol, containing more control bytes, addresses of the sender and the destination, check controls, counters, frames, etc, sometimes it sends a 2k frame just to pack 3 bytes of data.  One day you may want to understand TCP/IP, X.25, SDLC, HDLC, and other protocols, it becomes complicated.  The madness starts when all this protocols do compression and encryption, when only the destination has the keys to decrypt and reveal the real data inside.

In some way I love to do everything in assembly language, so I can actual *see* what is flowing inside the chip, controlling everything, but that is me, and people like me is disappearing fast.

Wagner Lipnharski
Orlando Florida USA

Last Edited: Mon. Apr 8, 2019 - 01:18 AM
Total votes: 0

Here is a code snippet from a serial monitor program I have, that uses two simple functions, one gets characters from the serial port the other builds a decimal number 0-255 into an unsigned byte.

```//---------------------
unsigned char getdecdig(void){
//return decimal digit 0..9 from rs232
//return ff (-1) of non hex, like carriage return
unsigned char b,c;

b=0xff; //error return value
c=toupper(getche());
if(isdigit(c)) b=c-0x30; //if c ok, cvt ascii digit to binary
return(b);
}

//-----------------------
unsigned char getnum(void){
//return number 0..255 from rs232  -1 to exit
char dig,n;

dig=getdecdig();
if(dig == -1) return dig;
n=dig;
//  if(n>2) n=2;
dig=getdecdig();
if(dig == -1) return dig;
n=n*10+dig;
//  if(n>25) n=25;
dig=getdecdig();
if(dig == -1) return dig;
n=n*10+dig;
if(n>255) n=255;
return(n);
}```

Hope it helps.

Jim

edit: if I were to rewrite this today, I would use uint8_t instead of unsigned char to make it more portable, but it works as is.

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

Last Edited: Mon. Apr 8, 2019 - 01:07 PM
Total votes: 0

wagnerlip wrote:

Mav1985, the first thing to learn about data communication is that as much you can program the sender to transmit what you want to receive, the better, but it is not always like that.

That's why you have a specification:)

If the sender doesn't follow the spec, not your fault (well, unless you are also responsible for the sender).

The OP seems to have 'specified' that the sequence of characters ends with a CR ('\r', 0x0d).

That's fine as long as that is what you are sending.

So I suggest OP should check what they are actually sending and see if it matches their specification.

(For example, what if you were actually sending, say, a CR followed by a LF ('\n', 0x0a), will your code cope with this?).

Total votes: 0

MrKendo wrote:

So I suggest OP should check what they are actually sending and see if it matches their specification.

(For example, what if you were actually sending, say, a CR followed by a LF ('\n', 0x0a), will your code cope with this?).

That is the thing about being more adaptable to the world.

Most of the data comm uses CR at the end of any line string, in the past CR+LF was a must.

I remember my very first printer for my 1976 Tandy (Radio Shack) Collor Computer was a huge teletype I was able to purchase locally.

That thing was a marvel of mechanics, the only electronics that enters in the mechanics was a small electromagnet that release some rotating latches (brakes) in the mechanics.

That coil received pulses from the receiver electronics, running at 134bps if I remember well, so that electromagnet could in fact move at 134Hz.

That particular machine needed the LineFeed command to advance the paper.  The Color Computer did NOT send LF at the end of each printed line at serial port.

I would be much easier to fix the mechanics than rewrite the code into the eprom of the Coco 6809E processor.

Interesting enough, each possible decoded character by the electromagnet movement sequence had a narrow metal arm, that would select a printing position of the printer head, or, launch a different mechanical action, like carriage return, line feed, power off, etc.  What I did was removing the line feed metal link and removed/included few teeth into the decoding area for it to be activated with hex 0x0D instead of 0x0A... so, whenever the teletype receives a CR it launched double operation, carriage return AND line feed at the same time.  Then I could print from the Coco and the teletype would advance paper each line.

In this case story, the "adaptability" was done mechanically, but today's software must be smart enough to understand what is coming from the remote, if after or before CR comes a LF, just ignore it, or not, depends of what you will do with that.  In some cases, CRC calculation takes CR+LF in account, so it needs to process before eliminate it.  Some other transmissions don't even use CR/LF, rather use a separation char, line tab, vertical bar, dollar sign, or other.

Wagner Lipnharski
Orlando Florida USA

Total votes: 0

wagnerlip wrote:
Most of the data comm uses CR at the end
You mean LF ?? In the Unix/Linux world line endings are simple 0x0A (LF) not 0x0D (CR), 0x0A (LF) as found in the WinDOS world.

Total votes: 0

Hi Mike

Thanks for responding. I redefined the function for getting number from (0-255) from Computer Terminal

uint8_t receiveByte(void) {
loop_until_bit_is_set(UCSR0A, RXC0);       /* Wait for incoming data */
return UDR0;                                /* return register value */
}

uint8_t getNumber(void) {
// Gets a numerical 0-255 from the serial port.
// Converts from string to number.
char hundreds = '0';
char tens = '0';
char ones = '0';
char thisChar = '0';
do {                                                   /* shift over */
hundreds = tens;
tens = ones;
ones = thisChar;
thisChar = receiveByte();                   /* get a new character */
transmitByte(thisChar);                                    /* echo */
} while (thisChar != '\r');                     /* until type return */
return (100 * (hundreds - '0') + 10 * (tens - '0') + ones - '0');
}

__________________________________________________________________________

#include <avr/io.h>
#include <util/delay.h>
#include "pinDefines.h"
#include "USART.h"
#include "USART.c"
int main(void) {
uint8_t serialCharacter;

// -------- Inits --------- //
LED_DDR = 0xff;                            /* set up LEDs for output */
initUSART();
printString("Hello World!\r\n");                          /* to test */

// ------ Event loop ------ //
while (1) {

serialCharacter= getNumber();
if (serialCharacter== 100)
{PORTB^=(1<<0);
}

}
}                                           /* End event loop */

The logic is correct. What I assume is there is some issue with '/r'/?  What do you think?

I appreciate your help.

Mav

Total votes: 0

clawson wrote:

You mean LF ?? In the Unix/Linux world line endings are simple 0x0A (LF) not 0x0D (CR), 0x0A (LF) as found in the WinDOS world.

You are correct, in the Unix/Linux world.

But all over the *other* world, RFC 0821 (SMTP), RFC 2616 (HTTP), RFC 1939 (POP), RFC 2060 (IMAP) uses CR+LF, even being driven by Unix/Linux servers.

The word here is compatibility

You can open a CR+LF text file in Linux and read it normally, I have problems to read a linux text file in windows Notepad.

```RFC 2616                        HTTP/1.1                       June 1999

OCTET          = <any 8-bit sequence of data>
CHAR           = <any US-ASCII character (octets 0 - 127)>
UPALPHA        = <any US-ASCII uppercase letter "A".."Z">
LOALPHA        = <any US-ASCII lowercase letter "a".."z">
ALPHA          = UPALPHA | LOALPHA
DIGIT          = <any US-ASCII digit "0".."9">
CTL            = <any US-ASCII control character
(octets 0 - 31) and DEL (127)>
CR             = <US-ASCII CR, carriage return (13)>
LF             = <US-ASCII LF, linefeed (10)>
SP             = <US-ASCII SP, space (32)>
HT             = <US-ASCII HT, horizontal-tab (9)>
<">            = <US-ASCII double-quote mark (34)>

HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all
protocol elements except the entity-body (see appendix 19.3 for
tolerant applications). The end-of-line marker within an entity-body
is defined by its associated media type, as described in section 3.7.

CRLF           = CR LF```

Wagner Lipnharski
Orlando Florida USA

This reply has been marked as the solution.
Total votes: 0
```uint8_t getNumber(void) {
// Gets a numerical 0-255 from the serial port.
// Converts from string to number.
char hundreds = '0';
char tens = '0';
char ones = '0';
char thisChar = '0';
do {                                                   /* shift over */
hundreds = tens;
tens = ones;
ones = thisChar;
thisChar = receiveByte();                   /* get a new character */
transmitByte(thisChar);                                    /* echo */
} while (thisChar != '\r');                     /* until type return */
return (100 * (hundreds - '0') + 10 * (tens - '0') + ones - '0');
}```

Suppose I send "437" how exactly are you going to pass back 437 using a uint8_t ?? (you'll actually get 181)

Can't help thinking that you might be better off with something like:

```uint16_t getNumber(void) {
char c, rxbuff[10];
uint8_t i = 0;

do {
c = receiveByte();
rxbuff[i++] = c;
} while ((c != '\n') && (i < 9));
rxbuff[i] = 0;
return atoi(rxbuff);
}```

That receives up to 9 digits (stopping at \n which is bound to be part of the termination sequence from either a Windows or Linux terminal (one sends \r\n, the other just \n) then makes sure the end of the buffer is marked with a terminating 0x00 then passes the buffer to atoi(). As it returns uint16_t it has the potential to return anything from 0 to 65535

Total votes: 0

I would change the logic flow to something like:

```uint8_t getNumber(void)
{
while (1)
{
char nextChar = receiveByte();

if (isdigit (nextChar))
{
// process the digit character
}
else if (nextChar == '\r')
{
// compute the value and return it
}
// else
// {
//     // ignore other characters
// }
}
}```

--Mike

Total votes: 0

Mav1985 wrote:

The logic is correct. What I assume is there is some issue with '/r'/?  What do you think?

It might be an issue, or it might not.

If you are using the return key from your terminal program to send the CR character, depending on which terminal program it is, which operating system you are using, and how you have the terminal program configured, it could send either a CR ('\r' value 0x0d) or a LF ('\n' value 0x0a), or CR followed by LF.

It's just something to be aware of.

In your code, if you were getting a CR followed by LF (you might not be, I don't know) then since you loop around until you get the CR, what happens to the following LF. Could it be picked up as the first character of the next number?

Ideally, you want the terminal program to show the value of each byte it sends out (probably in hex) then you can see exactly what is happening.

Alternatively, instead of using Return key, a lot of terminal programs will allow CTRL-M to send a CR (and CTRL-J to send a LF).

Total votes: 0

MrKendo wrote:

a lot of terminal programs will allow CTRL-M to send a CR (and CTRL-J to send a LF).

The letters chosen for these codes is based on

the alphabet.  M is the 13th letter and CR has

ASCII value 13 (0x0D).  J is the 10th letter and

LF has ASCII value 10 (0x0A).

Do you know if you can send other ASCII codes

using other alphabetic characters?  Or is this

only done for J and M?  Also how would you send

a zero byte or more than 26?

--Mike

Total votes: 0

avr-mike wrote:

Do you know if you can send other ASCII codes

using other alphabetic characters?  Or is this

only done for J and M?  Also how would you send

a zero byte or more than 26?

Good question. I don't know how universal it is, but at least in some cases you can send other non printable codes using other combinations.

I guess it might depend on whether the key combination is grabbed for something else first.

Using Linux, and as an example I just tried with a simple terminal program gtkterm, most of CTRL-A through to CRL-Z  work for 1 up to 26 (0x1a) (a few don't work).

Then

CTRL-1 just sends 0x31

but CTRL-2 sends 0x00,

and CTRL-3 up to CTRL-7 send 27 (0x1b) up to 31 (0x1f).

After that you're into printable chanracters.

As I said, I've no idea how universal this kind of thing is, but at least for CTRL-J and CTRL-M it's worth a try, it might work.

Total votes: 0

AFAIK most terminals will send all of 0x01 to 0x1A for Ctrl-A to Ctrl-Z. If you are lucky you'll even find Ctrl-@ can be used for 0x00 and Esc has generally always been 0x1B.
.
I'm guessing you folks are too young to remember the time when a "computer" was an IBM 360 or IBM 370 and a bunch of dumb TVI912 or, if you were dead posh, TVI920 terminals?
.
EDIT Ah just had a nostalgia overload looking at https://www.computerhistory.org/collections/catalog/102622187 and the posh one: https://terminals-wiki.org/wiki/index.php/TeleVideo_920
.
Crikey \$945 for a "dumb terminal". An online calculator I found says that's something like \$3000 in today's money!

Last Edited: Tue. Apr 9, 2019 - 09:08 PM
Total votes: 0

clawson wrote:

I'm guessing you folks are too young to remember the time when a "computer" was an IBM 360 or IBM 370 ...

I probably used one of these without knowing

what it was.  At the house of one of my grade-

school friends was a keyboard, a printer, and

one of those modems you put the telephone

handset in.  No monitor - everything you typed

or got back from the server was printed on

paper.

His dad would call up his work (no clue who he

worked for), log into the machine and set up an

adventure-style game for us to play.  The kind

where it tells you "You're standing in a field.  In

the distance is a forest.  What would you like to

do?" and you're supposed to guess what to do

and the game reveals stuff as you go.  We were

far too young to make any relevant progress,

but that didn't keep us from trying for hours on

end.

--Mike

Total votes: 0

ZX Spectrum (or if you were dead posh BBC micro).

I don't recall my spectrum having a CTRL, ESC or @ key :)

Total votes: 0

Mike, sounds a lot like "Colossal Cave" . Was there a snake for which you had to release a bird from a cage? I have a feeling you were linking to DEC or VAX not IBM ;-)

Last Edited: Tue. Apr 9, 2019 - 09:26 PM
Total votes: 0

I got started on an ASR33 on some remote "timeshare computer". Then a Tek terminal on a PDP-8 (booted from a punched tape); now that was POSH!

I suspect that the ASR33 used "5-bit ASCII" though it was probably called something else. No lower case, to be sure.

Later played a similar game called "Adventure" that involved a cave patterned after the US's Mammoth Cave. I still have the FORTRAN source code for that somewhere.

Jim

Until Black Lives Matter, we do not have "All Lives Matter"!

Last Edited: Tue. Apr 9, 2019 - 09:40 PM
Total votes: 0

clawson wrote:
AFAIK most terminals will send all of 0x01 to 0x1A for Ctrl-A to Ctrl-Z. If you are lucky you'll even find Ctrl-@ can be used for 0x00 and Esc has generally always been 0x1B.

You can also generate ESC with Ctrl-[ (which is why ESC sometimes shows up in the terminal as ^[). And Ctrl-\, Ctrl-], Ctrl-^, and Ctrl-_ (the last two are actually Ctrl-6 and Ctrl--, being the same keys as ^ and _) will give you 0x1C through 0x1F. Looking at an ASCII table will (or should) make it clear why they will give you those control codes.

Last Edited: Tue. Apr 9, 2019 - 11:13 PM
Total votes: 0

clawson wrote:

Mike, sounds a lot like "Colossal Cave" . Was there a snake for which you had to release a bird from a cage? I have a feeling you were linking to DEC or VAX not IBM ;-)

Honestly, we probably never made

it out of the field.  ;-)

--Mike

Total votes: 0

I miss my Decwriter and acoustic coupler modem!

Jim

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

Total votes: 0

No paper tape reader?

Total votes: 0

In true Monty Python Four Yorkshiremen style... my first terminal...

....sits back and waits for someone to post the inevitable...

Total votes: 0

MattRW wrote:
No paper tape reader?

No, but I had a bar code reader pen I could use to scan programs from magazines like byte, kilobyte, dr dobbs, etc., remember those?

Tape was KC standard 300 baud audio cassette....

Jim

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"