Skip to content

Commit 8480d66

Browse files
committed
Optimized readFrame to handle 5FPS
1 parent bb5bc4d commit 8480d66

File tree

2 files changed

+47
-24
lines changed

2 files changed

+47
-24
lines changed

src/OV767X.cpp

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,6 @@ int OV767X::begin(int resolution, int format, int fps)
145145
_pclkPort = portInputRegister(digitalPinToPort(_pclkPin));
146146
_pclkMask = digitalPinToBitMask(_pclkPin);
147147

148-
for (int i = 0; i < 8; i++) {
149-
_dataPorts[i] = portInputRegister(digitalPinToPort(_dPins[i]));
150-
_dataMasks[i] = digitalPinToBitMask(_dPins[i]);
151-
}
152-
153148
beginXClk();
154149

155150
Wire.begin();
@@ -214,36 +209,66 @@ int OV767X::bytesPerPixel() const
214209
return _bytesPerPixel;
215210
}
216211

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+
//
217236
void OV767X::readFrame(void* buffer)
218237
{
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+
219243
noInterrupts();
220244

221245
uint8_t* b = (uint8_t*)buffer;
222246
int bytesPerRow = _width * _bytesPerPixel;
223247

248+
// Falling edge indicates start of frame
224249
while ((*_vsyncPort & _vsyncMask) == 0); // wait for HIGH
225250
while ((*_vsyncPort & _vsyncMask) != 0); // wait for LOW
226251

227252
for (int i = 0; i < _height; i++) {
253+
// rising edge indicates start of line
254+
while ((*_hrefPort & _hrefMask) != 0); // wait for LOW
228255
while ((*_hrefPort & _hrefMask) == 0); // wait for HIGH
229256

230257
for (int j = 0; j < bytesPerRow; j++) {
258+
// rising edges clock each data byte
231259
while ((*_pclkPort & _pclkMask) != 0); // wait for LOW
260+
while ((*_pclkPort & _pclkMask) == 0); // wait for HIGH
232261

233-
uint8_t in = 0;
262+
uint32_t in = port->IN; // read all bits in parallel
234263

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
238267

239268
if (!(j & 1) || !_grayscale) {
240-
*b++ = in;
269+
*b++ = in;
241270
}
242-
243-
while ((*_pclkPort & _pclkMask) == 0); // wait for HIGH
244271
}
245-
246-
while ((*_hrefPort & _hrefMask) != 0); // wait for LOW
247272
}
248273

249274
interrupts();

src/OV767X.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010
#include <Arduino.h>
1111

1212
#define OV7670_VSYNC 8
13-
#define OV7670_HREF 10
14-
#define OV7670_PLK 12
13+
#define OV7670_HREF A1
14+
#define OV7670_PLK A0
1515
#define OV7670_XCLK 9
16-
#define OV7670_D0 0
17-
#define OV7670_D1 1
18-
#define OV7670_D2 2
19-
#define OV7670_D3 3
20-
#define OV7670_D4 4
16+
#define OV7670_D0 10
17+
#define OV7670_D1 0
18+
#define OV7670_D2 1
19+
#define OV7670_D3 2
20+
#define OV7670_D4 3
2121
#define OV7670_D5 5
2222
#define OV7670_D6 6
23-
#define OV7670_D7 7
23+
#define OV7670_D7 4
2424

2525
enum
2626
{
@@ -99,8 +99,6 @@ class OV767X
9999
uint32_t _hrefMask;
100100
volatile uint32_t* _pclkPort;
101101
uint32_t _pclkMask;
102-
volatile uint32_t* _dataPorts[8];
103-
uint32_t _dataMasks[8];
104102

105103
int _saturation;
106104
int _hue;

0 commit comments

Comments
 (0)