/*****************************************************************
// Demonstration program to show results of PXP rotations on small
// buffers in two configurations: 16x8 pixels and 16x16 pixels.
// The internal pixel block size is set to 8x8, so each of these
// sizes requires the rotation and movement of multiple blocks.
// The buffers are of type Uint16_t and occupy the same space
// as RGB565 and other two-byte pixels. The buffers are easily
// small enough to fit in DTCM with the program code.
***********************************************************************/
#define WIDTH 16
//#define HEIGHT 16 // use this one for square buffer
#define HEIGHT 8 // uncomment for rectangular buffer
#define BUFFSIZE (WIDTH * HEIGHT) // 2 bytes per pixel for RGB565 so use uint16_t
// To select the source and destination memory locations, uncomment ONE
// set of buffer definitions for source and destination, recompile and upload.
// Yes, this could all be done with a set of #ifdefs, but I turn a cold shoulder to
// #ifdef blizzards, and even one little instance irritates me. Either way, you
// have to edit something somewhere and recompile.
uint16_t srcBuff[2 * BUFFSIZE] __attribute__((aligned(64))); // DTCM by default;
//uint16_t srcBuff[2*BUFFSIZE]__attribute__ ((aligned (64))) DMAMEM;
//uint16_t srcBuff[2*BUFFSIZE]__attribute__ ((aligned (64))) EXTMEM;
uint16_t dstBuff[2 * BUFFSIZE] __attribute__((aligned(64))); // DTCM by default;
//uint16_t dstBuff[2*BUFFSIZE]__attribute__ ((aligned (64))) DMAMEM;
//uint16_t dstBuff[2*BUFFSIZE]__attribute__ ((aligned (64))) EXTMEM;
// We build the origin buffer here and memcpy it to srcBuff
uint16_t orgBuff[BUFFSIZE] EXTMEM;
const char compileTime[] = " Compiled on " __DATE__ " " __TIME__;
enum tRotval { ROT0,
ROT90,
ROT180,
ROT270 };
Stream *pxpstrm = &Serial; // used to send output from PXP functions
void setup() {
while (!Serial && millis() < 3000) {}
Serial.begin(9600);
delay(1000);
Serial.println("\n\n\n");
Serial.printf("\nPXP Rotation test 2 %s\n", compileTime);
memset(orgBuff, 0, sizeof(orgBuff)); // buffers may be in memory areas
memset(srcBuff, 0, sizeof(srcBuff)); // that are not automatically cleared at
memset(dstBuff, 0, sizeof(dstBuff)); // startup, so do that here
FillBuffer(orgBuff, WIDTH, HEIGHT); // put our unique pattern in origin buffer
Serial.println("origin Bufffer filled");
memcpy(srcBuff, orgBuff, sizeof(orgBuff));
Serial.println("origin buffer copied to srcBuff");
Serial.println("srcBuff corners: ");
ShowCorners(srcBuff, WIDTH, HEIGHT);
ShowBuffer(srcBuff, WIDTH, HEIGHT);
delay(100);
PXPInit();
}
#define LSTART (y * wd)
void FillBuffer(uint16_t buff[], uint32_t wd, uint32_t ht) {
uint32_t x, y;
for (x = 0; x < wd; x++) {
for (y = 0; y < ht; y++) {
buff[LSTART + x] = y;
}
}
buff[0] = 0x1111;
buff[wd - 1] = 0x2222;
buff[(ht - 1) * wd] = 0x3333;
buff[ht * wd - 1] = 0x4444;
Serial.println();
}
void ShowBuffer(uint16_t buff[], uint32_t wd, uint32_t ht) {
uint32_t x, y;
y = 0;
for (y = 0; y < ht; y++) {
for (x = 0; x < wd; x++) { // these values only work as long as wd and ht < 256
if ((x % wd) == 0) Serial.printf("\n%5d: ", LSTART); // one horizontal row per output line
Serial.printf("%04X ", buff[LSTART + x]);
}
}
Serial.println();
}
uint32_t BufferDifferences(uint16_t *ptr1, uint16_t *ptr2, uint32_t numpixels) {
uint32_t i, errCount;
errCount = 0;
for (i = 0; i < numpixels; i++) {
if (*ptr1++ != *ptr2++) errCount++;
}
return errCount;
}
void ShowCorners(uint16_t buff[], uint32_t wd, uint32_t ht) {
Serial.printf("Upper Left :%04X ", buff[0]);
Serial.printf("Upper Right :%04X ", buff[wd - 1]);
Serial.printf("Bottom Left:%04X ", buff[wd * (ht - 1)]);
Serial.printf("Bottom Right:%04X ", buff[wd * ht - 1]);
Serial.println();
}
// Rotate the src buffer by 90 degrees clockwise into dst buffer
void Rot90(uint16_t *psrc, uint16_t *pdst, uint16_t *pwd, uint16_t *pht) {
uint32_t newbuff;
uint16_t wd, ht, newwd, newht;
wd = *pwd;
ht = *pht;
uint32_t bsize = wd * ht * 2; // two bytes per pixel
PXPSetPS(psrc, wd, ht, PXP_RGB565);
PXPSetOutput(pdst, ht, wd, PXP_RGB565); // swap output ht and width for rotation
if ((uint32_t)PXP_PS_BUF > 0x2020000) { // makes camera dma data visible
arm_dcache_delete((void *)PXP_PS_BUF, bsize);
}
if ((uint32_t)PXP_OUT_BUF > 0x2020000) { // needed when doing DMA into memory
arm_dcache_delete((void *)PXP_OUT_BUF, bsize);
}
PXPRotate(ROT90); // waits until done
delay(100);
//Serial.printf("Output buffer at %p\n", PXP_OUT_BUF);
if ((uint32_t)PXP_OUT_BUF > 0x2020000) {
arm_dcache_flush((void *)PXP_OUT_BUF, bsize); // needed when doing DMA out of memory
}
delay(100);
Serial.print("\n 90-degree rotation done.");
PXPGetOutput(&newbuff, &newwd, &newht);
Serial.printf(" new width: %u new height: %u\n", newwd, newht);
*pwd = newwd;
*pht = newht; // returns new width to input variables
Serial.println("Destination Buffer Corners: ");
ShowCorners(pdst, newwd, newht);
ShowBuffer(pdst, newwd, newht);
}
void RunTest(uint16_t numrotations) {
uint32_t errCount;
uint16_t newwd, newht;
uint32_t newbuff;
memcpy(srcBuff, orgBuff, sizeof(orgBuff)); // put fresh data in srcBuff
Serial.println("\n\nTesting rotation");
newwd = WIDTH;
newht = HEIGHT;
delay(100);
// first rotate 90 degrees
Rot90(srcBuff, dstBuff, &newwd, &newht);
delay(100);
if (numrotations == 4) {
// Now rotate another 90 degrees 3 times
Rot90(dstBuff, srcBuff, &newwd, &newht);
delay(100);
Rot90(srcBuff, dstBuff, &newwd, &newht);
delay(100);
Rot90(dstBuff, srcBuff, &newwd, &newht); // Back to source buffer at end
delay(100);
Serial.println("360-degree rotation done");
PXPGetOutput(&newbuff, &newwd, &newht);
Serial.printf("new width: %u new height: %u\n", newwd, newht);
Serial.println("Result Buffer Corners: ");
ShowCorners(srcBuff, newwd, newht);
delay(100);
// srcBuff should now be the same as orgBuff, Let's verify
errCount = BufferDifferences(srcBuff, orgBuff, BUFFSIZE);
Serial.printf("Differences between final bufffer and original buffer: %d\n\n", errCount);
}
Serial.println();
}
void loop() {
char ch;
if (Serial.available()) {
ch = Serial.read();
if (ch == '1') RunTest(1);
if (ch == '4') RunTest(4);
if (ch == 's') PXPShow();
}
}
/**********************************************************
PXP Direct Control functions to avoid using library
I'm assuming that TD 1.57 has up-to-date PXP Register Definitions
***************************************/
// table of bytes per pixel for various output formats
// used for automatic setting of pitch at setup
const uint16_t bytesperpixel[32] = {
4,4,0,0,4,3,0,0,
2, 2,2,2,2,2,2,0,
4,0,2,2,1,1,0,0,
2,2,2,2,0,0,2,2
};
#define SetCorner( reg, h,v) reg = ((h<<16) + v)
// Set the default output pointer to USB Serial
// simplified initialization--now you have to set up ps, as, and output surfaces separately
void PXPInit(void){
// turn on the PXP Clock
CCM_CCGR2 |= CCM_CCGR2_PXP(CCM_CCGR_ON);
PXP_CTRL_SET = PXP_CTRL_SFTRST; //Reset the PXP
PXP_CTRL_CLR = PXP_CTRL_SFTRST | PXP_CTRL_CLKGATE; //Clear reset and gate
delay(10);
PXP_CTRL_CLR = PXP_CTRL_ROT_POS; // make sure rotate is done into output buffer
PXP_CTRL_CLR = PXP_CTRL_BLOCK_SIZE; // set block size to 8
PXP_CTRL_CLR = PXP_CTRL_ROTATE(0x03); // Set Rotation to zero
PXP_CSC1_COEF0 |= PXP_COEF0_BYPASS;
PXP_CTRL_SET = PXP_CTRL_IRQ_ENABLE;
// we don't actually use the interrupt but need to enable the bits
// in the PXP_STAT register
}
uint16_t BytesPerPixel(uint16_t mpt){
return bytesperpixel[mpt];
}
// Map type is PXB_RGB565 for example program
void PXPSetPS( void *psbuff,uint16_t inh, uint16_t inv, uint16_t maptype){
PXP_PS_CTRL_CLR = 0x1F;
PXP_PS_CTRL_SET = maptype; // PS buffer format specification
PXP_PS_BUF = (volatile void *)psbuff;
PXP_PS_UBUF = 0; // not using YUV planes
PXP_PS_VBUF = 0; // not using YUV planes+S
PXP_PS_BACKGROUND_0 = 0xFFFF; // white
PXP_OUT_PS_ULC = 0; // start processing at upper left 0,0
SetCorner(PXP_OUT_PS_LRC, (inh-1), (inv-1)); // end processing at h-1 and v-1
PXP_PS_PITCH = inh*bytesperpixel[maptype]; // input is width * format bytes per pixel
PXP_PS_SCALE = 0x10001000; // 1:1 scaling (0x1.000)
PXP_PS_CLRKEYLOW_0 = 0xFFFFFF; // this disables color keying
PXP_PS_CLRKEYHIGH_0 = 0x0; // this disables color keying
}
void PXPSetOutput( void *outbuff, uint16_t outh, uint16_t outv, uint16_t maptype){
PXP_OUT_CTRL_CLR = 0x0080031F; // clear format, interlaced output and alpha output bits
PXP_OUT_CTRL_SET = maptype; // specify output color format in bits 0:4
PXP_OUT_BUF = (volatile void *)outbuff;
PXP_OUT_PITCH = outh * bytesperpixel[maptype]; // assume Same as width * bytesperpixel
//PXP_OUT_LRC = ((outh-1) << 16 | (outv-1));
SetCorner(PXP_OUT_LRC, (outh-1) ,(outv-1));
}
// Return buffer address, width and height to variables pointed to by parameters
void PXPGetOutput( uint32_t* outbuffptr, uint16_t *pouth, uint16_t *poutv){
*outbuffptr = (uint32_t)PXP_OUT_BUF;
*pouth = ((PXP_OUT_LRC >> 16) & 0x03FFF) +1;
*poutv = (PXP_OUT_LRC & 0x03FFF) +1;
}
void PXPSetStream(Stream *psptr){
pxpstrm = psptr;
}
// This function prints a nicely formatted output of the PXP register settings
// The formatting does require using a monospaced font, like Courier
void PXPShow(void) {
pxpstrm->printf("CTRL: %08X STAT: %08X\n", PXP_CTRL, PXP_STAT);
pxpstrm->printf("OUT_CTRL: %08X OUT_BUF: %08X \nOUT_BUF2: %08X\n", PXP_OUT_CTRL,PXP_OUT_BUF,PXP_OUT_BUF2);
pxpstrm->printf("OUT_PITCH: %8lu OUT_LRC: %3u,%3u\n", PXP_OUT_PITCH, PXP_OUT_LRC>>16, PXP_OUT_LRC&0xFFFF);
pxpstrm->printf("OUT_PS_ULC: %3u,%3u OUT_PS_LRC: %3u,%3u\n", PXP_OUT_PS_ULC>>16, PXP_OUT_PS_ULC&0xFFFF,
PXP_OUT_PS_LRC>>16, PXP_OUT_PS_LRC&0xFFFF);
pxpstrm->printf("OUT_AS_ULC: %3u,%3u OUT_AS_LRC: %3u,%3u\n", PXP_OUT_AS_ULC>>16, PXP_OUT_AS_ULC&0xFFFF,
PXP_OUT_AS_LRC>>16, PXP_OUT_AS_LRC&0xFFFF);
pxpstrm->println(); // section separator
pxpstrm->printf("PS_CTRL: %08X PS_BUF: %08X\n", PXP_PS_CTRL,PXP_PS_BUF);
pxpstrm->printf("PS_UBUF: %08X PS_VBUF: %08X\n", PXP_PS_UBUF, PXP_PS_VBUF);
pxpstrm->printf("PS_PITCH: %8lu PS_BKGND: %08X\n", PXP_PS_PITCH, PXP_PS_BACKGROUND_0);
pxpstrm->printf("PS_SCALE: %08X PS_OFFSET: %08X\n", PXP_PS_SCALE,PXP_PS_OFFSET);
pxpstrm->printf("PS_CLRKEYLOW: %08X PS_CLRKEYLHI: %08X\n", PXP_PS_CLRKEYLOW_0,PXP_PS_CLRKEYHIGH_0);
pxpstrm->println();
pxpstrm->printf("AS_CTRL: %08X AS_BUF: %08X AS_PITCH: %6u\n", PXP_AS_CTRL,PXP_AS_BUF, PXP_AS_PITCH & 0xFFFF);
pxpstrm->printf("AS_CLRKEYLOW: %08X AS_CLRKEYLHI: %08X\n", PXP_AS_CLRKEYLOW_0,PXP_AS_CLRKEYHIGH_0);
pxpstrm->println();
pxpstrm->printf("CSC1_COEF0: %08X CSC1_COEF1: %08X \nCSC1_COEF2: %08X\n",
PXP_CSC1_COEF0,PXP_CSC1_COEF1,PXP_CSC1_COEF2);
pxpstrm->println(); // section separator
pxpstrm->printf("POWER: %08X NEXT: %08X\n", PXP_POWER,PXP_NEXT);
pxpstrm->printf("PORTER_DUFF: %08X\n", PXP_PORTER_DUFF_CTRL);
}
// The next three functions could all be macros
bool PXPDone(void) {
return PXP_STAT & PXP_STAT_IRQ;
}
void PXPStart(void){
PXP_STAT_CLR = PXP_STAT; // clears all flags
PXP_CTRL_SET = PXP_CTRL_ENABLE; // start the PXP
}
void PXPStop(void){
PXP_CTRL_CLR = PXP_CTRL_ENABLE; // stop the PXP
}
// Rotate output by 0,90,180,270 degrees
void PXPRotate(tRotval rot) {
PXP_CTRL_CLR = PXP_CTRL_ROTATE(3); // clear previous settings
PXP_CTRL_SET = PXP_CTRL_ROTATE(rot); // set requested rotation
PXPStart();
// wait until rotation finished
while (!PXPDone()) {};
PXPStop(); // stop the PXP
//Serial.printf("PXP Status = %04X\n", PXP_STAT &0xFF);
}