diff --git a/asyncSerial.cpp b/asyncSerial.cpp new file mode 100644 index 0000000..29b88ba --- /dev/null +++ b/asyncSerial.cpp @@ -0,0 +1,55 @@ +#include "asyncSerial.h" +#include + + +// Avoid being blocked in the serial write routine + +AsyncSerial::AsyncSerial(int baudRate){ + Serial.begin(baudRate); + txtw = 0; + txtr = 0; + tsze = 0; + terr = 0; + tmax = 0; +} + +// Copy text to the buffer for future printing +void AsyncSerial::print(char *txt) { + + int i, l = strlen(txt); + + // If this happens there is a programming bug + if (l > TBLEN) { // Can't handle more than TBLEN at a time + terr = TXT_TOOBIG; // say error and abort + return; + } + + // If the buffer is filling up to fast throw it away and return an error + if ((l + tsze) >= TBLEN) { // If there is no room in the buffer + terr = TXT_OVERFL; // Buffer overflow + return; // Simply stop printing when txt comming too fast + } + + // Copy the new text onto the ring buffer for later output + // from the loop idle function + for (i=0; i tmax) tmax = tsze; // track the max size +} + +// Take the next character from the ring buffer and print it, called from the main loop + +void AsyncSerial::PutChar() { + char c[2]; // One character zero terminated string + if ((tsze) && (!Serial.available())) { // If the buffer is not empty and not reading + //if ((tsze)) { // If the buffer is not empty and not reading + c[0] = txtb[txtr]; // Get the next character from the read pointer + c[1] = '\0'; // Build a zero terminated string + txtr = (txtr + 1) % TBLEN; // Get the next read pointer modulo TBLEN + tsze = (tsze - 1) % TBLEN; // Reduce the buffer size + Serial.print(c); // and print the character + } +} diff --git a/asyncSerial.h b/asyncSerial.h new file mode 100644 index 0000000..ac8798c --- /dev/null +++ b/asyncSerial.h @@ -0,0 +1,36 @@ +#ifndef __ASYNCSERIAL__ +#define __ASYNCSERIAL__ + +#include + +// These two routines are needed because the Serial.print method prints without using interrupts. +// Calls to Serial.print block interrupts and use a wait in kernel space causing all ISRs to +// be blocked and hence we could miss some timer interrupts. +// To avoid this problem call PushTxt to have stuff delivered to the serial line, PushTxt simply +// stores your text for future print out by PutChar. The PutChar routine removes one character +// from the stored text each time its called. By placing a call to PutChar in the outermost loop +// of the Arduino loop function, then for each loop one character is printed, avoiding blocking +// of interrupts and vastly improving the loops real time behaviour. + +class AsyncSerial { + // This is the text ring buffer for real time output to serial line with interrupt on + static const int TBLEN = 1024; // Serial line output ring buffer size, 8K + char txtb[TBLEN]; // Text ring buffer + uint32_t txtw, txtr, // Write and Read indexes + tsze, terr, // Buffer size and error code + tmax; // The maximum size the buffer reached + + typedef enum { TXT_NOERR=0, TXT_TOOBIG=1, TXT_OVERFL=2 } TxtErr; + + public: + AsyncSerial(int baudRate); + // Copy text to the buffer for future printing + void print(char *txt); + // Take the next character from the ring buffer and print it, called from the main loop + void PutChar(); + + +}; + + +#endif diff --git a/cosmicpi-arduino_V1.5.ino b/cosmicpi-arduino_V1.5.ino new file mode 100644 index 0000000..d6e561e --- /dev/null +++ b/cosmicpi-arduino_V1.5.ino @@ -0,0 +1,496 @@ +//Cosmic Pi software for Arduino - modified for STM32, +//J. Devine +//July 2019. +//Licensed under GPL V3 or later. +//cosmicpi.org + +//pinouts +/* + * PA0 - Pin 14 - Shaped Signal 1 + * PA1 - Pin 15 - Shaped Signal 2 + * PA2 - Pin 16 - TX0 + * PA3 - Pin 16 - RX0 + * PA4 - Pin 20 - LED1 - Power/GPS + * PA5 - Pin 21 - LED2 - Event + * PA6 - Pin 22 - Injection leds + * PA7 - Pin 23 - Bias FB1 + * PA8 - Pin 41 - SCL_Slave + * PA9 - Pin 42 - USB_OTG_VBUS + * PA10 - Pin 43 - GPSTX + * PA11 - Pin 44 - USBOTG DM + * PA12 - Pin 45 - USBOTG DP + * PA13 - Pin 46 - SWDIO + * PA14 - Pin 49 - SWCLK + * PA15 - Pin 50 - GPSPPS Input + * PB0 - Pin 26 - Bias FB2 + * PB1 - Pin 27 - Flag to RPi + * PB2 Pin 28 - NC + * PB 3 - Pin 55 - NC + * PB4 - Pin 56 - SDA_Slave + * PB5 - Pin 57 - NC + * PB6 - Pin 58 - GPSRX + * PB7 - Pin 59 - SDA0 + * PB8 - Pin 61 - SCL0 + * PB9 - Pin 62 - NC + * PB10 - Pin 29 - Trigout (input to STM) + * PB12 - Pin 33 - NC + * PB13 - Pin 34 0 HVPSU SCLK (Clock to MAX1932) + * PB14, PB15 - NC + * PC0 - Pin 8 - NC + * PC1 - Pin 9 - HVPSU CL1 + * PC2 - Pin 10 - HVPSU CL2 + * PC3 - Pin 11 - HV PSU DIN + * PC4, PC5, PC6- NC + * PC7 - Pin 38 - HVPSU CS2 + * PC8 - Pin 39 - HVPSU CS1 + * PC9 - Pin 40 - Mag Interrupt + * PC10 - Pin 51 - NC + * PC11 - Pin 52 - STRIGOUT B + * PC12 - Pin 53 - STRIGOUT A + * PC13 - Pin 2 - Baro Int + * PC14 - Pin 3 - Accelint 1 + * PC15 - Pin 4 - Accelint 2. + +*/ +#include "asyncSerial.h" +#include +#include + +static const int SERIAL_BAUD_RATE = 19200; // Serial baud rate for version 1.5 production +static const int GPS_BAUD_RATE = 9600; // GPS and Serial1 line + +// simulate events +static const bool simulateEvents = false; +unsigned long nextSimEvent = 0; + +// enable gps and sensor output +static const bool enableSensorOutput = true; +static const bool enableGPSPipe = true; + +// start our async serial connection for global use +// it would normally work just fine as an instance +// but I want to pass around the pointer to different classes +// to make sure that only one instance is ever used +AsyncSerial *aSer; + +// sensors for global use +#include "sensors.h" +Sensors sensors(aSer); + +// LED pins +#define PPS_PIN PA4 // PPS (Pulse Per Second) and LED +#define EVT_PIN PA5 // Cosmic ray event detected + +// Leds flag +bool leds_on = true; + +// time to print a sensor update (in ms before a PPL) +// the sensor update should always print inbetween the PPS, to avoid problems with the serial pipe from the GPS +static unsigned long distanceSensorUpdatePPS = 200; +unsigned long nextSensorUpdate = 0; + +// How long the event LED should light up (in ms) +static int event_LED_time = 15; + + +// string used for passing data to our asynchronous serial print class +static const int TXTLEN = 512; +static char txt[TXTLEN]; // For writing to serial + +static uint32_t displ = 0; // Display values in loop + +// Timer registers REGA.... +static uint32_t rega1, stsr1 = 0; +static uint32_t stsr2 = 0; + +boolean pll_flag = false; +boolean pll_pulse = false; + +long eventCount = 0; +unsigned long pps_micros = 0; +unsigned long target_mills = millis() + 1030; +unsigned long last_event_LED = 0; +int eventstack = 0; +#define maxevent 100 //we don't expect more events than this +unsigned long evttime [maxevent]; + + + +#define FREQ 84000000 // Clock frequency +#define MFRQ 80000000 // Sanity check frequency value + +// Timer chip interrupt handlers try to get time stamps to within 4 system clock ticks +static uint32_t rega0 = FREQ, // RA reg + stsr0 = 0, // Interrupt status register + ppcnt = 0, // PPS count + delcn = 0; // Synthetic PPS ms + +// GPS and time flags +boolean gps_ok = false; // Chip OK flag +boolean pps_recieved = false; + + + +// ---------------------- Timing + +// Initialize the timer chips to measure time between the PPS pulses and the EVENT pulse +// The PPS enters pin D2, the PPS is forwarded accross an isolating diode to pin D5 +// The event pulse is also connected to pin D5. So D5 sees the LOR of the PPS and the +// event, while D2 sees only the PPS. In this way we measure the frequency of the +// clock MCLK/2 each second on the first counter, and the time between EVENTs on the second +// I use a the unconnected timer block TC1 to make a PLL that is kept in phase by the PPS +// arrival in TC0 and which is loaded with the last measured PPS frequency. This PLL will +// take over the PPS generation if the real PPS goes missing. +// In this implementation the diode is implemented in software, see later + + +void TimersStart() { + + uint32_t config = 0; + + // Set up the power management controller for TC0 and TC2 + +/* + * pmc_set_writeprotect(false); // Enable write access to power management chip + pmc_enable_periph_clk(ID_TC0); // Turn on power for timer block 0 channel 0 + pmc_enable_periph_clk(ID_TC3); // Turn on power for timer block 1 channel 0 + pmc_enable_periph_clk(ID_TC6); // Turn on power for timer block 2 channel 0 + + // Timer block 0 channel 0 is connected only to the PPS + // We set it up to load regester RA on each PPS and reset + // So RA will contain the number of clock ticks between two PPS, this + // value is the clock frequency and should be very stable +/- one tick + + config = TC_CMR_TCCLKS_TIMER_CLOCK1 | // Select fast clock MCK/2 = 42 MHz + TC_CMR_ETRGEDG_RISING | // External trigger rising edge on TIOA0 + TC_CMR_ABETRG | // Use the TIOA external input line + TC_CMR_LDRA_RISING; // Latch counter value into RA + + TC_Configure(TC0, 0, config); // Configure channel 0 of TC0 + TC_Start(TC0, 0); // Start timer running + + TC0->TC_CHANNEL[0].TC_IER = TC_IER_LDRAS; // Enable the load AR channel 0 interrupt each PPS + TC0->TC_CHANNEL[0].TC_IDR = ~TC_IER_LDRAS; // and disable the rest of the interrupt sources + NVIC_EnableIRQ(TC0_IRQn); // Enable interrupt handler for channel 0 + + // Timer block 1 channel 0 is the PLL for when the GPS chip isn't providing the PPS + // it has the frequency loaded in reg C and is triggered from the TC0 ISR + + config = TC_CMR_TCCLKS_TIMER_CLOCK1 | // Select fast clock MCK/2 = 42 MHz + TC_CMR_CPCTRG; // Compare register C with count value + + TC_Configure(TC1, 0, config); // Configure channel 0 of TC1 + TC_SetRC(TC1, 0, FREQ); // One second approx initial PLL value + TC_Start(TC1, 0); // Start timer running + + TC1->TC_CHANNEL[0].TC_IER = TC_IER_CPCS; // Enable the C register compare interrupt + TC1->TC_CHANNEL[0].TC_IDR = ~TC_IER_CPCS; // and disable the rest + NVIC_EnableIRQ(TC3_IRQn); // Enable interrupt handler for channel 0 + + // Timer block 2 channel 0 is connected to the RAY event + // It is kept in phase by the PPS comming from TC0 when the PPS arrives + // or from TC1 when the PLL is active (This is the so called software diode logic) + + config = TC_CMR_TCCLKS_TIMER_CLOCK1 | // Select fast clock MCK/2 = 42 MHz + TC_CMR_ETRGEDG_RISING | // External trigger rising edge on TIOA1 + TC_CMR_ABETRG | // Use the TIOA external input line + TC_CMR_LDRA_RISING; // Latch counter value into RA + + TC_Configure(TC2, 0, config); // Configure channel 0 of TC2 + TC_Start(TC2, 0); // Start timer running + + TC2->TC_CHANNEL[0].TC_IER = TC_IER_LDRAS; // Enable the load AR channel 0 interrupt each PPS + TC2->TC_CHANNEL[0].TC_IDR = ~TC_IER_LDRAS; // and disable the rest of the interrupt sources + NVIC_EnableIRQ(TC6_IRQn); // Enable interrupt handler for channel 0 + + // Set up the PIO controller to route input pins for TC0 and TC2 + + PIO_Configure(PIOC,PIO_INPUT, + PIO_PB25B_TIOA0, // D2 Input + PIO_DEFAULT); + + PIO_Configure(PIOC,PIO_INPUT, + PIO_PC25B_TIOA6, // D5 Input + PIO_DEFAULT); +*/ + +} + +// Dead time is the time to wait after seeing an event +// before detecting a new event. There is ringing on the +// event input on pin 5 that needs suppressing + +uint32_t old_ra = 0; // Old register value from previous event +uint32_t new_ra = 0; // New counter value that must be bigger by dead time +uint32_t dead_time = 840000; // 10ms +uint32_t dead_cntr = 0; // Suppressed interrupts due to dead time +uint32_t dead_dely = 0; // Amout of time lost in dead time + +// Handle the PPS interrupt in counter block 0 ISR + +void TC0_Handler() { + //aSer->print("TC0debug\n"); + + // reset pps_millis + //pps_micros = micros(); + // disable our backup "timer" + //target_mills = millis() + 1010; + + // In principal we could connect a diode + // to pass on the PPS to counter blocks 1 & 2. However for some unknown + // reason this pulls down the PPS voltage level to less than 1V and + // the trigger becomes unreliable !! + // In any case the PPS is 100ms wide !! Introducing a blind spot when + // the diode creates the OR of the event trigger and the PPS. + // So this is a software diode +/* + TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG; // Forward PPS to counter block 2 + TC1->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG; // Forward PPS to counter block 1 + + rega0 = TC0->TC_CHANNEL[0].TC_RA; // Read the RA reg (PPS period) + stsr0 = TC_GetStatus(TC0, 0); // Read status and clear load bits + + if (rega0 < MFRQ) // Sanity check against noise + rega0 = FREQ; // Use nominal value + + TC_SetRC(TC1, 0, rega0); // Set the PLL count to what we just counted + + ppcnt++; // PPS count + gps_ok = true; // Its OK because we got a PPS + pll_flag = true; // Inhibit PLL, dont take over PPS arrived + pps_recieved = true; + + old_ra = 0; // Dead time counters + new_ra = 0; + dead_dely = 0; // Reset dead delay + + if (leds_on) { + digitalWrite(PPS_PIN, !digitalRead(PPS_PIN)); // Toggle led + } + + displ = 1; + + */ + } + +// Handle PLL interrupts +// When/If the PPS goes missing due to a lost lock we carry on with the last measured +// value for the second from TC0 + +void TC3_Handler() { + /* + //aSer->print("TC3debug\n"); + + stsr2 = TC_GetStatus(TC1, 0); // Read status and clear interrupt + + if (pll_flag == false) { // Only take over when no PPS + //aSer->print("PLL FALSE\n"); + TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG; // Forward PPS to counter block 2 + ppcnt++; // PPS count + displ = 1; // Display stuff in the loop + gps_ok = false; // PPS missing + pll_pulse = true; + } + pll_flag = false; // Take over until PPS comes back +*/ +} + +// Handle isolated PPS (via diode) LOR with the Event +// The diode is needed to block Event pulses getting back to TC0 +// LOR means Logical inclusive OR +// Now we are using the software diode implementation + + +void TC6_Handler() { + + /* + //this is the event interrupt + + //aSer->print("TC6 event debug\n"); + + //unsigned long us = micros() - pps_micros; + + + // send the event twice to make sure it is actually recieved without problems + // the reading software must be tuned to not double count this + //sprintf(txt,"Event: sub second micros:%d; Event Count:%d\n", us, eventCount); + //aSer->print(txt); + + // turn on LED, it will be turned off in the main loop + last_event_LED = millis(); + if (leds_on) { + digitalWrite(EVT_PIN, HIGH); + } + + + // Then unblock + rega1 = TC2->TC_CHANNEL[0].TC_RA; // Read the RA on channel 1 (PPS period) + if (eventstack > 0){ + evttime[eventstack] = rega1+evttime[eventstack-1]; + } + else + { + evttime[eventstack] = rega1; + } + eventCount++; + eventstack++; //increment the event stack for this second + + + stsr1 = TC_GetStatus(TC2, 0); // Read status clear load bits + +*/ +} + + + +// Push the GPS state when we recieve a PPS or PPL +void printPPS() { + sprintf(txt,"PPS: GPS lock:%d;\n", gps_ok); + aSer->print(txt); +} + +// Things that need handling on PPS and PLL, but are non time critical +void PPL_PPS_combinedHandling(){ + printPPS(); + // set the time for the next sensor update + nextSensorUpdate = target_mills - distanceSensorUpdatePPS; +} + + +// ------------------------- Arudino Functions + +// Arduino setup function, initialize hardware and software +// This is the first function to be called when the sketch is started + +//setup serials +HardwareSerial Serial1(PB6,PA10); +//HardwareSerial Serial(PA3, PA2); + +void setup() { + +aSer->print("poweron"); + + + if (simulateEvents) { + randomSeed(42); + } + + // The two leds on the front pannel for PPS and Event + if (leds_on) { + pinMode(EVT_PIN, OUTPUT); // Pin for the cosmic ray event + pinMode(PPS_PIN, OUTPUT); // Pin for the PPS (LED pin) + } + if (leds_on) { + digitalWrite(PPS_PIN, HIGH); // Turn on led + } + + //TimersStart(); // Start timers + //target_mills = millis() + 1010; // backup PPS + + aSer = new AsyncSerial(SERIAL_BAUD_RATE); // Start the serial line + // start the GPS + Serial1.begin(GPS_BAUD_RATE); + GpsSetup(); + + // start the i2c bus + Wire.begin(); + Wire.setSDA(PB7); + Wire.setSCL(PB8); + + // start the detector + setupDetector(); + + // start the sensors + sensors = Sensors(aSer); + + + + // initilize the sensors + if(!sensors.init()){ + aSer->print("WARNING: Error in sensor initialization - continuing - output may be inclomplete!\n"); + } else{ + aSer->print("INFO: Sensor setup complete\n"); + } + + aSer->print("INFO: Running\n"); +} + + +// Arduino main loop does all the user space work + +void loop() { +/* + // on a PPS from the GPS + if (pps_recieved){ + PPL_PPS_combinedHandling(); + pps_recieved = false; + } + */ +/* + // if no pps recieved + if (millis() >= target_mills){ + target_mills = millis() + 1000; + // reset pps_millis + pps_micros = micros(); + // while we have no pps we will keep the LED solid + if (leds_on) { + digitalWrite(PPS_PIN, HIGH); + } + gps_ok = false; + PPL_PPS_combinedHandling(); + } + + // simulate an interrupt if we want to simulate events + if (simulateEvents) { + if (millis() >= nextSimEvent){ + aSer->print("INFO: Simulating next event\n"); + TC6_Handler(); + nextSimEvent = millis() + random(100, 1000); + } + } + */ + + // reset event LED when enough time has passed + if (millis() >= (last_event_LED + event_LED_time)){ + if (leds_on) { + digitalWrite(EVT_PIN, LOW); + } + } + + // print out sensor updates + if (enableSensorOutput){ + if (millis() >= (nextSensorUpdate)){ + sensors.printAll(); + nextSensorUpdate += 1000; + } + } + //print out the events here + if (displ>0){ + for (int i=0; i < eventstack; i++){ + if (gps_ok) { + sprintf(txt,"Event: sub second micros:%d/%d; Event Count:%d\n", evttime[i], rega0, (eventCount-eventstack+i)); + } + else { + sprintf(txt,"Event: sub second micros:%d/%d; Event Count:%d\n", evttime[i], FREQ, (eventCount-eventstack+i)); + } + aSer->print(txt); + } + + eventstack=0;//reset the event stack for the next second. + displ=0; + } + + if ((pll_pulse)+(pps_recieved)) + { + printPPS(); + pps_recieved = false; + pll_pulse = false; + } + + // pipe GPS if it's available + if (enableGPSPipe){ + pipeGPS(); + } + + aSer->PutChar(); // Print one character per loop !!! +} diff --git a/detector_setting.ino b/detector_setting.ino new file mode 100644 index 0000000..39561e8 --- /dev/null +++ b/detector_setting.ino @@ -0,0 +1,211 @@ +#include + +// configure these to configure the detector +static const int HV_DEFAULT = 0xAC; +static const int DEFAULT_DAC_THRESH = 559; //modification for V1.5 production batch, this is a generic setting to be tuned by users + +// other constants +static const int HV_MAX = 89; +static const int HV_MIN = 255; +static const int DEFAULT_THRESH = 559; +static const bool USE_DAC = true; + +//set up the pins to remap SPI by hand +//static const int num_devices = 2; +//static const int SS_pin[num_devices] = {14, 15}; +//static const int SCK_pin = 17; +//static const int MISO_pin = 22; +//static const int MOSI_pin = 16; + +// I2C adress pins +//#define MAX5387_PA0_pin A9 +//#define MAX5387_PA1_pin A10 +//#define MAX5387_PA2_pin A11 + +byte thresh1; +byte thresh2; +int bigpart; +int smallpart; + +// initilizes the detector with default values +void setupDetector(){ + // setup pins + detecSetPinModes(); + detcSetConstantPins(); + // set defaults + if (USE_DAC){ + // analogWrite(DAC0, DEFAULT_DAC_THRESH); + // analogWrite(DAC1, DEFAULT_DAC_THRESH); + }else{ + setThreshold(3, DEFAULT_THRESH); + } + setHV(HV_DEFAULT); + sprintf(txt,"Detector threshold: %d\n", DEFAULT_DAC_THRESH); + aSer->print(txt); + sprintf(txt,"Detector setup finished\n"); + aSer->print(txt); +} + + +// sets pin modes needed for the detector +void detecSetPinModes(){ + //setup analog writemode +// analogWriteResolution(12); + // I2C adress pins for the MAX5387 +// pinMode(MAX5387_PA0_pin, OUTPUT); +// pinMode(MAX5387_PA1_pin, OUTPUT); +// pinMode(MAX5387_PA2_pin, OUTPUT); + // HV pins +// digitalWrite(SS, HIGH); // Start with SS high +// for (int i=0; i>8); + + + switch(pot_channel){ + case 1: +sprintf(txt,"Setting threshold on channel 1 to: %d\n", value); + aSer->print(txt); + + Wire.beginTransmission(address); + Wire.write(B00001000); // sends five bytes + Wire.write(bigpart); // sends one byte + Wire.write(smallpart); + Wire.endTransmission(); + break; + case 2: +sprintf(txt,"Setting threshold on channel 2 to: %d\n", value); + aSer->print(txt); + + Wire.beginTransmission(address); + Wire.write(B00000000); // sends five bytes + Wire.write(bigpart); // sends one byte + Wire.write(smallpart); + Wire.endTransmission(); + break; + case 3: + sprintf(txt,"Setting threshold on both channels to: %d\n", value); + aSer->print(txt); + + Wire.beginTransmission(address); + Wire.write(B00001000); // sends five bytes + Wire.write(bigpart); // sends one byte + Wire.write(smallpart); + Wire.endTransmission(); + + Wire.beginTransmission(address); + Wire.write(B00000000); // sends five bytes + Wire.write(bigpart); // sends one byte + Wire.write(smallpart); + Wire.endTransmission(); + break; + } + /* + // do a value check + if (value > 255 || value < 1){ + return; + } else { + value = byte(value); + } + + Wire.begin(); + Wire.beginTransmission(byte(0x28)); // transmit to device #112 + switch(pot_channel){ + case 1: + sprintf(txt,"Setting threshold on channel 1 to: %d\n", value); + aSer->print(txt); + Wire.write(byte(B00010001)); //sets value to the first channel + Wire.write(value); + thresh1 = value; + break; + case 2: + sprintf(txt,"Setting threshold on channel 2 to: %d\n", value); + aSer->print(txt); + Wire.write(byte(B00010010)); //sets value to the second channel + Wire.write(value); + thresh2 = value; + break; + case 3: + sprintf(txt,"Setting threshold on channel 1&2 to: %d\n", value); + aSer->print(txt); + Wire.write(byte(B00010011)); //sets value to both channels + Wire.write(value); + thresh1 = value; + thresh2 = value; + break; + } + + + + Wire.endTransmission(); +*/ +} + + +// set the two HV supplies +byte setHV(byte _send) // This function is what bitbangs the data +{ + if (_send > 0x5A){ //hardlimit values + + sprintf(txt,"INFO: Setting HV 1&2 to: %d\n", _send); + aSer->print(txt); + //reception isn't implemented in this version. + //byte _receive = 0; + + digitalWrite(PC7, LOW); + + for(int i=0; i<8; i++) // There are 8 bits in a byte + { + digitalWrite(PC3, bitRead(_send, 7-i)); // Set MOSI + //delay(1); + digitalWrite(PB13, HIGH); // SCK high + //bitWrite(_receive, i, digitalRead(MISO_pin)); // Capture MISO + digitalWrite(PB13, LOW); // SCK low + //digitalWrite(MOSI_pin, LOW); // Set MOSI + + } + + digitalWrite(PC7, HIGH); + + + digitalWrite(PC8, LOW); + + for(int i=0; i<8; i++) // There are 8 bits in a byte + { + digitalWrite(PC3, bitRead(_send, 7-i)); // Set MOSI + //delay(1); + digitalWrite(PB13, HIGH); // SCK high + //bitWrite(_receive, i, digitalRead(MISO_pin)); // Capture MISO + digitalWrite(PB13, LOW); // SCK low + //digitalWrite(MOSI_pin, LOW); // Set MOSI + + } + + digitalWrite(PC8, HIGH); + //return _receive; // Return the received data + } +} diff --git a/gps_reading.ino b/gps_reading.ino new file mode 100644 index 0000000..df1f81b --- /dev/null +++ b/gps_reading.ino @@ -0,0 +1,49 @@ + +// WARNING: One up the spout !! +// The GPS chip puts the next nmea string in its output buffer +// only if its been read, IE its empty. +// So if you read infrequently the string in the buffer is old and +// has the WRONG time !!! The string lies around like a bullet in +// the breach waiting for some mug. + +//I don't know who wrote that comment.. I'm guessing it was Julian! + +boolean pipeGPS() { + while (Serial1.available()) { + char c[2]; + c[0] = Serial1.read(); + c[1]='\0'; //null character for termination required. + aSer->print(c); + } +} + +// GPS setup + +void GpsSetup() { + +// definitions for different outputs +// only one of these strings can be used at a time +// otherwise they will overwrite each other +// for more information take a look at the QUECTEL L70 protocoll specification: http://docs-europe.electrocomponents.com/webdocs/147d/0900766b8147dbdd.pdf +#define RMCGGA "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" // RCM & GGA +#define ZDA "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0*29" // ZDA +#define GGAZDA "$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0*28" // GGA & ZDA +#define GGA "$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" // GGA + +// gets the firmware version +#define FMWVERS "$PMTK605*31" // PMTK_Q_RELEASE +// Sets the update intervall +#define NORMAL "$PMTK220,1000*1F" // PMTK_SET_NMEA_UPDATE_1HZ +// disables updates for the antenna status (only Adafruit ultimate GPS?) +#define NOANTENNA "$PGCMD,33,0*6D" // PGCMD_NOAN +delay(5000); //added delay to give GPS time to boot. + Serial1.println(NOANTENNA); + Serial1.println(GGAZDA); + Serial1.println(NORMAL); + Serial1.println(FMWVERS); + delay(10000); //wait a bit longer and repeat just in case it hasn't booted + Serial1.println(NOANTENNA); + Serial1.println(GGAZDA); + Serial1.println(NORMAL); + Serial1.println(FMWVERS); +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e8ae147 --- /dev/null +++ b/readme.md @@ -0,0 +1,46 @@ +# CosmicPi V1.5 Arduino DUE software + +This is the Arduino DUE software running on the CosmicPi V1.5. +This software is designed to work in a plug and play fashion, no interaction is required. + + +## Features +* Set up the detector with default values +* Send events from the detector via serial +* Send data from all sensors on the board via serial +* Configure the on board GPS and pipe it's data to serial + +### Meaning of the LEDs +* Lower LED (green): Power and GPS + * Solid: Power, but no GPS lock + * Blinking: Power and GPS lock +* Upper LED (red): Event + * Flash: An Event has been registered + + +## ToDo before release +* The timer needs a rework, its current implementation is imprecise +* The detector should be able to calibrate it's parameters automatically + * High voltage + * Thresholds + + +## Installation +For regular users of the CosmicPi V1.5 this should be taken care of automatically by the software on the RaspberryPi. + +For everybody interested in looking into developing this oneself: +1. Download the most recent Arduino IDE: https://www.arduino.cc/en/main/software +2. Install the SAM core: https://www.arduino.cc/en/Guide/Cores +3. Clone this repository: `git clone https://github.com/CosmicPi/cosmicpi-arduino_V1.5.git` +(note you can also download the .zip file from the menu above to the right, expand and remember to re-name the directory so cosmpicpi-arduino_v1.5 otherwise Arduino will give you errors) +4. Open the file `cosmicpi-arduino_V1.5.ino` with the Arduino IDE +5. Connect your CosmicPi to your computer via the USB **Programming** port +6. Select the newly appearing port in the Arduino IDE (Tools -> Port) +7. Compile and upload the firmware (Sketch -> Upload) + + +## Usage +As soon as the CosmicPi is connected you can open the software of your choice for monitoring serial data. Open the Arduinos serial port with a baudrate of 19200. +This firmware is designed to be a plug and play firmware. +It will only send data. It will not accept inputs. What data is sent is defined [here](https://github.com/CosmicPi/cosmicpi-rpi_V1.5/blob/master/documentation/CosmicPi_V15_serial_comm.txt). + diff --git a/sensors.cpp b/sensors.cpp new file mode 100644 index 0000000..c21c3b7 --- /dev/null +++ b/sensors.cpp @@ -0,0 +1,133 @@ +#include "sensors.h" +#include +#include "asyncSerial.h" +// LPS lib from here: https://github.com/pololu/lps-arduino +//LSM303 isn't used anymore - change this out. +#include "src/LPS.h" +// LSM303 lib from here: https://github.com/pololu/lsm303-arduino +#include "src/LSM303.h" +// HTU21D lib from here: https://github.com/adafruit/Adafruit_HTU21DF_Library +#include "src/SparkFunHTU21D.h" + +Sensors::Sensors(AsyncSerial *aS){ + aSer = aS; +} + +// initilize sensors +bool Sensors::init(){ + bool return_val = true; + baroOK = baro.init(); + if (!baroOK) + { + sprintf(txt,"WARINING: Couldn't initilize the barometer!\n"); + aSer->print(txt); + return_val = false; + } else{ + baro.enableDefault(); + } + + accelMagnetoOK = accelMagneto.init(); + if (!accelMagnetoOK) + { + sprintf(txt,"WARINING: Couldn't initilize the accelerometer and magnetometer!\n"); + aSer->print(txt); + return_val = false; + } else{ + accelMagneto.enableDefault(); + } + + humidOK = humidity.begin(); + if (!humidOK) + { + sprintf(txt,"WARINING: Couldn't initilize the humidity sensor!\n"); + aSer->print(txt); + return_val = false; + } + + return return_val; +} + + +// different ways of printing data +void Sensors::printBaro(){ + if (baroOK) { + float pressure = baro.readPressureMillibars(); + float altitude = baro.pressureToAltitudeMeters(pressure); + float temperature = baro.readTemperatureC(); + + sprintf(txt,"Pressure: %f;\nAltitude: %f;\nTemperatureCBaro: %f;\n", pressure, altitude, temperature); + aSer->print(txt); + } +} + +void Sensors::printAccel(){ + if (accelMagnetoOK) { + accelMagneto.read(); + float x = AclToMs2(accelMagneto.a.x); + float y = AclToMs2(accelMagneto.a.y); + float z = AclToMs2(accelMagneto.a.z); + sprintf(txt,"AccelX: %f;\nAccelY: %f;\nAccelZ: %f;\n", x, y, z); + aSer->print(txt); + } +} + +void Sensors::printMagneto(){ + if (accelMagnetoOK) { + accelMagneto.read(); + float x = MagToGauss(accelMagneto.m.x); + float y = MagToGauss(accelMagneto.m.y); + float z = MagToGauss(accelMagneto.m.z); + sprintf(txt,"MagX: %f;\nMagY: %f;\nMagZ: %f;\n", x, y, z); + aSer->print(txt); + } +} + +void Sensors::printHumid(){ + if (humidOK) { + sprintf(txt,"TemperatureCHumid: %f;\nHumidity: %f;\n", humidity.readTemperature(), humidity.readHumidity()); + aSer->print(txt); + } +} + +void Sensors::printTempAvg(){ + short count = 0; + int sum = 0; + if (humidOK) { + count++; + sum += humidity.readTemperature(); + } + if (baroOK) { + count++; + sum += baro.readTemperatureC(); + } + + if (count != 0){ + float out = sum / count; + sprintf(txt,"TemperatureC: %f;\n", out); + aSer->print(txt); + } +} + +void Sensors::printAll(){ + printBaro(); + printAccel(); + printMagneto(); + printHumid(); + printTempAvg(); +} + +static const double GEARTH = 9.80665; +// normaly there is no - in ACL_FS, but the Accel is on upwards down, so it was necessary to get correct values +static const double ACL_FS = -2.0; // +-2g 16 bit 2's compliment + +// Convert to meters per sec per sec + +float Sensors::AclToMs2(int16_t val) { + return (ACL_FS * GEARTH) * ((float) val / (float) 0x7FFF); +} + +#define MAGNETIC_FS 4.0 // Full scale Gauss 16 bit 2's compliment + +float Sensors::MagToGauss(int16_t val) { + return (MAGNETIC_FS * (float) val) / (float) 0x7FFF; +} diff --git a/sensors.h b/sensors.h new file mode 100644 index 0000000..181efa7 --- /dev/null +++ b/sensors.h @@ -0,0 +1,50 @@ +#ifndef __SENSORS__ +#define __SENSORS__ + +#include +#include "asyncSerial.h" +// LPS lib from here: https://github.com/pololu/lps-arduino +#include "src/LPS.h" +// LSM303 lib from here: https://github.com/pololu/lsm303-arduino +#include "src/LSM303.h" +// HTU21D lib from here: https://github.com/sparkfun/SparkFun_HTU21D_Breakout_Arduino_Library +#include "src/SparkFunHTU21D.h" + +class Sensors { + // object for the pressure sensor + LPS baro; + // object for the accel and magnetometer + LSM303 accelMagneto; + // object for humidity sensor + HTU21D humidity; + + // vars for checking if a sensor initilized correctly + bool baroOK; + bool accelMagnetoOK; + bool humidOK; + + // cute class for printing and the needed char array + AsyncSerial *aSer; + static const int TXTLEN = 512; + char txt[TXTLEN]; // For writing to serial + + public: + Sensors(AsyncSerial *aS); + // initilize sensors + bool init(); + // different ways of printing data + void printBaro(); + void printAccel(); + void printMagneto(); + void printHumid(); + void printTempAvg(); + void printAll(); + + private: + float AclToMs2(int16_t val); + float MagToGauss(int16_t val); + +}; + + +#endif