Sida 1 av 1

Problem med mjukvaru UART

Postat: 24 oktober 2006, 01:51:10
av FS
Har ett problem med min mjukvaru UART. Det yttrar sig som att TX-pinnen helt omotiverat går låg ett tag. Detta sker innan jag sänder data. Resultatet blir att första byten som sänds blir felaktig resten blir bra...

Skriver C för tiny45 med AVR Studio 4

Lite analyserande med logikanalysatorn gav:
Bild
Allt ser snyggt och rätt ut!


Bild
Men om man zoomar in vid triggpunkten ser man att det händer något innan! Vad är denna dip för något? Har försökt isolera felet men utan framgång... Någon som har lite ideer?

main.c

Kod: Markera allt

#include <avr/io.h>
#include <avr/interrupt.h>
#include "SwUART.h"

int main(void) {
	int i;
	InitSwUART(); // init UART
	sei(); // enable interrupts

	DDRB |= (1 << PB0);
	PORTB &= ~(1 << PB0); // toggle PB0 (trig)
	PORTB |= (1 << PB0);
	PORTB &= ~(1 << PB0);

	unsigned char a[] = {0xFF, 0x00, 0x01, 0x82, 0x83};

	for(i = 0; i < 5; i++) {
		put_char(a[i]);
	}

	while(1) {
	}
}
SwUART.h

Kod: Markera allt

// Some I/O and interrupt specific defines (hardware setup)
#define TX_PIN	PB4
#define RX_PIN	PB2 // INT0 have to be on this pin...
#define TRXDDR	DDRB
#define TRXPORT	PORTB
#define TRXPIN	PINB

#define ENABLE_TIMER_INTERRUPT() (TIMSK |= (1 << OCIE0A))
#define DISABLE_TIMER_INTERRUPT() (TIMSK &= ~(1 << OCIE0A))
#define ENABLE_EXTERNAL_INTERRUPT() (GIMSK |= (1 << INT0))
#define DISABLE_EXTERNAL_INTERRUPT() (GIMSK &= ~(1 << INT0))


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

#define TICK_ONEANDHALF	75
#define TICK_ONE		50


// Functions
void InitSwUART(void);
void put_char(const unsigned char c);
void print_string(const unsigned char *data);

// Variables
typedef enum {
	IDLE,
	TRANSMIT,
	RECEIVE,
	DATA_PENDING
}states;

static volatile states SwUART_state;
static volatile unsigned char SwUART_TXData;
static volatile unsigned char SwUART_TXBitCount;
static volatile unsigned char SwUART_RXData;
static volatile unsigned char SwUART_RXBitCount;

SwUART.c

Kod: Markera allt

// 8MHz clock and 19200 as baudrate designed for ATtiny45
// No parity bit, 8 data bits, 1 stop bit

#include <avr/io.h>
#include <avr/interrupt.h>
#include "SwUART.h"

ISR(TIM0_COMPA_vect) { // timer interrupt handler

	// transmit byte
	if(SwUART_state == TRANSMIT) {
		if(SwUART_TXBitCount < 8) {
			if(SwUART_TXData & 0x01) {
				SET_TX_PIN(); // send a logic 1
			}
			else {
				CLEAR_TX_PIN(); // send a logic 0
			}

			SwUART_TXData = SwUART_TXData >> 1;
			SwUART_TXBitCount++;
		}

		// send stop bit
		else if(SwUART_TXBitCount == 8) {
			SET_TX_PIN();
			SwUART_TXBitCount++;
		}

		// done transmitting
		else {
			DISABLE_TIMER_INTERRUPT();
			SwUART_state = IDLE;
		}	
	}

	// receive byte
	else if(SwUART_state == RECEIVE) {
		OCR0A = TICK_ONE; // count one period
		// receiving, LSB first
		if(SwUART_RXBitCount < 8) {
			if(GET_RX_PIN() != 0) {
				SwUART_RXData = 0x80; // if a logic one let the data mirror this
			}
			SwUART_RXData = (SwUART_RXData >> 1); // shift due to LSB first
			SwUART_RXBitCount++;
		}
		// done receiving
		else {
			SwUART_state = DATA_PENDING; // one byte received enter DATA_PENDING
			DISABLE_TIMER_INTERRUPT();
			GIFR |= (1 << INTF0); // reset flag to not enter external intrrupt one extra time
			ENABLE_EXTERNAL_INTERRUPT(); // enable external interrupt to receive more bytes
		}
	}

	// error, should not occur -> go to safe state
	else {
		SwUART_state = IDLE;
	}

}

ISR(INT0_vect) { // external interrupt handler
	SwUART_state = RECEIVE;
	
	DISABLE_EXTERNAL_INTERRUPT(); // disable external interrupt during data bits

	DISABLE_TIMER_INTERRUPT(); // disable timer interrupt to change register
	TCNT0 = 0; // clear counter
	OCR0A = TICK_ONEANDHALF; // wait one and a half period
	ENABLE_TIMER_INTERRUPT(); // enable timer again
}

void InitSwUART(void) {
	TRXDDR |= (1 << TX_PIN); // set TX pin as output
	TRXDDR &= ~(1 << RX_PIN); // set RX pin as input


	// Timer0 init
	TCCR0A |= (1 << WGM01); // CTC mode
	TCCR0B |= (1 << CS01); // prescaler 8
	TIMSK |= (1 << OCIE0A); // interrupt on compare
	SET_TX_PIN(); // set TX-line to idle

	// External interrupt
	MCUCR |= (1 << ISC01); // interrupt on falling edge
	ENABLE_EXTERNAL_INTERRUPT(); // turn external interrupt on

	SwUART_state = IDLE; // set init state
}

void put_char(const unsigned char c) {
	while(SwUART_state != IDLE) {} // don't send while busy receiving or transmitting

	SwUART_state = TRANSMIT;
	SwUART_TXData = c;
	SwUART_TXBitCount = 0;

	TCNT0 = 0; // clear counter
	OCR0A = TICK_ONE; // tick one period
	TIFR |= (1 << OCF0A); // clear flag

	CLEAR_TX_PIN(); // clear TX-line... start of preamble
	ENABLE_TIMER_INTERRUPT();
}
Edit: Sorry för bildbredden...

Postat: 24 oktober 2006, 08:48:20
av Icecap
Om det är felet eller inte men ändå:

Kod: Markera allt

void put_char(const unsigned char c)
  {
   while(SwUART_state != IDLE) {} // don't send while busy receiving or transmitting
   // Hoppla 1
   SwUART_state = TRANSMIT; // Hoppla 2
   SwUART_TXData = c; // Hoppla 3
   SwUART_TXBitCount = 0; // Hoppla 4
   // Hoppla 5
   TCNT0 = 0; // clear counter
   OCR0A = TICK_ONE; // tick one period
   TIFR |= (1 << OCF0A); // clear flag

   CLEAR_TX_PIN(); // clear TX-line... start of preamble
   ENABLE_TIMER_INTERRUPT();
}
Hoppla 1: här bör du disabla interrupt!
Hoppla 2-4: Du risikerar annars att dessa instruktioner inte fullförs innan en timer-interrupt kommer
Hoppla 5: enabla interrupt igen
Vad du risikerar är att det kan komma en interrupt mellan hoppla 2 & 3, mellan 3 & 4 eller liknande. En möjlighet kan även vara att stuva omkring på raderna:

Kod: Markera allt

   SwUART_TXData = c;
   SwUART_TXBitCount = 0;
   SwUART_state = TRANSMIT;

Re: Problem med mjukvaru UART

Postat: 24 oktober 2006, 10:04:20
av Andax

Kod: Markera allt

void InitSwUART(void) {
	TRXDDR |= (1 << TX_PIN); // set TX pin as output
	TRXDDR &= ~(1 << RX_PIN); // set RX pin as input


	// Timer0 init
	TCCR0A |= (1 << WGM01); // CTC mode
	TCCR0B |= (1 << CS01); // prescaler 8
	TIMSK |= (1 << OCIE0A); // interrupt on compare
	SET_TX_PIN(); // set TX-line to idle

	// External interrupt
	MCUCR |= (1 << ISC01); // interrupt on falling edge
	ENABLE_EXTERNAL_INTERRUPT(); // turn external interrupt on

	SwUART_state = IDLE; // set init state
}
Det är inte så enkelt som att du bör göra SET_TX_PIN() innan du gör TX till output TRXDDR |= (1 << TX_PIN)

Postat: 24 oktober 2006, 10:11:35
av FS
Nix, det hjälpte tyvärr ej... Man skulle ha en JTAG ICE mkII då hade detta varit löst för länge sedan :roll:

Postat: 24 oktober 2006, 10:21:17
av Icecap
Kollade lite mer noga....

Det verkar komma under InitSwUART, alltså verkar Andax utsago mycket vettigt.

Tips: din synk-puls.... höja den strax innan InitSwUART och sänka den strax efter, då ser du om det är i den snudd som det blir konstigt.

Postat: 24 oktober 2006, 12:04:13
av blueint
I put_char() om du sätter 'CLEAR_TX_PIN();' så får du en '0' på utgången. Och sen tar det en stund innan datat skickas. Verkar stämma med logikanalysatorn. I övrigt så håller jag med Andax ang interrupt.

Postat: 24 oktober 2006, 12:55:18
av Andax
Blueint, den CLEAR_TX_PIN() i put_char är ju startbiten '0' som skickas. Som du ser sätts ju timerinterrupten till att aktiveras en serie-bit senare (TICK_ONE) och det är då timerinterrupten börjar skicka det riktiga datat och stoppbiten '1'.

Men problemet i orginalfrågan händer ju före triggen och då har man inte anropat put_char ännu.

Re: Problem med mjukvaru UART

Postat: 24 oktober 2006, 22:57:26
av FS
Andax skrev:Det är inte så enkelt som att du bör göra SET_TX_PIN() innan du gör TX till output TRXDDR |= (1 << TX_PIN)
Klockrent, det löste alla problem! :)

Postat: 24 oktober 2006, 23:33:37
av sodjan
Konstigt,
direkt efter Andex inlägg (som du citerar) skrev du att det *inte* löste problemet...

Postat: 25 oktober 2006, 00:53:58
av Andax
Det var nog Icecaps ändringar som han hade gjort som inte löste orginalproblemet. Vi skrev ju nästan samtidigt så han missade nog min postning bara 7 minuter före hans egen.

I vilket fall som helst är det kul att det funkar nu! :)

Postat: 25 oktober 2006, 10:17:35
av FS
sodjan skrev:Konstigt,
direkt efter Andex inlägg (som du citerar) skrev du att det *inte* löste problemet...
Hehe jo det var Icecaps inlägg jag svarade på... :)