View previous topic :: View next topic |
Author |
Message |
jake1272
Joined: 15 Mar 2021 Posts: 37
|
Data Logger |
Posted: Wed Mar 31, 2021 9:51 am |
|
|
Hello,
I want to design a data logger that samples temperature every second, by ADCing the temperature sensor, converting the value and storing it as a centigrade degree value in internal EEPROM taking 200 samples before overwriting the start EEPROM address, continuously.
And after, uploading all 200 samples in EEPROM to RS232 the instant a push-button is pressed and then return to log temperature.
I am using pic16f1847 and MCP9700/9700A 10 mV/ C. @ 3.3V linear thermometer.
The compiler is CCS v5.015
The IDE is MPLAB X IDE v5.40
Can anybody guide me with the code structure?
As far I know
To write: write_eeprom address, value
To read: value = read_eeprom address
And to send entire contents of EEPROM to PC via predefined RS232, in hex format.
Code: | void DumpEEprom (){ //EEPROM to RS232 subroutine
for(i =0;i<255;i++) printf ( "Data at address %X is: %u", i , read_eeprom i )
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Mar 31, 2021 3:01 pm |
|
|
Setup a timer interrupt that gives you an interval of 1 second.
I gave you a link to code for that in this thread:
http://www.ccsinfo.com/forum/viewtopic.php?t=59242
Inside the timer isr, read the ADC once per second. Put the result in a
global variable. Set a global flag variable that indicates you have new data.
In main(), create a while() loop that polls that flag. If it's set, then write
the data to eeprom and clear the flag. If the data is 16-bit, then you
should move the data into a local variable before you use the data.
Code: |
void main()
{
int16 temp;
while(TRUE)
{
if(adc_data_flag)
{
// Copy adc_result (updated with new data in the timer interrupt)
// into a local variable with interrupts disabled, to avoid reading
// a partially updated int16 adc value.
disable_interrupts(GLOBAL);
temp = adc_result;
enable_interrupts(GLOBAL);
adc_data_flag = FALSE;
// Then use the data in 'temp' to write to eeprom or to display
}
} |
Also, poll the push-button inside the while() loop. If it's pressed, then
take action. |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Wed Mar 31, 2021 8:31 pm |
|
|
And make sure to look up "write endurance" about the PIC's internal memories.
You will be surprised there are a limited number of write cycles that can easily be burned up with data-logging style operations.
Just so you're fully informed. _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19510
|
|
Posted: Thu Apr 01, 2021 12:22 am |
|
|
and (of course), the functions need brackets. read_eeprom(address) etc.. |
|
|
jake1272
Joined: 15 Mar 2021 Posts: 37
|
|
Posted: Thu Apr 01, 2021 5:02 am |
|
|
Thank you all for your advice.
Here is my attempt. Any further suggestions?
Code: | #include <16F1847.h>
#DEVICE ADC=10 //Select 10 bit mode; use 8 for 8 bit mode
#fuses HS, NOWDT, PUT, BROWNOUT, NOLVP
#use delay(clock=20M)
#use rs232(baud=9600, UART1, ERRORS)
#define MCP9700 PIN_A0
#define SW PIN_A1
#define INTS_PER_SECOND 76
int8 data_seconds_timer = 0;
int16 i;
unsigned long value ;
//--------------------------
#INT_TIMER0
void t0_isr(void)
{
static int8 int_count = INTS_PER_SECOND;
int_count-- ;
if(int_count == 0) // Has 1 second passed ?
{
int_count = INTS_PER_SECOND; // If so, reload counter
if(data_seconds_timer) // Is the data timer running ?
{
data_seconds_timer--; // If so, decrement it
if(data_seconds_timer == 0) // Is it done ?
{
value = dac_write(i);
}
}
}
}
void main()
{
int16 temp;
int1 adc_data_flag;
int1 adc_result;
while(TRUE)
{
if(adc_data_flag)
{
// Copy adc_result (updated with new data in the timer interrupt)
// into a local variable with interrupts disabled, to avoid reading
// a partially updated int16 adc value.
write_eeprom (0x00 , value);
disable_interrupts(GLOBAL);
temp = adc_result;
enable_interrupts(GLOBAL);
setup_oscillator(OSC_8MHZ | OSC_PLL_ON); // Set internal oscillator to 32MHz (8MHz and PLL)
setup_adc(ADC_CLOCK_DIV_32); // Set ADC conversion time to 32Tosc
setup_adc_ports(sAN0); // Configure AN0 pin as analog
set_adc_channel(0);
adc_data_flag = FALSE;
if(input(SW))value = read_eeprom (0x00);
// Then use the data in 'temp' to write to eeprom or to display
printf("\r\n\nAll Done.\n");
}
}} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Apr 01, 2021 7:22 am |
|
|
Quote: | value = dac_write(i); |
This function is for the DAC, not the ADC. Also the function doesn't return
any value. That's shown in the CCS manual.
Quote: | void main()
{
int16 temp;
int1 adc_data_flag;
int1 adc_result; |
As written above, adc_data_flag is not global. It's local to main(). It
won't work as a way to communicate between the timer interrupt routine
and main().
adc_result can not be a 1-bit variable. If you're doing 10-bit ADC
readings, it must be 16 bits. Also, it must be global, not local.
Also, you were supposed to read the ADC in the interrupt routine and
put the result in adc_result. Then set adc_data_flag = TRUE there.
You need to learn the C language before you do anything else.
If you don't learn it first, you can't understand instructions from us. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19510
|
|
Posted: Thu Apr 01, 2021 7:32 am |
|
|
As another comment, there is no setup code for the timer, or to enable
the timer interrupt itself. You might want to consider taking an ADC
reading on each timer tick, and averaging. Otherwise the value may well
show some significant noise. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Thu Apr 01, 2021 8:00 am |
|
|
from your original post...
Quote: | for(i =0;i<255;i++) printf ( "Data at address %X is: %u", i , read_eeprom i ) |
This implies that the temperature readings will be 1 byte, so 20,21,22 or 68,69,70, so you'll need to convert the actual ADC bits into a single byte.
Get that code working properly and send to PC, NOT to EEPROM ! EPROM has a limited life and you could easily use it all up BEFORE your code actually works properly.
For this datalogger, you could just use an inline delay, to make coding simple..
Code: |
main()
loop start...
for loopcount=0 to <200...
read sensor
convert to byte
send to PC
delay_ms(1000)
loop end
|
This basic, simple code is easy to code....
ONCE it works THEN add the 'save to EEPROM/send EEPROM functions. |
|
|
jake1272
Joined: 15 Mar 2021 Posts: 37
|
|
Posted: Thu Apr 01, 2021 9:16 am |
|
|
Thanks for the help |
|
|
jake1272
Joined: 15 Mar 2021 Posts: 37
|
|
Posted: Fri Apr 02, 2021 5:07 am |
|
|
Here is my second attempt The code does not give correct reading like 16 17 degrees.
Not sure it prints 200 samples though and now I am struggling with dump EEPROM
Code: | #include <16F1847.h>
#DEVICE ADC=10 //Select 10 bit mode; use 8 for 8 bit mode
#fuses INTRC_IO, NOWDT, NOPROTECT, MCLR, NOBROWNOUT
#use delay(clock=8000000)
#use rs232(baud=9600, UART1)
void main(){
unsigned long i; //A long (16 bit) variable type is needed to hold ADC value
float degreesC; //degreesC is floating point number
signed int8 temp_eeprom;
signed int8 address=0;
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(sAN0);
set_adc_channel(0);
delay_us(10);
while(1){
i = read_adc();
degreesC = (((float)i* 0.0048828) - 0.5)*100.0;
temp_eeprom = (int8)degreesC;
write_eeprom(address,temp_eeprom);
address = address + 1;
if (address > 199)address = 0;
printf("\r\nTemperature is:%f",degreesC);
delay_ms(5);
}
} |
|
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Fri Apr 02, 2021 9:02 am |
|
|
Do NOT code for the EEPROM writes and reads !
Get the read sensor/send correct data working properly first!!
Forget about floating point numbers, the 'math' takes a very,very long time and not accurate. You're far better to use 'scaled integers'. VERY fast (10-50x) and better accuracy.
If you keep testing r/w to the EEPROM, there is the probability that you'll destroy the device (EEPROM has a limited number of 'cycles')
Concentrate on getting/sending the temperature first ! ONCE that is done THEN add the 'save to EEPROM' and 'send to PC' functions. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19510
|
|
Posted: Fri Apr 02, 2021 10:57 am |
|
|
First thing to understand is that though you can read a value from the
sensor and ADC to a tiny fraction of a degree, this is completely pointless.
You are using your supply as your 'reference', so best case perhaps 1%
error. The sensor itself has a quoted accuracy of +/-2 degrees. So
setup your code to just return integer degrees. Anything else is just adding
extra complexity and gaining nothing.
Then the bytes stored are just simple degrees. No floating point needed or
wanted. The conversion for the reading is simply:
degrees=((adc_val*12)+6)/25;
This will give you a simple integer value to the limits of the accuracy you
can actually use, much faster than the floating maths (and much smaller). |
|
|
|