Pololu3piPlus32U4 library
Pololu3piPlus32U4LineSensors.cpp
1 // Copyright (C) Pololu Corporation. See www.pololu.com for details.
2 
4 
5 namespace Pololu3piPlus32U4
6 {
7 
8 void LineSensors::setTimeout(uint16_t timeout)
9 {
10  if (timeout > 32767) { timeout = 32767; }
11  _timeout = timeout;
12  _maxValue = timeout;
13 }
14 
16 {
17  for (uint8_t i = 0; i < _sensorCount; i++)
18  {
21  if (calibrationOn.minimum) { calibrationOn.minimum[i] = _maxValue; }
22  if (calibrationOff.minimum) { calibrationOff.minimum[i] = _maxValue; }
23  }
24 }
25 
27 {
28  // manual emitter control is not supported
29  if (mode == LineSensorsReadMode::Manual) { return; }
30 
31  if (mode == LineSensorsReadMode::On)
32  {
33  calibrateOnOrOff(calibrationOn, LineSensorsReadMode::On);
34  }
35 
36  if (mode == LineSensorsReadMode::Off)
37  {
38  calibrateOnOrOff(calibrationOff, LineSensorsReadMode::Off);
39  }
40 }
41 
42 void LineSensors::calibrateOnOrOff(CalibrationData & calibration, LineSensorsReadMode mode)
43 {
44  uint16_t sensorValues[_sensorCount];
45  uint16_t maxSensorValues[_sensorCount];
46  uint16_t minSensorValues[_sensorCount];
47 
48  // (Re)allocate and initialize the arrays if necessary.
49  if (!calibration.initialized)
50  {
51  uint16_t * oldMaximum = calibration.maximum;
52  calibration.maximum = (uint16_t *)realloc(calibration.maximum,
53  sizeof(uint16_t) * _sensorCount);
54  if (calibration.maximum == nullptr)
55  {
56  // Memory allocation failed; don't continue.
57  free(oldMaximum); // deallocate any memory used by old array
58  return;
59  }
60 
61  uint16_t * oldMinimum = calibration.minimum;
62  calibration.minimum = (uint16_t *)realloc(calibration.minimum,
63  sizeof(uint16_t) * _sensorCount);
64  if (calibration.minimum == nullptr)
65  {
66  // Memory allocation failed; don't continue.
67  free(oldMinimum); // deallocate any memory used by old array
68  return;
69  }
70 
71  // Initialize the max and min calibrated values to values that
72  // will cause the first reading to update them.
73  for (uint8_t i = 0; i < _sensorCount; i++)
74  {
75  calibration.maximum[i] = 0;
76  calibration.minimum[i] = _maxValue;
77  }
78 
79  calibration.initialized = true;
80  }
81 
82  for (uint8_t j = 0; j < 10; j++)
83  {
84  read(sensorValues, mode);
85 
86  for (uint8_t i = 0; i < _sensorCount; i++)
87  {
88  // set the max we found THIS time
89  if ((j == 0) || (sensorValues[i] > maxSensorValues[i]))
90  {
91  maxSensorValues[i] = sensorValues[i];
92  }
93 
94  // set the min we found THIS time
95  if ((j == 0) || (sensorValues[i] < minSensorValues[i]))
96  {
97  minSensorValues[i] = sensorValues[i];
98  }
99  }
100  }
101 
102  // record the min and max calibration values
103  for (uint8_t i = 0; i < _sensorCount; i++)
104  {
105  // Update maximum only if the min of 10 readings was still higher than it
106  // (we got 10 readings in a row higher than the existing maximum).
107  if (minSensorValues[i] > calibration.maximum[i])
108  {
109  calibration.maximum[i] = minSensorValues[i];
110  }
111 
112  // Update minimum only if the max of 10 readings was still lower than it
113  // (we got 10 readings in a row lower than the existing minimum).
114  if (maxSensorValues[i] < calibration.minimum[i])
115  {
116  calibration.minimum[i] = maxSensorValues[i];
117  }
118  }
119 }
120 
121 void LineSensors::read(uint16_t * sensorValues, LineSensorsReadMode mode)
122 {
123  switch (mode)
124  {
126  emittersOff();
127  readPrivate(sensorValues);
128  return;
129 
131  readPrivate(sensorValues);
132  return;
133 
135  emittersOn();
136  readPrivate(sensorValues);
137  emittersOff();
138  return;
139 
140  default: // invalid - do nothing
141  return;
142  }
143 }
144 
145 void LineSensors::readCalibrated(uint16_t * sensorValues, LineSensorsReadMode mode)
146 {
147  // manual emitter control is not supported
148  if (mode == LineSensorsReadMode::Manual) { return; }
149 
150  // if not calibrated, do nothing
151 
152  if (mode == LineSensorsReadMode::On)
153  {
155  {
156  return;
157  }
158  }
159 
160  if (mode == LineSensorsReadMode::Off)
161  {
163  {
164  return;
165  }
166  }
167 
168  // read the needed values
169  read(sensorValues, mode);
170 
171  for (uint8_t i = 0; i < _sensorCount; i++)
172  {
173  uint16_t calmin, calmax;
174 
175  // find the correct calibration
176  if (mode == LineSensorsReadMode::On)
177  {
178  calmax = calibrationOn.maximum[i];
179  calmin = calibrationOn.minimum[i];
180  }
181  else if (mode == LineSensorsReadMode::Off)
182  {
183  calmax = calibrationOff.maximum[i];
184  calmin = calibrationOff.minimum[i];
185  }
186 
187  uint16_t denominator = calmax - calmin;
188  int16_t value = 0;
189 
190  if (denominator != 0)
191  {
192  value = (((int32_t)sensorValues[i]) - calmin) * 1000 / denominator;
193  }
194 
195  if (value < 0) { value = 0; }
196  else if (value > 1000) { value = 1000; }
197 
198  sensorValues[i] = value;
199  }
200 }
201 
202 uint16_t LineSensors::readLinePrivate(uint16_t * sensorValues, LineSensorsReadMode mode,
203  bool invertReadings)
204 {
205  bool onLine = false;
206  uint32_t avg = 0; // this is for the weighted total
207  uint16_t sum = 0; // this is for the denominator, which is <= 64000
208 
209  // manual emitter control is not supported
210  if (mode == LineSensorsReadMode::Manual) { return 0; }
211 
212  readCalibrated(sensorValues, mode);
213 
214  for (uint8_t i = 0; i < _sensorCount; i++)
215  {
216  uint16_t value = sensorValues[i];
217  if (invertReadings) { value = 1000 - value; }
218 
219  // keep track of whether we see the line at all
220  if (value > 200) { onLine = true; }
221 
222  // only average in values that are above a noise threshold
223  if (value > 50)
224  {
225  avg += (uint32_t)value * (i * 1000);
226  sum += value;
227  }
228  }
229 
230  if (!onLine)
231  {
232  // If it last read to the left of center, return 0.
233  if (_lastPosition < (_sensorCount - 1) * 1000 / 2)
234  {
235  return 0;
236  }
237  // If it last read to the right of center, return the max.
238  else
239  {
240  return (_sensorCount - 1) * 1000;
241  }
242  }
243 
244  _lastPosition = avg / sum;
245  return _lastPosition;
246 }
247 
248 // the destructor frees up allocated memory
249 LineSensors::~LineSensors()
250 {
255 }
256 
257 void LineSensors::readPrivate(uint16_t * sensorValues)
258 {
259  FastGPIO::Pin<line0Pin>::setOutputHigh();
260  FastGPIO::Pin<line1Pin>::setOutputHigh();
261  FastGPIO::Pin<line2Pin>::setOutputHigh();
262  FastGPIO::Pin<line3Pin>::setOutputHigh();
263  FastGPIO::Pin<line4Pin>::setOutputHigh();
264  _delay_us(10);
265 
266  sensorValues[0] = _timeout;
267  sensorValues[1] = _timeout;
268  sensorValues[2] = _timeout;
269  sensorValues[3] = _timeout;
270  sensorValues[4] = _timeout;
271 
272  noInterrupts();
273  uint16_t startTime = micros();
274  FastGPIO::Pin<line0Pin>::setInput();
275  FastGPIO::Pin<line1Pin>::setInput();
276  FastGPIO::Pin<line2Pin>::setInput();
277  FastGPIO::Pin<line3Pin>::setInput();
278  FastGPIO::Pin<line4Pin>::setInput();
279  interrupts();
280 
281  uint16_t time = 0;
282  while (true)
283  {
284  noInterrupts();
285  time = micros() - startTime;
286  if (time >= _timeout)
287  {
288  interrupts();
289  break;
290  }
291  if (!FastGPIO::Pin<line0Pin>::isInputHigh() && time < sensorValues[0]) { sensorValues[0] = time; }
292  if (!FastGPIO::Pin<line1Pin>::isInputHigh() && time < sensorValues[1]) { sensorValues[1] = time; }
293  if (!FastGPIO::Pin<line2Pin>::isInputHigh() && time < sensorValues[2]) { sensorValues[2] = time; }
294  if (!FastGPIO::Pin<line3Pin>::isInputHigh() && time < sensorValues[3]) { sensorValues[3] = time; }
295  if (!FastGPIO::Pin<line4Pin>::isInputHigh() && time < sensorValues[4]) { sensorValues[4] = time; }
296  interrupts();
297  __builtin_avr_delay_cycles(4); // allow interrupts to run
298  }
299 }
300 
301 }
void emittersOff()
Turns the IR LEDs off.
CalibrationData calibrationOn
Data from calibrating with emitters on.
void resetCalibration()
Resets all calibration that has been done.
void readCalibrated(uint16_t *sensorValues, LineSensorsReadMode mode=LineSensorsReadMode::On)
Reads the sensors and provides calibrated values between 0 and 1000.
static const uint8_t _sensorCount
The 3pi+ 32U4 has 5 line sensors.
void read(uint16_t *sensorValues, LineSensorsReadMode mode=LineSensorsReadMode::On)
Reads the raw sensor values into an array.
CalibrationData calibrationOff
Data from calibrating with emitters off.
void setTimeout(uint16_t timeout)
Sets the timeout for RC sensors.
void calibrate(LineSensorsReadMode mode=LineSensorsReadMode::On)
Reads the sensors for calibration.
Top-level namespace for the Pololu3piPlus32U4 library.
LineSensorsReadMode
Emitter behavior when taking readings.
uint16_t * maximum
Highest readings seen during calibration.
bool initialized
Whether array pointers have been allocated and initialized.
uint16_t * minimum
Lowest readings seen during calibration.