commit 3645d1eec90d141dee6cf8aa0aec5aa9eb75c1cf Author: James Devine Date: Fri Dec 16 13:42:15 2016 +0100 diff --git a/CosmicOscilloscope b/CosmicOscilloscope new file mode 100644 index 0000000..3ae22cc --- /dev/null +++ b/CosmicOscilloscope @@ -0,0 +1,273 @@ +#undef HID_ENABLED + +// Arduino Due ADC->DMA->USB 1MSPS +// by stimmer +// from http://forum.arduino.cc/index.php?topic=137635.msg1136315#msg1136315 +// Input: Analog in A0 +// Output: Raw stream of uint16_t in range 0-4095 on Native USB Serial/ACM + +// on linux, to stop the OS cooking your data: +// stty -F /dev/ttyACM0 raw -iexten -echo -echoe -echok -echoctl -echoke -onlcr + +volatile int bufn,obufn; +uint16_t buf[4][256]; // 4 buffers of 256 readings + +//HV PSU init +const int SS_pin = 42; //tbc +const int SCK_pin = 44; +const int MISO_pin = 22; +const int MOSI_pin = 43; +byte sendValue; // Value we are going to send + + +void ADC_Handler(){ // move DMA pointers to next buffer + int f=ADC->ADC_ISR; + if (f&(1<<27)){ + bufn=(bufn+1)&3; + ADC->ADC_RNPR=(uint32_t)buf[bufn]; + ADC->ADC_RNCR=256; + } +} + +void setup(){ +//HV Setup +digitalWrite(SS, HIGH); // Start with SS high +pinMode(SS_pin, OUTPUT); +pinMode(SCK_pin, OUTPUT); +pinMode(MISO_pin, INPUT); //this is the avalanche pin, not implemented yet +pinMode(MOSI_pin, OUTPUT); + +sendValue = 0xFF; //Set the HV here onetime +sendValue = bitBang(sendValue); // Transmit data + + +//set up the thresholds +pinMode(35, OUTPUT); //setup for the MAX5387 - later set to 0. signal PA0 +pinMode(36, OUTPUT);//setup for the MAX5387 - later set to 0. signal PA1 +pinMode(37, OUTPUT);//setup for the MAX5387 - later set to 0. signal PA2 +digitalWrite(35, LOW);//configure the address of the MAX5387 pot +digitalWrite(36, LOW);//configure the address of the MAX5387 pot +digitalWrite(37, LOW);//configure the address of the MAX5387 pot +pinMode(5, INPUT); //set the interrupt pin for trigger as high impedance, probably not required + +Wire.begin(); +Wire.beginTransmission(byte(0x28)); // transmit to device #112 +Wire.write(byte(B00010011)); //set both registers +//Wire.write(byte(B00010001)); //set both register A only +//Wire.write(byte(B00010010)); //set both register B only +Wire.write(byte(0x50));// sets registers to this value (1 step = 13mV) +Wire.endTransmission(); // stop transmitting + + + //ADC setup + + + SerialUSB.begin(0); + while(!SerialUSB); + + pmc_enable_periph_clk(ID_ADC); + adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST); + ADC->ADC_MR |=0x80; // free running + + ADC->ADC_CHER=0x80; + + NVIC_EnableIRQ(ADC_IRQn); + ADC->ADC_IDR=~(1<<27); + ADC->ADC_IER=1<<27; + ADC->ADC_RPR=(uint32_t)buf[0]; // DMA buffer + ADC->ADC_RCR=256; + ADC->ADC_RNPR=(uint32_t)buf[1]; // next DMA buffer + ADC->ADC_RNCR=256; + bufn=obufn=1; + ADC->ADC_PTCR=1; + ADC->ADC_CR=2; +} + +void loop(){ + while(obufn==bufn); // wait for buffer to be full + SerialUSB.write((uint8_t *)buf[obufn],512); // send it - 512 bytes = 256 uint16_t + SerialUSB.write((uint8_t *)buf[obufn],512); // send it - 512 bytes = 256 uint16_t + obufn=(obufn+1)&3; +} + +byte bitBang(byte _send) // This function is what bitbangs the data +{ + + //reception isn't implemented in this version. + byte _receive = 0; + digitalWrite(SS_pin, LOW); // SS low + for(int i=0; i<8; i++) // There are 8 bits in a byte + { + digitalWrite(MOSI_pin, bitRead(_send, 7-i)); // Set MOSI + //delay(1); + digitalWrite(SCK_pin, HIGH); // SCK high + //bitWrite(_receive, i, digitalRead(MISO_pin)); // Capture MISO + digitalWrite(SCK_pin, LOW); // SCK low + //digitalWrite(MOSI_pin, LOW); // Set MOSI + + } + digitalWrite(SS_pin, HIGH); // SS high again + + //return _receive; // Return the received data +} + +//timing system + +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); +} + +// Timer chip interrupt handlers try to get time stamps to within 4 system clock ticks + +static uint32_t displ = 0; // Display values in loop + +static uint32_t rega0 = FREQ, // RA reg + stsr0 = 0, // Interrupt status register + ppcnt = 0, // PPS count + delcn = 0; // Synthetic PPS ms + +static uint32_t rega1, stsr1 = 0; + +static uint32_t stsr2 = 0; + +boolean pll_flag = false; + +int old_ra = 0; +int new_ra = 0; +#define DEAD_TIME 42000 // 1ms + +// Handle the PPS interrupt in counter block 0 ISR + +void TC0_Handler() { + + // 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 + + SwapBufs(); // Every PPS swap the read/write buffers + ppcnt++; // PPS count + displ = 1; // Display stuff in the loop + gps_ok = true; // Its OK because we got a PPS + pll_flag = true; // Inhibit PLL, dont take over PPS arrived + + old_ra = 0; // Dead time counters + new_ra = 0; + + IncDateTime(); // Next second + + if (pps_led) { + digitalWrite(PPS_PIN,HIGH); + pps_led = false; + } else { + digitalWrite(PPS_PIN,LOW); + pps_led = true; + } +} + +// 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() { + + stsr2 = TC_GetStatus(TC1, 0); // Read status and clear interrupt +#if FLG_PIN + digitalWrite(FLG_PIN,HIGH); // Flag set (for debug) + digitalWrite(FLG_PIN,LOW); +#endif + + if (pll_flag == false) { // Only take over when no PPS + + TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG; // Forward PPS to counter block 2 + SwapBufs(); // Every PPS swap the read/write buffers + ppcnt++; // PPS count + displ = 1; // Display stuff in the loop + gps_ok = false; // PPS missing + + IncDateTime(); // Next second + } + pll_flag = false; // Take over until PPS comes back +} + +// We need a double buffer, one is being written by the ISR while +// the other is read from user space within one second. \ No newline at end of file