USART interrupt in boot mode not working in XMega128c3

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

I try to make a bootloader, with interrupt driven USART.

DRE interrupt is working.

But TXD and RXD interrupts are never activated.

 

Was anyone successfull with boot mode USART with interrupts?

 

The strange thing is that i can not write USARTD.CTRLA register.

No matter what value I set, it remains 0x01;

 

This the relevant init section:

 //usart init
  PORTD.DIRSET = PIN3_bm; /* PD3 (TXD0) as output. */
  PORTD.DIRCLR = PIN2_bm; /* PD2 (RXD0) as input. */
  PORTD.PIN2CTRL = 0b00011000;  // ha ez nincs benne akkor ha egyszerre kuld és fogad isi, akkor a fogadas byte ok elszarodnak

  // bsel = (I/O clock frequency) / (16*Baudrate) - 1
  // Clock=14745600
  // 95 : 9600
  // 7 : 115200
  usart_init(&OTAU_data,&USARTD0, 7);

  //usart enable interrupts (this is duplaceted init, still not working, USARTD0.CTRLA remains 0x01 no matter what.
  USARTD0.CTRLA = 21; //0b00010101 = 21 

  //enable interrupts
  CCP = 0xd8; //ENABLE_CHANGE_PROTECTION_REGISTER
  PMIC.CTRL = 0x47;

  sei();

where usart_init() looks like this:

void usart_init(USART_data_t * usart_data, USART_t * usart, unsigned int baudrate)
{
  USART_InterruptDriver_Initialize(usart_data, usart, USART_DREINTLVL_LO_gc);       /* Use USARTD0 and initialize buffers. */
  USART_Format_Set(usart_data->usart, USART_CHSIZE_8BIT_gc,USART_PMODE_DISABLED_gc, false); /* USARTC0, 8 Data bits, No Parity, 1 Stop bit. */

  USART_RxdInterruptLevel_Set(usart_data->usart, USART_RXCINTLVL_LO_gc);            /* Enable RXC interrupt. */
  //#define USART_RxdInterruptLevel_Set(_usart, _rxdIntLevel)   ((_usart)->CTRLA = ((_usart)->CTRLA & ~USART_RXCINTLVL_gm) | _rxdIntLevel)

  USART_TxdInterruptLevel_Set(usart_data->usart, USART_TXCINTLVL_LO_gc);            /* Enable TXC interrupt. */
  //#define USART_TxdInterruptLevel_Set(_usart, _txdIntLevel)   (_usart)->CTRLA = ((_usart)->CTRLA & ~USART_TXCINTLVL_gm) | _txdIntLevel

  USART_DreInterruptLevel_Set(usart_data->usart, USART_DREINTLVL_LO_gc);            /* Enable TXC interrupt. */
  //#define USART_DreInterruptLevel_Set(_usart, _dreIntLevel)   (_usart)->CTRLA = ((_usart)->CTRLA & ~USART_DREINTLVL_gm) | _dreIntLevel

  //these lines are the same as USARTD0.CTRLA = 21; 0b00010101 = 21

  if (baudrate & 0x8000)
  {
    USART_CLK2X_Enable(usart);        // Double Speed Modus
    baudrate &= ~0x8000;
    USART_Baudrate_Set(usart, baudrate , 0);
  }
  else
  {
    USART_Baudrate_Set(usart, baudrate , 0);
  }

  USART_Rx_Enable(usart_data->usart); // Enable RX
  //#define USART_Rx_Enable(_usart)     ((_usart)->CTRLB |= USART_RXEN_bm)    // Enable USART receiver.
  USART_Tx_Enable(usart_data->usart); // Enable TX
  //#define USART_Tx_Enable(_usart)     ((_usart)->CTRLB |= USART_TXEN_bm)    // Enable USART transmitter.

}

 

Any help would be highly anticipated!

This topic has a solution.
Last Edited: Thu. Jul 11, 2019 - 03:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jsaak wrote:
I try to make a bootloader, with interrupt driven USART.
Almost certainly your first mistake! There's only one rule about bootloaders and that is (because they are the only bit of the entire code you cannot replace) they must be faultless on day one. You achieve faultless by making it as simple as possible. As soon as you introduced multiple paths of execution you increase complexity 10 fold.

 

Anyway, when we've seen threads about vectors in bootloaders in the past it's always been about IVSEL. Now I don't know what the Xmega equivalent of IVSEL is but presumably the Xmega do have some mechanism to say "don't use the vector table down near 0x0000 but this one up here in the bootloader code"? That may be what you are missing.

 

Oh and don't do this:

  CCP = 0xd8; //ENABLE_CHANGE_PROTECTION_REGISTER
  PMIC.CTRL = 0x47;

You can't be sure the PMIC write will occur within 4 cycles of the CCP write. This is why xmega.h has _PROTECTED_WRITE() which is done in Asm to guarantee this timing constraint.

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

I do not think it is a mistake.

The bootloader will be on a multi master RS485 bus with 115200 baud rate. Timing is critical, I do not think i can do it without interrupts.

(If you do not write any code at all that would be the best solution, since there will be no bugs, and will be stable until the end of times, but still we have a problem to solve, and this seems to be the only way)

 

We have IVSEL on this platform. And it is set. (PMIC.CTRL |= 0x40)

Additionally we enabled all interrupt levels with (PMIC.CTRL |= 0x07)

And two other interrupts are working. (TCC1_CCA_vect and USARTD0_DRE_vect)

 

The problem is, that i can not enable the other two interrupts(USARTD0_RXC_vect, USARTD0_TXC_vect), the register to do that ( USARTD0.CTRLA ) appears to be READONLY in boot mode, and R/W in application mode.

 

You may be right about the CHANGE_PROTECTION_REGISTER, i copied it from atmel bootloader source, they use this, and it seems to be working. (https://www.microchip.com/wwwApp...)

(the optimizer did not reorder it thankfully)

 

 

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

Found it.

 

It was me who set USARTD0.CTRLA to a wrong value in usart_putc(), and i was debugging using usart_putc(). So Interrupts do indeed work in boot mode.