Butterfly + USART = odd behaviour

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

Has any of you bumped into this kind of behaviour:

I wrote an USART driver and a simple main to test it.
Input and echoing seems to work fine, but outputting several lines one after another produces all the lines on terminal, but the last line is written twice.

Also, the board double-boots after about 8 seconds. It just boots twice - not a loop.

I can put the code here if needed, but I didn't want to flood this with a lot of code text.

The code is supposed to drive other peripherals too: pins, ADC, timer 0 clock, timer 1 PWM (but no LCD). There are interrupt code for them in the code, but the peripherals are not yet initialised (I wanted to get debugging I/O ready first).

The memory:

Size after:
AVR Memory Usage
----------------
Device: atmega169

Program: 9908 bytes (60.5% Full)
(.text + .data + .bootloader)

Data: 728 bytes (71.1% Full)
(.data + .bss + .noinit)

Any ideas what to look for?

Debugging is for sissies and delivery for surgeons. Real men do demonstration.

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

Quote:
I can put the code here if needed,

It's needed. I don't think that we can even speculate what is causing it without seeing the code.

Regards,
Steve A.

The Board helps those that help themselves.

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

What does it do when running on the simulator?

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

I haven't tried it on the simulator - I don't know how to handle the USART on it. I also don't have any ICE or such. Anyway, the most part of the code is here. The dataflash code is directly from the famous demo program.

// main.h

#ifndef MAIN_H
#define MAIN_H
//Revisions number
#define SWHIGH  0
#define SWLOW   1
#define SWLOWLOW 1

#define BOOL    char

#define FALSE   0
#define TRUE    (!FALSE)
//#define NULL    0

#define SAATOPERIODI 100
#define TILAPERIODI 5

#ifdef DEBUG
#define MAX_CAL_LOOPS 2
#else
#define MAX_CAL_LOOPS 100
#endif

extern int g_v_set;

void OSCCAL_calibration(void);
void Initialization(void);

#endif


/****************************************************/
//
//  main.c


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

#include "main.h"
#include "kytkimet.h"
#include "kaasu.h"
#include "ajastin.h"
#include "nopeusmittari.h"
#include "ADC.h"
#include "UART.h"
#include "dataflash.h"

int g_v_set;

const char GREET[] PROGMEM = "Starting up\r\n";
const char ADC_HELP1[] PROGMEM = "adc help:\r\n";
const char ADC_HELP2[] PROGMEM = "adc \r\n"; // 25 chars + null
const char ADC_HELP3[] PROGMEM = "Max. number of samples 64\r\n";
const char ADC_HELP4[] PROGMEM = "One sample per millisecond\r\n";
const char PWM_HELP[] PROGMEM = "pwm help:\r\npwm [+|-] \r\n";
const char TMR_HELP[] PROGMEM = "tmr help:\r\ntmr  \r\n";
const char CNT_HELP1[] PROGMEM = "cnt help:\r\n";
const char CNT_HELP2[] PROGMEM = "cnt p  or\r\n";
const char CNT_HELP3[] PROGMEM = "cnt f  \r\n";

/*****************************************************************************
*
*   Function name : main
*
*   Returns :       None
*
*   Parameters :    None
*
*   Purpose :       Contains the main loop of the program
*
*****************************************************************************/
char cmd[40];
sampletype sample_array[MAX_SAMPLES];

int main(void)
{
//	int kytkimet;
//	PGM_P msg;
	int len, i;
	char *comptr;
	char *loc;
	int param1, param2;
	int sign;
	char outbuff[40];
	char mode;
	
//	msg = GREET;
	g_v_set = 60;
	
	Initialization();

//	rundown();

	sprintf_P(outbuff, GREET);
	uart_putstr(outbuff);
	for(;;)
	{
		len = uart_getstr(cmd, 39);
		uart_putstr("saatiin: ");
		uart_putstr(cmd);
		uart_putstr("\r\n");
		sprintf(outbuff, "len: %d\r\n", len);
		uart_putstr(outbuff);
		comptr = cmd;
		loc = strsep(&comptr," \r\n");
		if (strncmp(loc, "adc", 4) == 0)
		{
			if (*comptr == '?')
			{
				sprintf_P(outbuff, ADC_HELP1);
				uart_putstr(outbuff);
				sprintf_P(outbuff, ADC_HELP2);
				uart_putstr(outbuff);
				sprintf_P(outbuff, ADC_HELP3);
				uart_putstr(outbuff);
				sprintf_P(outbuff, ADC_HELP4);
				uart_putstr(outbuff);
			}
			else
			{
				sscanf(comptr, "%d\n", ¶m1);
				sprintf(outbuff, "saatiin: adc %d\r\n", param1);
				uart_putstr(outbuff);
/*				adc_read(param1, sample_array);
				for (i=0; i ?\r\n");
		}
	}
    return 0; 
}



/*****************************************************************************
*
*   Function name : Initialization
*
*   Returns :       None
*
*   Parameters :    None
*
*   Purpose :       Initializate the different modules
*
*****************************************************************************/
void Initialization(void)
{
    char tst;           // dummy

	/* JTAG pois käytöstä - varo pinnien tasoja ennen tätä */
	/* Ainoa JTAG output on TDO = PF6, Kaikki JTAG pinnit menevät kytkimille */
	cli(); 
	volatile uint8_t MD = (MCUCR | (1 << JTD)); 
	MCUCR = MD;                                  // Turn off JTAG via code 
	MCUCR = MD; 
	sei(); 

    OSCCAL_calibration();       // calibrate the OSCCAL byte
        
    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable

    // set system clock prescaler = 1, Internal RC 8MHz
    CLKPR = 0;

	// Disable LCD
	PRR = (PRR & 0xef) | 0x10;

    // Disable Analog Comparator (power save)
    ACSR = (1<<ACD);

    // Disable Digital input on PF0-2 (power save)
	// Analog input enable
    DIDR0 = (7<<ADC0D);

    PORTB = (15<<PB0);       // Enable pullup on flash
    PORTE = (15<<PE4);		  // Enable pullup on flash and USI

    //alusta_ajastin();         // 1 ms timer

	// DDR=0 -> input, PORT=0 -> no pull-up !!! Glitches?
	// Initialise pins for ON/OFF, set, inc, dec, resume ja jarru/kytkin
	//alusta_kytkimet();
	
	//alusta_kaasu();

	// Initialise port for interrupts on speedometer pulses
	//alusta_nopeusmittari();

	// PWM-generaattori
	//alusta_pwm();

	/* Flash-interfacen alustuksia - älä koske! */
    DF_SPI_init();              // init the SPI interface to communicate with the DataFlash
    
    tst = Read_DF_status();

    DF_CS_inactive;             // disable DataFlash

	uart_init(UART_BAUDRATE_19200);
	
	//adc_init();
	
	/* Voidaan tarvita myöhemmin
    RTC_init();                 // Start timer2 asynchronous, used for RTC clock
    USART_Init(12);             // Baud rate = 9600bps
    Button_Init();              // Initialize pin change interrupt on joystick
	Init_watchdog();
	*/
}






/*****************************************************************************
*
*   Function name : Delay
*
*   Returns :       None
*
*   Parameters :    unsigned int millisec
*
*   Purpose :       Delay-loop
*
*****************************************************************************/
void Delay(unsigned int millisec)
{
	// mt, int i did not work in the simulator:  int i; 
	uint8_t i;

	while (millisec--) {
		for (i=0; i<125; i++) {
			asm volatile ("nop"::);
		}
	}
}



/*****************************************************************************
*
*   Function name : OSCCAL_calibration
*
*   Returns :       None
*
*   Parameters :    None
*
*   Purpose :       Calibrate the internal OSCCAL byte, using the external 
*                   32,768 kHz crystal as reference
*
*****************************************************************************/
void OSCCAL_calibration(void)
{
    unsigned char calibrate = FALSE;
    int temp;
    unsigned char tempL;
	int cal_cnt = 0;

    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler for timer1 = 8, Inter RC 8Mhz / 8 = 1Mhz
    CLKPR = (1<<CLKPS1) | (1<<CLKPS0);
    
    TIMSK2 = 0;             //disable OCIE2A and TOIE2

    ASSR = (1<<AS2);        //select asynchronous operation of timer2 (32,768kHz)
    
    OCR2A = 200;            // set timer2 compare value 

    TIMSK0 = 0;             // delete any interrupt sources
        
    TCCR1B = (1<<CS10);     // start timer1 with no prescaling (~1MHz RC osc)
    TCCR2A = (1<<CS20);     // start timer2 with no prescaling (32,768kHz crystal osc)
							// 0,006103515625 s
    while((ASSR & 0x01) | (ASSR & 0x04));       //wait for TCN2UB and TCR2UB to be cleared

#ifndef DEBUG
    Delay(1000);    // wait for external crystal to stabilise
#endif    
    while(!calibrate)
    {
		cal_cnt++;
		if (cal_cnt > MAX_CAL_LOOPS)
		{
			break;
		}
        cli(); // disable all interrupts
        
        TIFR1 = 0xFF;   // delete TIFR1 flags
        TIFR2 = 0xFF;   // delete TIFR2 flags
        
        TCNT1H = 0;     // clear timer1 counter
        TCNT1L = 0;
        TCNT2 = 0;      // clear timer2 counter
           
        while ( !(TIFR2 & (1<<OCF2A)) );   // wait for timer2 compareflag = 200 tics

        TCCR1B = 0; // stop timer1

        sei(); // enable all interrupts
    
        // Out of range
        if ( (TIFR1 & (1<<TOV1)) )
        {
            temp = 0xFFFF;      // if timer1 overflows, set the temp to 0xFFFF
        }
        else
        {   // read out the timer1 counter value
            tempL = TCNT1L;
            temp = TCNT1H;
            temp = (temp << 8);
            temp += tempL;
        }
    
		// OK if timer1 tics within 200 timer2 tics is between 6250 and 6120
		// 6.25 ms .. 6.12ms
        if (temp > 6250)
        {
            OSCCAL--;   // the internRC oscillator runs to fast, decrease the OSCCAL
        }
        else if (temp < 6120)
        {
            OSCCAL++;   // the internRC oscillator runs to slow, increase the OSCCAL
        }
        else
            calibrate = TRUE;   // the interRC is correct

        TCCR1B = (1<<CS10); // start timer1
    }
}

/*************************************************/
// uart.h
#ifndef UART_H
#define UART_H

#define MAX_INCHARS 40

/* Special characters - check these */
#define BEL_CHAR 7   /* ctrl-G */
#define BS_CHAR 8    /* Backspace */
#define DEL_CHAR 127 /* Delete */

/* Baudrate settings */
#define UART_BAUDRATE_115200 3
#define UART_BAUDRATE_19200 25
#define UART_BAUDRATE_9600 51

void uart_init(int baudrate);
void flush_uart(void);
void uart_putstr(char *text);
void uart_putc(char ch);
int uart_getstr(char *buff, int maxchars);
char uart_getc(void);

#endif

/************************************************/
// uart.c

#include 
#include 
#include 
#include "UART.h"

/* 0x90 = bin 10010000, 0x48 = bin 01001000 */
#define UART_TX_DISABLE UCSRB = 0x90
#define UART_TX_ENABLE UCSRB = (0x48 | 0x90)

#define MAX_OUTCHARS 16
#define INC_UART_ECHO(x) ((x + 1) & 0x0f)

/* Buffer for output line */
volatile char uart_typeahead[MAX_INCHARS];
/* Buffer for echo */
volatile char uart_echo_buff[MAX_OUTCHARS];
volatile int uart_echo_head;
volatile int uart_echo_tail;

volatile char rxline = 0; /* Flag - 0 = line receiving not ready */
volatile char txline = 0; /* Flag - 0 = string transmission not ready */
volatile char txrdy = 1;  /* Flag - UART transmitter is ready for another character */
volatile int typeahead_idx = 0; /* First empty char in typeahead */
volatile char *tx_str; /* Output string */
volatile char currchar;

void uart_init(int baudrate)
{
	cli();
	PRR = PRR & 0xfd; // Power on for UART
	UCSRA = 0;
	UCSRB = 0;
	UCSRC = 6;
	UBRR = baudrate;
	sei();
//#if NEEDED
	DDRE = DDRE | 2; 	// PE1 output
	DDRE = DDRE & 0xfe; // PE0 input
//#endif
	flush_uart();
	cli();
	UART_TX_ENABLE; // enable int and start rx
	sei();
}

void start_uart(void)
{
	while (!txrdy); /* Wait until transmitter is ready */
	UDR = 0; 		 /* Send null character to start interrupts */
	txrdy = 0;
}

void flush_uart(void)
{
	cli();
	txline = 0;
	rxline = 0;
	typeahead_idx = 0;
	memset((char *)&uart_typeahead[0], 0, MAX_INCHARS);
	memset((char *)&uart_echo_buff[0], 0, MAX_OUTCHARS);
	uart_echo_head = uart_echo_tail = 0;
	tx_str = NULL;
	sei();
}

void uart_putstr(char *text)
{
	if (text) /* string exists */
	{
		if (*text) /* Not an empty string */
		{
			while (txline); /* Another putstr in progress */	
			while (!txrdy); /* Wait until transmitter is ready */
			cli();
			tx_str = text;
			txline = 1;
			txrdy = 0;
			UDR = *(tx_str++);
			sei();
		}
	}
}

void uart_putc(char ch)
{
	while (txline);
	while (!txrdy);
	cli();
	UDR = ch;
	txrdy = 0;
	sei();
}

void uart_echo(char ch)
{
	if (!txline)
	{
		if (txrdy)
		{
			UDR = ch;
			txrdy = 0;
		}
		else
		{
			if (INC_UART_ECHO(uart_echo_head) != uart_echo_tail)
			{
				uart_echo_buff[uart_echo_head] = ch;
				uart_echo_head = INC_UART_ECHO(uart_echo_head);
			}
		}
	}
	else
	{
		if (INC_UART_ECHO(uart_echo_head) != uart_echo_tail)
		{
			uart_echo_buff[uart_echo_head] = ch;
			uart_echo_head = INC_UART_ECHO(uart_echo_head);
		}
	}
}

int uart_getstr(char *buff, int maxchars)
{
	int getstr_len;
	
	/* Clear typeahead buffer */
	cli();
	memset((char *)&uart_typeahead[0], 0, MAX_INCHARS);
	typeahead_idx = 0;
	getstr_len = 0;
	sei();
	/* set up line receiving */
	rxline = 1;
	while (rxline); /* wait for line to get ready */
	/* Collect the characters */
	(void) strncpy(buff, (const char *)&uart_typeahead[0], maxchars-2);
	buff[maxchars-1] = (char) 0;
	getstr_len = strnlen(buff, maxchars);
	return getstr_len;
}

char uart_getc(void)
{
	char ch;
	ch = currchar;
	currchar = '\0';
	return ch;
}

ISR(USART0_RX_vect)
{
	currchar = UDR; /* Get character from UART */
	if (rxline) /* line receiving in progress */
	{
		if (currchar != '\0') /* Null-chars are discarded */
		{
			/* Handle "delete" character */
			if (currchar == DEL_CHAR)
			{
				/* Remove last char from buffer */
				typeahead_idx--; 
				uart_typeahead[typeahead_idx] = '\0';
				/* Remove last char from display */
				uart_echo(BS_CHAR);
				uart_echo(' ');
				uart_echo(BS_CHAR);
			}
			/* Input buffer not full */
			else if (typeahead_idx < MAX_INCHARS - 2)
			{
				/* Handle "end of line" character */
				if (currchar == '\r')
				{
					uart_typeahead[typeahead_idx++] = currchar;
					/* Make sure that the line is null terminated */
					uart_typeahead[typeahead_idx++] = '\0';
					rxline = 0; /* Line can be returned */
					/* New line to display too */
					uart_echo('\r');
					uart_echo('\n');
				}
				else /* Some other character */
				{
					uart_typeahead[typeahead_idx++] = currchar;
					uart_echo(currchar);
				}
			}
			else /* Input buffer full */
			{
				uart_echo(BEL_CHAR);
			}
		}
	}
}

ISR(USART0_TX_vect)
{
	if (txline) /* String output in progress */
	{
		if (tx_str) /* String is present */
		{
			if (*tx_str) /* Not end of string */
			{
				UDR = *(tx_str++); /* char to UART */
			}
			else /* End of string */
			{
				txline = 0;		/* Mark string output as ended */
				tx_str = NULL;  /* Just in case */
				if (uart_echo_head == uart_echo_tail)
				{
					txrdy = 1;      /* No data in UART output register */
				}
				else
				{
					UDR = uart_echo_buff[uart_echo_tail];
					uart_echo_tail = INC_UART_ECHO(uart_echo_tail);
				}
			}
		}
		else /* String output but output string disappeared */
		{
			txline = 0;		/* Mark string output as ended */
			if (uart_echo_head == uart_echo_tail)
			{
				txrdy = 1;      /* No data in UART output register */
			}
			else
			{
				UDR = uart_echo_buff[uart_echo_tail];
				uart_echo_tail = INC_UART_ECHO(uart_echo_tail);
			}
		}
	}
	else /* Single character output, like echo */
	{
		/* if not empty */
		if (uart_echo_head != uart_echo_tail)
		{
			UDR = uart_echo_buff[uart_echo_tail];
			uart_echo_tail = INC_UART_ECHO(uart_echo_tail);
		}
		else
		{
			tx_str = NULL;  /* Just in case */
			txrdy = 1; /* No data in UART output register */
		}
	}
}

Debugging is for sissies and delivery for surgeons. Real men do demonstration.

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

Oh yes, and does anyone have a good way of starting the application on power-on without need to touch the joystick?

Debugging is for sissies and delivery for surgeons. Real men do demonstration.

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

I'm glad you can understand the code! It all seems just a little complex for what is required. Personally, I'd just be having two circular buffers, one to rx and one for tx. Any echoing and backspace stuff would be done outside of the ISR. As I said previously, try the simulator - once you get the feel for it you'll track down your bug quickly. You don't need to understand how the uart works fully, you'll be able to see what characters from where are being sent - that should give you a hint as to where the code goes wrong.

When I refer to 'complex' I'm meaning 'cyclomatic complexity' - do a Goggle on that to get the background. Basically, the more complex the code, the harder it is to debug - avoid writing code you can't debug easily!

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

I thought about the circular buffers for the output too, but the amount of available RAM is a restriction. I still put a small echo buffer there. The echoing, I think, should come from the device - it should show what it got, not what it was supposed to have got.

How do I simulate the UART interrupts? How about data receiving?
(Do you mean the studio simulator.)

Debugging is for sissies and delivery for surgeons. Real men do demonstration.

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

Yes, I mean the studio simulator. As for simulating the uart operation - read the manual! I've not done it myself as I rarely use the simulator or a jtag debugger. I'd guess you could set the uart bits manually then trace through the code. It might take a little time, but hard evidence sure beats scratching your head wondering what is going wrong.

After you've found the problem, I'd look at rewriting that section of code so it is less clumsy - you've got flags, pointers and indexes - reduce the amount of decisions and you reduce complexity. Less complexity usually equals less bugs - since the code is easier to comprehend.

I pulled out the simulator earlier this week to debug some code that I'd ported across - seems a macro didn't expand the way I'd hoped and the buffer was too small. Saved hours of guessing.