OV7670, Generic STM32F103RC and ILI9341 display

What are you developing?
stevestrong
Posts: 1818
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by stevestrong » Sat Sep 23, 2017 8:30 am

Roger, you should first stop the timer before you write to SD. This way the timer DMA, even if enabled, will not be activated.
After writing to SD card, simply resume the timer (XCLK).

EDIT1
Ah, I think the pixel reading will not work if the XCLK is stopped.
In this case one should temporarily de-activate the timer DMA, or the DMA channel itself.
And resume it after SD card writing.

EDIT2
I admit, it is strange that the timer DMA conflicts with the SPI DMA.
I think Victor made a comprehensive study on what happens when both SPI 1 and 2 runs in parallel (with DMA). I think he concluded that they are conflicting when SPI 1 runs with 36MHz, some data bytes are sporadically lost.

User avatar
RogerClark
Posts: 7459
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by RogerClark » Sat Sep 23, 2017 8:37 am

I tried using timer_pause(TIMER2); but it didn't help

I had to disable the DMA

(TIMER2->regs).gen->DIER = (0); // disable DMA request on TIM2 update

stevestrong
Posts: 1818
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by stevestrong » Sat Sep 23, 2017 8:40 am

Strange, this means that the DMA is triggered even if the timer is stopped?
Hmm.

User avatar
RogerClark
Posts: 7459
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by RogerClark » Sat Sep 23, 2017 8:47 am

Yes.

Just running the first part of code in Timer_setup()

Code: Select all

void TIMER_Setup(void)
{
	gpio_set_mode(GPIOA, 1, GPIO_INPUT_FLOATING);
	timer_pause(TIMER2); // stop timer
	timer_init(TIMER2);  // turn timer RCC on

#define TIMER_RELOAD_VALUE 2 // must be adapted according to the results
	// as this mode is not supported by the core lib, we have to set up the registers manually.
	//(TIMER2->regs).gen->CR1 = TIMER_CR1_CEN;
	(TIMER2->regs).gen->CR2 = 0;
	(TIMER2->regs).gen->SMCR = (TIMER_SMCR_TS_TI2FP2 | TIMER_SMCR_SMS_RESET);//TIMER_SMCR_SMS_TRIGGER);


 
	(TIMER2->regs).gen->DIER = (TIMER_DIER_UDE); // enable DMA request on TIM2 update
Causes SD to not work, even if I dont call DMA_Setup()

Its just these lines in Timer_Setup are enough to cause the problem.

stevestrong
Posts: 1818
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by stevestrong » Sat Sep 23, 2017 9:00 am

After receiving a complete line from the camera, before writing the data to LCD, I do:

Code: Select all

    timer_pause(TIMER2); // stop timer
    dma_disable(DMA1, DMA_CH2);
    dma_clear_isr_bits(DMA1, DMA_CH2);
and then I re-enable them within the loop:

Code: Select all

    dma_set_num_transfers(DMA1, DMA_CH2, 2*PIXELS_PER_LINE+1); // 2 readings for each pixel
    dma_clear_isr_bits(DMA1, DMA_CH2);
    dma_enable(DMA1, DMA_CH2);
    timer_resume(TIMER2); // start timer

User avatar
RogerClark
Posts: 7459
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by RogerClark » Sat Sep 23, 2017 9:03 am

OK

I'll try that

BTW.
Currently after writing to SD, just re-enabling the Timer DMA enable does not start the camera running again, but if I have to completely re-init the timer and DMA etc that would be OK, as saving to SD is not a a quick process as it takes around 700mS

stevestrong
Posts: 1818
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by stevestrong » Sat Sep 23, 2017 9:09 am

You could try to leave the timer settings, just disable the DMA channel

Code: Select all

  //timer_pause(TIMER2); // stop timer
    dma_disable(DMA1, DMA_CH2);
    dma_clear_isr_bits(DMA1, DMA_CH2);
and re-enable the DMA channel.

Code: Select all

    dma_set_num_transfers(DMA1, DMA_CH2, 2*PIXELS_PER_LINE+1); // 2 readings for each pixel
    dma_clear_isr_bits(DMA1, DMA_CH2);
    dma_enable(DMA1, DMA_CH2);
  //timer_resume(TIMER2); // start timer
Important is to do the saving to SD card after a complete frame has been received, and resume the DMA after a Vsync has been detected.

BTW, just for the record, saving image to SD and sending it to PC: http://www.deviceplus.com/how-tos/ardui ... -tutorial/

User avatar
RogerClark
Posts: 7459
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by RogerClark » Sat Sep 23, 2017 10:49 am

Hi Steve

I don't understand why, but calling

Code: Select all

   dma_disable(DMA1, DMA_CH2);
    dma_clear_isr_bits(DMA1, DMA_CH2);
before using the SD lib didnt help.

I still get the same problem and the SD card gets corrupted, I get a file on the SD, but the size is 0 and the name is some non- ASCII characters


I definitely need to disable the DMA request from TIMER2

After saving to SD, the LCD does not get updated again by the camera, but the serial terminal is showing the "." to indicate that its still processing frames

So I think something has happened with the LCD configuration which is causing a problem

I've tried calling

tft.setAddrWindow(0, 0, screen_w, screen_h);

and

SPI.setDataSize(DATA_SIZE_8BIT); // set to 8 bit mode

But this has not helped

Anyway. Its getting too late here to continue today.

I'll try again tomorrow, and perhaps go back to my more simple example, and prove I can update the LCD after saving to SD as this may not be possible even if not using your camera / DMA code

Thanks for your help

Roger

stevestrong
Posts: 1818
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by stevestrong » Sun Sep 24, 2017 8:32 am

I wired up the SD card, removed the resistors, SdInfo works, so I will check your sketch soon.
Where is the defined readPixels24()? I think it should read and stores 4 bytes instead of 3, because as far as I know the BMP pixel data must be 4 bytes aligned.

User avatar
RogerClark
Posts: 7459
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: OV7670, Generic STM32F103RC and ILI9341 display

Post by RogerClark » Sun Sep 24, 2017 8:37 am

Code: Select all

uint16_t Adafruit_ILI9341_STM::readPixels24(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t *buf)
{
  mSPI.beginTransaction(SPISettings(_safe_freq, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT));

  writecommand(ILI9341_CASET); // Column addr set
  spiwrite16(x1);
  spiwrite16(x2);
  writecommand(ILI9341_PASET); // Row addr set
  spiwrite16(y1);
  spiwrite16(y2);
  writecommand(ILI9341_RAMRD); // read GRAM
  (void)spiread();             //dummy read
  uint8_t r, g, b;
  uint16_t len = (x2-x1+1)*(y2-y1+1);
  uint16_t ret = len;

  mSPI.dmaTransfer(buf, buf, len*3);
  cs_set();

  mSPI.beginTransaction(SPISettings(_freq, MSBFIRST, SPI_MODE0, DATA_SIZE_16BIT));
  return ret;
}
Note. This leaves SPI in 16 bit, but the display code needs 8 bit, so I call the func to set to 8 bit from the camera sketch


Also.

I'm trying to isolate why the camera cant update the display after writing to SD

I have a test program which just writes some text to the screen and writes it to LCD

I just double checked, and it looks like writing to the SD is somehow sending data to my LCD

If you run this code , it upates the display every second, with a new random number,

But if you change

boolean doFileActions=false;

to

boolean doFileActions=true

It writes the LCD to SD, but then the display does not update correctly.

It looks like the address window is set to 0,0,320,0 but the function which writes the text to the display calls fillrect, so it should be OK as that sets the address window.


Code: Select all

#include <SdFat.h>
#include "Adafruit_ILI9341_STM.h"

#define TFT_CS         PB0                  
#define TFT_DC         PA2               
#define TFT_RST        PB1 
Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST); // Use hardware SPI

const uint8_t SD_CS = PC15;
SdFat sd;

unsigned long testText()
{
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_RED);  tft.setTextSize(3);
  tft.println("LCD Screen reader\n");
  tft.setTextColor(ILI9341_GREEN); tft.setTextSize(3);
  tft.println("rogerclark.net\n");
  tft.setTextColor(ILI9341_BLUE); tft.setTextSize(3);
  tft.println(__TIME__);
  tft.println(random(100000));

  return 0;
}

void LCD2SD()
{
const int w = 320;
const int h = 240;
SdFile file;  
#define NUM_LINES_BUFFERED 12
uint8_t lineBufSD[w*3*NUM_LINES_BUFFERED];
boolean doFileActions=false;

  unsigned char bmpFileHeader[14] = {'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0 };
  unsigned char bmpInfoHeader[40] = {40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0 };  
  unsigned long m;
  char name[] = "LCD_00.bmp";

       if (doFileActions)
      {
  // if name exists, create new filename
  for (int i = 0; i < 100; i++)
  {
    name[4] = i / 10 + '0';
    name[5] = i % 10 + '0';
    if (file.open(name, O_CREAT | O_EXCL | O_WRITE))
    {
      break;
    }
  }
      }
  // create image data
  int filesize = 54 + 4 * w * h;      //  w is image width, h is image height
  bmpFileHeader[ 2] = (unsigned char)(filesize    );
  bmpFileHeader[ 3] = (unsigned char)(filesize >> 8);
  bmpFileHeader[ 4] = (unsigned char)(filesize >> 16);
  bmpFileHeader[ 5] = (unsigned char)(filesize >> 24);

  bmpInfoHeader[ 4] = (unsigned char)(       w    );
  bmpInfoHeader[ 5] = (unsigned char)(       w >> 8);
  bmpInfoHeader[ 6] = (unsigned char)(       w >> 16);
  bmpInfoHeader[ 7] = (unsigned char)(       w >> 24);
  bmpInfoHeader[ 8] = (unsigned char)(       h    );
  bmpInfoHeader[ 9] = (unsigned char)(       h >> 8);
  bmpInfoHeader[10] = (unsigned char)(       h >> 16);
  bmpInfoHeader[11] = (unsigned char)(       h >> 24);
     if (doFileActions)
    {
  file.write(bmpFileHeader, sizeof(bmpFileHeader));    // write file header
  file.write(bmpInfoHeader, sizeof(bmpInfoHeader));    // " info header
      }
  m=millis();
  uint8_t t;
  int lineOffset;
  for (int y = 0 ; y < h ; y++) 
  {

    lineOffset = y%NUM_LINES_BUFFERED *3 * w;
    tft.readPixels24(0,(h-1)-y,320,(h-1)-y, lineBufSD + lineOffset);
    if ((y+1)%NUM_LINES_BUFFERED==0)
    {
      // swap colour channels from  RGB to BGR
      for (int x = 0; x < w * 3 * NUM_LINES_BUFFERED; x+=3) 
      {
        t=lineBufSD[x+2];
        lineBufSD[x+2]=lineBufSD[x];
        lineBufSD[x]=t;
      }
       if (doFileActions)
      {
       file.write(lineBufSD, 3 * w * NUM_LINES_BUFFERED);
      }
    }
  }
  Serial.print("Saved in ");Serial.print(millis()-m);Serial.println(" mS ");
 if (doFileActions)
 {
  file.close();  
 }
}

void setup() 
{
  Serial.begin(9600);
  delay(500);
  Serial.println("Starting");

  if (!sd.begin(SD_CS, SD_SCK_MHZ(50)))
  {
    sd.initErrorHalt();
  }
  
  tft.begin();
  uint16_t screen_w = ILI9341_TFTHEIGHT;
  uint16_t screen_h = ILI9341_TFTWIDTH;

  tft.setRotation(1);

}

void loop()
{  
  testText();// Draw something
  LCD2SD();
  delay(1000);
}
Anyway,

Time for my evening meal now.

Back later

Post Reply