Skip to content

Commit 875cb72

Browse files
authored
Merge pull request #15 from hadoopmarc/fix-overflow
Fix overflow
2 parents f8abc9a + 033eac8 commit 875cb72

File tree

2 files changed

+78
-68
lines changed

2 files changed

+78
-68
lines changed

arduino/waveform-h-bridge/gps_shutter_control.cpp

Lines changed: 60 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/*
2-
Script for generating digital signals as input for a driver of an LCD-shutter.
2+
Script for generating digital signals as input for an H-bridge driver of an LCD-shutter.
33
The signals have a specific pattern useful for meteor photography and are synced
44
to the 1 Hz GPS TIMEPULSE signal, also called PPS signal. For this, the script
55
applies three mechanisms:
66
1. Shortly after each GPS pulse, so during the blanking period of a 1 second cycle, a
77
new train of 15 pulses is started that initially is synced very closely to the GPS
88
signal.
9-
2. The CPU frequency is measured during a relatively long period (> 60 seconds) with
9+
2. The CPU frequency is measured during a relatively long period (>= 60 seconds) with
1010
the GPS pulses as an accurate reference. After that, the timing of the train of 15
1111
pulses is done using this calibrated CPU frequency. This significantly improves
1212
the syncing of the pulse train at the end of a cycle. Note that the Arduino clock
@@ -37,11 +37,6 @@ between successive micros() calls are used, which are always correct irrespectiv
3737
occurred between the two calls.
3838
*/
3939

40-
/* Temporary extra script for testing with an H-bridge as driver. The LCD-shutter needs the slow decay mode of the H-bridge to
41-
* become transparent. In the slow decay mode the terminals of the LCD-shutter are shortcut. The slow decay mode requires
42-
* a logical high signal on both input terminals of the DRV8833 module. This is different from driving the opamp as in the
43-
* regulare script.
44-
*/
4540
#include <arduino.h>
4641

4742
const int PIN_GPS = 2; // Match with hardware connection
@@ -56,20 +51,20 @@ float calibratedFreq = CPU_FREQ; // Optionally set to a log
5651
const int N_HALF_WAVE = 32; // Inverse of BLOCK_TICKS;
5752
const int PRESCALER = 64; // Prescaler value to be set for TIMER1
5853
const int TICK_MICROS = 4; // TIMER1 resolution with prescaler at 64
59-
const int N_STABLE = 10; // Number of successive GPS pulses required for stability
60-
const int N_CALIBRATE = 20; // Number of successive GPS pulses required for calibration (< 4200)
54+
const int N_STABLE = 10; // Number of successive GPS pulses required for starting second markers
55+
const int N_CALIBRATE = 60; // Number of successive GPS pulses required for calibration (< 4200)
6156
const unsigned int TIMER_SAFETY = 1; // For being sure the duration of the pulse train < 1.000000 second
6257
// This is for block length, so impact = N * 32 * 4 = N * 128 microseconds
6358

6459
volatile bool gpsHit = false; // Set by the gpsIn interrupt only and cleared after processing
6560
volatile unsigned long lastGpsMicros = 0; // Set by the gpsIn interrupt only and ignored before gpsHit = true
66-
unsigned long iGpsPulse = 0; // Number of successive GPS pulses counted
67-
unsigned long prevGpsMicros = 4290000000; // For incrementing iGpsPulse
61+
unsigned iGpsStable = 0; // Number of successive GPS pulses counted for stabilization
62+
unsigned iGpsPulse = 0; // Number of successive GPS pulses counted for calibration
63+
unsigned long prevGpsMicros = 4290000000; // For checking timely arrival of current iGpsPulse
6864
unsigned long gpsStartMicros = 0; // Start time of a sequence of successive GPS pulses
6965
volatile unsigned long iHalfWave = 0; // Phase of shutter waveform in terms of block half waves
70-
volatile unsigned long iIsr = 0; // For debugging
66+
volatile unsigned long iIsr = 0; // For monitoring
7167
float lastTaskWarningMillis; // Used to check if run_shutter_control is called in time
72-
int halfWaveValues[36]; // For debugging
7368

7469
/*
7570
* In the main loop lastGpsMicros is used:
@@ -95,10 +90,14 @@ ISR(TIMER1_COMPA_vect)
9590
// ihalfWave == 0: blanking period
9691
// iHalfWave == 1, 3, 5, ..., 31 odd, negative pulses on PIN3
9792
// iHalfWave == 2, 4, 6, ..., 30 even, positive pulses on PIN4
93+
//
94+
// The LCD-shutter needs the slow decay mode of the H-bridge to become transparent. In the slow decay mode the terminals of the
95+
// LCD-shutter are shortcut. The slow decay mode requires a logical high signal on both input terminals of the TB6612 module.
96+
// This is different compared to the script version for controlling an opamp-based driver.
97+
9898
byte pinVals;
9999
iHalfWave++;
100-
if ( (iHalfWave % 2 == 1) || (iGpsPulse && (iHalfWave % N_HALF_WAVE == 0)) ) {
101-
// pinVals = (PORTD & ZERO_MASK); regular script with opamp driver
100+
if ( (iHalfWave % 2 == 1) || ((iGpsStable == N_STABLE) && (iHalfWave % N_HALF_WAVE == 0)) ) {
102101
pinVals = (PORTD & ZERO_MASK) | HIGH_MASK;
103102
} else if (iHalfWave % 4 == 2) {
104103
pinVals = (PORTD & ZERO_MASK) | NEG_MASK;
@@ -107,7 +106,29 @@ ISR(TIMER1_COMPA_vect)
107106
}
108107
PORTD = pinVals;
109108
iIsr++;
110-
halfWaveValues[iIsr % 36] = iHalfWave; // iIsr and iHalfWave should rotate synchronously, but iIsr may be equal to 32 sometimes
109+
}
110+
111+
void calibrate()
112+
{
113+
int S = 90;
114+
char s[S];
115+
116+
// Phase lock mechanisms 2 for entire pulse train (see explanation at top of file)
117+
// Set OCR1A to the ideal value, calculated from the calibrated CPU clock
118+
// Apply *** down *** rounding for the OCR1A calculation so that the block frequency is slightly
119+
// too high rather than slightly too low and the syncing occurs during the blanking period
120+
unsigned long calibrationMicros = lastGpsMicros - gpsStartMicros;
121+
calibratedFreq = CPU_FREQ * calibrationMicros / iGpsPulse / 1000000.;
122+
OCR1A = calibratedFreq / PRESCALER / N_HALF_WAVE - TIMER_SAFETY;
123+
snprintf(s, S, "Micros: %u %lu", iGpsPulse, calibrationMicros);
124+
Serial.println(s);
125+
snprintf(s, S, "CPU: %ld", (long)calibratedFreq);
126+
Serial.println(s);
127+
snprintf(s, S, "Block: %u ticks", OCR1A);
128+
Serial.println(s);
129+
130+
// Phase lock mechanisms 3 (see explanation at top of file)
131+
// Will not do; change to Nucleo-32 STM32G431 with crystal clock and 32-bit hardware timers
111132
}
112133

113134
void setup_shutter_control()
@@ -133,36 +154,39 @@ void setup_shutter_control()
133154

134155
void run_shutter_control()
135156
{
136-
char s[90];
137-
// unsigned long entryMicros = micros(); // Temporary during development
157+
int S = 90;
158+
char s[S];
138159

139160
if (!gpsHit) {
140161
return;
141162
}
163+
142164
long phaseDiff = lastGpsMicros - prevGpsMicros - 1000000; // Uncalibrated measurement
143165
if (abs(phaseDiff) < 10000) { // 2 x 0.5% tolerance of Arduino ceramic resonator
144-
iGpsPulse ++; // Overflow takes 136 years
166+
iGpsPulse++;
167+
if (iGpsStable < N_STABLE) {
168+
iGpsStable++;
169+
if (iGpsStable == N_STABLE) {
170+
calibrate(); // preliminary calibration based on N_STABLE seconds only
171+
}
172+
}
145173
} else {
146-
//ToDo: somehow this section can be entered two times in one second!
147-
//11:51:23.005 -> Unexpected GPS pulse arrival. Deviation: -996604 microsecon
148-
//11:51:23.052 -> Unexpected GPS pulse arrival. Deviation: -999816 microsecon
149-
//11:51:23.989 -> Unexpected GPS pulse arrival. Deviation: -37864 microsecond
150-
//11:51:24.035 -> Unexpected GPS pulse arrival. Deviation: -987736 microsecon
151-
//11:51:24.967 -> Unexpected GPS pulse arrival. Deviation: -37856 microsecond
152-
//11:51:25.015 -> Unexpected GPS pulse arrival. Deviation: -987744 microsecon
153-
//11:51:25.993 -> Unexpected GPS pulse arrival. Deviation: -47440 microsecond
174+
// Tested: the script recovers OK from a manually triggered unexpected GPS pulse
175+
// (pin D2 connected momentarily to GND via a test lead with a 100 Ohm resistor)
176+
iGpsStable = 0;
154177
iGpsPulse = 0;
155178
gpsStartMicros = lastGpsMicros;
156179
if (millis() > 2000) { // phaseDiff during 2s of startup is expected
157-
snprintf(s, 60, "Unexpected GPS pulse arrival. Deviation: %ld microseconds", phaseDiff);
180+
snprintf(s, S, "Unexpected GPS pulse arrival. Deviation: %ld microseconds", phaseDiff);
158181
Serial.println(s);
159182
}
160183
}
161-
if (iGpsPulse > N_STABLE) {
184+
185+
if (iGpsStable == N_STABLE) {
162186
// Beware of concurrency issues; do not touch TIMER1 close to an ISR, so delay for a while
163187
// OCR1A is calculated such that this should not happen
164188
unsigned int delayTicks = OCR1A - TCNT1;
165-
if (delayTicks < 128) {
189+
if (delayTicks < 200) { // 200 arbitrary number of ticks sufficient to execute the code below
166190
Serial.println("Avoidance triggered");
167191
delayMicroseconds((delayTicks + 8) * TICK_MICROS);
168192
}
@@ -180,46 +204,24 @@ void run_shutter_control()
180204

181205
// Log experienced phase difference to serial monitor
182206
oldHalfWave = oldHalfWave % N_HALF_WAVE;
183-
snprintf(s, 90, "LCD phase: %lu %lu %lu %u", iIsr, observedTicks, oldHalfWave, oldTCNT1);
207+
snprintf(s, S, "LCD phase: %lu %lu %lu %u", iIsr, observedTicks, oldHalfWave, oldTCNT1);
184208
Serial.println(s);
185209

186210
if ( (iGpsPulse % N_CALIBRATE) == 0) {
187-
// Phase lock mechanisms 2 for entire pulse train (see explanation at top of file)
188-
// Set OCR1A to the ideal value, calculated from the calibrated CPU clock
189-
// Apply *** down *** rounding for the OCR1A calculation so that the block frequency is slightly
190-
// too high rather than slightly too low and the syncing occurs during the blanking period
191-
unsigned long calibrationMicros = lastGpsMicros - gpsStartMicros;
192-
calibratedFreq = CPU_FREQ * calibrationMicros / iGpsPulse / 1000000.;
193-
OCR1A = calibratedFreq / PRESCALER / N_HALF_WAVE - TIMER_SAFETY;
194-
snprintf(s, 60, "Micros: %lu %lu", iGpsPulse, calibrationMicros);
195-
Serial.println(s);
196-
snprintf(s, 60, "CPU: %ld", (long)calibratedFreq);
197-
Serial.println(s);
198-
snprintf(s, 60, "Block: %u ticks", OCR1A);
199-
Serial.println(s);
200-
201-
// Phase lock mechanisms 3 (see explanation at top of file)
202-
// Will not do; change to Nucleo-32 STM32G431 with crystal clock and 32-bit hardware timers
211+
calibrate();
212+
// Simple reset of the calibration window; a longer rolling window seems unnecessary
213+
iGpsPulse = 0;
214+
gpsStartMicros = lastGpsMicros;
203215
}
204216
}
205217

206-
// // For debugging: print iHalfwave with respect to GPS pulse
207-
// snprintf(s, 90, "hws %u %u %u ... %u %u %u",
208-
// halfWaveValues[0], halfWaveValues[1], halfWaveValues[2],
209-
// halfWaveValues[29], halfWaveValues[30], halfWaveValues[31]);
210-
// Serial.println(s);
211-
212218
// Check behaviour of other tasks in the waveform.ino loop() function
213219
if (micros() - lastGpsMicros > 50000 && millis() - lastTaskWarningMillis > 3600000) {
214220
lastTaskWarningMillis = millis();
215221
Serial.println("WARN - tasks other than LCD shutter control take longer than 50 ms!");
216222
}
217223

218-
// Prepare for next GPS 1 Hz pulse
224+
// Prepare for next 1 Hz GPS pulse
219225
prevGpsMicros = lastGpsMicros;
220226
gpsHit = false;
221-
222-
// // For debugging
223-
// snprintf(s, 90, "Loop duration: %lu", micros() - entryMicros);
224-
// Serial.println(s);
225227
}

doc/arduino-programming.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,34 @@ Programming the Arduino Nano should now be so simple as:
2828

2929
## Checking the logs
3030

31-
Once the uploaded program runs, it produces log statements over the serial interface. You can check these by opening the serial monitor of the Arduino IDE (far right button on the taskbar). On powering up the PCB, the GPS-module needs about half a minute to lock on one or more satellites and after that the LCD shutter program need 10 GPS pulses before generating second markers in the driver output. During that time the serial monitor only shows:
31+
Once the uploaded program runs, it produces log statements over the serial interface. You can check these by opening the serial monitor of the Arduino IDE (far right button on the taskbar). On powering up the PCB, the GPS-module needs about half a minute to lock on one or more satellites and after that the LCD shutter program needs 10 GPS pulses before generating the second markers in the driver output towards the LCD shutter. During that time the serial monitor only shows:
3232

3333
```log
3434
Configuration of LCD shutter completed.
3535
Stabilizing...
3636
```
37-
38-
After this time the serial monitor shows something like the text below, every second:
37+
After this, the serial monitor shows a few log lines related to the preliminary calibration of the CPU clock frequency relative to the Pulse Per Second (PPS) signal from the GPS module. These lines have the following format:
3938

4039
```log
41-
LCD phase: 0 3 0 12
40+
Micros: 10 10014560
41+
CPU: 16023260
42+
Block: 7822 ticks
4243
```
44+
The CPU frequency should be 16 +/- 0.08 MHz (large error margins because of the cheap ceramic resonator used on the Arduino module). The CPU frequency should only show minor variations during operation. The block ticks refer to the number of required timer events during one half wave (block pulse) of the driver output. This number is derived from the measured CPU frequency and should also show only minor variations.
4345

44-
These are variables used in synchronizing the timer outputs derived from MCU clock with the Pulse Per Second signal from the GPS module. The first and third number should be zero and the second number should be small, indicating that synchronization takes place immediately after the incoming GPS pulse. The difference between the fourth and the second number should is the accumulated phase differerence in "ticks" of 4 microseconds during one second, just before synchronization happens. This should be a figure between 0 and 32 ticks (meaning between 0 and 128 microseconds).
46+
The calibration, including logging, is repeated every 60 seconds to account for changes in the operating conditions of the Arduino (power voltage, temperature).
4547

46-
In addition to the logs that occur every second, another log message appears every 20 seconds and shows the result of calibrating the MCU clock frequency against the GPS PPS signal. It has the following format:
48+
In addition to the calibration every minute, the serial monitor shows the phase stability of the driver pulse train, relative to the GPS PPS signal. The log lines look something like the text below, every second:
4749

4850
```log
49-
Micros: 20 20001234
50-
CPU: 16012345
51-
Block: 7810 ticks
51+
LCD phase: 0 277 9 1684
52+
LCD phase: 0 3 0 12
53+
LCD phase: 0 4 0 12
5254
```
53-
The CPU frequency should be 16 MHz with an error margin of 0.5% (so large because of the cheap ceramic resonator used on the Arduino module). The CPU frequency should only show minor variations during operation. The block ticks refer to the number of required timer events during one half wave (block pulse) of the driver output. This number is derived from the measured CPU frequency and should also show only minor variations.
55+
56+
These are variables used in synchronizing the timer outputs derived from the MCU clock with the GPS PPS signal. The first and third number should be zero and the second number should be small, indicating that synchronization takes place immediately after the incoming GPS pulse. The difference between the fourth and the second number is the accumulated phase differerence during one second in "ticks" of 4 microseconds, just before synchronization happens. This should be a figure between 0 and 32 ticks (meaning between 0 and 128 microseconds). Only the very first log line shows a large phase difference, because the driver pulse train was not synchronized to the GPS signal before that time.
57+
58+
The logging described above is the logging during normal operation. There are two additional log messages that can occur and they indicate possible failures in the system:
59+
60+
1. "Unexpected GPS pulse arrival. Deviation: 21432 microseconds". This indicates instable operation of the GPS module. This can have all kinds of causes, including a too weak GPS signal, an instable power supply, interference from other systems, hardware failure, etc.
61+
2. "Avoidance triggered". This indicates that synchronization starts before the pulse train of the previous second has finished. Possible causes are a bug in the Arduino script and a hardware failure of the Arduino module.

0 commit comments

Comments
 (0)