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

Timer & Prescaler basics, explain ?

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



Joined: 10 Feb 2004
Posts: 12
Location: Denmark, (Scandinavia-Europe)

View user's profile Send private message ICQ Number

Timer & Prescaler basics, explain ?
PostPosted: Sun Feb 22, 2004 2:04 pm     Reply with quote

Hi guys,

...still doing my finals with:
1*Pic16F877, 20Mhz quartz.
1*HD44780 lcd
3*24LC256 I2C eeproms
1*DS1621 I2C temp/ther.
1*Packet Whacker

A user can specify the following in a PC program, transmitted to the Pic via ethernet:
Elapsing time: 1s-24hours, in seconds.
Time between temp measures: 1s-1h, in seconds. => min 1 measure pr. hour and max 3600 measures pr. hour.

Problem:
I need to setup a timer/prescaler so I can have a base to calculate excactly when to make the DS1621 measure current temp.
After each measure I put the value in the 24LC256' EEproms.

My question:
I don't fully understand how to use the available timers (Timer0, 1 & 2, I believe) and make an interrupt fire for measuring, please advice me :-)

Basicly the following is important:
1) the timing between each measure MUST be accurate down to 1 sec.
2) the reading and writing of the I2C bus must not interfere with "1"
3) Sending over ethernet, displaying chars on lcd etc is not an issue as I have segmented the program, by reading a dibswitches before starting anything (2 pins, 4 different modes).

So what I thought of was something like this:
- somehow setup the timers to make a base of excactly 1s.
- multiply base in software according to user specifications (1s-1hour)
- start the "timer"
- on each user specified interval, somehow not interfering with the above:
---- I2C: measure the temp,
---- I2C: store in eeprom.
- repeat the procedure.

Can a gentle soul show me a very simple example of setting up the timers correctly for the most possibly accurate timing ?
The project must be able to measure: every second for up to 24 hours => 3600s*24= 86400s.
I'm fairly sure I don't use the timers for anything else

wow, that was a load of text, hope I didn't scare anyone Surprised


Always a pleasure reading this forum.

Best Regards
Rasmus 'Izolator'
dyeatman



Joined: 06 Sep 2003
Posts: 1937
Location: Norman, OK

View user's profile Send private message

Temp measuring
PostPosted: Sun Feb 22, 2004 2:38 pm     Reply with quote

Your basic approach sounds good.

Just curious, why do you have to have such accurate times between measurements?? Are you also trying to use the program as a time clock of some sort? If not, being off a small fraction of second (5-50 hundredths) will have zero impact on the measurements since temp cannot change that rapidly ....

If you are worried about extreme accuracy the best bet is an external RTC chip because software timekeeping will always have some drift regardless of how you implement it.

Your will be running tight on time to read temps, update an LCD and store data to EEPROM once every single second and send/receive data via TCP/IP. It will be almost impossible to do all this and keep any type of accurate time in software.

(My assumptions are all based on the info you listed as being used below)

1*Pic16F877, 20Khz quartz.
1*HD44780 lcd
3*24LC256 I2C eeproms
1*DS1621 I2C temp/ther.
1*Packet Whacker
Izolator



Joined: 10 Feb 2004
Posts: 12
Location: Denmark, (Scandinavia-Europe)

View user's profile Send private message ICQ Number

PostPosted: Sun Feb 22, 2004 2:47 pm     Reply with quote

Thankyou for the very quick reply !

If you refresh your browser you will see:
20Mhz Quartz, my mistake.

Also only the following needs be done:

timer running, continously when startet.
Measure temp, DS1612
write temp to 24LC256

Im not that scared of the time drifting af few ms due to innacurate oscilator etc. only seconds are important.

What I am really scared of is not being able to:
1) setup the timers for best possible precision (not using external devices becides the 1 oscillator to make the Pic fly high)
2) seperate the two I2C functions from the timer routine, so they wont affect timer with several delay_ms(11)'s.

BR
Rasmus 'Izolator'
dyeatman



Joined: 06 Sep 2003
Posts: 1937
Location: Norman, OK

View user's profile Send private message

PostPosted: Sun Feb 22, 2004 6:42 pm     Reply with quote

If the clock function and I2C is all you are going to be using then you will be just fine.

The main thing to keep clock accuracy is to use the hardware I2C and interrrupt drive everything giving the Timekeeping timer interrupt priority.

You will need to leave Timer0 available for watchdog so Timer1 looks like the best bet for your clock.

Just curious: why did you list all the other stuff if you are not going to use them?

As far as using I2C interrupts (you would be using #int_ssp) do a search on for "int_ssp" on this board and you will find a lot of stuff. I came up with 83 matches with I2C interrupt routines/code in them.

Good Luck!
jds-pic2
Guest







PostPosted: Sun Feb 22, 2004 10:34 pm     Reply with quote

Izolator wrote:

What I am really scared of is not being able to:
1) setup the timers for best possible precision (not using external devices becides the 1 oscillator to make the Pic fly high)
2) seperate the two I2C functions from the timer routine, so they wont affect timer with several delay_ms(11)'s.
Rasmus 'Izolator'


ok, timer setup basics look like this:

Code:

#use delay(clock=8000000)    /* sets microcontroller clock speed */

setup_timer_0(RTCC_INTERNAL);
setup_wdt(WDT_1152MS);
/* the function restart_wdt() needs to be  */
/* run at least every 1152 ms.             */

setup_timer_1(T1_INTERNAL|T1_DIV_BY_4);
enable_interrupts(INT_TIMER1);
/* timer 1 is a hw-based interupt gen; it  */
/* expires every (PRESCALE*(2^18))/CLK sec */
/* in this case, at 8MHz ~= 131.072ms.     */
/* the timer val is preset to give 125ms.  */

setup_timer_2(T2_DIV_BY_16,78,15);
enable_interrupts(INT_TIMER2);
/* timer 2 is a hw-based interupt gen; it  */
/* expires every (1/CLK)*4*16*78*16 sec,   */
/* in this case, at 8MHz ~= 10ms.          */

enable_interrupts(GLOBAL);



as you can see, it's a combination of RTFM for the formulas, and then setting up the correct paramters within CCS's environment. you WILL need to read the databook for your particular PIC. the values above are for the PIC16C67B. note that, if you can't get the timer value to a nice round value (or integral product) of your desired value, then use the "set_timer*" functions in your ISR to preload the timer register so that it overflows "sooner"...

Code:

#int_timer1          /* Timer1 overflow interupt service routine --   */
void timer1_isr()    /* this interupt is called every 125ms by timer1 */
{
   if (kma_enabled) {
      if (kma_counter < kma_limit) {
         kma_counter++;
         kma_expired=FALSE;
         }
      else {
         set_alarm_relay_all(1);     /* actuate all relays */
         kma_expired=TRUE;
         }
      }
   set_timer1(3035);    /* this preloads the timer to next overflow in 125ms */
}


and similarly for a "straight up" counter...

Code:

#int_timer2             /* Timer2 overflow interupt service routine --  */
void timer2_isr()       /* this interupt is called every 10ms by timer2 */
{                       /* causing the LED to blink 800ms ON/200ms OFF  */
   while (!(LEDcounter--)) {
      if (LEDheartbeat) {            /* if LEDheartbeat TRUE = LED was OFF */
         OUTPUT_LOW(LED_HEARTBEAT); /* hw logic is inverted; set i/o pin low = LED now ON */
         LEDcounter=80;
         }
      else {
         OUTPUT_HIGH(LED_HEARTBEAT);
         LEDcounter=20;
         }
      LEDheartbeat=1-LEDheartbeat;
      }
}



now then, in the code above i do a modest amount of instructions within the interrupt handler. in this case it was not that critical. for other applications, it is generally advisable to do as little as possible within the interupt handler. this helps keep your mainline code reasonably deterministic. for your case it will help prevent clock drift to keep interrupt code brief. moreover, for some quasi-atomic operations (like reading from an i2c device), this prevents the occurence of interrupts from screwing up critical timings. so it is perfectly OK to have a VERY short interrupt routine ...

Code:

#int_ext
void pushbutton_isr() {             // got an pushbutton int
   pushbutton_flag=TRUE;
   }


then within your mainline code you can act upon the flag *when you have the time to do so*. otherwise, for truly atomic operations, you will need to disable interrupts completely so as to make the code completely deterministic WRT time.

jds-pic

ps here is a start on your DS1621 code...

Code:

#define I2CWRITE     0b00000000
#define I2CREAD      0b00000001

// this is a handy routine for taking the i2c physical address
// and shifting the bits left so that the address can simply be
// OR'd with an i2c write command...
int   i2c_addr_mask(int device_addr)
{
   /* xxxxxIJK --> 00000IJK, then 00000IJK --> 0000IJK0 */
   return((device_addr & 0x07)<<1);
}


//////////////////////////////////////////////////////////////////////////////
////   Library for the Dallas DS1624 i2c-based (temp + 256b eeprom) ic    ////
////     -- j.d.sandoz  jds !at! losdos.dyndns.org                        ////
////   essentials:                                                        ////
////   ds1624_init();      initializes chip for continous T readings      ////
////   ds1624_eeprom_read();           reads a byte from address          ////
////   ds1624_eeprom_write();          writes a byte into address         ////
////   ds1624_eeprom_clear();          clears the contents of the eeprom  ////
////   ds1624_read_temperature_c();    reads temperature in deg C         ////
//////////////////////////////////////////////////////////////////////////////

#define DS1624_ID                      0b10010000
#define DS1624_CMD_ACCESSCONFIG        0xAC
#define DS1624_CMD_INITIATECONVERT     0xEE
#define DS1624_CMD_ACCESSTEMPERATURE   0xAA
#define DS1624_CMD_ACCESSMEMORY        0x17

void i2c_ds1624_init(int device_addr)
{
   int addr_mask;
   addr_mask=i2c_addr_mask(device_addr);
   i2c_start();
   i2c_write(DS1624_ID | addr_mask | I2CWRITE); /* mode is write */
   i2c_write(DS1624_CMD_ACCESSCONFIG);       /* send access config command */
   i2c_write(0x00);       /* set up for continuous temp conversions */
   i2c_stop();
   delay_ms(20);          /* wait for the data to be written */
   i2c_start();
   i2c_write(DS1624_ID | addr_mask | I2CWRITE);   /* mode is write */
   i2c_write(DS1624_CMD_INITIATECONVERT);   /* initiate temperature conversions */
   i2c_stop();
}

int i2c_ds1624_read_temp_c(int device_addr)
{
   int datah, datal;
   int addr_mask;
   addr_mask=i2c_addr_mask(device_addr);
   i2c_start();
   i2c_write(DS1624_ID | addr_mask | I2CWRITE); /* mode is write */
   i2c_write(DS1624_CMD_ACCESSTEMPERATURE); /* send access temperature command */
   i2c_start();
   i2c_write(DS1624_ID | addr_mask | I2CREAD);  /* mode is read */
   datah=i2c_read();                  /* msb */
   datal=i2c_read(0);                 /* lsb (=0.5 deg C) & No Ack */
   i2c_stop();
   if (BIT_TEST(datah,7)) /* defeat the two's complement data output; */
      return(0);          /* this means NO negative temps are returned */
   else                   /* if datal is > 0.5C, round up the value */
      return( ((datal & 0b10000000) ? datah+1 : datah) ); /* returns 0->125 deg C */
}

int i2c_ds1624_eeprom_read(int device_addr, int address)
{
   int data;
   int addr_mask;
   addr_mask=i2c_addr_mask(device_addr);
   i2c_start();
   i2c_write(DS1624_ID | addr_mask | I2CWRITE); /* mode is write */
   i2c_write(DS1624_CMD_ACCESSMEMORY);  /* access memory command */
   i2c_write(address);                /* address of memory to read from */
   i2c_start();                       /* repeated start */
   i2c_write(DS1624_ID | addr_mask | I2CREAD); /* mode is read */
   data=i2c_read(0);                  /* read the data, No Ack the read */
   i2c_stop();
   return(data);
}

int i2c_ds1624_eeprom_write(int device_addr, int address, int data)
{
   int addr_mask;
   addr_mask=i2c_addr_mask(device_addr);
   i2c_start();
   i2c_write(DS1624_ID | addr_mask | I2CWRITE); /* mode is write */
   i2c_write(DS1624_CMD_ACCESSMEMORY);  /* access memory command */
   i2c_write(address);                /* address of memory to write to */
   i2c_write(data);                   /* data to be written */
   i2c_stop();
   delay_ms(20); /* worst case write time for the eeprom */
   return (sctl_i2c_ds1624_eeprom_read(device_addr, address));
}


long i2c_ds1624_eeprom_clear(int device_addr, int fill_byte)
{
   long i, memsum=0;
   for (i=0;i<=255;i++)    /* mind the (int) rollover! */
      {
      sctl_i2c_ds1624_eeprom_write(device_addr,(int)i,fill_byte);
      memsum+=sctl_i2c_ds1624_eeprom_read(device_addr,(int)i);
      restart_wdt();
      }
   return(memsum);
}


and for your memory...

Code:

#define EEPROM_24CXX 0b10100000

short int i2c_device_exists(int device_type, int device_addr)
{
   short int result=FALSE;
   int addr_mask;
   int testbyte;
   addr_mask=i2c_addr_mask(device_addr);
   testbyte=(device_type | addr_mask | I2CWRITE); // mode is write
   i2c_start();
   if (i2c_write(testbyte))  // if the ACK error bit is set, target is absent
        result=FALSE;
     else
        result=TRUE;
   i2c_stop();
   return(result);
}


int i2c_24cXX_eeprom_read(int device_type, int device_addr, int16 memory_addr)
{
   int data;
   int addr_mask;
   addr_mask=i2c_addr_mask(device_addr);
   i2c_start();
   i2c_write(EEPROM_24CXX | addr_mask | I2CWRITE); // mode is write
      if (device_type==ADDR_LEN_16) {
      i2c_write(make8(memory_addr,1));
      i2c_write(make8(memory_addr,0));
      }
   else
      i2c_write((int)memory_addr);     // address of memory to read from
   i2c_start();                        // repeated start
   i2c_write(EEPROM_24CXX | addr_mask | I2CREAD); // mode is read
   data=i2c_read(0);                   // read the data, No Ack the read
   i2c_stop();
   return(data);
}


int i2c_24cXX_eeprom_write(int device_type, int device_addr, int16 memory_addr, int memory_val)
{
   int data;
   int addr_mask;
   addr_mask=i2c_addr_mask(device_addr);
   i2c_start();
   i2c_write(EEPROM_24CXX | addr_mask | I2CWRITE); // mode is write
   if (device_type==ADDR_LEN_16) {
      i2c_write(make8(memory_addr,1));
      i2c_write(make8(memory_addr,0));
      }
   else
      i2c_write((int)memory_addr);
   i2c_write(memory_val);
   i2c_stop();
   if (device_type==ADDR_LEN_16) // only for rev B process parts!  see ST data sheet,
      delay_ms(5);               // http://us.st.com/stonline/books/pdf/docs/4578.pdf
   else
      delay_ms(10);  // enforced delay for writing
   return(i2c_24cXX_eeprom_read(device_type,device_addr,memory_addr));
}
[/code]
Izolator



Joined: 10 Feb 2004
Posts: 12
Location: Denmark, (Scandinavia-Europe)

View user's profile Send private message ICQ Number

PostPosted: Mon Feb 23, 2004 9:18 am     Reply with quote

#dyeatman
I guess I fed you too much information.
Basicly I have a dibswitch with 2 switches connected with pullup Vdd/ground, so I can read what the pins level are. I do this once at startup and then the Pic goes to 1 of four modes accordingly.

Mode 1: konfig mode => Ethernet enabled, ready to recieve config data from pc program and store in eeprom.
Mode 2: Ambient measure mode => I measure temp as fast as I can and display it on display whenever temp is different from last run (e.g. no lcd update unless temp changes)
Mode 3: Logging mode => the mode I needed help with: time driven measure + store in EEprom
Mode 4: debug mode => lots of RS232 stuff, hehe.

So you see, only mode 3 is problematic.
Mode 1 is basicly done with EDTP's example, but only with UDP + custom protocol on top. The other modes are just fer show :-)

#jds-pic2
wow.....
I think I have enough to start :-)

What I gather from your post is:
1) I need to combine f.inst. Timer1 and Timer0, make it interrupt every second or similar, ok
2) I then need to have the I2C done in main code. I guess this can be done in less than a second and thus not messing up the timers, cool.

I hope I can make this work and quick :-)

Thankyou so much for your help guys, it means the world to me !

*sends a case of redwhine when it works*

BR
Rasmus 'Izolator'
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