AVR304 software serial

Go To Last Post
66 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?

This topic has a solution.
Last Edited: Wed. Mar 10, 2021 - 02:52 PM
  • 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

 

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

  • 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

 

 

 

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

  • 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

 

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

  • 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

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

  • 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.

Moderation in all things. -- ancient proverb

  • 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 ...

Hello. I used the AVR's USART to transmit the same string. I placed the function UART_SendString("Temperature: "); just after the software uart print_string("Temperature: "); instruction. 

I find that the USART's output to Realterm is proper, while the software uart's output is still jumbled. So it is confirmed that there is some timing issue with the software uart once it comes back after a delay loop.

Some queries:

1. Even though the s/w uart output is jumbled, it is consistent for all time the printing is being done. Please see the Realterm screen shot sent before. I checked on the AS7 simulator, there seems to be no issue. 

2. Without the delay loop, the s/w uart output is proper as mentioned before.

3. There is no issue with the baudrate. If there was, the jumbled output would not be consistent. 

I have to use the AVR USART to send information to ESP-01 (with AT command set) and then on to the Thingspeak cloud. So this USART cannot be used to send data to a terminal. I have to use the s/w uart.

Any ideas on what is going on?

Regards.

 

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

amorawala wrote:
I checked on the AS7 simulator, there seems to be no issue.

could be a limitation of the simulation - would be better to check in real hardware

 

 

3. There is no issue with the baudrate. If there was, the jumbled output would not be consistent. 

Not necessarily; if it were a fixed baud rate error, it could produce consistent wrong output.

 

I have to use the s/w uart.

Or choose an AVR with sufficient hardware UARTs in the first place?

 

Any ideas on what is going on?

I'd still be looking into a state thing - the delay means you're catching something in the wrong state; eg,

 

In #34, sparrow2 wrote:
A quick look :

It looks like the byte are shifted 2 places down.

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

awneil wrote:

amorawala wrote:

I checked on the AS7 simulator, there seems to be no issue.

 

could be a limitation of the simulation - would be better to check in real hardware

 

 

3. There is no issue with the baudrate. If there was, the jumbled output would not be consistent. 

Not necessarily; if it were a fixed baud rate error, it could produce consistent wrong output.

 

I have to use the s/w uart.

Or choose an AVR with sufficient hardware UARTs in the first place?

 

Any ideas on what is going on?

I'd still be looking into a state thing - the delay means you're catching something in the wrong state; eg,

 

 

 

 

In #34, sparrow2 wrote:

A quick look :

 

It looks like the byte are shifted 2 places down.

Bytes shifted two places "down". Please elaborate, down = left shift or right shift? I did check some time back, but cold not find any sequence. But I will check again.

I will try to rewrite the code without a state machine concept. Then I will check.

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

There's no need to quote the entire post just to refer to one line:

 

https://www.avrfreaks.net/forum/man-forum-how-reply-post-forum

 

down = left shift or right shift?

"down" = "towards the lower end" = to the right.

 

 

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

1. Even though the s/w uart output is jumbled, it is consistent for all time the printing is being done. Please see the Realterm screen shot sent before. I checked on the AS7 simulator, there seems to be no issue. 

So what does this mean ?

 

Have you debugged the code with the simulator ?

set a breakpoint everywhere the uart tx pin can change, run your code, and if nothing else (timers etc.) then write down the numbers of cpu clocks there are between each bit, and the letter itit try to send.

from that it should be relative easy to see what is wrong. 

 

Do you actually need to use RX ? 

 

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

The simple code in #36 will work, either the pin specific version or the any/multi pin version. Difficult to do software serial with other interrupts running, so really no need for an interrupt version for sending as you will most likely be blocking anyway. 

 

I'm not sure if anyone reads the links I post, but this is what the pin specific version is doing- 

 

//atmega328p, PD6
static void txHigh  () { PORTD |= 1<<6; }
static void txLow   () { PORTD &= ~(1<<6); }
static void txIdle  () { DDRD |= 1<<6; txHigh(); } //output, high

 

//FILE put compatible

static int txPut(char c, FILE* f){
    txLow(); //start 1 bit low
    __builtin_avr_delay_cycles( F_CPU/BAUD - 3 ); //tweak -3 as needed
    for( u8 i = 0; i < 8; i++, c >>= 1 ){
        if( c & 1 ) txHigh(); else txLow();
        __builtin_avr_delay_cycles( F_CPU/BAUD - 8 ); //tweak -8 as needed
    }
    txHigh(); //stop 2 bits
    __builtin_avr_delay_cycles( F_CPU/BAUD *2 );
    return 0;
}

 

Quite simple. Adjust the -3 and -8 to account for time that is not part of the delay. If other interrupts are being used, then they need to be disabled via the global irq before printing. The printing blocks until done. As #36 links show, you can easily interface to vfprintf.

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

sparrow2 wrote:

Just to be sure

which chip ?

which optimizing level ?

ATMega328P, Optimisation at -O1

 

 

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   

I am testing the code with AS7 simulator. I tried

- using Timer1 in CTC mode to generate the delay using ISR.

- before the delay I put cli() and after delay I put sei().

The results are consistently same. 

One observation I always made is that the CR/LF non printable characters are correct. In fact the correct hex codes are displayed. But all printable characters are not correct.

 

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

again what is the time between each change of the TX pin, some timing must be off. (we know that the basic logic is correct)

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

sparrow2 wrote:

again what is the time between each change of the TX pin, some timing must be off. (we know that the basic logic is correct)

I checked the stopwatch on the simulator for each bit sent out. It is about 104 microseconds @ 9600 baud. This is correct. 

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

sparrow2 wrote:

A quick look :

It looks like the byte are shifted 2 places down.

I checked this possibility. Please see the following for just the first few chars:

Char   on terminal       >>2          Proper ASCII

T          AA                   2A               54

e          59                   16                65 

m         5B                   16                6D

 

I cannot see any pattern in this. Can anyone "see" some clues?

 

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. 

I did use AS7 simulator. The bits on PORTD6 seem fine for each iteration. It is a bit tedious and confusing to breakpoint the entire string. So I will try to print just one char 'T' and check.          

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

curtvm wrote:

 

Quite simple. Adjust the -3 and -8 to account for time that is not part of the delay. If other interrupts are being used, then they need to be disabled via the global irq before printing. The printing blocks until done. As #36 links show, you can easily interface to vfprintf.

I have seen the code mentioned in the link you sent & will try this out. I have to understand the FILE concept, which I will do. Thanks.

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

Since you seem to want to make the irq version work...

 

You should probably clear the timer interrupt flag before enabling the timer interrupt. Since the timer is still running, the flag is getting set unless you are quick enough to get back to putChar (no delay in use). Since the flag is set, you get an isr firing sooner than you planned.

 

// 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

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

 

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

curtvm wrote:
Since the timer is still running, the flag is getting set unless you are quick enough to get back to putChar (no delay in use). Since the flag is set, you get an isr firing sooner than you planned.

Yep - that's exactly the kind of thing I was getting at ...

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. Mar 9, 2021 - 01:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

???

It is a bit tedious and confusing to breakpoint the entire string. So I will try to print just one char 'T' and check. 

Since the T also is wrong you don't need to change anything. (you should already have found the error by then). 

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

curtvm wrote:

Since you seem to want to make the irq version work...

 

You should probably clear the timer interrupt flag before enabling the timer interrupt. Since the timer is still running, the flag is getting set unless you are quick enough to get back to putChar (no delay in use). Since the flag is set, you get an isr firing sooner than you planned.

 

    ...... (rest of the putchar() code)

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

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

[\quote]

Thanks Curt. If you see the code the timer interrupt is disabled after the stop bit is sent out. The code snippet is as follows.        

 // 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;

So the interrupt is disabled and enabled again in the putchar() code. Hope this clarifies.

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

sparrow2 wrote:

???

It is a bit tedious and confusing to breakpoint the entire string. So I will try to print just one char 'T' and check. 

Since the T also is wrong you don't need to change anything. (you should already have found the error by then). 

To trace the problem, it is simpler to output just one char. It may be that the issue is with a large number of chars being printed. Sounds illogical, but let me check. I can also use my oscilloscope to see one char easily in single sweep mode.

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

> If you see the code the timer interrupt is disabled after the stop bit is sent out

 

The timer is still running, though. Since it is running, a compare match sets the compare flag. And since the flag is set when the irq is enabled again, you will get to the isr as soon as you enable the irq, and what you thought was going to be a 1 bit time wait, is now no bit time wait.

 

It does seem odd they could put out appnote code that has this problem, but it seems so unless I'm missing something obvious.

Last Edited: Tue. Mar 9, 2021 - 02:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

 

The timer is still running, though. Since it is running, a compare match sets the compare flag.

Please let me know if I understood yo correctly. The putchar() code is:

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
}

Here, the timer is reset and started before the timer interrupt is enabled. So the timer is already running. Now, if we clear the interrupt flag just before enabling it again, how will that help? The timer is already running and few microseconds have passed before the flag if cleared and set again. I understand clearing the interrupt flag does not affect the timing? 

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

curtvm wrote:
The timer is still running, though. Since it is running, a compare match sets the compare flag. And since the flag is set when the irq is enabled again, you will get to the isr as soon as you enable the irq, and what you thought was going to be a 1 bit time wait, is now no bit time wait.

+1

 

in #27, I wrote:
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 ...

 

In #6, I wrote:
But it doesn't look like you have any proper handling for the IDLE state ?

so perhaps entry to IDLE should stop (and reset?) the timer?

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

> Now, if we clear the interrupt flag just before enabling it again, how will that help?

 

It seems like I'm doing too much explaining, so must not be making it very clear.

 

Timer is still running while you delay.

On a compare match, compare flag is set. Doesn't matter that the irq is disabled. A match sets the flag.

 

Now, back to putChar for some more printing-

You clear TCNT to 0.

OCR is set to give an irq in 1 bit time.

The timer irq is enabled.

problem here- the timer flag is already set so we jump to the isr here instead of waiting until the tcnt/ocr match

 

 

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

awneil wrote:

in #27, I wrote:
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 ...

 

In #6, I wrote:
But it doesn't look like you have any proper handling for the IDLE state ?

so perhaps entry to IDLE should stop (and reset?) the timer?

I feel you are right Awneil. There is no IDLE state in switch statement. The IDLE state is merely to dictate that no char may be written if not in this state. 

I think I may have to rewrite the code to include IDLE handler or look at the code mentioned by Curt in the link  https://godbolt.org/z/ox3Eaq

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

curtvm wrote:

> Now, if we clear the interrupt flag just before enabling it again, how will that help?

 

It seems like I'm doing too much explaining, so must not be making it very clear.

 

Timer is still running while you delay.

On a compare match, compare flag is set. Doesn't matter that the irq is disabled. A match sets the flag.

 

Now, back to putChar for some more printing-

You clear TCNT to 0.

OCR is set to give an irq in 1 bit time.

The timer irq is enabled.

problem here- the timer flag is already set so we jump to the isr here instead of waiting until the tcnt/ocr match

 

 

I understand. How will it be if I stop the timer before the delay loop and let putchar() restart it? Also, I can disable the interrupt flag itself. I will check the code and try. Effectively:

- stop timer, clear flag, disable timer interrupt

- delay loop

 

After the delay, when the program goes to execute the code, the time will start again. Would this be right?

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

Add the clearing of the flag, Try it. It either helps, or it doesn't.

 

 

 

>or look at the code mentioned

 

If other irq's are in use, you can surround this function with saving/disabling/restoring interrupts. That would at least allow interrupts to fire between chars.

 

static int txPut(char c, FILE* f){

    u8 sreg = SREG;

    asm("cli");
    txLow(); //start 1 bit low
    __builtin_avr_delay_cycles( F_CPU/BAUD - 3 ); //tweak as needed
    for( u8 i = 0; i < 8; i++, c >>= 1 ){
        if( c & 1 ) txHigh(); else txLow();
        __builtin_avr_delay_cycles( F_CPU/BAUD - 8 );
    }
    txHigh(); //stop 2 bits

    SREG = sreg; //timing no longer important
    __builtin_avr_delay_cycles( F_CPU/BAUD *2 ); //not important if takes longer

    return 0;
}

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

curtvm wrote:

Add the clearing of the flag, Try it. It either helps, or it doesn't.

 

 

 

>or look at the code mentioned

 

 

Ok Curt. I will fist try to edit my code. Else I will work on the code you mention. Anyway, I have to get it working. I am looking at more projects where I will need a soft uart.

Thanks.

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

amorawala wrote:
stop the timer before the delay loop 

Wouldn't it make more sense to have the end of your stop bit handling stop the timer?

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

awneil wrote:

amorawala wrote:
stop the timer before the delay loop 

Wouldn't it make more sense to have the end of your stop bit handling stop the timer?

Right. I can do so easily. Also, to be sure, clear the timer flag & interrupt. 

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

It make totally sense with a hanging ISR, that way there will be no start bit (yes but shorter than 1/2 bit time).

So some of the low data bits become the start bit.

 

My guess is that this is written for the mega8 and works, then the mega328 is added and some of the ISR things is working a bit different.

 

 

 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

Since you seem to want to make the irq version work...

 

You should probably clear the timer interrupt flag before enabling the timer interrupt. Since the timer is still running, the flag is getting set unless you are quick enough to get back to putChar (no delay in use). Since the flag is set, you get an isr firing sooner than you planned.

Hello all. After due consultations with yo all, the issue seems to be solved. As suggested by Curt & Awneil, I have stopped he timer, cleared the OCR flag bit, disabled the OCIE interrupt after the TRANSMIT_STOP_BIT.

The code snippet is shown

 // Go to idle one bit time after stop bit was sent. This ensures one stop bit.
  case TRANSMIT_STOP_BIT:
	TCCR_P &= ~(( 1 << CS21 )|( 1 << CS20 )); // Reset prescaler counter, stop timer.
	TCNT = 0;                        // Clear counter register.
	CLEAR_TIMER_INTERRUPT( );			//reset OCR flag bit
    DISABLE_TIMER_INTERRUPT( );           // Stop the timer interrupts.
    state = IDLE;                         // Go back to idle.
    ENABLE_EXTERNAL0_INTERRUPT( );        // Enable reception again.
  break;

I have removed cli() and sei() from before and after the delay loop. 

After building the code, I checked the output, it is proper now. I have also tried this at 38400 Baud ( it was at 9600 Baud first). All ok for now.

Thanks a lot to all the experts who assisted in solving the issue.

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

amorawala wrote:
the issue seems to be solved

Great - thanks for feedback.

 

Please mark the solution - see Tip #5:

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...