@@ -145,11 +145,6 @@ int OV767X::begin(int resolution, int format, int fps)
145
145
_pclkPort = portInputRegister (digitalPinToPort (_pclkPin));
146
146
_pclkMask = digitalPinToBitMask (_pclkPin);
147
147
148
- for (int i = 0 ; i < 8 ; i++) {
149
- _dataPorts[i] = portInputRegister (digitalPinToPort (_dPins[i]));
150
- _dataMasks[i] = digitalPinToBitMask (_dPins[i]);
151
- }
152
-
153
148
beginXClk ();
154
149
155
150
Wire.begin ();
@@ -214,36 +209,66 @@ int OV767X::bytesPerPixel() const
214
209
return _bytesPerPixel;
215
210
}
216
211
212
+ //
213
+ // Optimized Data Reading Explanation:
214
+ //
215
+ // In order to keep up with the data rate of 5 FPS, the inner loop that reads
216
+ // data from the camera board needs to be as quick as possible. The 64Mhz ARM
217
+ // Cortex-M4 in the Nano 33 would not be able to keep up if we read each bit
218
+ // one at a time from the various GPIO pins and combined them into a byte.
219
+ // Instead, we chose specific GPIO pins which all occupy a single GPIO "PORT"
220
+ // In this case, P1 (The Nano 33 exposes some bits of P0 and some of P1).
221
+ // The bits on P1 are not connected to sequential GPIO pins, so the order
222
+ // chosen may look a bit odd. Below is a map showing the GPIO pin numbers
223
+ // and the bit position they correspond with on P1 (bit 0 is on the right)
224
+ //
225
+ // 20-19-18-17-16-15-14-13-12-11-10-09-08-07-06-05-04-03-02-01-00 (bit)
226
+ // ~ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
227
+ // ~ |xx|xx|xx|xx|xx|04|06|05|03|02|01|xx|12|xx|xx|xx|xx|00|10|11|xx| (pin)
228
+ // ~ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
229
+ //
230
+ // The most efficient way to read 8-bits of data with the arrangement above
231
+ // is to wire the pins for P1 bits 2,3,10,11,12,13,14,15. This allows other
232
+ // features such as SPI to still work and gives us 2 groups of contiguous
233
+ // bits (0-1, 10-15). With 2 groups of bits, we can read, mask, shift and
234
+ // OR them together to form an 8-bit byte with the minimum number of operations.
235
+ //
217
236
void OV767X::readFrame (void * buffer)
218
237
{
238
+ uint32_t ulPin = 33 ; // P1.xx set of GPIO is in 'pin' 32 and above
239
+ NRF_GPIO_Type * port;
240
+
241
+ port = nrf_gpio_pin_port_decode (&ulPin);
242
+
219
243
noInterrupts ();
220
244
221
245
uint8_t * b = (uint8_t *)buffer;
222
246
int bytesPerRow = _width * _bytesPerPixel;
223
247
248
+ // Falling edge indicates start of frame
224
249
while ((*_vsyncPort & _vsyncMask) == 0 ); // wait for HIGH
225
250
while ((*_vsyncPort & _vsyncMask) != 0 ); // wait for LOW
226
251
227
252
for (int i = 0 ; i < _height; i++) {
253
+ // rising edge indicates start of line
254
+ while ((*_hrefPort & _hrefMask) != 0 ); // wait for LOW
228
255
while ((*_hrefPort & _hrefMask) == 0 ); // wait for HIGH
229
256
230
257
for (int j = 0 ; j < bytesPerRow; j++) {
258
+ // rising edges clock each data byte
231
259
while ((*_pclkPort & _pclkMask) != 0 ); // wait for LOW
260
+ while ((*_pclkPort & _pclkMask) == 0 ); // wait for HIGH
232
261
233
- uint8_t in = 0 ;
262
+ uint32_t in = port-> IN ; // read all bits in parallel
234
263
235
- for ( int k = 0 ; k < 8 ; k++) {
236
- bitWrite (in, k, (*_dataPorts[k] & _dataMasks[k]) != 0 );
237
- }
264
+ in >>= 2 ; // place bits 0 and 1 at the "bottom" of the register
265
+ in &= 0x3f03 ; // isolate the 8 bits we care about
266
+ in |= (in >> 6 ); // combine the upper 6 and lower 2 bits
238
267
239
268
if (!(j & 1 ) || !_grayscale) {
240
- *b++ = in;
269
+ *b++ = in;
241
270
}
242
-
243
- while ((*_pclkPort & _pclkMask) == 0 ); // wait for HIGH
244
271
}
245
-
246
- while ((*_hrefPort & _hrefMask) != 0 ); // wait for LOW
247
272
}
248
273
249
274
interrupts ();
0 commit comments