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?