Nios® V/II Embedded Design Suite (EDS)
Support for Embedded Development Tools, Processors (SoCs and Nios® V/II processor), Embedded Development Suites (EDSs), Boot and Configuration, Operating Systems, C and C++
12603 Discussions

Clever UARTs (Interrupt Driven)

Altera_Forum
Honored Contributor II
1,398 Views

Hello all, 

 

By this time I know that everybody is tired of this topic, but I can't seem to find a clear answer anywhere. 

 

Firstly, I am not using a RTOS but am wanting to use the RX & TX of a UART in an interrupt driven fashion, not polling it. 

 

My RX portion works great using a ringbuffer to receive and read the characters out, but the problem is when I try to use the TX interrupt. Now before I get to using the ISR, setting up my UART is doing strange things. The exact code that I use is: (Note I have quite a few UARTs in my system (26). Now do you understand why I need to have them interrupt driven???) 

 

 

void uart_init(unsigned int uart_number, unsigned int baudrate) 

// disable all the interrupts for this uart 

iowr_altera_avalon_uart_control(uart_25_base, 0); 

 

// set the baud rate 

iowr_altera_avalon_uart_divisor(uart_25_base, baudrate); 

 

// enable the interrupts required for this uart 

// rx & tx interrupt  

iowr_altera_avalon_uart_control(uart_25_base, (altera_avalon_uart_control_trdy_msk | altera_avalon_uart_control_rrdy_msk)); 

 

// setup the isr reference 

alt_irq_register(uart_25_irq ,(void*) &uart_context,uart25_isr); 

 

// init the ringbuffers 

memset(&uart[uart_number].rx_ringbuffer[0],0x00,uart_rx_ringbuffer_size); 

uart[uart_number].rx_counter = 0; 

uart[uart_number].rx_rd_index = 0; 

uart[uart_number].rx_wr_index = 0; 

uart[uart_number].rx_buffer_overflow = 0; 

memset(&uart[uart_number].tx_ringbuffer[0],0x00,uart_tx_ringbuffer_size); 

uart[uart_number].tx_counter = 0; 

uart[uart_number].tx_rd_index = 0; 

uart[uart_number].tx_wr_index = 0; 

 

uart_service(uart_number);  

uart_flush(uart_number); 

} // void uart_init(unsigned int uart_number, unsigned int baudrate) 

 

 

Now at this stage what happens is, if I read the control register after every step, then all goes well up until I set the Control register flags. If I set them and I read the control register directly after that, all seems fine, but as soon as I just write anything arbitrary using printf (basically just waiting some time) and then read the control register again, I get a result of 0x0880 (expecting 0x00C0). This is where my confusion point is. http://forum.niosforum.com/work2/style_emoticons/<#EMO_DIR#>/huh.gif  

 

Can any of you Nios gods share your wisdom please? If you need anything else regarding the code please request it. I don&#39;t want to bombard this post with too much at once! 

 

Regards, 

Tyrone 

0 Kudos
7 Replies
Altera_Forum
Honored Contributor II
424 Views

Hi Tyrone, 

I&#39;m doing a similar thing, but with only 7 UARTs, and I have no problems. Your initialisation is similar to mine except in the order things are done. I&#39;d always register the ISR and setup the ring indexes BEFORE enabling the IRQs. Another thing, why are you enabling Tx IRQ here? Probably better to only enable Rx at this point. I only enable Tx after putting something in the Tx ring, the Tx ISR then disables the Tx IRQ when the ring is empty. 

I hope this is of some help. 

 

Banx.
0 Kudos
Altera_Forum
Honored Contributor II
424 Views

Hi Banx, 

 

I&#39;ve tried it but then my app just stops. If I do everything and move my ISR registration to the end, then it actually doesn&#39;t lock-up my program. The thing that I cannot understand is that if I read the control register using iord_altera_avalon_uart_control(uart_25_base), then it does as expected except for the interrupt enabling part. Whould you agree that the control register should read 0x0000 all the way and only after enabling the interrupts it should read 0x00C0 ?? (Note that I do NOT have Flow Control enabled in hardware) Now the result that I get is 0x0880!!! This shows that RRDY & RTS is enabled. http://forum.niosforum.com/work2/style_emoticons/<#EMO_DIR#>/blink.gif I can&#39;t quite figure it out. The documentation says that if Flow Control is not enabled then reading this bit will always be 0. 

I am somewhat confused as to this. 

 

As far as your suggestion about only enabling the TX interrupt once I send and then disabling once the ringbuffer is sent, I haven&#39;t tried it yet. Why would this make a difference???? Surely the interrupts are relatively independant of each other??? 

 

The other thing that I have tried is to use the open(), read() & write() functions that seem to work fine, but I need to control the RX & TX interrupts. The reason for this is that the UARTs that I am using will be communicating with smart cards according to ISO 7816. 

 

Thanx for your help thus far! 

 

Regards, 

Tyrone 

0 Kudos
Altera_Forum
Honored Contributor II
424 Views

Hi Tyrone, 

So you enable the Tx IRQ during init, this will immidiately generate an interrupt (assuming the ISR has been registered) because the UART Tx holding register is empty. Your Tx ring is also, presumably, empty so what does the ISR do? 

I&#39;m assuming the ISR writes to the status register in order to clear the source of the interrupt (otherwise you would keep re-entering the ISR), if this is the case then what is the mechanism to make interrupts happen again after you put something in the ring? 

The following works for me, maybe you can compare stuff. The first section is my init function, the second is my function to put characters into the ring and the third is the ISR. Note, I&#39;ve bypassed the INDESCRIBABLY_TEDIOUS_ALTERA_IO_DEVICE_DEFINITIONS and gone for the IORD and IOWR functions directly (and used numbers!). 

 

void serial_init(unsigned int channel, unsigned int baud) 

unsigned int i; 

 

/* inhibit all IRQ sources */ 

IOWR(UART_1_BASE, 3, 0x00); 

/* empty the Rx and Tx buffers */ 

rx_in_index_0 = rx_out_index_0 = 0; 

tx_in_index_0 = tx_out_index_0 = 0; 

rx_overrun_0 = 0; 

/* set Baud rate */ 

IOWR(UART_1_BASE, 4, baud); 

/* flush any characters sitting in the holding register */ 

i = IORD(UART_1_BASE, 0); 

i = IORD(UART_1_BASE, 0); 

/* reset most of the status register bits */ 

IOWR(UART_1_BASE, 2, 0x00); 

/* install IRQ service routine */ 

alt_irq_register(UART_1_IRQ, 0, serial_irq_0); 

/* enable irq for Rx. */ 

IOWR(UART_1_BASE, 3, 0x0080); 

 

 

void tx_char(unsigned int channel, unsigned char txch) 

unsigned int temp; 

 

while (((tx_in_index_0+1) & TXB_SIZE_MASK) == tx_out_index_0) ; 

txring_0[tx_in_index_0] = txch; 

tx_in_index_0++; 

tx_in_index_0 &= TXB_SIZE_MASK; 

temp = IORD(UART_1_BASE, 3); 

IOWR(UART_1_BASE, 3, (temp | 0x40)); 

 

 

static void serial_irq_0(void* context, alt_u32 id) 

unsigned int stat, chr, temp; 

 

/* get serial status */ 

stat = IORD(UART_1_BASE, 2); 

 

/* character Rx */ 

if (stat & 0x0080) { 

chr = IORD(UART_1_BASE, 0); 

(*rx_vect_0)(chr); 

if (((rx_in_index_0+1) & RXB_SIZE_MASK) != rx_out_index_0) { 

rxring_0[rx_in_index_0] = (unsigned char)chr; 

rx_in_index_0++; 

rx_in_index_0 &= RXB_SIZE_MASK; 

else rx_overrun_0 = 1; 

 

/* character Tx */ 

if (stat & 0x0040) { 

if (tx_in_index_0 == tx_out_index_0) { 

/* buffer is empty, turn off Tx irq */ 

temp = IORD(UART_1_BASE, 3); 

IOWR(UART_1_BASE, 3, (temp&(~0x40))); 

else { 

/* send next character */ 

IOWR(UART_1_BASE, 1, txring_0[tx_out_index_0]); 

tx_out_index_0++; 

tx_out_index_0 &= TXB_SIZE_MASK; 

 

 

Banx.
0 Kudos
Altera_Forum
Honored Contributor II
424 Views

http://forum.niosforum.com/work2/style_emoticons/<#EMO_DIR#>/sad.gif  

Hi Banx, 

 

Thanx for your code. I&#39;ve compared it to mine and it is essentially the same. I&#39;ve even gone as far as re-writing my code the same as yours and trying it. The result remains the same. As soon as I enable the TRDY interrupt, the code breaks. It just doesn&#39;t make sense!!! 

 

Just out of interest, I don&#39;t quite follow how the statement tx_in_index_0 &= txb_size_mask; works. I realise that you only use two pointers for your ring buffer. What do you determine by the statement??? (Please excuse my apparent stupidity!!!) I just use another variable as a counter. I suppose it boils down to being the same. 

 

Out of interest, my current functions are: 

 

// ============================================================ 

void uart_placechar(unsigned int uartnumber, unsigned char datacharacter) 

while (uart[uartnumber].tx_counter == uart_tx_ringbuffer_size); 

 

uart[uartnumber].tx_ringbuffer[uart[uartnumber].tx_wr_index] = datacharacter; 

 

uart[uartnumber].tx_wr_index++; 

if (uart[uartnumber].tx_wr_index == uart_tx_ringbuffer_size) 

uart[uartnumber].tx_wr_index = 0; 

 

// enable the tx (thr) interrupt 

iowr_altera_avalon_uart_control(uart_base_address_lookup(uartnumber), (iord_altera_avalon_uart_control(uart_base_address_lookup(uartnumber)) | altera_avalon_uart_control_trdy_msk)); 

} // void uart_placechar(unsigned int uartnumber, unsigned char datacharacter) 

 

 

// ============================================================ 

void uart25_isr(void* context, alt_u32 id) 

unsigned char datacharacter; 

unsigned int status, 

uart_base_address, 

uartnumber; 

 

uartnumber = 25; 

 

uart_base_address = uart_base_address_lookup(uartnumber); 

status = iord_altera_avalon_uart_status(uart_base_address); 

status &= (altera_avalon_uart_status_rrdy_msk | altera_avalon_uart_status_trdy_msk); 

 

// clear any error flags and clear interrupt 

iowr_altera_avalon_uart_status(uart_base_address, 0);  

 

// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

// rx part 

if (status & altera_avalon_uart_status_rrdy_msk) 

datacharacter = iord_altera_avalon_uart_rxdata(uart_base_address); 

 

if ((status & (altera_avalon_uart_status_fe_msk | altera_avalon_uart_status_pe_msk | altera_avalon_uart_status_roe_msk)) == 0) 

uart[uartnumber].rx_ringbuffer[uart[uartnumber].rx_wr_index] = datacharacter; 

if (++uart[uartnumber].rx_wr_index == uart_rx_ringbuffer_size) 

uart[uartnumber].rx_wr_index = 0; 

if (++uart[uartnumber].rx_counter == uart_rx_ringbuffer_size) 

uart[uartnumber].rx_counter = 0; 

uart[uartnumber].rx_buffer_overflow++; 

} // if (++uart[uartnumber].rx_counter == uart_rx_ring_buffer_size) 

} // if ((status & (altera_avalon_uart_status_fe_msk | altera_avalon_uart_status_pe_msk | altera_avalon_uart_status_roe_msk)) == 0) 

} // if (status & altera_avalon_uart_status_rrdy_msk) 

 

// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

// tx part 

if (status & altera_avalon_uart_status_trdy_msk) 

if (uart[uartnumber].tx_counter == 0) 

iowr_altera_avalon_uart_control(uart_base_address, (iord_altera_avalon_uart_control(uart_base_address) | ~altera_avalon_uart_control_trdy_msk)); 

} // if (uart[uartnumber].tx_rd_index == uart[uartnumber].tx_wr_index) 

else 

iowr_altera_avalon_uart_txdata(uart_base_address, uart[uartnumber].tx_ringbuffer[uart[uartnumber].tx_rd_index]); 

 

uart[uartnumber].tx_counter--; 

 

uart[uartnumber].tx_rd_index++; 

if (uart[uartnumber].tx_rd_index == uart_tx_ringbuffer_size) 

uart[uartnumber].tx_rd_index = 0; 

} // else 

} // if (status & altera_avalon_uart_status_trdy_msk) 

} // void uart25_isr(void* context, alt_u32 id) 

 

Note: The function UART_Base_Address_Lookup(UARTNumber) just returns the base for the current UART. (In this way I can use the same code for all my UARTs, barring the ISRs, those are uniquely named but the body of the function remains the same. The only way I differentiate is by the "hardcoded" variable UARTNumber) 

 

Also, I tried to se if my other UARTs are doing the same and they all are. 

 

Regards, 

Tyrone 

0 Kudos
Altera_Forum
Honored Contributor II
424 Views

Hi tyrone, 

My ring buffer sizes are powers of 2, usually 512 bytes. I have two variables that tell me where the &#39;in&#39; and &#39;out&#39; positions are. When characters are inserted or removed these indexes are incremented, I then &= them with a mask that keeps them in range and takes care of the wrap around. For a 512 byte buffer the mask is 0x1ff. Quick and simple. 

Banx.
0 Kudos
Altera_Forum
Honored Contributor II
424 Views

Hey Gizmo did you get your code working yet?  

I know its an old topic but i need a code like yours.. 

 

can you maybe give me a little explanation of what your doing in this code? 

 

// Init the ringbuffers 

memset(&UART[UART_Number].RX_RingBuffer[0],0x00,UART_RX_RINGBUFFER_SIZE); 

UART[UART_Number].Rx_Counter = 0; 

UART[UART_Number].Rx_Rd_Index = 0; 

UART[UART_Number].Rx_Wr_Index = 0; 

UART[UART_Number].Rx_Buffer_Overflow = 0; 

memset(&UART[UART_Number].TX_RingBuffer[0],0x00,UART_TX_RINGBUFFER_SIZE); 

UART[UART_Number].Tx_Counter = 0; 

UART[UART_Number].Tx_Rd_Index = 0; 

UART[UART_Number].Tx_Wr_Index = 0; 

 

i kinda lost it in this part of code.. I&#39;m just a newbie so i would apreciate the help 

 

when i try to build this i got a few errors and i got all of them away except the ones related to this piece of code..? is it something like a struct or a union?  

 

kind regards
0 Kudos
Altera_Forum
Honored Contributor II
424 Views

TO_BE_DONE

0 Kudos
Reply