µTasker Forum

µTasker Forum => NXPTM M522XX, KINETIS and i.MX RT => Topic started by: thamanjd on January 28, 2010, 01:29:32 AM

Title: MCF52235 UARTS and UART Interrupts -> no transmit complete int?
Post by: thamanjd on January 28, 2010, 01:29:32 AM
I was going to experiment using MCF52235 UARTs with RS485 but have just discovered that the only transmit interrupt available is effectively Transmit Data Register Empty (TXRDY).

But for RS485 you need to know when it is safe to switch the transmit enable bit on the RS485 driver. That is, you need to know when the byte has completely finished transmitting, stop bit and all. It would have been nice for there to be a transmit complete (TXEMP) interrupt but this seems to be lacking in the UART module. I considered setting some routine/timer to check the TXEMP flag(after reception of TXRDY interrupt) but this solution isn't fast or elegant.

Is it just me or is the UART module in some (all?) of the MCF522xx devices a bit clunky?

ideas? at the moment i'm thinking i should look at some other micros but 128K program plus 128k flash storage is attractive in devices like the MCF52235.

John Dowdell
Title: Re: MCF52235 UARTS and UART Interrupts -> no transmit complete int?
Post by: mark on January 28, 2010, 02:33:08 AM
Hi John

You are right about the difficulty getting a trigger for RS485. See the following for how it is done: http://www.utasker.com/docs/uTasker/uTaskerUART.PDF (chapter 8 ). This does indeed use a timer as you suggested but, as long as you have a DMA timer available for each UART you need, it does work well. The timing depending on Baud and whether working in interrupt or DMA driven mode (plus stop bits).

Here is a table used by the MODBUS extension module to set the correct timer value for the M522XX (note that for each Baud there are values in 7 bit and 8 bit mode - because these use 1 and 2 stop bits - and interrupt/DMA):

Code: [Select]

#define REFERENCE_BIT_TIME (float)8.681                                  // bit period at 115200 Baud in us

#define RTS_BIT_DELAY_8BIT_INT (float)10.9                           // the last transmit character interrupt arrives this many bit periods before the RTS should be negated
#define RTS_BIT_DELAY_7BIT_INT (float)9.9
#define RTS_BIT_DELAY_8BIT_DMA (float)21.7
#define RTS_BIT_DELAY_7BIT_DMA (float)20.7


static const unsigned short usRTSTimes[SERIAL_BAUD_115200 - SERIAL_BAUD_600][RTS_TIMES] = { // this table holds the required delay from the last interrupt in us for each speed/character length/int/dma combination
    {                                                                    // 1200 Baud time
    (unsigned short)((RTS_BIT_DELAY_8BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 1200)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 1200)),
    #ifdef SERIAL_SUPPORT_DMA
    (unsigned short)((RTS_BIT_DELAY_8BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 1200)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 1200))
    #endif
    },
    {                                                                    // 2400 Baud time
    (unsigned short)((RTS_BIT_DELAY_8BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 2400)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 2400)),
    #ifdef SERIAL_SUPPORT_DMA
    (unsigned short)((RTS_BIT_DELAY_8BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 2400)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 2400))
    #endif
    },
    {                                                                    // 4800 Baud time
    (unsigned short)((RTS_BIT_DELAY_8BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 4800)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 4800)),
    #ifdef SERIAL_SUPPORT_DMA
    (unsigned short)((RTS_BIT_DELAY_8BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 4800)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 4800))
    #endif
    },
    {                                                                    // 9600 Baud time
    (unsigned short)((RTS_BIT_DELAY_8BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 9600)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 9600)),
    #ifdef SERIAL_SUPPORT_DMA
    (unsigned short)((RTS_BIT_DELAY_8BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 9600)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 9600))
    #endif
    },
    {                                                                    // 14400 Baud time
    (unsigned short)((RTS_BIT_DELAY_8BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 14400)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 14400)),
    #ifdef SERIAL_SUPPORT_DMA
    (unsigned short)((RTS_BIT_DELAY_8BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 14400)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 14400))
    #endif
    },
    {                                                                    // 19200 Baud time
    (unsigned short)((RTS_BIT_DELAY_8BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 19200)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 19200)),
    #ifdef SERIAL_SUPPORT_DMA
    (unsigned short)((RTS_BIT_DELAY_8BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 19200)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 19200))
    #endif
    },
    {                                                                    // 38400 Baud time
    (unsigned short)((RTS_BIT_DELAY_8BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 38400)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 38400)),
    #ifdef SERIAL_SUPPORT_DMA
    (unsigned short)((RTS_BIT_DELAY_8BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 38400)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 38400))
    #endif
    },
    {                                                                    // 57600 Baud time
    (unsigned short)((RTS_BIT_DELAY_8BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 57600)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 57600)),
    #ifdef SERIAL_SUPPORT_DMA
    (unsigned short)((RTS_BIT_DELAY_8BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 57600)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 57600))
    #endif
    },
    {                                                                    // 115200 Baud time
    (unsigned short)((RTS_BIT_DELAY_8BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 115200)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_INT * REFERENCE_BIT_TIME) * (float)(115200 / 115200)),
    #ifdef SERIAL_SUPPORT_DMA
    (unsigned short)((RTS_BIT_DELAY_8BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 115200)),
    (unsigned short)((RTS_BIT_DELAY_7BIT_DMA * REFERENCE_BIT_TIME) * (float)(115200 / 115200))
    #endif
    },
};

The defines above were tuned for the M522XX (other chips have their own tuned set).
To chose the value is then easy: ptrSetupRTS->timer_us_value = usRTSTimes[ucSpeed][ucType];


I find that the USART in the ATMEL SAM7X (and AVR32) are very nice to use. They have RS485 mode so will automatically set the RTS line at the correct time without SW interaction, plus inter-character space timers (very useful for generating interrupts when a certain gap is detected - perfect for MODBUS RTU mode) and more.

Now I don't want you to defect from the Freescale chips but it is worth noting that they have up to 128k RAM and 512k FLASH (although they need an external PHY). Also the uTasker project is compatible between all (supported) chips so moving around between them and using the one which suits best for each project doesn't involve a lot of effort and learning. Moving existing projects (or half developed ones) from one to another is usually no major job so helps keep flexible....

Regards

Mark
Title: Re: MCF52235 UARTS and UART Interrupts -> no transmit complete int?
Post by: aersek on January 28, 2010, 12:19:13 PM
ColdFire UART also have possibility to disable RTS line automatically after finish of transfer.
From another forum http://forums.freescale.com/freescale/board/message?board.id=CFCOMM&message.id=30&query.id=129543#M30 (http://forums.freescale.com/freescale/board/message?board.id=CFCOMM&message.id=30&query.id=129543#M30)

I've just been experimenting with this very feature on a MCF5213. This chip requires the following:
- When setting up the serial port, enable the TxRTS bit in UMR2 and set the bit(s) in the pin assignment register for the GPIO port that makes the pin work as a RTS output.
- When sending a group of characters, enable the transmitter by writing 0x04 to the UCR register, and manually assert RTS by writing 0x01 to the UOP1 register (RTS is active low, so this actually takes the output low) before writing anything to the transmit register.
- After writing the last character to the transmit register, disable the transmitter by writing 0x08 to the UCR register.
All of the described actions are necessary. Omitting any one of them results in the RTS output not functioning as intended.
One should be aware that the serial ports in different ColdFire derivatives aren't all identical. Sometimes there are significant differences. Read the manual carefully and don't automatically assume things will work as you're used to from another ColdFire variant.   
Title: Re: MCF52235 UARTS and UART Interrupts -> no transmit complete int?
Post by: thamanjd on April 09, 2010, 11:21:54 AM
It's been a while since i originally posted this one. It's also been a while since i implemented it. Everything went fine.  Thanks for all your helpful suggestions.

I ended up starting a DMA timer on tx register empty interrupt. It then checked
the txcomplete flag i think maybe every 1/10th bit time or something like that.

I looked at the automatic RTS but it didnt look like it was going to do exactly what i wanted. I'm happy using the DMA timer.

Not having dived comletely into Coldfire yet i was surprised to see while figuring out how to set up a DMA timer interrupt that you can change the priority of the DMA timer interrupt. Can you change the priority of all the interrupts?

JD
Title: Re: MCF52235 UARTS and UART Interrupts -> no transmit complete int?
Post by: mark on April 09, 2010, 12:52:16 PM
Hi John

Each interrupt source can be given (in fact must be given) a unique interrupt level (1..7, whereby 7 is non-maskable) and priority - see the list in app_hw_m522x.h.

Note however that the Coldfire project doesn't support nested interrupts - this means that a higher level/priority interrupt won't interrupt a lower priority one which is being handled. However if there are multiple interrupts waiting, the one with highest priority will be handled as next.

Regards

Mark