View previous topic :: View next topic |
Author |
Message |
haxan7
Joined: 27 Jul 2013 Posts: 79
|
How to fprintf in ISR? |
Posted: Fri May 27, 2016 1:45 am |
|
|
I am using a serial device that expects an "ACK" very very quickly.
Sending the "ACK" in the main loop is not an option, because the period of the main loop is larger than required.
The only solution that I can think of is to send the ACK in the ISR. How do I printf in the ISR without any inviting any reentrancy issues? |
|
|
kWoody_uk
Joined: 29 Jan 2015 Posts: 47 Location: United Kingdom
|
|
Posted: Fri May 27, 2016 4:10 am |
|
|
If your ACK is only one char, then just use:-
Code: |
/* Where c is the char to output */
putc( c );
|
However, remember that if a char is already being transmitted or TX buf is full, it will WAIT in the interrupt until OK to send - not good practice.
Also remember that interrupts take a modest amount of time to enter.. how quickly is the device expecting the char?
Keith |
|
|
haxan7
Joined: 27 Jul 2013 Posts: 79
|
|
Posted: Fri May 27, 2016 4:17 am |
|
|
The ACK string is 10 characters, it includes a hex representation of a checksum. Currently I use "%02X" in the fprintf for string formatting and it works fine.
kWoody_uk wrote: |
Also remember that interrupts take a modest amount of time to enter.. how quickly is the device expecting the char?
|
Approximately one second. |
|
|
kWoody_uk
Joined: 29 Jan 2015 Posts: 47 Location: United Kingdom
|
|
Posted: Fri May 27, 2016 4:22 am |
|
|
One second is a good period of time for an acknowledge request. What are you doing in your main loop that is taking more than several hundred milliseconds? Are you waiting for something to occur?
I would never fprintf() in an interrupt unless routing through a buffering routine to be output in the UART TX interrupt, as demonstrated in CCS example file "ex_stisr.c"
Keith |
|
|
haxan7
Joined: 27 Jul 2013 Posts: 79
|
|
Posted: Fri May 27, 2016 4:29 am |
|
|
There is a lot of stuff happening in the main loop, like talking to a modem, reading/writing to a flash memory etc. It does not always takes 1 second, but it happens often. |
|
|
kWoody_uk
Joined: 29 Jan 2015 Posts: 47 Location: United Kingdom
|
|
Posted: Fri May 27, 2016 4:37 am |
|
|
OK, sounds like you'll have to re-prioritise your code.
If, the rest of your code can take a back seat when the ACK is needed, then I would say to follow my suggestion of buffering your ACK bytes for automatic transmission in the UART TX isr.
Keith |
|
|
haxan7
Joined: 27 Jul 2013 Posts: 79
|
|
Posted: Fri May 27, 2016 4:49 am |
|
|
Even with interrupt based transmission I would still have to use a function like sprintf in the ISR. |
|
|
kWoody_uk
Joined: 29 Jan 2015 Posts: 47 Location: United Kingdom
|
|
Posted: Fri May 27, 2016 4:59 am |
|
|
Yes, but NOT in the INT_TBE (Uart transmit interrupt).
Which interrupt are you currently using the fprintf() function?
Use:-
Code: |
printf( functionName, cstring, values )
|
.. in the function that has received the command to request an acknowledgement in the first place, where functionName is a function which will handle the formatted string.
e.g.
Code: |
/* Not the formatting chars you need, just an example
printf( bufTxByte, "n=%u", n );
|
This function will store each byte into your software circular buffer which will be output, a byte at a time, in the INT_TBE interrupt, each time a character has been sent. At 9600 baud each byte takes around 1ms to transmit; allowing your code to progress until another byte need to go.
If this sounds a bit involved, you'll have to look at the example I mentioned.
Keith |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Fri May 27, 2016 11:58 am |
|
|
haxan7 wrote: | Even with interrupt based transmission I would still have to use a function like sprintf in the ISR. |
All you need to do, is put the required data into the software buffer.
You can do two buffer write routines.
One that simply adds data to the buffer. Then the second, which disables interrupts before it writes to the buffer, and re-enables them afterwards. printf to this in the main code. This is like bputc in ex_stisr, except for adding the disable/enable interrupts.
In the ISR, after writing the data, enable INT_TBE.
In the main, have the buffer write, enable INT_TBE. This is as in the ex_stisr example.
Do a search here on how to avoid using % in the buffer maths.
If the reply is a fixed string then you can simply use:
interrupt_bputc("The string you want");
Then just enable INT_TBE, and you are done.
If the hardware buffer is empty, as soon as you leave the ISR, the data will start transmitting. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri May 27, 2016 3:13 pm |
|
|
you say:
Quote: |
very very quickly.
....
Approximately one second.
.......
10 characters, it includes a hex representation of a checksum. |
I find the intersection of these 3 statements fascinating.
to me "very" is milliseconds while
"very very" suggests microseconds of delay -
that's very very quickly IMB
( VERY VERY VERY - that's nanosecs ;-))
Fast just means "eventually"
But then you say the response can take a second - that's SLOW by me, unles your Fosc is 32khz
And then the meat of the fast response. The fixed-character-string time to begin the 'ACK' is baud rate determined,
but do you not calculate the CRC - and then send it ?
or
are you incrementally doing CRC calcs "rolling forward"
as new chars arrive?
Have you time profiled how long your response takes to send ? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Sat May 28, 2016 2:18 pm |
|
|
Also, don't get me wrong, but converting a byte to two hex characters is very much more efficiently 'doable' than using printf.
Assuming 'interrupt_bputc', is the routine to write a character to the buffer:
Code: |
#inline void digit(int8 val)
{
val&=0xF;
if (val<10)
interrupt_bputc(val+'0');
else
interrupt_bputc(val+('A'-10));
}
#inline void to_hex(int8 val)
{
int8 temp = val;
swap(val); //this codes as the processors swap instruction
digit(val); //outputs the MS nibble
digit(temp); //Now the LS nibble
}
|
Called with 'to_hex(checksum_value);', this is close to assembler efficiency to output two hex digits (relatively bulky, doing the digits inline - leaving them separate saves space but costs a little speed). |
|
|
|