CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

#int_TIMER0 for multiplexing 8 displays

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
potato



Joined: 23 Mar 2014
Posts: 16

View user's profile Send private message

#int_TIMER0 for multiplexing 8 displays
PostPosted: Sun Mar 23, 2014 12:17 pm     Reply with quote

Hi everyone, and thanks for dedicating this post some of your time.

I'm using a PIC18F4550 and I want to manage 8 displays - with dynamic visualisation, of course. I am using a 3 to 8 DEMUX to avoid using so many I/O pins in the PIC. So far I've managed to display any number in the displays, but using delays Sad The problem is that I also have a LCD, buttons, leds, etc. and I cannot delay my program for the displays, as they are supposed to be ALWAYS on.

The solution that I found was to use the timer0 instead of the delays, so whenever it overflows (every 5 ms, so I need to start the timer at 0xEB) I choose a different number and a different display.

Up to now I'm only trying to manage 2 displays with a fixed number. I'm simulating everything in Proteus 8, and the CCS version that I use is the 4.032.

Here is the code:

Code:

#include "D:\...\prova_comptador.h"

//global variables
unsigned int8 comptador0=34;
unsigned int8 d0,u0;

#byte portd=0xF83                         //direction of portD
#byte porte=0xF84                         //direction of portE

//------------------------------------------------------------------------------------
void config_ports (void)
{
 set_tris_d(0x00);                   //output
 set_tris_e(0x00);                   //output
}
//------------------------------------------------------------------------------------
unsigned int8 conversio_bcd (unsigned int8 comptador,*u,*d)       

  unsigned int8 comptador_updated;
 
  if (comptador<=99)
  {
  *d = comptador/10;                     
  *u = comptador%10;                 
  return(comptador);
  }
 
  else                //if it arrives to 99, reset it to 00
  {
  comptador_updated = 0;
  *d = 0;
  *u = 0;
  return (comptador_updated);
  }
}
//------------------------------------------------------------------------------------       
#int_TIMER0
void TIMER0_isr () 

   porte = 0x00;                           //select the 1st display
   portd = u0;               //write the number stored in u0
   set_TIMER0(0xEB);                       //init timer0 to 5 ms
   
   porte = 0x01;                          //select the 2nd display
   portd = d0;                            //show the next number
   set_TIMER0(0xEB);                       //init timer0 to 5 ms
}

//------------------------------------------------------------------------------------
void main(void)

   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256|RTCC_8_bit); 
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);
   set_TIMER0(0xEB);                      //init timer 0
   
   
   config_ports();                  //call the subroutine
 
   while(1)
   {
   
    comptador0 = conversio_bcd(comptador0,&u0,&d0);

   }
}


I am completely lost about the interruption routine, maybe it's completely wrong... I've been looking at many threads but I've been unable to find something that helped me, so I'd really appreciate if somebody could throw some light on the case.

Hundreds of thanks! Very Happy
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Mar 23, 2014 12:38 pm     Reply with quote

Post this file: prova_comptador.h
potato



Joined: 23 Mar 2014
Posts: 16

View user's profile Send private message

PostPosted: Sun Mar 23, 2014 2:12 pm     Reply with quote

PCM programmer wrote:
Post this file: prova_comptador.h


In fact I haven't edited this file, it is automatically created... The quartz crystal that I'm using is a 4 MHz one.

Code:
#include <18F4550.h>
#device adc=8
//WDT,
#FUSES WDT1                     //Watch Dog Timer uses 1:1 Postscale
#FUSES XT                       //Crystal osc <= 4mhz
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES BORV20                   //Brownout reset at 2.0V
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOCPD                    //No EE protection
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES LVP                      //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOWRTD                   //Data EEPROM not write protected
#FUSES IESO                     //Internal External Switch Over mode enabled
#FUSES FCMEN                    //Fail-safe clock monitor enabled
#FUSES PBADEN                   //PORTB pins are configured as analog input channels on RESET
#FUSES NOWRTC                   //configuration not registers write protected
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOEBTRB                  //Boot block not protected from table reads
#FUSES NOCPB                    //No Boot Block code protection
#FUSES MCLR                     //Master Clear pin enabled
#FUSES LPT1OSC                  //Timer1 configured for low-power operation
#FUSES XINST                    //Extended set extension and Indexed Addressing mode enabled
#FUSES PLL12                    //Divide By 12(48MHz oscillator input)
#FUSES CPUDIV4                  //System Clock by 4
#FUSES USBDIV                   //USB clock source comes from PLL divide by 2
#FUSES VREGEN                   //USB voltage regulator enabled
#FUSES ICPRT                    //ICPRT enabled

#use delay(clock=4000000)
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Sun Mar 23, 2014 3:29 pm     Reply with quote

The Wizard, has no intelligence at all.

It only creates what you tell it, and the settings _will_ be wrong, unless you know what you are doing.

Some serious errors leap out in the fuses:
First LVP. Unless you are using a programmer that requires this (99% of programmers don't), and have a board wired to use this, the chip will not run properly with this selected. You need NOLVP.
Then you must not have ICPRT selected. This must only be selected on 44pin devices, with external ICPRT hardware. NOICPRT needed.
Then XINST. CCS does not support this. You need NOXINST selected, or code won't run.
Then you are saying you have a 4MHz crystal, but that you want to use division by 4, and yet you have the clock set at 4Mhz. You need CPUDIV1 to run at 4MHz with a 4MHz crystal.
You should always have PUT when using a crystal (this just causes erratic initial behaviour).

There are several more that will stop other things working, but these are the ones that will prevent the chip from initially working....
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Mar 23, 2014 4:01 pm     Reply with quote

He's using Proteus. It shows what a terrible learning tool Proteus (ISIS) is.
Fuses (Config Bits) are critical in real hardware and ISIS doesn't care.

In your code below, why are reloading Timer0 twice ? It only needs to
be done once:
Quote:

#int_TIMER0
void TIMER0_isr ()
{
porte = 0x00; //select the 1st display
portd = u0; //write the number stored in u0
set_TIMER0(0xEB); //init timer0 to 5 ms

porte = 0x01; //select the 2nd display
portd = d0; //show the next number
set_TIMER0(0xEB); //init timer0 to 5 ms
}
potato



Joined: 23 Mar 2014
Posts: 16

View user's profile Send private message

PostPosted: Mon Mar 24, 2014 3:11 am     Reply with quote

Thanks for your comments Ttelmah. Now my .h code is the following:

Quote:
#include <18F4550.h>
#device adc=8

#FUSES WDT1 //Watch Dog Timer uses 1:1 Postscale
#FUSES XT //Crystal osc <= 4mhz
#FUSES NOPROTECT //Code not protected from reading
#FUSES BROWNOUT //Reset when brownout detected
#FUSES BORV20 //Brownout reset at 2.0V
#FUSES PUT //------------desnegat -- No Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOLVP //-----------negat--- Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES NOPBADEN //PORTB pins are configured as analog input channels on RESET
#FUSES NOWRTC //configuration not registers write protected
#FUSES NOWRTB //Boot block not write protected
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#FUSES NOCPB //No Boot Block code protection
#FUSES MCLR //Master Clear pin enabled
#FUSES LPT1OSC //Timer1 configured for low-power operation
#FUSES NOXINST //-------------negat--Extended set extension and Indexed Addressing mode enabled
#FUSES PLL12 //Divide By 12(48MHz oscillator input)
#FUSES CPUDIV1 //-----canviat a CPUDIV1 enlloc de 4 -- System Clock by 4
#FUSES USBDIV //USB clock source comes from PLL divide by 2
#FUSES VREGEN //USB voltage regulator enabled
#FUSES NOICPRT //-----------negat---ICPRT enabled

#use delay(clock=4000000)


PCM programmer, is it only necessary to reload Timer0 once? If so, when do I have to do it, in the Timer0 interruption routines or in the main? (at the moment I have it in both places, despite I've tried to take it off the main... with the same result). Only the units display is showing the number, and the DEMUX is not changing its outputs...


Code:
#int_TIMER0
void TIMER0_isr () 

   set_TIMER0(0xEB);                     
   
   porte = 0x00;                           
   portd = u0;
     
   porte = 0x01;                     
   portd = d0;                           
}


The funny thing is that, if I change the order of this code -i.e. writing first porte = 0x01 and portd = d0, then the only display that is ON is the one corresponding to the tens... So it seems like the interruption is only valid for the first set of lines. Any ideas why?

Thanks.
potato



Joined: 23 Mar 2014
Posts: 16

View user's profile Send private message

PostPosted: Mon Mar 24, 2014 5:12 am     Reply with quote

By the way, is it necessary to clear the interrupts or is it automatically done? And what about disable/enable the interrupts in the same interruption?

I've seen hundreds of codes and I've tried this, just to see if I was luckier, but no Sad

Code:
#int_TIMER0
void TIMER0_isr () 

   set_TIMER0(0xEB);                       //only once
   
   disable_interrupts(INT_TIMER0);
   porte = 0x01;                          //2nd display
   portd = d0;                            //
   clear_interrupt(INT_TIMER0);      // clears the timer0 interrupt flag
   enable_interrupts(INT_TIMER0);
   
   disable_interrupts(INT_TIMER0);
   porte = 0x00;                           //1st display
   portd = u0;
   clear_interrupt(INT_TIMER0);      // clears the timer0 interrupt flag
   enable_interrupts(INT_TIMER0);   
}


Is it better or is it a complete disaster? Rolling Eyes
jeremiah



Joined: 20 Jul 2010
Posts: 1358

View user's profile Send private message

PostPosted: Mon Mar 24, 2014 6:55 am     Reply with quote

Interrupts clear automatically at the end of the interrupt due to CCS doing it for you under the hood.

I'm not as familiar with the PIC18F4550, but on the PIC24 chips, trying to write to the PORTx registers twice in a row leads to a race condition where the first write overrides the 2nd. You should probably be using the output_e() and output_d() functions anyways.

I am confused why you make two changes in a row to the same ports in the interrupt. One will always overwrite the other. Shouldn't you be using a state machine to cycle through the displays instead?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Mar 24, 2014 7:44 am     Reply with quote

Quote:
So it seems like the interruption is only valid for the first set of lines.

The #int_timer0 doesn't interrupt itself. You get an interrupt, then
the entire code in your #int_timer0 routine executes once, then it exits
the routine. It won't execute that routine again until the next interrupt
occurs about 5ms later.


Quote:
disable_interrupts(INT_TIMER0);
clear_interrupt(INT_TIMER0);
enable_interrupts(INT_TIMER0);

Remove all this type of code (above) which is inside the #int_timer0
routine. It's not necessary and won't do anything to help you. As I said,
you get one pass through the #int_timer0 routine every 5ms.

Now, maybe Jeremiah's comment about using a state machine will make
sense to you. He wants you to create a static variable inside the
#int_timer0 routine. Initialize it in the declaration statement to TRUE.
Then every time the routine executes, check that flag and choose which
of the two blocks of PortE code you want to execute. Use if-else
statements to do this. Then toggle the flag variable to the opposite
state. If it was TRUE, set it to FALSE, or vice-versa. Remember you
will declare this flag (could be an int8) as 'static'. Then the next you
come into the routine, 5 ms later, your if-else statements will execute
the other block of PortE code.

For you to understand this, it's essential that you get the concept that one
Timer0 interrupt occurs every 5ms, and executes your #int_timer0 routine
one time per interrupt, and then exits.
potato



Joined: 23 Mar 2014
Posts: 16

View user's profile Send private message

PostPosted: Mon Mar 24, 2014 5:03 pm     Reply with quote

PCM programmer wrote:

The #int_timer0 doesn't interrupt itself. You get an interrupt, then
the entire code in your #int_timer0 routine executes once, then it exits
the routine. It won't execute that routine again until the next interrupt
occurs about 5ms later.


PCM programmer wrote:

For you to understand this, it's essential that you get the concept that one
Timer0 interrupt occurs every 5ms, and executes your #int_timer0 routine
one time per interrupt, and then exits.


Thanks a lot! This has really helped me! Very Happy

About the states machine, I considered it to be too complex for what I wanted to do, so my solution, knowing what PCM programmer has said, has been to create a global variable indicating the display which I wanted to light on, and do a switch contemplating the 8 values.

The result is the following:

Code:
#int_TIMER0
void TIMER0_isr () 

 set_TIMER0(0xEB);                     
 porte = display;
 
 if (display<7)                           
  {
   switch (display)
   {
   case 0: portd = u0; break;
   case 1: portd = d0; break;
   case 2: portd = u1; break;
   case 3: portd = d1; break;
   case 4: portd = u2; break;
   case 5: portd = d2; break;
   case 6: portd = u3; break;
   case 7: portd = d3; break;
   }
  display++;
 }
 else
  display = 0;
}


The only strange thing is that, if I write (display<=7), as it should be, then it copies what is stored in the 7th display to the 0... That's why I've omitted the '='.

And, dear all, NOW IT WORKS!!! Laughing

The next step is to use the interruption int0 to detect when a button has been pressed... I hope the functionality is quite similar to timer0! Moreover, I've seen there is an example and some explanation on the CCS manual, so maybe it is easier.

Thanks a lot!! Wink
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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