/****************************************************
Collect camera data and rotate using restored PXP
setups copied from output of SavePXP program
m. borgerson 12/5/2020
******************************************************** */
#include <OV7670.h>
#include <ILI9341_t3n.h>
// need to include PXP definitions if not using the
// latest imxrt.h from GitHub (as of 12/4/2020).
#ifndef PXP_CTRL_SET
#include "PXP_Defs.h"
#endif
//Specify the pins used display for Non-SPI functions
#define TFT_CS 10 // AD_B0_02
#define TFT_DC 9 // AD_B0_03
#define TFT_RST 8
ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST);
// we are using QVGA settings for camera
const uint16_t bwidth = 320;
const uint16_t bheight = 240;
// define memory buffers in different locations for speed testing
uint16_t srcdma[320l * 240l]__attribute__ ((aligned (64))) DMAMEM;
uint16_t dstdma[320l * 240l]__attribute__ ((aligned (64))) DMAMEM;
uint16_t srcext[320l * 240l]__attribute__ ((aligned (64))) EXTMEM;
uint16_t dstext[320l * 240l]__attribute__ ((aligned (64))) EXTMEM;
#define OUTBUFIDX 3
#define PSBUFIDX 12
// PXP_Next struct is 32 register setings, but we just save as an array
uint32_t Rot_EXT_EXT [32] = {
0x00800302, 0x00000000, 0x0000000E, 0x70000000, 0x00000000, 0x000001E0,
0x014000F0, 0x00000000, 0x014000F0, 0x3FFF3FFF, 0x00000000, 0x0000000E,
0x70025800, 0x00000000, 0x00000000, 0x00000280, 0x00000000, 0x10001000,
0x00000000, 0x00FFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00FFFFFF, 0x00000000, 0x44000000, 0x01230208, 0x079B076C, 0x00000000,
0x00000000, 0x00000000
};
uint32_t Rot_DMA_DMA [32] = {
0x00800302, 0x00000000, 0x0000000E, 0x20200000, 0x00000000, 0x000001E0,
0x014000F0, 0x00000000, 0x014000F0, 0x3FFF3FFF, 0x00000000, 0x0000000E,
0x20225800, 0x00000000, 0x00000000, 0x00000280, 0x00000000, 0x10001000,
0x00000000, 0x00FFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00FFFFFF, 0x00000000, 0x44000000, 0x01230208, 0x079B076C, 0x00000000,
0x00000000, 0x00000000
};
#define OUTBUFIDX 3
#define PSBUFIDX 12
// CSI frame buffer 2 isn't used by PXP rotations
uint16_t fb2[320l * 240l];
const char compileTime [] = " Compiled on " __DATE__ " " __TIME__;
const int pinCamReset = 14;
void setup() {
uint8_t *srcptr = (uint8_t *)Rot_DMA_DMA [PSBUFIDX];
Serial.begin(9600);
delay(200);
Wire.begin();
pinMode(pinCamReset, OUTPUT);
digitalWriteFast(pinCamReset, LOW);
delay(10);
digitalWriteFast(pinCamReset, HIGH); // subsequent resets via SCB
if (OV7670.begin(QVGA, (uint8_t *)srcptr, (uint8_t *)&fb2)) {
Serial.println("OV7670 camera initialized.");
} else {
Serial.println("Error initializing OV7670");
}
// 12 MHz gives 15FPS. 16MHz will do 20FPS, but leaves little time
// for anything but video display.
OV7670.SetCamClock(12);
// Start ILI9341
tft.begin();
tft.setRotation(0); // testing external rotation
CMSI();
Serial.println("Adjusting PXP buffer addresses.");
delay(10);
SetNextBuffers(Rot_EXT_EXT, srcext, dstext);
SetNextBuffers(Rot_DMA_DMA, srcdma, dstdma);
Serial.println("Initializing PXP");
delay(50);
PXP_Start((uint32_t)&Rot_DMA_DMA);
delay(10);
Serial.println("Ready");
}
void loop() {
// Only 3 choices: 's' System Info 'f' capture single frame 't' run rotate tests
char ch;
if (Serial.available()) {
ch = Serial.read();
if (ch == 's') CMSI();
if (ch == 'f') CMGF();
if (ch == 't') TestSpeeds();
}
}
// adjust the source and destination buffers in the saved PXP settings to match
// the variables declared in this program
void SetNextBuffers(uint32_t pxpnxt[], uint16_t src[], uint16_t dst[]) {
pxpnxt[PSBUFIDX] = (uint32_t)src;
pxpnxt[OUTBUFIDX] = (uint32_t)dst;
}
void CMSI(void) {
Serial.printf("\n\nOV7670 Camera and ILI9341 QVGA Test 3 %s\n", compileTime);
OV7670.ShowCamConfig();
}
// Capture, rotate and display a single frame from OV7670
void CMGF(void) {
uint16_t readyframe;
uint32_t imagesize;
imagesize = OV7670.ImageSize();
OV7670.begin(QVGA, (uint8_t *)PXP_PS_BUF, (uint8_t *)&fb2);
OV7670.ClearFrameReady();
do {
readyframe = OV7670.FrameReady();
} while (readyframe != 1 ); // wait until frame 1 just completed
if ((uint32_t)PXP_PS_BUF > 0x2020000) { // makes camera dma data visible
arm_dcache_delete((void *)PXP_PS_BUF, imagesize);
}
if ((uint32_t)PXP_OUT_BUF > 0x2020000) { // needed when doing DMA into memory
arm_dcache_delete((void *)PXP_OUT_BUF, imagesize);
}
PXP_Rotate();
Serial.printf("Output buffer at %p\n", PXP_OUT_BUF);
if ((uint32_t)PXP_OUT_BUF > 0x2020000) {
arm_dcache_flush((void *)PXP_OUT_BUF, imagesize); // needed when doing DMA out of memory
}
tft.writeRect(0, 0, tft.width(), tft.height(), (uint16_t *)PXP_OUT_BUF);
}
// Use two different PXP_NEXT settings to compare the rotation speeds
void TestSpeeds(void) {
Serial.println("\nDMAMEM to DMAMEM");
PXP_NEXT = (uint32_t)&Rot_DMA_DMA;
PXP_CTRL_CLR = PXP_CTRL_ENABLE; // stop automatic execution on PXP_NEXT write
CMGF();// get, rotate and display a frame
delay(1000);
Serial.println("\nEXTMEM to EXTMEM");
PXP_NEXT = (uint32_t)&Rot_EXT_EXT;
PXP_CTRL_CLR = PXP_CTRL_ENABLE; // stop automatic execution on PXP_NEXT write
CMGF(); // get, rotate and display a frame
}
bool PXP_Done(void) {
return PXP_STAT & PXP_STAT_IRQ;
}
// Restart PXP with settings from a PXP_Next array
void PXP_Start(uint32_t pxnptr) {
// turn on clock to PXP
CCM_CCGR2 |= CCM_CCGR2_PXP(CCM_CCGR_ON);
PXP_CTRL_SET = PXP_CTRL_SFTRST; //Reset
PXP_CTRL_CLR = PXP_CTRL_SFTRST | PXP_CTRL_CLKGATE; //Clear reset and gate
delay(10);
// storing pointer in PXP_NEXT causes PXP to restore settings
PXP_NEXT = pxnptr;
}
elapsedMicros rutime;
void PXP_Rotate(void) {
uint32_t etime;
PXP_STAT_CLR = PXP_STAT; // clears all flags
PXP_CTRL_SET = PXP_CTRL_ENABLE; // start the PXP
rutime = 0; // reset the timing counter
// wait until rotation finished
while (!PXP_Done()) { };
PXP_CTRL_CLR = PXP_CTRL_ENABLE; // stop the PXP
etime = rutime;
Serial.printf("PXP Rotation took %lu microseconds\n", etime);
}