|
|
View previous topic :: View next topic |
Author |
Message |
doloran
Joined: 06 Dec 2012 Posts: 5 Location: Germany
|
LCD.h disables interrupts? |
Posted: Thu Dec 06, 2012 2:31 pm |
|
|
Hello,
I use a PIC16F1937 with an LCD. Display content is managed by the driver LCD.h supplied by CCS.
My compiler Version is 4.133.
There are some interrupts acting which are time critical and each time the LCD is serviced all interrupts seem to be disabled.
Checking details with a scope I figured out that each time the LCD is called by a i.e. printf(lcd_putc,"\fSave Setup?") the function "lcd_send_byte" will disable interrupts.
Can anybody explain why and how to avoid this?
Here are the relevant parts of the code:
Whenever this part of code is executed interrupts are disabled:
Code: |
...
void lcd_send_byte(BYTE address, BYTE n)
{
#if defined(__PCB__)
set_tris_lcd(LCD_OUTPUT_MAP);
#else
lcd_enable_tris();
lcd_rs_tris();
lcd_rw_tris();
#endif
lcd_output_rs(0);
while ( bit_test(lcd_read_byte(),7) ) ;
lcd_output_rs(address);
delay_cycles(1);
lcd_output_rw(0);
delay_cycles(1);
lcd_output_enable(0);
lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
void lcd_putc(char c)
{
switch (c)
{
case '\a' : lcd_gotoxy(1,1); break;
case '\f' : lcd_send_byte(0,1);
delay_ms(2);
#if defined(LCD_EXTENDED_NEWLINE)
g_LcdX = 0;
g_LcdY = 0;
#endif
break;
#if defined(LCD_EXTENDED_NEWLINE)
case '\r' : lcd_gotoxy(1, g_LcdY+1); break;
case '\n' :
while (g_LcdX++ < LCD_LINE_LENGTH)
{
lcd_send_byte(1, ' ');
}
lcd_gotoxy(1, g_LcdY+2);
break;
#else
case '\n' : lcd_gotoxy(1,2); break;
#endif
case '\b' : lcd_send_byte(0,0x10); break;
#if defined(LCD_EXTENDED_NEWLINE)
default :
if (g_LcdX < LCD_LINE_LENGTH)
{
lcd_send_byte(1, c);
g_LcdX++;
}
break;
#else
default : lcd_send_byte(1,c); break;
#endif
}
}
... |
In main the LCD.h is called by lines like:
Code: |
...
printf(lcd_putc,"\fSave Setup?");
... |
The whole program works fine, but for the time of character sending the interrupts are disabled. When clearing the display this can take up to approx. 1.6 ms.
Would be great to find some hints here. Thanks! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Thu Dec 06, 2012 3:09 pm |
|
|
The lcd code itself, _does not disable interrupts_. Check your compiler warnings.. I'd guess you are calling the lcd code from inside one of the interrupt handlers somewhere. If you do, then interrupts _have_ to be disabled when the same code is called outside the interrupts. The compiler will be giving you a warning 'Interrupts disabled to prevent re-entrancy'.
If you think about it, imagine what would happen if you were half way through outputting a byte, and then tried to output another byte inside an interrupt....
If you want to send code to the lcd from inside the interrupt, write it to a buffer, and if this is not empty, output it at some point in the main code.
Best Wishes |
|
|
doloran
Joined: 06 Dec 2012 Posts: 5 Location: Germany
|
|
Posted: Thu Dec 06, 2012 3:58 pm |
|
|
Thanks for the prompt response!
I thought about something similar, but there are no warnings shown by the compiler and also no errors.
The lcd code won't be called from an interrupt. An interrupt sets only a flag which is checked in main (="tasten"). If the flag is set, a function calls the lcd code:
Code: |
// Interrupts
#int_RB
void detect_rb_change()
{
if(interrupt_active(INT_RB0_L2H)/*||interrupt_active(INT_RB5_L2H)*/||interrupt_active(INT_RB7_L2H))
{
output_high(LED3); // For scope only
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
set_timer1(64535);
output_low(LED3);
}
clear_IOC_flags();
disable_interrupts(INT_RB);
}
#int_timer1
void double_check()
{
setup_timer_1(T1_DISABLED);
tasten=1;
}
#int_timer6 // For test only
void test() // Called every 100µs
{
output_high(LED3); // For scope only
output_low(LED3);
}
...
...
void display_update(void)
{
long word=12345; // For test only
switch(menu)
{
case 0:
printf(lcd_putc,"\fTurn-On: %3uA\nTurn-Off:%3uA",upper_ths,lower_ths);
gedrueckt=FALSE;
break;
case 1:
printf(lcd_putc,"\f%lu",word); // <--- interrupts not disabled here!
if(submenu)
printf(lcd_putc,"\n--- %3uA ---",upper_ths);
break;
case 2:
lcd_putc("\fTurn-Off Level");
if(submenu)
printf(lcd_putc,"\n--- %3uA ---",lower_ths);
break;
case 3:
lcd_putc("\fSave Setup?");
if(submenu)
switch (decision)
{
case 1: lcd_putc("\n YES"); break;
case 0: lcd_putc("\n NO"); break;
}
break;
}
display=0; // Reset Display_Update-Flag
}
... |
An interesting effect I have found out meanwhile is that in the function display_update() case 0,2 and 3 will disable interrupts. But if an integer value (="word") is passed to the function like in case 1 the interrupts won't be disabled. Somehow I'm lost here... |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Dec 06, 2012 5:24 pm |
|
|
Quote: | but there are no warnings shown by the compiler and also no errors.
|
Make sure you have warnings enabled in the Project / Build Options
menu. There should be a tickbox for it. |
|
|
doloran
Joined: 06 Dec 2012 Posts: 5 Location: Germany
|
|
Posted: Fri Dec 07, 2012 2:02 am |
|
|
Warnings are enabled. I simply double checked by adding a call to the LCD inside an interrupt:
Code: | ...
#int_timer1
void double_check()
{
setup_timer_1(T1_DISABLED);
lcd_putc("ddd");
tasten=1;
}
...
|
With that I get some warnings:
>>> Warning 216 "Testprojekt_A.c" Line 329(0,1): Interrupts disabled during call to prevent re-entrancy: (lcd_send_nibble)
>>> Warning 216 "Testprojekt_A.c" Line 329(0,1): Interrupts disabled during call to prevent re-entrancy: (lcd_send_byte)
>>> Warning 216 "Testprojekt_A.c" Line 329(0,1): Interrupts disabled during call to prevent re-entrancy: (@PSTRINGC7_95)
>>> Warning 216 "Testprojekt_A.c" Line 329(0,1): Interrupts disabled during call to prevent re-entrancy: (lcd_putc)
Memory usage: ROM=20% RAM=5% - 15%
0 Errors, 4 Warnings.
Build Successful.
This is what Ttelmah explained at the beginning, which is clear to me but this seems not to be the root cause.
The timer6 interrupt is just for test purposes to have a lot of pulses at an output which I made visible on a scope.
Inside the LCD.h driver there is a point where the function waits for a response from the display itself on data line D7:
Code: | while ( bit_test(lcd_read_byte(),7) ) ;
|
When the "clear display (=\f)" is sent, the LCD needs up to 1.57ms according to spec. to process and answer. So basically nothing is happening inside the PIC. It's just waiting and polling inside this loop.
This time is clearly visible on the scope when sending only this command. With a small delay after that time the interrupt pulses reappear.
A few µs before sending the control character itself (\f) the interrupt pulses will stop appearing on the scope.
I will try to narrow down the exact time when the interrupts will be disabled by letting them pulse every 10µs and see on the lst-file where I didn't find any critical part so far. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Fri Dec 07, 2012 2:21 am |
|
|
Try using the flex driver instead.
Only take a few seconds to load this instead, and change it's defines to match your LCD.
I have a 'niggling memory', of someone else having this problem a time ago. Haven't yet been able to find it searching the forum, but seem to remember it was actually a chip problem, with a return from the interrupt handler, if it occurred in a particular place, not resetting the GIE. Because the code was spending a lot of time waiting in the LCD code, it 'seemed' to be related to this, but actually wasn't.
As a comment, are you using the ADC?. Are you aware of the really foul erratum on this with this chip?.
Best Wishes |
|
|
doloran
Joined: 06 Dec 2012 Posts: 5 Location: Germany
|
Solution found: LCD.h disables interrupts? |
Posted: Fri Dec 07, 2012 3:37 am |
|
|
With 10µs interrupt pulses I could see that somewhere around the call to
Code: | void lcd_putc(char c) | inside LCD.h something would disable the interrupts. In the lst-file however I couldn't find any hint.
So I tried to set GIE at various locations to see what happens.
When inserting it into the LCD.h at the beginning of the "lcd_putc()" function
Code: | ...
void lcd_putc(char c)
{
enable_interrupts(GLOBAL);
switch (c)
{
case '\a' : lcd_gotoxy(1,1); break;
case '\f' : lcd_send_byte(0,1);
delay_ms(2);
#if defined(LCD_EXTENDED_NEWLINE)
g_LcdX = 0;
g_LcdY = 0;
#endif
break;
.
.
.
}
...
|
everything worked perfectly after!
With this workaround the interrupts will not be disabled. Or maybe just for a very short time.
On the scope all 10µs pulses generated by the timer 6 interrupt were visible without any interruption during accessing the LCD.
I still don't know the actual root cause, whether it is the chip or compiler, but thank you Ttelmah, you've had the right hint to find a solution!
Best Wishes
PS: This is a great forum with a lot of valuable information! |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|