|
|
View previous topic :: View next topic |
Author |
Message |
Izolator
Joined: 10 Feb 2004 Posts: 12 Location: Denmark, (Scandinavia-Europe)
|
Timer & Prescaler basics, explain ? |
Posted: Sun Feb 22, 2004 2:04 pm |
|
|
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
Always a pleasure reading this forum.
Best Regards
Rasmus 'Izolator' |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1933 Location: Norman, OK
|
Temp measuring |
Posted: Sun Feb 22, 2004 2:38 pm |
|
|
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)
|
|
Posted: Sun Feb 22, 2004 2:47 pm |
|
|
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: 1933 Location: Norman, OK
|
|
Posted: Sun Feb 22, 2004 6:42 pm |
|
|
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
|
|
Posted: Sun Feb 22, 2004 10:34 pm |
|
|
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)
|
|
Posted: Mon Feb 23, 2004 9:18 am |
|
|
#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' |
|
|
|
|
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
|