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 support@ccsinfo.com

Data Logger

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



Joined: 15 Mar 2021
Posts: 37

View user's profile Send private message

Data Logger
PostPosted: Wed Mar 31, 2021 9:51 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Mar 31, 2021 3:01 pm     Reply with quote

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: 1611
Location: Central Illinois, USA

View user's profile Send private message

PostPosted: Wed Mar 31, 2021 8:31 pm     Reply with quote

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: 19215

View user's profile Send private message

PostPosted: Thu Apr 01, 2021 12:22 am     Reply with quote

and (of course), the functions need brackets. read_eeprom(address) etc..
jake1272



Joined: 15 Mar 2021
Posts: 37

View user's profile Send private message

PostPosted: Thu Apr 01, 2021 5:02 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Apr 01, 2021 7:22 am     Reply with quote

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: 19215

View user's profile Send private message

PostPosted: Thu Apr 01, 2021 7:32 am     Reply with quote

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: 9097
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Apr 01, 2021 8:00 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Apr 01, 2021 9:16 am     Reply with quote

Thanks for the help
jake1272



Joined: 15 Mar 2021
Posts: 37

View user's profile Send private message

PostPosted: Fri Apr 02, 2021 5:07 am     Reply with quote

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: 9097
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Apr 02, 2021 9:02 am     Reply with quote

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: 19215

View user's profile Send private message

PostPosted: Fri Apr 02, 2021 10:57 am     Reply with quote

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).
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