How can I distinct between a string and single char in USART?

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

I am learning C and avr programming, using atmega328p for now and I have this code that I put together so that when I run in it PicSimLab serial terminal it outputs a random "dice" number 1-6 if I input single "r" else it outputs my error message. What I am having trouble with comprehending is how can I distinct between single "r" and "rr" or "mmmrr"? Because now I get like two random numbers for "rr" and input error + input error + random number for "mmr" .

 

Can I somehow gather the input chars into an array and then check if the array size is 1 and array[0] == 'r' then roll , else error message?  I know I am thinking this much like a high-level language java style, but that is my first and only language - since I just started embedded C.

 

#define F_CPU 4000000UL
#include <avr/io.h>
#include <avr/eeprom.h>
#define UBRR_val 12
volatile unsigned char character;
static uint16_t EEMEM seed; //helps the compiler to find address for me in EEPROM
unsigned char roll = 'r';
unsigned char error[18] = {"Not a valid input!"};


int createSeed(){
	uint16_t number; //unsigned 16 bit numeric value to use for srand()
	number = eeprom_read_dword(&seed); //Read the location
	eeprom_write_dword(&seed, number+1);//Just add one to it each time
	return number;
}

void receiveData(void){
	while(!(UCSR0A & (1<<RXC0))){}
	character = UDR0;	
}

void transmitRoll(void){

	while(!(UCSR0A & (1<<RXC0))){}
		if(character == roll ){
			uint8_t diceRoll = rand()%6+1;
			UDR0 =  diceRoll;
		}
		else{
			for(int i = 0; i<18;i++){
			UDR0 = error[i];	}
			}	
}


int main(void)
{	
	UBRR0L = UBRR_val; //Baud rate set
	UBRR0H = 0x00;
	UCSR0B = (1<<TXEN0)|(1<<RXEN0);//Enable transmitter and receiver			
	srand(createSeed());
	while (1)
	{		
		receiveData();
		transmitRoll();		 
	  
	}
}

 

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

LeonSteinn wrote:
I am learning C

it would be far better/easier to learn that 'C' language on a PC - away from all the added complications & restrictions of small microcontrollers.

 

Once you've learned the language, then you'll be in a better position to apply it to microcontrollers.

 

here are some 'C' learning & reference materials:

 

https://blog.antronics.co.uk/2011/08/08/so-youre-thinking-of-starting-with-c/

 

Remember that the UART just works one byte at a time: so you start by writing basic functions to send one byte to the UART, and receive one byte from the UART.

 

Then you can build on these to send a string, and to receive a string.

 

Sending/receiving a string is just a matter of sending/receiving each byte in turn until you reach the end.

 

EDIT

 

For example:

 

// Send a single byte
void usart_txByte( uint8_t data )
{
    while ( !( UCSR0A & (1<<UDRE0) ) )
    {
        // Wait for empty transmit buffer
    }

    // Put data into buffer, sends the data
    UDR0 = data;
}

// Send an entire string - one byte at a time
void usart_txString( char * string )
{
    while ( *string )
    {
        usart_txByte( (uint8_t)*string );
        ++string;
    }
}

From: https://www.avrfreaks.net/forum/what-happens-if-you-read-udr0-multiple-times

 

EDIT 2

 

Also see Tip #6 - 'Getting Started' - in my signature, below:

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...
Last Edited: Sat. Oct 17, 2020 - 09:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In serial data receiving programs, it is common to use a "delimiter" character to signal the end of a string (or a "line"). Often, this is a carriage-return character or a line-feed character. If you are using a terminal application to send characters, such characters can often be generated when you press the "return" or "enter" key (depending on the keyboard).

 

What you would do in this case is to read the character from the UART. Check whether or not the character is your delimiter character. If not, add it to a receive buffer. If it is, then add a null character (0x00) to the buffer. This makes the contents of the buffer a c-standard null-terminated string. Now, you can use all of the standard c functions for manipulating strings.

 

Jim

 

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

 

 

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

>Because now I get like two random numbers for "rr"

 

What is the difference between two r's 2ms apart, and two r's 10 minutes apart, what is the difference between two switch presses 2ms apart, and two switch presses 10 minutes apart.

 

Time.

 

Just using what you already have, you can also 'debounce' the data coming in just like a switch would be debounced. Probably limited uses for this with a uart, but in this case it may get what you want even if the idea may not last very long until you move to better things.

 

void transmitRoll(void){

    while(!(UCSR0A & (1<<RXC0))){}
    if(character == roll ){
        uint8_t diceRoll = rand()%6+1;
        UDR0 =  diceRoll;
    }
    else{
        for(int i = 0; i<18;i++) UDR0 = error[i];
    }

    //wait until no rx data seen for specified time
    //2 seconds for example-
    for( uint16_t i = 0; i < 2000; _delay_ms(1), i++ ){        
        if( ! (UCSR0A & (1<<RXC0) ) ) continue; //if no rx, keep counting

        //we have an rx char, read udr0 to remove it from buffer
        UDR0;   //will simply read since is a volatile
        i = 0;  //reset counter, need 2 seconds again
    }

}

//will also need #include <util/delay.h>, after the F_CPU define

 

 

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

I have been reading and trying so many different things but ended up with this and it "almost" works... which is of course the same as not working in the programming world ;) My aim with all this is to imitate a dice roll so when in the

serial terminal in cuteCom one enters "r" and is returned a number from 1-6 but if then one enters "rr" "mr" or just something else then single "r" the error message is returned.

What I am doing in this code is :
1. gather characters untill one enters enter/return

2. add null character to determine the end of input in this loop/turn

3. check if the first input is "r" and second is "0"

4.  output either random number or error message depending on nr. 3

5. set the index counter back to 0 to prepare for the next round.
 

As can be seen on my screenshot all is working fine, except of course the input "r0" that is understood as an acceptable input for roll, but it isn't since it is not just "r"

Here is my code and I am bit confused how I can differ between 0 by delimiter or 0 by input and I hope I can, because I have been spending a lot of time on this small, small program haha

#define F_CPU 4000000UL
#include <avr/io.h>
#include <stdlib.h>
#include <avr/eeprom.h>
#include <string.h>
#define UBRR_val 12
static uint32_t EEMEM seed; //helps the compiler to find address for me in EEPROM
volatile unsigned  char input[20];
volatile uint8_t i = 0;
 char error[19] = {"Not a valid input!\n"};

int createSeed(){
	uint32_t number; //unsigned 16 bit numeric value to use for srand()
	number = eeprom_read_dword(&seed); //Read the location
	eeprom_write_dword(&seed, number+1);//Just add one to it each time
	return number;
}

uint8_t receiveData(void){
	while(!(UCSR0A & (1<<RXC0))){}
	return UDR0;
}

void transmitByte(uint8_t data){
	while(!(UCSR0A & (1<<UDRE0))){}
		UDR0 = data;
}

void transmitString(char * string){
	while(*string){
		transmitByte((uint8_t) *string);
			++string;
		}
	}



int main(void)
{		
	UBRR0L = UBRR_val; //Baud rate set
	UBRR0H = 0x00;
	UCSR0B = (1<<TXEN0)|(1<<RXEN0);//Enable transmitter and receiver			
	srandom(createSeed());	
	
	while (1)
	{			
		//Gather the input character into a array until return is pressed
		while(1){
			input[i] = receiveData();
			if(input[i] == '\r' || input[i] == '\n') break;
			i++;
		}	
		input[i] = '0'; //Set the null character to end the buffer
		 
			
		if(input[0] == 'r' && input[1] == '0' ){ //This is not working for for me just entering 'r' and enter and then compare to the char 'r'
			int randomNUmber = random() % 6 +1;
		    char c = (char)  randomNUmber + '0';
			char line_break = '\n';
			transmitByte(c);
			transmitByte(line_break);
		}	
		else
		  transmitString(error);
		
		i = 0;		
			
	}			
	  
	
}

 

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

Delpux wrote:

		input[i] = '0'; //Set the null character to end the buffer

You are using '0' which is the ascii value of the character '0'.

The null character is the actual value 0.

input[i] = 0;

input[i] = '\0';

The above 2 are identical. The '\0' is often used as a convention when dealing with strings.

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

That was it, thanks.

Last Edited: Sun. Oct 18, 2020 - 11:42 AM