Make printf work in ISRs with interrupt driven UART driver

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 
已解决

Make printf work in ISRs with interrupt driven UART driver

跳至解决方案
2,159 次查看
trailman
Contributor V

I use interrupt driven UART driver ("ittya:") on my project and wanted to have unexpected exception beeing displayed.

 

First, in the mqx/source/bsp/<myboard>/init_bsp.c, I added 

   _int_install_unexpected_isr();

to set the default handler for unexpected ISRs; this is  _int_unexpected_isr() defined in mqx/source/psp/coldfire/int_unx.c

But as said in this file " This default I/O must NOT be an interrupt drive I/O channel"

 

However, with a very small change to the UART driver this works fine. The trick is : when printf is called from an ISR, send the characters directly to the UART.

For this, just edit

mqx/source/io/serial/int/serl_int.c

to add the following lines at the beginning of _io_serial_int_putc_internal()  (just after /* End CR 388 */) :

   if (_io_serial_int_write_force_polled_mode) {
       /* directly write to UART, bypassing normal interrupt driven code */
       int_io_dev_ptr->DEV_PUTC(int_io_dev_ptr, c);
       return;
   }

Then add to mqx/source/fio/io_util.c :

boolean _io_serial_int_write_force_polled_mode = FALSE;

 

And to mqx/source/include/fio.h

extern boolean _io_serial_int_write_force_polled_mode;

 

Then in mqx/source/psp/coldfire/int_unx.c, before first printf() add :

   _io_serial_int_write_force_polled_mode = TRUE; /* force polled UART mode (needed when called from ISR) */

and after last printf() :

   _io_serial_int_write_force_polled_mode = FALSE; /* no more force polled mode */

You can also force the output to be written to the default serial port instead of the current stdout (useful if the terminal of the task producing the exception is not a serial port; a telnet session for example); for this declare :

   pointer stdout_orig;
then before first printf :

   _io_serial_int_write_force_polled_mode = TRUE; /* force polled UART mode (needed when called from ISR) */
   stdout_orig = td_ptr->STDOUT_STREAM;
   td_ptr->STDOUT_STREAM = kernel_data->PROCESSOR_STDOUT; /* force printf output on default console */
and after last one :

   td_ptr->STDOUT_STREAM = stdout_orig; /* restore stdout */
   _io_serial_int_write_force_polled_mode = FALSE; /* no more force polled mode */

0 项奖励
回复
1 解答
1,066 次查看
MarkP_
Contributor V

Hi,

Does this:  int_io_dev_ptr->DEV_PUTC(int_io_dev_ptr, c);

call the function: void _kuart_int_putc().

 

The function waits TX-register to be empty:

   while (!(sci_ptr->S1 & UART_S1_TDRE_MASK)) {
      /* Wait while buffer is full */
   } /* Endwhile */

Prevents/blocks other interrupts to be served during this time.

 

How about, if you increase a global variable in unexpected int routine and check and print

that variable in main task of you system.

~Mark

 

 

在原帖中查看解决方案

0 项奖励
回复
3 回复数
1,067 次查看
MarkP_
Contributor V

Hi,

Does this:  int_io_dev_ptr->DEV_PUTC(int_io_dev_ptr, c);

call the function: void _kuart_int_putc().

 

The function waits TX-register to be empty:

   while (!(sci_ptr->S1 & UART_S1_TDRE_MASK)) {
      /* Wait while buffer is full */
   } /* Endwhile */

Prevents/blocks other interrupts to be served during this time.

 

How about, if you increase a global variable in unexpected int routine and check and print

that variable in main task of you system.

~Mark

 

 

0 项奖励
回复
1,066 次查看
trailman
Contributor V

Hi Mark,

 

You're right, the function called by int_io_dev_ptr->DEV_PUTC() polls the TX register before sending a character.

For MCF52259, this function is _mcf52xx_uart_serial_int_putc() defined in mqx/source/io/serial/int/serl_int_mcf52xx.c (this is set when calling _io_serial_int_install() in mqx/source/io/serial/int/serl_int.c to install the driver)

But this is not worse than doing this when using the polled UART driver. The purpose here is just to have the same functionality than with the polled UART driver.

 

OK, other interrupts are lost when printing characters from the ISR, but it's OK for me because the purpose is not to use prinf() in the ISRs used by the drivers (which would not be a good idea), but to display fatal exceptions (data access error, code corruption, ...) or unhandled ISRs (that should never occur).

On my system, I added a reset at the end of the int_unx.c to reset the board when such a problem occur, so I don't care about loosing interrupts. The more important to me is to know the task, the type of error, and the address in code at which the porblem occured.

0 项奖励
回复
1,066 次查看
trailman
Contributor V

There's probably a simplier solution without the extra variable  _io_serial_int_write_force_polled_mode, but I have not tested it yet.

Use

  if (_mqx_kernel_data->IN_ISR) {

instead of

  if (_io_serial_int_write_force_polled_mode) {

 

or cleaner but longer :

  KERNEL_DATA_STRUCT_PTR kernel_data;
  _GET_KERNEL_DATA(kernel_data);
  if (kernel_data->IN_ISR) {

0 项奖励
回复