I am using a KL15 device for my project. I am using the TDRE and RDRF interrupts for my UART. The RDRF interrupt is always turned on so that I dont miss any incoming byte. The TDRE interrupt is turned on only when I have some data in my UART buffer that needs to be sent. This is what i check inside the UART ISR
void UART1_IRQHandler()
{
/* A variable to capture the Data in the RX buffer*/
uint8_t RcvdByte;
/* Check for the source of the interrupt */
/* Start with checking the TDRE flag */
if(UART1_S1 & UART_S1_TDRE_MASK)
{
/* Interrupt triggered due to TX buffer being empty */
// Call a function to send some data
}
/* Then check the RDRF flag */
if (UART1_S1 & UART_S1_RDRF_MASK)
{
/* Interrupt triggered due to Rx Buffer full */
RcvdByte = UART1_D; // Interrupt is cleared here, so no re-entrance will occur
// Call the function to collect the data
}
}
When my device boots up, the RDRF interrupt is on and the TDRE interrupt is off (because there is no data to send). So now, when i receive a byte the ISR runs and finds that the TDRE flag is set (default value after reset) although it wasnt the source of the interrupt. So the function assigned the TDRE flag gets called unnecessarily. How am I able to pin point the source of the interrupt in such cases?
Hi Safwat,
I think when you enable TDRE interrupt, there should be a TX buffer ready, right? if so, then you may implement the ISR like that:
uin8 tx_buffer[128];
uint8* ptr = tx_buffer;
uint32 tx_length = 0;
Hope that helps,
Kan
Hi Kan, thanks for your reply.
Actually what you said is almost similar to what is currently going on in my firmware. Every time the "if condition" on line 7 is validated as true, the ISR makes a function call to a block of code that transmits bytes from the TX buffer. If the buffer is empty, it has no effect and the function call returns without doing anything.
I just don't like the way this implementation causes unnecessary function calls even when the interrupt is not originated by the TDRE flag in the first place. The TDRE flag can very likely be true even when the interrupt is originated by RDRF or something else.
For simple code, like in your sample, this extra overhead is nothing much to worry about. But I am running a RTOS and each interrupt gives a semaphore. So for very high speed UART data transfer (460800 Baud rate or above), this extra function call+semaphore operation can take away some much needed performance.
Hi Safwat,
I understand your concerns , so I think for your case, send character in UART ISR is not a good solution, you may use DMA to do that. In other words, ISR acts as RX ISR, and when code start to send , enable the TX DMA. Please also note enable CS(Cycle steal) in that case, which Forces a single read/write transfer per DMA request.
Best Regards,
Kan Li
Technical Information & Commercial Support AP (Kinetis/ColdFire)
Freescale Semiconductor (China) Limited Shanghai Branch Office
I am trying to use DMA for transferring data from UART1 to UART0 on the KL15. I am using DMA channel 1. My code for initializing the DMA is below.
void DMA_Init()
{
/* Enable the clock to DMA MUX and DMA */
SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;
SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;
// Disable the DMA0 channel
DMAMUX0_CHCFG1 = 0x00;
// Clear pending errors or the done bit for channel 1
if (((DMA_DSR_BCR1 & DMA_DSR_BCR_DONE_MASK) == DMA_DSR_BCR_DONE_MASK)
| ((DMA_DSR_BCR1 & DMA_DSR_BCR_BES_MASK) == DMA_DSR_BCR_BES_MASK)
| ((DMA_DSR_BCR1 & DMA_DSR_BCR_BED_MASK) == DMA_DSR_BCR_BED_MASK)
| ((DMA_DSR_BCR1 & DMA_DSR_BCR_CE_MASK) == DMA_DSR_BCR_CE_MASK))
DMA_DSR_BCR1 |= DMA_DSR_BCR_DONE_MASK;
// Set Source Address for DMA (UART1_D register)
DMA_SAR1 = 0x4006B007;
// Clear the DCR bits
DMA_DCR1 &= ~(DMA_DCR_SSIZE_MASK | DMA_DCR_CS_MASK);//0x0202;
// Set the DMA configuration
DMA_DCR1 |= (DMA_DCR_SSIZE(1)
| DMA_DCR_DSIZE(1)
| DMA_DCR_CS_MASK
| DMA_DCR_ERQ_MASK
| DMA_DCR_EADREQ_MASK
);
// Set DMA byte counter
DMA_DSR_BCR1 = DMA_DSR_BCR_BCR(3);
// Set Destination Address (UART0_D)
DMA_DAR1 = 0x4006A007;
// Enable UART1 operation in the DMA source slot
DMAMUX0_CHCFG1 = 0x4;
// Enable the DMA MUX
DMAMUX0_CHCFG1 |= DMAMUX_CHCFG_ENBL_MASK;
}
This setup is passing only the first 3 bytes of data received at UART1. Which makes sense as I set the BCR to 3. My question is, is there any way to configure the DMA so that it is not dependent upon the counter. I want the DMA to transfer incoming byte at UART1 to UART0 as long as DMA request bit in UART1 is enabled. Can it be configured to work that way?
Okay I figured out I can use the DMA transfer complete interrupt to reload the BCR. One interrupt for every 0xFFFFF bytes is obviously acceptable. Sorry for asking without thinking much.
Yes I think I have to change my code to use Tx DMA. Initially I started off with slower baud rate so things where not so bad. But unfortunately the requirements of my application increased. Hopefully I can get DMA working without much issue.
Thanks for providing the direction Kan.