AVR304 software serial

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

Hello. I wish to use AVR-304 based software serial routine to send some strings to a putty terminal. The concept is simple, send a couple of strings, use avr gcc delay_ms() to have some delay and repeat sending the strings. I use Atmel Studio 7 and Arduino Pro Mini. Please note, the Arduino is only used as a hardware board, I do not use Arduino IDE.
The code is posted. there are two files, main.c and sw_uart.c. No Header files for such a small project. The problem is that upon power on or reset, the board transmits the two strings shown perfectly the first time. Once it goes through the delay routine, the strings are unprintable characters. But they are in proper sequence, with proper delays in between. It is just that the strings are garbled.
I have tried many things, including reducing the baud rate to 9600 (as at present). I am not using the RECEIVE part at present.
One thing to note: In the same main.c() program, if I write a code to make the on board LED at D13 toggle with the same delay, it works perfectly. I feel the issue is some interaction between the software delay program and the software delay loop.
Been on this for a week now, any assistance will be much appreciated.

The code files as attached:

sw_uart.c

#include // Device specifics.
#include // Interrupt specifics.
#include

//Desired baudrate...choose one, comment the others.
#define BR_9600
//#define BR_19200
//#define BR_38400

//This section chooses the correct timer values for the chosen baudrate.

#ifdef BR_9600 //****CHECK BIT VALUES**** These are for 16MHz, prescalar = 32
#define TICKS2COUNT 52 //Ticks between two bits.
#define TICKS2WAITONE 52 //Wait one bit period.
#define TICKS2WAITONE_HALF 78 //Wait one and a half bit period.

#endif
#ifdef BR_19200 //****CHECK BIT VALUES**** These are for 16MHz, prescalar = 8
#define TICKS2COUNT 104 //Ticks between two bits.
#define TICKS2WAITONE 104 //Wait one bit period.
#define TICKS2WAITONE_HALF 156 //Wait one and a half bit period.
#endif
#ifdef BR_38400 //****CHECK BIT VALUES**** These are for 16MHz, prescalar = 8
#define TICKS2COUNT 52 //Ticks between two bits.
#define TICKS2WAITONE 52 //Wait one bit period.
#define TICKS2WAITONE_HALF 78 //Wait one and a half bit period.
#endif

#define INTERRUPT_EXEC_CYCL 9 // Cycles to execute ISR after interrupt.

//Some IO, timer and interrupt specific defines.

//MCU definitions - CHECK for proper Timer SFR definitions
#if (defined(__ATmega8__))
#define ENABLE_TIMER_INTERRUPT( ) ( TIMSK |=_BV(OCIE2))
#define DISABLE_TIMER_INTERRUPT( ) ( TIMSK &= ~_BV(OCIE2))
#define CLEAR_TIMER_INTERRUPT( ) ( TIFR |=_BV(OCF2))
#define ENABLE_EXTERNAL0_INTERRUPT( ) ( GICR |=_BV(INT0))
#define DISABLE_EXTERNAL0_INTERRUPT( ) ( GICR &= ~_BV(INT0))
#define TX_PIN PD6 //Transmit data pin
#define RX_PIN PD2 //Receive data pin, must be INT0
#define TCCR TCCR2 //Timer/Counter Control Register
#define TCCR_P TCCR2 //Timer/Counter Prescaler
#define OCR OCR2 //Output Compare Register
#define TCNT TCNT2 //Tmr counter register
#define EXT_IFR GIFR //Ext Interrupt Flag Register
#define EXT_ICR MCUCR //Ext Interrupt Control Register
//#define TIMER_COMP_VECT TIMER0_COMP_vect //!< Timer Compare Interrupt Vector

#elif (defined (__AVR_ATmega328P__))
#define ENABLE_TIMER_INTERRUPT( ) ( TIMSK2 |= ( 1<< OCIE2A ) )
#define DISABLE_TIMER_INTERRUPT( ) ( TIMSK2 &= ~( 1<< OCIE2A ) )
#define CLEAR_TIMER_INTERRUPT( ) ( TIFR2 |= ((1 << OCF2A) ) )
#define ENABLE_EXTERNAL0_INTERRUPT( ) ( EIMSK |= ( 1<< INT0 ) )
#define DISABLE_EXTERNAL0_INTERRUPT( ) ( EIMSK &= ~( 1<< INT0 ) )
#define TX_PIN PORTD6 //Transmit data pin,PDx is for ATMega8
#define RX_PIN PORTD2 // Receive data pin, must be INT0
#define TCCR TCCR2A // Timer/Counter Control Register
#define TCCR_P TCCR2B //Timer/Counter ControlPrescaler
#define OCR OCR2A // Output Compare Register
#define TCNT TCNT2 //Tmr counter register
#define EXT_IFR EIFR // External Interrupt Flag Register
#define EXT_ICR EICRA // External Interrupt Control Register
#define TIMER_COMP_VECT TIMER2_COMPA_vect // Timer Compare Interrupt Vector

#endif

#define TRXDDR DDRD //Set Tx/Rx ports, pins
#define TRXPORT PORTD
#define TRXPIN PIND

#define SET_TX_PIN( ) ( TRXPORT |=_BV(TX_PIN))
#define CLEAR_TX_PIN( ) ( TRXPORT &= ~_BV(TX_PIN))
#define GET_RX_PIN( ) ( TRXPIN & (_BV(RX_PIN)))

//Type defined enumeration holding software UART's state.

typedef enum
{
IDLE, //Idle state, both transmit and receive possible.
TRANSMIT, // Transmitting byte.
TRANSMIT_STOP_BIT, //Transmitting stop bit.
RECEIVE, //Receiving byte.
DATA_PENDING //Byte received and ready to read.

}AsynchronousStates_t;

static volatile AsynchronousStates_t state; //Holds state of UART.
static volatile unsigned char SwUartTXData; //Data to be Tx'ed.
static volatile unsigned char SwUartTXBitCount; //TX bit counter.
static volatile unsigned char SwUartRXData; //Storage for Rx'ed bits.
static volatile unsigned char SwUartRXBitCount; //RX bit counter.

// External interrupt service routine.

ISR(INT0_vect) //Receive interrupt - falling edge
{

state = RECEIVE; // Change state
DISABLE_EXTERNAL0_INTERRUPT( ); // Disable intr during the data bits.

DISABLE_TIMER_INTERRUPT( ); // Disable timer to change its reg.
TCCR_P &= ~(( 1 << CS21 )|( 1 << CS20 )); // Reset prescaler counter.

TCNT = INTERRUPT_EXEC_CYCL; // Clear counter register. Include time to run ISR.

TCCR_P |= (( 1 << CS21 )|( 1 << CS20 )); // Start prescaler clock.

OCR = TICKS2WAITONE_HALF; // Count 1.5 bits to sample first bit.

SwUartRXBitCount = 0; // Clear received bit counter.
CLEAR_TIMER_INTERRUPT( ); // Clear interrupt bits
ENABLE_TIMER_INTERRUPT( ); // Enable timer0 interrupt on again

}

// Timer2 interrupt service routine.

ISR(TIMER_COMP_VECT)
{

switch (state)
{
// Transmit Byte.
case TRANSMIT:
// Output the TX buffer.
if( SwUartTXBitCount < 8 ) { if( SwUartTXData & 0x01 ) // If LSB of the TX buffer is 1: { SET_TX_PIN(); // Send a logic 1 on the TX_PIN. } else { // Otherwise: CLEAR_TX_PIN(); // Send a logic 0 on the TX_PIN. } SwUartTXData = SwUartTXData >> 1; // Bitshift the TX buffer and
SwUartTXBitCount++; // increment TX bit counter.
}

//Send stop bit.
else
{
SET_TX_PIN(); // Output a logic 1.
state = TRANSMIT_STOP_BIT;
}
break;

// Go to idle after stop bit was sent.
case TRANSMIT_STOP_BIT:
DISABLE_TIMER_INTERRUPT( ); // Stop the timer interrupts.
state = IDLE; // Go back to idle.
ENABLE_EXTERNAL0_INTERRUPT( ); // Enable reception again.
break;

//Receive Byte.
case RECEIVE:

//Count one period after the falling edge is trigged.
OCR = TICKS2WAITONE;

if( SwUartRXBitCount < 8 ) //Receiving, LSB first. { SwUartRXBitCount++; SwUartRXData = (SwUartRXData>>1); // Shift due to receiving LSB first.
if( GET_RX_PIN( ) != 0 )
{
SwUartRXData |= 0x80; // If a logical 1 is read, let the data mirror this.
}
}

//Done receiving
else
{
// Enter DATA_PENDING when one byte is rx'ed.
state = DATA_PENDING;
// Disable this interrupt.
DISABLE_TIMER_INTERRUPT( );
// Reset flag not to enter the ISR one extra time.
EXT_IFR |= (1 << INTF0 );
// Enable interrupt to receive more bytes.
ENABLE_EXTERNAL0_INTERRUPT( );
}
break;

// Unknown state.
default:
state = IDLE; // Error, should not occur. Going to a safe state.
} //end switch
} // end intr

// Function to initialize the software UART.
//This function will set up Tx & Rx pins, tmr CTC mode, prescalar & interrupts.
//Leaves init routine after setting uart in Rx mode by enabling ext INT0.

void initSoftwareUart( void )
{
//Set PORT
TRXPORT |=_BV(RX_PIN); // RX_PIN is input, pulled up.
TRXDDR |=_BV(TX_PIN); // TX_PIN is output.
SET_TX_PIN( ); // Set the TX line to idle state.

//Set Timer2
DISABLE_TIMER_INTERRUPT( ); //(TIMSK &= ~_BV(OCIE2))
TCCR = 0x00; //Ensure TCCR is initialised on power on
TCCR_P = 0x00;
TCCR |= (1 << WGM21); // Timer in CTC mode.

TCCR_P |= (( 1 << CS21 )|( 1 << CS20 )); // set prescaler.

//Set External interrupt
EXT_ICR = 0x00; // Init MCUCR
EXT_ICR |= (1 << ISC01 ); // Interrupt on falling edge.
ENABLE_EXTERNAL0_INTERRUPT( ); // Turn external interrupt on.

//Internal State Variable
state = IDLE;
}

// Send an unsigned char.

static void putChar( const unsigned char c )
{
while( state != IDLE )
{
; // Don't send while busy receiving or transmitting.
}

state = TRANSMIT;
DISABLE_EXTERNAL0_INTERRUPT( ); // Disable reception.
SwUartTXData = c; // Put byte into TX buffer.
SwUartTXBitCount = 0; //set bit count 0

TCCR_P &= ~(( 1 << CS21 )|( 1 << CS20 )); // Reset prescaler counter.
TCNT = 0; // Clear counter register.
TCCR_P |= (( 1 << CS21 )|( 1 << CS20 )); // CTC mode. Start prescaler clock.

CLEAR_TX_PIN(); // Clear TX line...start of preamble.

CLEAR_TX_PIN( ); // Clear TX line...start of preamble

ENABLE_TIMER_INTERRUPT( ); // Enable o/p compare interrupt
}

// Print unsigned char string.
//param const unsigned char* Pointer to the string that is to be printed.
//retval void

void print_string( const unsigned char *data )
{
OCR = TICKS2WAITONE; //set bit times
while( *data != '\0' )
{
putChar( *data++ );
}
}

main.c

#include #include #include #include #include #include

#include "ds18b20.h"

//function declarations
void initSoftwareUart( void );
void print_string( const char * );

int main(void)
{

//init uart
initSoftwareUart();

//init interrupt
sei();

print_string("DS18B20 Temperature Probe\r\n");

for (;;)
{
//software serial routines
print_string("Temperature: ");

print_string("\r\n");

_delay_ms(1000);

}

return 0;
}

I regret to post the code like this, I could not see any special way to post the code. Also, some "toolbar" is mentioned for formatting, I cannot see any such toolbar. May I request assistance on how to use this highly helpful forum more effectively?

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


No one can read that. I realise it might have lost the indent when you pasted it in but in future I'd suggest using:

 

which will bring up the code editor so you can paste your code in. 

 

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

amorawala wrote:
the strings are unprintable characters

See Tip #2 in my signature (below) - also FAQ#3 in clawson's

 

Something must be going wrong with your timing ...

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

Here is the properly posted code.

Thanks for the advice, Clawson

 


#include <avr/io.h>     	// Device specifics.
#include <avr/interrupt.h>     // Interrupt specifics.
#include <util/delay_basic.h>

//Desired baudrate...choose one, comment the others.
#define BR_9600     
//#define BR_19200    
//#define BR_38400      

//This section chooses the correct timer values for the chosen baudrate.


#ifdef  BR_9600					//****CHECK BIT VALUES**** These are for 16MHz, prescalar = 32
    #define TICKS2COUNT         52  //Ticks between two bits.
    #define TICKS2WAITONE       52  //Wait one bit period.
    #define TICKS2WAITONE_HALF  78  //Wait one and a half bit period.
	
#endif  
#ifdef  BR_19200				//****CHECK BIT VALUES**** These are for 16MHz, prescalar = 8
    #define TICKS2COUNT          104  //Ticks between two bits.
    #define TICKS2WAITONE        104 //Wait one bit period.
    #define TICKS2WAITONE_HALF   156 //Wait one and a half bit period.
#endif
#ifdef  BR_38400				//****CHECK BIT VALUES**** These are for 16MHz, prescalar = 8
    #define TICKS2COUNT          52  //Ticks between two bits.
    #define TICKS2WAITONE        52  //Wait one bit period.
    #define TICKS2WAITONE_HALF   78  //Wait one and a half bit period.
#endif

#define INTERRUPT_EXEC_CYCL   9   // Cycles to execute ISR after interrupt.

//Some IO, timer and interrupt specific defines.

//MCU definitions - CHECK for proper Timer SFR definitions
#if  (defined(__ATmega8__)) 
  #define ENABLE_TIMER_INTERRUPT( )       ( TIMSK |=_BV(OCIE2))
  #define DISABLE_TIMER_INTERRUPT( )      ( TIMSK &= ~_BV(OCIE2))
  #define CLEAR_TIMER_INTERRUPT( )        ( TIFR |=_BV(OCF2))
  #define ENABLE_EXTERNAL0_INTERRUPT( )   ( GICR |=_BV(INT0))
  #define DISABLE_EXTERNAL0_INTERRUPT( )  ( GICR &= ~_BV(INT0))
  #define TX_PIN           PD6          //Transmit data pin
  #define RX_PIN           PD2          //Receive data pin, must be INT0
  #define TCCR             TCCR2        //Timer/Counter Control Register
  #define TCCR_P           TCCR2     	//Timer/Counter Prescaler
  #define OCR              OCR2       //Output Compare Register
  #define TCNT			   TCNT2		//Tmr counter register
  #define EXT_IFR          GIFR         //Ext Interrupt Flag Register
  #define EXT_ICR          MCUCR        //Ext Interrupt Control Register
//#define TIMER_COMP_VECT  TIMER0_COMP_vect  //!< Timer Compare Interrupt Vector

#elif (defined (__AVR_ATmega328P__))
  #define ENABLE_TIMER_INTERRUPT( )       ( TIMSK2 |= ( 1<< OCIE2A ) )
  #define DISABLE_TIMER_INTERRUPT( )      ( TIMSK2 &= ~( 1<< OCIE2A ) )
  #define CLEAR_TIMER_INTERRUPT( )        ( TIFR2 |= ((1 << OCF2A) ) )
  #define ENABLE_EXTERNAL0_INTERRUPT( )   ( EIMSK |= ( 1<< INT0 ) )
  #define DISABLE_EXTERNAL0_INTERRUPT( )  ( EIMSK &= ~( 1<< INT0 ) )
  #define TX_PIN           PORTD6    //Transmit data pin,PDx is for ATMega8
  #define RX_PIN           PORTD2          // Receive data pin, must be INT0
  #define TCCR             TCCR2A        // Timer/Counter Control Register
  #define TCCR_P           TCCR2B   	 //Timer/Counter ControlPrescaler 
  #define OCR              OCR2A         // Output Compare Register
  #define TCNT			   TCNT2		//Tmr counter register
  #define EXT_IFR          EIFR          // External Interrupt Flag Register
  #define EXT_ICR          EICRA         // External Interrupt Control Register
  #define TIMER_COMP_VECT  TIMER2_COMPA_vect  // Timer Compare Interrupt Vector

#endif

#define TRXDDR  DDRD			//Set Tx/Rx ports, pins
#define TRXPORT PORTD
#define TRXPIN  PIND

#define SET_TX_PIN( )    ( TRXPORT |=_BV(TX_PIN))
#define CLEAR_TX_PIN( )  ( TRXPORT &= ~_BV(TX_PIN))
#define GET_RX_PIN( )    ( TRXPIN & (_BV(RX_PIN)))


//Type defined enumeration holding software UART's state.
 
typedef enum
{
    IDLE,             //Idle state, both transmit and receive possible.
    TRANSMIT,                          // Transmitting byte.
    TRANSMIT_STOP_BIT,                 //Transmitting stop bit.
    RECEIVE,                           //Receiving byte.
    DATA_PENDING                 //Byte received and ready to read.

}AsynchronousStates_t;

static volatile AsynchronousStates_t state;    //Holds state of UART.
static volatile unsigned char SwUartTXData;     //Data to be Tx'ed.
static volatile unsigned char SwUartTXBitCount; //TX bit counter.
static volatile unsigned char SwUartRXData;     //Storage for Rx'ed bits.
static volatile unsigned char SwUartRXBitCount; //RX bit counter.


// External interrupt service routine.

ISR(INT0_vect)	//Receive interrupt - falling edge
{

  state = RECEIVE;                  // Change state
  DISABLE_EXTERNAL0_INTERRUPT( );   // Disable intr during the data bits.

  DISABLE_TIMER_INTERRUPT( );       // Disable timer to change its reg.
  TCCR_P &= ~(( 1 << CS21 )|( 1 << CS20 )); // Reset prescaler counter.

  TCNT = INTERRUPT_EXEC_CYCL;      // Clear counter register. Include time to run ISR.

  TCCR_P |= (( 1 << CS21 )|( 1 << CS20 ));  // Start prescaler clock.

  OCR = TICKS2WAITONE_HALF;         // Count 1.5 bits to sample first bit.

  SwUartRXBitCount = 0;             // Clear received bit counter.
  CLEAR_TIMER_INTERRUPT( );         // Clear interrupt bits
  ENABLE_TIMER_INTERRUPT( );        // Enable timer0 interrupt on again

}


// Timer2 interrupt service routine.

ISR(TIMER_COMP_VECT)
{

  switch (state) 
{
  // Transmit Byte.
  case TRANSMIT:
    // Output the TX buffer.
    if( SwUartTXBitCount < 8 ) 
	{            
      if( SwUartTXData & 0x01 )         // If LSB of the TX buffer is 1:
       { 
		SET_TX_PIN();                  // Send a logic 1 on the TX_PIN.
       }
      else 
	 {                                // Otherwise:
        	CLEAR_TX_PIN();                // Send a logic 0 on the TX_PIN.
       }
      SwUartTXData = SwUartTXData >> 1;     // Bitshift the TX buffer and
      SwUartTXBitCount++;                   // increment TX bit counter.
 	}

    //Send stop bit.
    else 
	{
      SET_TX_PIN();                         // Output a logic 1.
      state = TRANSMIT_STOP_BIT;
    	}
  break;

  // Go to idle after stop bit was sent.
  case TRANSMIT_STOP_BIT:
    DISABLE_TIMER_INTERRUPT( );           // Stop the timer interrupts.
    state = IDLE;                         // Go back to idle.
    ENABLE_EXTERNAL0_INTERRUPT( );        // Enable reception again.
  break;

  //Receive Byte.
  case RECEIVE:
    
//Count one period after the falling edge is trigged.
OCR = TICKS2WAITONE; 
    
    if( SwUartRXBitCount < 8 ) //Receiving, LSB first.
	{
        SwUartRXBitCount++;
        SwUartRXData = (SwUartRXData>>1);   // Shift due to receiving LSB first.
        if( GET_RX_PIN( ) != 0 ) 
		{
            SwUartRXData |= 0x80;     // If a logical 1 is read, let the data mirror this.
        	}
    	}

    //Done receiving
    else 
	{
	// Enter DATA_PENDING when one byte is rx'ed.        
state = DATA_PENDING; 
	// Disable this interrupt.
 		DISABLE_TIMER_INTERRUPT( );         
      // Reset flag not to enter the ISR one extra time.
		EXT_IFR |= (1 << INTF0 );   
       // Enable interrupt to receive more bytes. 
		ENABLE_EXTERNAL0_INTERRUPT( );  
    	}
  break;

  // Unknown state.
  default:        
    state = IDLE;          // Error, should not occur. Going to a safe state.
  } //end switch
}	// end intr


// Function to initialize the software UART.
//This function will set up Tx & Rx pins, tmr CTC mode, prescalar & interrupts. 
//Leaves init routine after setting uart in Rx mode by enabling ext INT0.

void initSoftwareUart( void )
{
  //Set PORT
  TRXPORT |=_BV(RX_PIN);       // RX_PIN is input, pulled up.
  TRXDDR |=_BV(TX_PIN);        // TX_PIN is output.
  SET_TX_PIN( );                    // Set the TX line to idle state.

  //Set Timer2
  DISABLE_TIMER_INTERRUPT( );	//(TIMSK &= ~_BV(OCIE2)) 
  TCCR = 0x00;    				//Ensure TCCR is initialised on power on
  TCCR_P = 0x00;    				
  TCCR |= (1 << WGM21);			// Timer in CTC mode.
  
  TCCR_P |= (( 1 << CS21 )|( 1 << CS20 )); // set prescaler.

  //Set External interrupt
  EXT_ICR = 0x00;                   // Init MCUCR
  EXT_ICR |= (1 << ISC01 );       // Interrupt on falling edge.
  ENABLE_EXTERNAL0_INTERRUPT( );    // Turn external interrupt on.

  //Internal State Variable
  state = IDLE;
}


// Send an unsigned char.
  
static void putChar( const unsigned char c )
{
  while( state != IDLE )
  {
    ;                // Don't send while busy receiving or transmitting.
  }

  state = TRANSMIT;
  DISABLE_EXTERNAL0_INTERRUPT( );  // Disable reception.
  SwUartTXData = c;             // Put byte into TX buffer.
  SwUartTXBitCount = 0;         //set bit count 0

  TCCR_P &= ~(( 1 << CS21 )|( 1 << CS20 )); // Reset prescaler counter.
  TCNT = 0;                        // Clear counter register.
  TCCR_P |= (( 1 << CS21 )|( 1 << CS20 )); // CTC mode. Start prescaler clock.

  CLEAR_TX_PIN();               // Clear TX line...start of preamble.

  CLEAR_TX_PIN( );                 // Clear TX line...start of preamble

  ENABLE_TIMER_INTERRUPT( );        // Enable o/p compare interrupt
}


// Print unsigned char string.
//param  const unsigned char* Pointer to the string that is to be printed.
//retval void
 
void print_string( const unsigned char *data )
{
  OCR = TICKS2WAITONE;				//set bit times
  while( *data != '\0' )
  {
    putChar( *data++ );
  }
}



main.c


#include <avr/io.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include "ds18b20.h"

//function declarations
void initSoftwareUart( void );
void print_string( const char * );

int main(void) 
{

	//init uart
	initSoftwareUart();

	//init interrupt
	sei();
	
	print_string("DS18B20 Temperature Probe\r\n");
	
	for (;;) 
	{
		//software serial routines
		print_string("Temperature: "); 

		print_string("\r\n");
	
		_delay_ms(1000);
		
	}
	
	return 0;
}






 

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

Agreed, if the baud rates do not match, there will be junk characters. However, since the strings are properly displayed in the first run of the program, it confirms the baud rate is correct. Also, if I comment out the delay_ms() routines, the string is properly displayed repeatedly, of course at a good speed, confirming the baud rate set in the program (9600) matches that of the PUTTY terminal. I too understand something going wrong with the timing, but have not been able to identify the issue. Hence requested support.

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

Looks like your strings are in RAM - so maybe they're getting corrupted?

 

Do you have a debugger to see what's actually going on?

 

EDIT

 

Or something in your timers and/or soft-UART stuff isn't being properly reset ready to again after the first set of strings - again, a debugger would help here.

 

Or maybe use the real UART for debug output?

 

EDIT 2

 

Your code indentation is a bit random, which makes the code hard to follow - especially the timer ISR state machine.

 

But it doesn't look like you have any proper handling for the IDLE state ?

 

So, for the first run through, the first string is sent immediately after initialisation, and each subsequent string is sent immediately the previous one has finished - so there is no IDLE time.

 

But, when you put the delay in, you get a whole load of IDLE time ...

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: Tue. Feb 23, 2021 - 05:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to be sure

which chip ?

which optimizing level ?

 

normally _delay_ms() and interrupts isn't a good mix, but normally it's just the delay that get's to long.

 

does it work in the simulator ? (tx is easy to simulate)

 

you should use the timer ISR to make the delay, but for test try to cli() sei() around the delay   

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

https://www.avrfreaks.net/forum/...

here is code from another freak you may want to try out if you can't get this to work.

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Are you using a crystal--please don't say you are trying to use the internal RC osc...you will go on a goose hase.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

In general, yes - always use a crystal where async serial comms is required.

 

But OP said it works first time through - so must be correct baud rate - just on subsequent iterations of the loop it fails.

 

I would think it unlikely that the internal RC would change so much & so quickly for that to be the cause here?

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

A couple of thoughts...

 

First, I ought to stay out of this, as I don't know C.

 

Next, thank you for including comments in your code!

 

Finally, one HW comment, one SW comment:

 

HW:

Is the Receiving data input pin held in a known state, with a pull-up / pull-down, (whatever) resistor?

Or is it floating?

I didn't study your code to see how a string of Rx interrupts could distort your Tx data transmission timings.

If the Rx input is floating it could generate a seemingly endless string of Rx interrupts and that might totally trash your transmitter.

 

SW:

The difference between your initial power up data string transmission and your subsequent data string transmissions is that you have just finished your USART initialization subroutine the first time.

Presumably, but perhaps erroneously, one of the setup parameters was somehow trashed on the final exit of the Tx  Data ISR and the PutChar routine.

 

In theory it shouldn't make a difference, but what happens if you disable interrupts, rerun your InitSoftwareUart routine, and then re-enable interrupts, AFTER your Main Loop's delay, before the code loops back to the PrintString instruction.

 

That would mimic the entry into the PrintString/PutChar / Tx ISR code as if it had been called via the initial, post power up, entry.

 

If that works, then we can focus on what got trashed when the Tx data ISR exited for the "final" time from the first time through.

 

JC

 

Edit: Typos

Last Edited: Tue. Feb 23, 2021 - 05:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DocJC wrote:
The difference between your initial power up data string transmission and your subsequent data string transmissions is that you have just finished your USART initialization subroutine the first time.

Yes, I think that's key.

 

See EDIT 2 in #6

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

I am sorry to respond after long, but was busy today. Also the time zones may not be matching, I am in Bombay, India. My sincere apologies.

Awneil, I do not have a debugger, but will use the Atmel Studio 7 built in simulator. To check if there are issues with long delay_ms() loops, I have reduced the delay to 10ms. I have placed a putty screen capture snippet below:

As you can see, the first two lines are perfect. Subsequent prints are a mess. But what can be observed is that all subsequent lines are identical. Does this ring any bells? If the strings are corrupted after every delay loop, should they not spew out different garbage in new lines? 

But I will try to trace the flow and check the string locations in the simulator for any changes after the delay_ms().

Next, after all 8 bits are transmitted ( line 146), the STOP BIT is transmitted. This just pulls up the Tx line and changes state to TRANSMIT_STOP_BIT. When the next timer interrupt enters this state, it changes the state to IDLE and breaks, back to the main().  Now the delay loop starts. So, we can see that the state is placed in IDLE before the delay loop. Agreed, the time between the system entering IDLE and the execution of the delay loop is just a couple of instruction cycles, but it is there.

However, I will look at the program through the simulator and revert.

 

Just a thought: when the print_string function is called every time, does it not re-initialise the string? So even if the string is corrupted during the delay loop, will the function not set it properly again?

 

Last Edited: Wed. Feb 24, 2021 - 02:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sparrow2, I am using a standard Arduino Mini Pro, ATMega 328P, 16MHz. But I am using it as a bare board, not with Arduino IDE. 

I will check your good advice to have the delay loop in between cli() and sei(). This will surely disable the interrupts during the delay execution, and enable after.

As mentioned in another reply, I will check out with the simulator, it should be a bit easy.

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

Yes, I am using the Arduino board, which has a 16MHz crystal. 

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

amorawala wrote:
Awneil, I do not have a debugger,

so what about using a real UART for some debug output?

 

If the strings are corrupted after every delay loop, should they not spew out different garbage in new lines? 

Maybe  it's always the same corruption that happens?

 

It seems that you leave the timer running after the end of transmission - I'd suggest you check that doesn't cause any problems ...

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

amorawala wrote:
ATMega 328P, 16MHz. But I am using it as a bare board, not with Arduino IDE. 

then how about getting an ATmega328p Xplained Mini - then you will have a debugger.

 

eg, https://in.element14.com/microch...

 

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

DocJC, the receiving data pin is INT0, enabled to generate interrupt on falling edge. But I am not using the system in receive mode at this juncture, so am I right in saying this may not matter? Or is there some "hidden" hardware / software issue here? The receive part begins only with an INT0 falling edge, which is not the case here.

 

"The difference between your initial power up data string transmission and your subsequent data string transmissions is that you have just finished your USART initialization subroutine the first time.

Presumably, but perhaps erroneously, one of the setup parameters was somehow trashed on the final exit of the Tx  Data ISR and the PutChar routine."

 

Yes, this will come out when I run through the simulator. Now that the delay is reduced, the simulation may not take too long. Also I only have to check the first time after the delay loop to know whether the string is corrupted.

 

"In theory it shouldn't make a difference, but what happens if you disable interrupts, rerun your InitSoftwareUart routine, and then re-enable interrupts, AFTER your Main Loop's delay, before the code loops back to the PrintString instruction."

Doing the above may not help if the string is corrupted. It will still stay corrupted.

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

 

This shouts to me you are not converting your temperature value from it's binary value to an ASCII (printable) value before sending it to the terminal.

As your terminal only displays ASCII values, you need to convert your data to ASCII.  Perhaps using itoa() function before sending out the USART(soft or hard)!

Jim

 

 

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

awneil wrote:

amorawala wrote:
ATMega 328P, 16MHz. But I am using it as a bare board, not with Arduino IDE. 

then how about getting an ATmega328p Xplained Mini - then you will have a debugger.

 

eg, https://in.element14.com/microch...

 

I will look at this debugger. 

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

ki0bk wrote:

This shouts to me you are not converting your temperature value from it's binary value to an ASCII (printable) value before sending it to the terminal.

As your terminal only displays ASCII values, you need to convert your data to ASCII.  Perhaps using atoi() function before sending out the USART(soft or hard)!

Jim

 

 

Jim, I only want to check if the text strings are printed out correctly. In the present code, I am not measuring the temperature. Of course, I will use the proper functions to convert the temperature readings into proper strings.

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

awneil wrote:

amorawala wrote:
Awneil, I do not have a debugger,

so what about using a real UART for some debug output?

 

If the strings are corrupted after every delay loop, should they not spew out different garbage in new lines? 

Maybe  it's always the same corruption that happens?

 

It seems that you leave the timer running after the end of transmission - I'd suggest you check that doesn't cause any problems ...

It is a good idea, I will use the UART after the delay loop to print the strings. This will provide proof if the strings are indeed corrupted.

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

amorawala wrote:
I will look at this debugger. 

Note that it's not just a debugger - it is a complete development board, with built-in debugger/programmer.

 

See https://www.avrfreaks.net/forum/xplained-mini-mega328pb for examples

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

I would simplify this code:

TCCR_P &= ~(( 1 << CS21 )|( 1 << CS20 )); // Reset prescaler counter.
TCNT = 0;                        // Clear counter register.
TCCR_P |= (( 1 << CS21 )|( 1 << CS20 )); // CTC mode. Start prescaler clock.

to this:

TCCR_P = 0; // Reset prescaler counter.
TCNT = 0;                        // Clear counter register.
TCCR_P = ((1 << WGM21)|( 1 << CS21 )|( 1 << CS20 )); // CTC mode. Start prescaler clock.

and see what happens.

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

I'm confused. The code actually says:

	print_string("DS18B20 Temperature Probe\r\n");
	
	for (;;) 
	{
		//software serial routines
		print_string("Temperature: "); 

		print_string("\r\n");
	
		_delay_ms(1000);
		
	}

So on the output we might expect to see:

DS18B20 Temperature Probe
Temperature:
Temperature:
Temperature:
Temperature:

There isn't actually any code there (that I can see) that would be attempting to print an actual temperature value at all.

 

So presumably those corrupted lines are the actual text "Temperature: " after it has been corrupted?

 

That would then lead to the question "exactly where in memory are the letters 'T-e-m-p-..." located ?". As it's the parameter to:

print_string("Temperature: "); 

then C will start with a copy of those letters in flash. But during initialisation they will be copied out to RAM and then the parameter to the function call will be the address in RAM where they are now located. Something is presumably writing to those locations?

 

So then you would wonder what mechanisms exist in the code to make memory writes. Each time you see:

something = value

then if the location of something (often an uninitialised pointer) were corrupt then it could well accidentally hold the location in RAM to which Temperature had been copied.

 

But when I look at the code and every = assignment in it I cannot immediately spot any that might be writing to a rogue location.

 

Next to consider is often "has the stack crashed into .data/.bss?" but for that to happen you either have to have heavy memory allocation or recursivity or a stack inbalance or something that might cause it but, again, I don't see that.

 

Another common way to "corrupt stuff" is as soon as you stop using C and start embedding asm() sections without a total understanding of what it might be doing. But again I cannot see examples of that.

 

So it's a bit of a mystery but perhaps someone else can spot one of these things going on (for example the #define's of common symbols then a write through those can sometimes cause problems if a level of indirection is missed).

 

But anyway I would be concentrating on trying to understand where "Temperature :" ultimately resides and then what may be writing to the location.

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

Just to see if there is a correlation between "Temperature" and the rest of the lines can you show the hex values of what you receive.

It seems that you receive the \r\n correct !

 

it looks like a false start bit or too short stop bit or ???

 

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

clawson wrote:
There isn't actually any code there (that I can see) that would be attempting to print an actual temperature value at all.

No, there isn't - he hasn't done that bit yet:

in #21, amorawala wrote:
I only want to check if the text strings are printed out correctly. In the present code, I am not measuring the temperature. 

 

Other than data corruption, My guess is it's something to do with the timer being allowed to "free run" during the delay; and/or not being properly re-synced/re-started for subsequent iterations of the 'for' loop ...

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: Wed. Feb 24, 2021 - 03:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

I'm confused. The code actually says:

	print_string("DS18B20 Temperature Probe\r\n");
	
	for (;;) 
	{
		//software serial routines
		print_string("Temperature: "); 

		print_string("\r\n");
	
		_delay_ms(1000);
		
	}

So on the output we might expect to see:

DS18B20 Temperature Probe
Temperature:
Temperature:
Temperature:
Temperature:

There isn't actually any code there (that I can see) that would be attempting to print an actual temperature value at all.

 

So presumably those corrupted lines are the actual text "Temperature: " after it has been corrupted?

 

That would then lead to the question "exactly where in memory are the letters 'T-e-m-p-..." located ?". As it's the parameter to:

print_string("Temperature: "); 

then C will start with a copy of those letters in flash. But during initialisation they will be copied out to RAM and then the parameter to the function call will be the address in RAM where they are now located. Something is presumably writing to those locations?

 

So then you would wonder what mechanisms exist in the code to make memory writes. Each time you see:

something = value

then if the location of something (often an uninitialised pointer) were corrupt then it could well accidentally hold the location in RAM to which Temperature had been copied.

 

But when I look at the code and every = assignment in it I cannot immediately spot any that might be writing to a rogue location.

 

Next to consider is often "has the stack crashed into .data/.bss?" but for that to happen you either have to have heavy memory allocation or recursivity or a stack inbalance or something that might cause it but, again, I don't see that.

 

Another common way to "corrupt stuff" is as soon as you stop using C and start embedding asm() sections without a total understanding of what it might be doing. But again I cannot see examples of that.

 

So it's a bit of a mystery but perhaps someone else can spot one of these things going on (for example the #define's of common symbols then a write through those can sometimes cause problems if a level of indirection is missed).

 

But anyway I would be concentrating on trying to understand where "Temperature :" ultimately resides and then what may be writing to the location.

Yes Clawson. The string "Temperature: " (without quotes) is located at 011B to 0128 in RAM. Using breakpoints at proper locations in the code and running the program, I could not see any corruption in this string. 

It seems there is some timing problem as others have mentioned, which is consistent, since the subsequent lines all have same bytes in the strings. 

I removed the software delay (_delay_ms()) an used the Timer1 in CTC mode for delays. Here too the same issue occurs. This means there is some timing changes that happens after a delay. 

I will have to check the codes in depth and see what I can do. This issue has to be resolved so that we can use software uart when we are short of the AVR's UART.

 

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

A really stupid question:

What is the level of your RX pin ?

 

(if that is constant low(start bit) I think it could make a mess).

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

amorawala wrote:
This means there is some timing changes that happens after a delay. 

Not necessarily a change in the timing itself - possibly just that you start at the "wrong" point in a timing cycle ... ?

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

sparrow2 wrote:

A really stupid question:

What is the level of your RX pin ?

 

(if that is constant low(start bit) I think it could make a mess).

I am not using the receive mode. But it is pulled up.

I have attached a Realterm capture snapshot as below. One can see the consistent wrong hex characters after the first line is properly output.

 

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

awneil wrote:

amorawala wrote:
This means there is some timing changes that happens after a delay. 

Not necessarily a change in the timing itself - possibly just that you start at the "wrong" point in a timing cycle ... ?

If the start point in the timing cycle is wrong, then the bytes should relate, either by left shift or right shift. E.g., the first byte which is supposed to be 0x54 ('T'), should be either 0xA8 (if <<)  or 0x4A (if>>).

This is because the wrong bytes are consistently wrong, not random for each line. Hence timing shifts should be consistent. This is my logic, which may be corrected.

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

Well, that's a hypothesis - now you need to test that hypothesis: http://www.8052mcu.com/faqs/120313

 

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

A quick look :

It looks like the byte are shifted 2 places down.

 

Why don't you simulate it ? (TX is easy), then get the time between each bit, and is speed really don't matter perhaps make 2 stop bit so you are sure you don't start to early. 

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

It is interesting that after the first line is printed, ending in <cr><lf>, every line after that ends with <sp><sp><cr><lf>, yet in the program posted, there is no <sp><sp> shown, on the <cr><lf>?

Where are these two spaces coming from?

Jim

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

If your needs are as simple as you say, why go through all the extra work of trying to get a timer/interrupt system involved when something real simple would work-

 

https://godbolt.org/z/ox3Eaq

 

Tested on a tiny3217, changed a few lines of code.

 

 

 

and a version that can print out to every pin you have-

 

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

Last Edited: Tue. Mar 2, 2021 - 03:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Testing whether the string literal is corrupted should not be too hard even without a debugger.

Use it to initialize a const array.

Pass it to a function in a separate file.

If the array does not have the correct data,

the function can blink an LED at you or something.

Iluvatar is the better part of Valar.