SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By zws
#134789
Hi guys,

I recently succeeded in updating the Geiger Counter (http://www.sparkfun.com/products/9848) firmware to v13. I a total novice with AVR Studio so doing this is a real achievement. :)

But I searched the net and realized that the counter should output counts per minute instead of counts per second (as available with v13) as to be able to really understand the output and properly compare it with other people and the accepted limit. See: https://pachube.com/feeds?tag=radiation

Anyone willing to modify it in order to output counts per minute? Or any suggestions?


Thank you,
zws

Here is the code (v13) supplied by Aaron Weiss at: https://github.com/a1ronzo/SparkFun-Gei ... its/master
Code: Select all
/*
    9-1-09
    Aaron Weiss
    
	Geiger Counter
	
	v13: displays counts per second, every second
	
	Internal 8MHz: lfuse = 0xE2, hfuse = 0xDF
    
*/
#include <stdlib.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#define FOSC 8000000
#define BAUD 9600
#define MYUBRR 51 

#define sbi(var, mask)   ((var) |= (uint8_t)(1 << mask))
#define cbi(var, mask)   ((var) &= (uint8_t)~(1 << mask))

#define STATUS_LED 5

//uint16_t global_clock;
volatile long i=0;

///============Initialize Prototypes=====================///////////////////////
void ioinit(void);      // initializes IO
static int uart_putchar(char c, FILE *stream);
uint8_t uart_getchar(void);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
void delay_ms(uint16_t x); // general purpose delay
/////===================================================////////////////////////

ISR (INT0_vect) 
{		
	i++;
	cbi(PORTC, STATUS_LED);
}

ISR(TIMER1_OVF_vect)
{
	TCNT1 = 34000;	//(256/8MHz)*(65536bits-34000)~=1.009s
	printf("counts per second: %ld  \r", i);
	i=0;
}

//=========MAIN================/////////////////////////////////////////////////
int main(void)
{
	ioinit(); //Setup IO pins and defaults
	
	delay_ms(1200); //wait to settle
	
	while(1)
	{	
		sbi(PORTC, STATUS_LED);
		delay_ms(30);
	}
	
	cli();
}

////////////////////////////////////////////////////////////////////////////////
///==============Initializations=======================================/////////
////////////////////////////////////////////////////////////////////////////////
void ioinit (void)
{
    //1 = output, 0 = input
    DDRB = 0b11101111; //PB4 = MISO 
    DDRC = 0b00110001; //Output on PORTC0, PORTC4 (SDA), PORTC5 (SCL), all others are inputs
    DDRD = 0b11110010; //PORTD (RX on PD0), input on PD2
	
    //USART Baud rate: 9600
    UBRR0H = MYUBRR >> 8;
    UBRR0L = MYUBRR;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);    
	
	stdout = &mystdout; //Required for printf init
	
	//pin change interrupt on INT0
	EICRA = (1<<ISC01);//falling edge generates interrupt
	EIMSK = (1<<INT0);
	
	// Setting Timer 1:
	// normal mode
	TCCR1A = 0x00;
	// Set to clk/256 
	TCCR1B |= (1<<CS12);
	//enable overflow interrupt
	TIMSK1 |= (1<<TOIE1);
	//load timer with a value to optimize for 1 second, (256/8MHz)*(65536bits-34000)~=1.009s
	TCNT1 = 34000;
	
	sei(); //turn on global interrupts
}

static int uart_putchar(char c, FILE *stream)
{
    if (c == '\n') uart_putchar('\r', stream);
  
    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = c;
    
    return 0;
}

uint8_t uart_getchar(void)
{
    while( !(UCSR0A & (1<<RXC0)) );
    return(UDR0);
}

//General short delays
void delay_ms(uint16_t x)
{
  uint8_t y, z;
  for ( ; x > 0 ; x--){
    for ( y = 0 ; y < 40 ; y++){
      for ( z = 0 ; z < 40 ; z++){
        asm volatile ("nop");
      }
    }
  }
}
By zws
#134877
Hey, thank you Xantor!

But it seems to me a bit weird to just put the calculations with the printf and not not do all the calculations before.

For example there is this:
"//load timer with a value to optimize for 1 second, (256/8MHz)*(65536bits-34000)~=1.009s
TCNT1 = 34000;"

which asks to be modified :P

I don't have a clue about interrupts (which this code seems to use), but the logic, in my opinion, would be to listen to all the counts for one minute, add them up and then output the resulting number each minute. Am I wrong?

Your modification doesn't seem to do that. But I might be wrong here as well.

Any thoughts!
By Xantor
#134897
TCNT1 = 34000;
reloads the timer for the next period. (1 second)

If you want to listen for a full minute, your display would also update only once per minute, which is very very slow.

Better would be to keep a running one minute average updated every second, but that's a little more complicated.

something like (out of the top of my head, so debugging needed)
Code: Select all

// This replaces:

/*
ISR(TIMER1_OVF_vect)
{
   TCNT1 = 34000;   //(256/8MHz)*(65536bits-34000)~=1.009s
   printf("counts per second: %ld  \r", i);
   i=0;
}
*/

ISR(TIMER1_OVF_vect)
{

// Variables we need. (static keep their values between subroutine invocations)
  static long count[60], total;
  static int current=0, firstrun=1;
  long c;
  int j;

// Start by resetting the timer
  TCNT1 = 34000;  // Set timer to interrupt again when done with the next second
  c = i; i=0;          // Store counts, clear counts so the ISR will start counting from 0.

  if(firstrun)          // on startup, fill all the counts with the first read value, for initial display 
  {
    for(j=0;j<60;j++) count[j]=c;
    firstrun=0;
  }

  count[current] = c;  // Store current count in the next place in the array (60 places ringbuffer)
  current = ++current%60;  // equivalent to: current +=1; if(current==60) current=0;
  
  // sum the ringbuffer (sum of the previous 60 seconds of reads)
  total = 0;
  for(j=0;j<60;j++) total+=count[j];

  // display CPM and CPS counts
  printf("CPM: %ld  CPS: %ld\n",total,c);
}
Last edited by Xantor on Sat Oct 22, 2011 6:42 am, edited 4 times in total.
By zws
#134938
Better would be to keep a running one minute average updated every second, but that's a little more complicated.
Brilliant idea!

..Unfortunately your code is written with Chinese characters for me! :((. Tomorrow I will try to look at it while searching through the internet. Maybe I will get to decode it :)
By zws
#134952
Hi Xantor,

Would you mind putting some comments on the lines of code you wrote? :) Just to understand what each one does..

And could you please tell me which part of code should I replace from the old code?


Thank you so much for all your help!
By Xantor
#134955
Updated above post. Also changed a few bits around.

This will need about 250 bytes of extra RAM on the controller, to store a minute worth of readings.
By zws
#135029
Hey Xantor!!

I just tried it and it seems to work!! :D Happy!!


When I compile it, it gives me this warning:


../geiger_v14.c:64:11: warning: operation on 'current' may be undefined

Should I define it at the start? Like I said it seems to work without any other modifications.


By the way, do you maybe know why it outputs each sign of the printf separately and not on a single line?

i mean it gives something like this:
C
P
M
:

10


C
P
S
:

1

instead of: CPM: 10 CPS: 1

I thought that the "\n" thing at the end was supposed to create a new line with each update. Is it maybe because I use a couple of XBees 1 to get the readout? Do they maybe change the original output?
By zws
#135030
I am using this code to listen with an Arduino with an Xbee shield (in case someone is wondering):
Code: Select all
#include <NewSoftSerial.h>
//  http://forums.fungizmos.com/viewtopic.php?f=6&t=4
//  To easily use the Xbee shield as a PC <-> Xbee passthrough we can tie the Xbee serial ports
//  to unused arduino i/o pins and use a software serial library to interface with the Xbee shield.
//  This leaves the hardware serial port fre to be used by the arduino usb connection to the PC.
//  We can now send/receive commands from the computer, process on the host arduino if needed and then relay
//  to the remote Xbee.  Any data sent back from the remote Xbee can be received on the host Xbee, data again 
//  processed if needed and info can be sent via usb to the pc.

// Shield still needs to be removed before programming arduino.

//remove the two serial select jumpers on the Xbee shield and wire the two middle pins to unused i/o
//pins on the arduino.  In this case we are using 2 & 3

//LadyAda's Serial library can be found here: http://www.ladyada.net/make/eshield/download.html
NewSoftSerial XbeeSerial =  NewSoftSerial(3, 2); 

void setup()  {

  Serial.begin(9600); //connection to host computer
  Serial.println("Xbee Software Serial Test!");

  XbeeSerial.begin(9600);    //connection to Xbee shield

}

void loop()                     
{
  //simple relay test.  Of course we could be processing data here as needed before sending out in either direction.
  if (XbeeSerial.available()) {
      Serial.print((char)XbeeSerial.read());  //grab avail data from Xbee and send to host pc
  }
  if (Serial.available()) {
      XbeeSerial.print((char)Serial.read());  //Now take any incoming data from pc and relay to Xbee
  }
}