Problem writing to USART

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

I've been working with an atmega328p using WinAVR and I've run into an odd problem. I have no problems reading the USART, I'm communicating to a BT peripheral and the messages are correct. When it comes to transmitting the code I wrote seems to "get stuck". Even if I call the my writeUart()

 

function just once, it keeps writing to the USART non-stop. At first I though I might have some odd TX interrupt related problem, but after looking through the datasheets nothing jumps at me. I've been reducing the code to isolate the problem and the code below is just that.

 

#define BAUDRATE 76800
#define BAUD_PRESCALER (((F_CPU / (BAUDRATE * 8UL))) - 1)

void initUART()
{
	UBRR0H = (BAUD_PRESCALER>>8);
	UBRR0L = (BAUD_PRESCALER);
	UCSR0B &= 0b10011000; // sets char size and disables 9 bit mode
	UCSR0B |= 0b10011000; // sets char size and disables 9 bit mode
	UCSR0C &= 0b00000110; // Disable parity bit, Enable asynchronous mode, select 1 stop bit, disable clock mode
	UCSR0C |= 0b00000110; // SET char size
	UCSR0A &= 0b11111110; // Disable multi comm mode
	UCSR0A |= 0b00000010; // Set double speed bit
}

void writeUART(unsigned char data)
{
	while (!( UCSR0A & _BV(UDRE0)));
	UDR0 = (unsigned char) data; // If I comment this line out I have no problems
}

int main(void) {
	initUART();
	initIO();
	initInterrupts();
	dbg_tx_init(); // Debug port

	writeUART('c'); // Suspect function
   while (1) {
		delay_1s();
	    PORTD ^= _BV(PORTD7);

   }
}

After a bit of experimenting with my oscilloscope I can see that removing the line which writes to UDR0 removes the problem (obviously not a solution), so I was curious as to why writing one char to UDR0 would keep calling it?

I've tried using other peoples register settings and using TXC0 instead of UDRE0, but nothing seems to resolve the problem.. I'm starting to suspect it maybe a hardware problem, but it still seems unlikely.

Do I have to clear the register before writing, disable RX interrupts or do something else I may have forgotten?

 

Thank you ahead of time for your help! 

This topic has a solution.
Last Edited: Mon. Oct 30, 2017 - 05:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Flabelle wrote:
I've been reducing the code to isolate the problem and the code below is just that.
You are enabling the receive interrupt, but you don't have an ISR for it.

Stefan Ernst

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

You are enabling the receive interrupt, but you don't have an ISR for it.

No, I just didn't post it, here it is. I thought it was unrelated, but if it could be useful.

 

ISR(USART_RX_vect)
{
   char temp = UDR0;             //read UART register into value
   writeRingBuffer(&recBuf,&temp); 

}
Last Edited: Mon. Oct 30, 2017 - 04:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Go on.   Your programming style is guaranteed to introduce errors.

 

I suggest that you follow the convention of using the official BIT_NAMEs.

I had no idea what your magic bit patterns mean.

 

If you had written

        UCSR0B = (1<<RXCIE0)|(1<<TXEN0)|(1<<RXEN0);   //enable RX interrupt without an ISR()
	

It would have been fairly obvious that the AVR will simply Reset every time that an RX interrupt occurs.

 

It is much clearer if you use = statements instead of |= and &=

 

David.

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

I should have taken the time to do that, I kind of got caught up in figuring what setting was off rather then making it clear. I have rewritten it as you suggested, but still have the same problem.

 

void initUART()
{
	UBRR0H = (BAUD_PRESCALER>>8);
	UBRR0L = (BAUD_PRESCALER);
	UCSR0A = _BV(U2X0);
	UCSR0B = _BV(RXEN0) | _BV(TXEN0) |  _BV(RXCIE0);
	UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
	
}

 

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

How about showing us the code for these functions:

 

initIO();
initInterrupts();
dbg_tx_init();
writeRingBuffer();

 

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

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

Greg_Muth wrote:

How about showing us the code for these functions:

 

initIO();
initInterrupts();
dbg_tx_init();
writeRingBuffer();

 

Below is the code to the interrupts, IO and the ring buffer. Again the registers are set with masks, I'll use the bit values and edit this post once it's done.

 

void initInterrupts()
{
	SREG |= 0b10000000; // Sets GEI
	UCSR0B |= 0b11000000; // Sets rx and tx interupt enable
	EICRA |= 0b00000111; // Detects all changes on INT1 & Rising Edge on INT0
	EICRA &= 0b11110111; // Ditto
	EIMSK |= 0b00000011; // Enables both INT0&INT1
	EIFR |= 0b00000011; // Clears the INT0 and INT1 flags
	PCICR |= 0b00000100; // Enables PCINT[23:16]
	PCMSK2 |= 0b01000000; // Sets the interupt on PD6
  // CONSIDER CLEARING THE RX AND TX FLAGS AFTER (READING THE BUFFER AND CLEARING THE TX FLAG)
}

void initIO()
{
	DDRB |= _BV(DDB5); // Consider keeping if it doesn't impact programmer, currently used to debug
	DDRB |= _BV(DDB2); // To be removed later, currently used to debug
	DDRB |= _BV(DDB1); // Ready state light
	DDRD |= _BV(DDD7); // Carrier detect light
	DDRD |= _BV(DDD4); // PTT
	DDRD &= ~_BV(DDD2); // User input
	DDRD &= ~_BV(DDD3); // Ready state input
	DDRD &= ~_BV(DDD6); // CD state input
}

typedef struct
{
  size_t writePos;
  size_t readPos;
  char recBuf[MAX_RING_BUFFER_SIZE];
}ringBuffer_t;

char peekRingBuffer(ringBuffer_t *ringBuf,size_t pos)
{
  return ringBuf->recBuf[(ringBuf->readPos+pos)%MAX_RING_BUFFER_SIZE];
}

bool readRingBuffer(ringBuffer_t *ringBuf,char * bufVal)
{
  if(ringBufferIsEmpty(ringBuf))
  {
	return false;
  }
  else
  {
    *bufVal = ringBuf->recBuf[ringBuf->readPos];
    ringBuf->readPos = (ringBuf->readPos + 1) % MAX_RING_BUFFER_SIZE;
	return true;
  }
}

void writeRingBuffer(ringBuffer_t *ringBuf,char *bufVal)
{
    ringBuf->recBuf[ringBuf->writePos] = *bufVal;
    ringBuf->writePos = (ringBuf->writePos + 1) % MAX_RING_BUFFER_SIZE;

	if(ringBuf->writePos == ringBuf->readPos)
    {
		ringBuf->readPos = (ringBuf->readPos + 1) % MAX_RING_BUFFER_SIZE;
	}

}

void initRingBuffer(ringBuffer_t *ringBuf,bool flushBuffer)
{
  ringBuf->writePos = 0;
  ringBuf->readPos = 0;
  if(flushBuffer)
    memset(ringBuf->recBuf,'\0',MAX_RING_BUFFER_SIZE);
}

 

Last Edited: Mon. Oct 30, 2017 - 05:05 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just found my mistake... I enabled the TX interrupt in this second function and forgot to remove it. I forgot that I also put that there. Thank you guys for all your help! I'll use Bit values in the future, lesson learned.

Last Edited: Mon. Oct 30, 2017 - 05:10 PM