UART can receive, but not send

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

I'm working with ATxmega16D4 and I'm configuring the USART (actually UART) interface on port C.

The purpose is to transmit/receive the data on RS232 interface (currently using a converter to USB port of my PC).

 

  • The USARTC0 is setup up as Asynchronous, interrupt-driven.
  • The datarate is set to 115200 baud (but the problem exists also on 9600), double speed
  • 8 bit, no parity, 1 stop bit

 

I can receive the data from PC to the micro, but I cannot send it.

 

The code is partly taken from Atmel USART driver:

void uart_init()
{
	// setting TX/RX ports directions
	PORTC_set_pin_dir(2, PORT_DIR_IN);
	PORTC_set_pin_output_pull_mode(2, PORT_CONFIGURATION_TOTEM);
	
	PORTC_set_pin_dir(3, PORT_DIR_OUT);
	PORTC_set_pin_level(3, false);

        int8_t   exp;
        uint32_t div;
        uint32_t limit;
        uint32_t ratio;
        uint32_t min_rate;
        uint32_t max_rate;
    
        uint32_t cpu_hz = F_CPU;
        uint32_t baud = UART_BAUDRATE;

	<--- CODE REMOVED FOR CLEANUP, it sets "div" and "exp" -->

	USARTC0.BAUDCTRLB = (uint8_t)(((div >> 8) & 0X0F) | (exp << 4));
	USARTC0.BAUDCTRLA = (uint8_t)div;	

	// Low Level interrupts
	USARTC0.CTRLA =   USART_RXCINTLVL_LO_gc
	                | USART_TXCINTLVL_LO_gc
	                | USART_DREINTLVL_LO_gc;
	PMIC.CTRL |= PMIC_LOLVLEN_bm;

	USARTC0.CTRLC = USART_PMODE_DISABLED_gc		        // No Parity
				| 0 << USART_SBMODE_bp		// Stop Bit Mode: disabled
				| USART_CHSIZE_8BIT_gc		// Character size: 8 bit
				| 0 << USART_CHSIZE2_bp		// SPI Master Mode, Data Order: disabled
				| 1 << USART_CHSIZE1_bp		// SPI Master Mode, Clock Phase: enabled
				| USART_CMODE_ASYNCHRONOUS_gc;	// Asynchronous Mode

	USARTC0.CTRLB =   1 << USART_CLK2X_bp // Double transmission speed: enabled
	                | 0 << USART_MPCM_bp  // Multi-processor Communication Mode: disabled
	                | 1 << USART_RXEN_bp  // Receiver Enable: enabled
	                | 1 << USART_TXEN_bp; // Transmitter Enable: enabled
}

The main:

 

ISR(USARTC0_RXC_vect)
{
	// This works! I can read the received data
	uint8_t received = USARTC0.DATA;
}

ISR(USARTC0_TXC_vect)
{
        // This not! The interrupt is never called
}

ISR(USARTC0_DRE_vect)
{
	// This works as expected
}

int main(void)
{
        <--- initializes system.... --->

        uart_init(); // ...and the uart
	ENABLE_INTERRUPTS();
	
	uint8_t send = 0;
        while (1) 
        {
		_delay_ms(100);

                // wait for sending...
		while (!(USARTC0.STATUS & USART_DREIF_bm));
		USARTC0.DATA = (send++);
        }
}

 

The main just loops sending to my PC the byte, increased.

If I send 1 char from my PC, I correctly receive it in the ISR so I believe everything is ok at my host application.

Instead, setting "USARTC0.DATA = (send++);" does not seem to send anything. In depth, if I debug it using the I/O tool on Atmel Studio, the value of DATA does not change right after that line (or maybe is it expected and the data is immediately moved to the TX register?).

 

Can you help me understanding? Thanks

 

This topic has a solution.
Last Edited: Mon. Nov 23, 2020 - 03:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A quick google search for AVR APP NOTE ATxmega usart came up with this example interrupt driven usart code: https://lost-contact.mit.edu/afs...

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Do not enable interrupts that you do not use.
If you use interrupts, do not use _delay_ms.

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

kabasan wrote:

Do not enable interrupts that you do not use.
If you use interrupts, do not use _delay_ms.

 

Ok for delay, I though it was not a problem. Anyway it does not solve if I remove it.

For interrupts... what should I filter? RX/TX/DRE seems ok for this.

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

ki0bk wrote:

A quick google search for AVR APP NOTE ATxmega usart came up with this example interrupt driven usart code: https://lost-contact.mit.edu/afs...

 

Jim

 

 

My application is partly based on it, though I don't use buffers for now. I just send one single byte at once. May this be an issue?

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

AN_8049 - AVR1307: Using the XMEGA USART

  

This application note describes how to set up and use the USART in asynchronous mode in the XMEGA™. C code drivers and examples are included for both polled and interrupt controlled USART applications.

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:

AN_8049 - AVR1307: Using the XMEGA USART

  

This application note describes how to set up and use the USART in asynchronous mode in the XMEGA™. C code drivers and examples are included for both polled and interrupt controlled USART applications.

 

Same answer as for @ki0bk. You both are referring to the same example ;)

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

misterbirra wrote:
My application is partly based on it, though I don't use buffers for now. I just send one single byte at once. May this be an issue?

For single byte TX/RX, then you don't need interrupts, simple polling will work.

However, in most of my apps, I use interrupt RX buffering, and polled TX, but I haven't used xmega, so I don't have any examples for you, but another Freak may.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

ki0bk wrote:

misterbirra wrote:
My application is partly based on it, though I don't use buffers for now. I just send one single byte at once. May this be an issue?

For single byte TX/RX, then you don't need interrupts, simple polling will work.

However, in most of my apps, I use interrupt RX buffering, and polled TX, but I haven't used xmega, so I don't have any examples for you, but another Freak may.

 

Jim

 

 

My next step will be to use buffers, this is just a starting point... and I already have an issue :)

Thanks anyway

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

misterbirra wrote:

ISR(USARTC0_TXC_vect)
{
        // This not! The interrupt is never called
}

I fear you have missed the purpose of transmission interrupts! "TXC" means "Transmission Complete". The idea of using TX interrupts is this. Say you have a number of bytes to send ("Hello"). You put them in a buffer (array) somewhere. Completely outside of any interrupt you first enable TXC interrupts then you pick up and put the first character ('H') into the Uart data register. The UART starts to send it. When it completes it raise the TXC flag to say "I've done the one you asked". Then in the TXC interrupt handler (that now fires if it were enabled) you move along the array and pick up the next character to be sent (so this time 'e') and put that in the UATY data registers inside the ISR(). The process repeats. The 'e' is transmitted and then TXC is raised so the interrupt is re-entered when that one is done. So you move on again and send the first 'l' from the buffer. And so it goes on. Eventually you will send the 'o' at the end. When the TXC happens for that you go to the buffer and realise "oh, look, I've used up all the buffer". So now you disable the TXC interrupt.

 

When you are ready to send " World" then just repeat the entire process. So the only action in main() triggered code is to put the first character from the buffer in UDR and enable the TXC and then leave it to it.

 

To be honest I rarely see the point in using interrupts for transmission. Usually you have a character to send and are happy simply to put it straight into UDR. If one is already in the process of being sent you might sit and wait in a loop for a flag to say "the register is ready to take another one". The place you really do need interrupts is on the receive side. Unlike transmission where "you are in charge" and you can choose when to do it, for reception you have no idea when characters might start to arrive and so your program may be in the middle of something else and won't immediately notice. If it carried on with the current work for too long there may be a flood of arrivals and you miss some of them. So in this case you DO employ an interrupt. All it has o do is that each time the flag triggers to say "another one arrived" you just whip it out of UDR and straight into some buffer (usually a "ring"/"circular" buffer) and then return from the interrupt. The characters that arrive are then all in the "holding tank" and you can pick them out and use them later when your program then has the time available - all that matters is that you buffer has sufficient capacity that it can cope with at least as many bytes as the longest piece of work in your program might prevent you getting back to process.

 

So maybe start with TX sync, Rx async for starters and only consider making the Tx asynchronous (interrupts not polling) when you have the base system all working and find that you do need a "fire and forget" system on sending groups of bytes.

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

misterbirra wrote:
The purpose is to transmit/receive the data on RS232 interface (currently using a converter to USB port of my PC).

misterbirra wrote:
The main just loops sending to my PC the byte, increased.

Note the first 32 or so characters sent are non-printable ascii characters, so will not show on a terminal program!

So if you were expecting to see... 0123456789...   that will not happen until the value is 33 decimal and greater, starting with characters...  !@#$%^&*()...

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

As already mentioned, disable interrupts, especially USART_DRE

 

misterbirra wrote:

 

ISR(USARTC0_DRE_vect)
{
	// This works as expected
}

 

 

 

I don't think so, unless you explain the way it should work. This interrupt is triggered all the time, while DRE flag is set. Since 3 bytes are not sent out, you sit in this handler forever. 

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

By the way, initialization of CTRLC register is somewhat strange. You try to setup ASYNCHRONOUS and SPI at the same time.

Just leave

	USARTC0.CTRLC = USART_CHSIZE_8BIT_gc; // Character size: 8 bit        
// all other values are equal to 0
//                    | USART_PMODE_DISABLED_gc		        // No Parity
//		      | 0 << USART_SBMODE_bp		// Stop Bit Mode: disabled
//		      | USART_CMODE_ASYNCHRONOUS_gc;	// Asynchronous Mode
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm with you saying that, for my specific purpose, the TX interrupt might be not necessary.

I've removed the ISR for TX and DRE, and setup the CTRLA like this:

// Low Level interrupts
USARTC0.CTRLA = USART_RXCINTLVL_LO_gc;
PMIC.CTRL |= PMIC_LOLVLEN_bm;

But, again, no improvements and I still cannot see the data coming to my PC.

To @ki0bk, I'm using HTerm that lets you see every raw byte received, not only readables.

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

I use the following initialization

// 115200 @ 32MHz
#define BSEL   1047
#define BSCALE ( -6)
	USARTC0.BAUDCTRLA = (BSEL & 0xFF);
	USARTC0.BAUDCTRLB = (BSCALE << USART_BSCALE_gp) | (BSEL >> 8);

	USARTC0.CTRLA     = 0;//USART_RXCINTLVL_MED_gc;
	USARTC0.CTRLC     = USART_CHSIZE_8BIT_gc;

	USARTC0.CTRLB     = USART_TXEN_bm;// | USART_RXEN_bm;// | USART_CLK2X_bm;

	while(1)
	{
		_delay_us(300);
		uart_send_byte(0x55);
	}

 

Last Edited: Fri. Nov 20, 2020 - 03:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mccrtay wrote:

By the way, initialization of CTRLC register is somewhat strange. You try to setup ASYNCHRONOUS and SPI at the same time.

Just leave

	USARTC0.CTRLC = USART_CHSIZE_8BIT_gc; // Character size: 8 bit        
// all other values are equal to 0
//                    | USART_PMODE_DISABLED_gc		        // No Parity
//		      | 0 << USART_SBMODE_bp		// Stop Bit Mode: disabled
//		      | USART_CMODE_ASYNCHRONOUS_gc;	// Asynchronous Mode

 

No change after your suggestion, I don't think is related to the issue

Last Edited: Fri. Nov 20, 2020 - 03:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mccrtay wrote:

I use the following initialization

// 115200 @ 32MHz
#define BSEL   1047
#define BSCALE ( -6)
	USARTC0.BAUDCTRLA = (BSEL & 0xFF);
	USARTC0.BAUDCTRLB = (BSCALE << USART_BSCALE_gp) | (BSEL >> 8);

	USARTC0.CTRLA     = 0;//USART_RXCINTLVL_MED_gc;
	USARTC0.CTRLC     = USART_CHSIZE_8BIT_gc;

	USARTC0.CTRLB     = USART_TXEN_bm;// | USART_RXEN_bm;// | USART_CLK2X_bm;

	while(1)
	{
		_delay_us(300);
		uart_send_byte(0x55);
	}

 

Please share implementation of uart_send_byte()

Last Edited: Fri. Nov 20, 2020 - 04:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define USART USARTC0

void uart_send_byte(uint8_t data)
{
	while(!(USART.STATUS & USART_DREIF_bm));

	USART.DATA = data;
}

 

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

STOP working on the AVR ...how do you know the PC can  get anything??  Have you actually tried  it?  Did you connect the the PC TX & RX lines together down by the AVR (with the AVR disconnected)  This loopback must travel through any serial adapters you are using.

 

Don't mess with the avr until you know the PC loopback is working.

 

 

Or at the minimum, have you verified your AVR TX piin is sending out pulses on a scope or logic analyzer?  

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Fri. Nov 20, 2020 - 04:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mccrtay wrote:

#define USART USARTC0

void uart_send_byte(uint8_t data)
{
	while(!(USART.STATUS & USART_DREIF_bm));

	USART.DATA = data;
}

 

 

No difference compared to mine :)

I will make some electrical tests next week... I'm starting to think that the HW may be part of the issue
EDIT: or SW at host-side as suggested by @avrcandies

Last Edited: Fri. Nov 20, 2020 - 04:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

See # 19  quit messing with the AVR until verified

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

avrcandies wrote:

See # 19  quit messing with the AVR until verified

Or just tie pins PC2,PC3 together. Most probably, you will receive your data back

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

Or just tie pins PC2,PC3 together. Most probably, you will receive your data back

No, that is the wrong step

He knows data is coming out of the PC....so it needs looped back to the PCrx through any adapters & test if the PC terminal shows the same  in vs out

 

You can loopback the AVR too  (that will NOT verify the PCrx, or anything about the PC is working, since it is then disconnected from the AVR. Make sure any adapters to the PC are disconnected, so they don't interfere with the AVR loopback)  

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

To everyone, my code was ok, but I missed to enable an additional port (PA0) that controls the enabling/disabling of the TX side. This was specific of the schematic the micro is mounted on.

Now everything works, thanks all.