Forum: Mikrocontroller und Digitale Elektronik DMA-Datentransfer OV7670 -> ARM-MCU


von leluno (Gast)


Lesenswert?

Ich möchte den 8-bit Datenstrom eines OV7670 per DMA zunächst in den RAM 
eines LPC1768 speichern.

Der DMA soll durch pclk, vsync und href gesteuert werden.

Wenn ich das manual richtig verstanden habe, muss ein 
Software-Burst-Request ausgelößt werden.

D.h. die CPU wertet vsync, href und pclk aus und schreibt dann in 
DMACSoftBreq  1<<9. Der DMA wertet dies dann als Timer0/mach1 und leitet 
den Datentransfer von src nach dest ein.

Ist  das ungefähr zutreffend?

von leluno (Gast)


Lesenswert?

ich habe jetzt das Beispiel AN10850 - Memory to IOPin - umgesetzt

Erstes Problem: Mir ist nicht klar, wie das Pin-Register dem 
DMA-Zielregister  DMACCDestAddr zugewiesen wird.

Funktion und Funktionsaufruf in der main:
1
/* extensions for test DMA to FIOPIN */
2
int32_t dma_start_m2pin(int32_t ch,
3
                     void *src,
4
                     void *dest,
5
                     DMAC_LL_T *plli,
6
                     int32_t trans)
7
{
8
  int32_t sts = _ERROR;
9
10
  /* Verify that the selected channel has been allocated */
11
  if (dmadrv_dat.alloc_ch [ch] == TRUE)
12
  {
13
    /* Setup source and destination and clear LLI */
14
    dmadrv_dat.pdma->dma_chan [ch].src_addr = (uint32_t) src;
15
    dmadrv_dat.pdma->dma_chan [ch].dest_addr = (uint32_t) dest;
16
    //dmadrv_dat.pdma->dma_chan [ch].dest_addr = (uint32_t) 0x2009C054+3;
17
18
    dmadrv_dat.pdma->dma_chan [ch].lli = (uint32_t) plli;
19
20
    /* Use linked list control word if available */
21
    if (plli != NULL)
22
    {
23
      dmadrv_dat.pdma->dma_chan [ch].control = plli->next_ctrl;
24
    }
25
    else
26
    {
27
      /* Setup channel configuration */
28
      dmadrv_dat.pdma->dma_chan [ch].control =
29
          (DMAC_CHAN_INT_TC_EN |
30
          DMAC_CHAN_SRC_AUTOINC | DMAC_CHAN_DEST_WIDTH_8 |
31
          DMAC_CHAN_SRC_WIDTH_8 | DMAC_CHAN_DEST_BURST_1 |
32
          DMAC_CHAN_SRC_BURST_1 |
33
          DMAC_CHAN_TRANSFER_SIZE(trans));
34
    }
35
36
    /* Start channel transfer */
37
    dmadrv_dat.pdma->dma_chan [ch].config_ch =
38
      (DMAC_CHAN_FLOW_D_P2M | DMAC_CHAN_ENABLE | DMAC_SRC_PERIP(9) );
39
40
    sts = _NO_ERROR;
41
  }
42
43
  return sts;
44
}
45
46
47
48
...   
49
/* Use DMA to copy the data  PIN2/2-6 */
50
  dma_start_m2pin(dmach, (void *)(MEMSTART_U),(void *)(&(LPC_GPIO2->FIOPIN)), NULL, MEMSIZE);
51
//pin 3/25
52
//  dma_start_m2pin(dmach, (void *)(MEMSTART_U),(void *)(&(LPC_GPIO3->FIOPIN)), NULL, MEMSIZE);
53
....


Es wird doch nur der Anfang des Pin-Registers übergeben, woher weiß das 
Programm welcher Pin gemeint ist?


Wie ändere ich die DMACCDestAddr so, dass der von mir gewünschte PIn 
3.25 blinkt?

von leluno (Gast)


Lesenswert?

Vielen Dank für die breite Unterstützung. Es werden immer ganze 32-bit 
Register übertragen. Die Auswahl der Pins geschieht über Transfer Width. 
Die LEDs blinken jetzt per DMA.

von leluno (Gast)


Lesenswert?

kleine DMA-Fortschritte. Das Programm überträgt den Registerinhalt 
DMA_SRC nach GPIO3-Port und läßt die dort angeschlossenen LEDs blinken. 
Request-Quelle ist Timer1 und LPC_GPDMA->DMACSoftSReq=(1<<9):
1
void dma_ch0_m2p(void){
2
    LPC_SC->DMAREQSEL = (1<<1);//i.V.m. DMAC_SRC_PERIP(9)LPC_GPDMACH0->DMACCConfig
3
    LPC_SC->PCONP |= (1<<29);//Enable clock to DMA controller
4
    LPC_GPDMA->DMACConfig=DMAC_CTRL_ENABLE;
5
    LPC_GPDMACH0->DMACCSrcAddr = DMA_SRC;
6
//   LPC_GPDMACH0->DMACCDestAddr = DMA_DST;
7
   LPC_GPDMACH0->DMACCDestAddr = &(LPC_GPIO3->FIOPIN);
8
    LPC_GPDMACH0->DMACCControl =   \
9
    (DMAC_CHAN_INT_TC_EN |
10
      DMAC_CHAN_SRC_AUTOINC | DMAC_CHAN_DEST_WIDTH_32 |
11
    DMAC_CHAN_SRC_WIDTH_32 | DMAC_CHAN_DEST_BURST_1 |
12
    DMAC_CHAN_SRC_BURST_1 |
13
    DMAC_CHAN_TRANSFER_SIZE(DMA_SIZE/4));
14
    LPC_GPDMACH0->DMACCConfig |= \
15
      (DMAC_CHAN_FLOW_D_P2M | DMAC_CHAN_ENABLE | DMAC_SRC_PERIP(9) );
16
}

Als nächstes wird ein Eingangsport mittels DMA in den Ram eingelesen.

von Dirk K. (dekoepi)


Lesenswert?

Danke für die Dokumentation deiner Fortschritte. Auch, wenn keiner 
antwortet: Andere sind grade an ähnlicher Stelle oder sind es in einem 
Jahr, da kann das immer noch hilfreich sein :)

Ich habe mich gestern endlich dazu durchgerungen, die Non-FIFO-Kamera an 
den STM32F103 anzuschließen. Das Verkabeln alleine hat länger als eine 
Viertelstunde gedauert. Jetzt muss ich mich nur aufraffen, I2C zu 
basteln, XCLK auf PA8 mit Takt versorgen, ... das dürfte noch ein paar 
Tage dauern bei mir. Ganz am Ende steht dann auch die Idee, die Daten 
per DMA umzuschieben.

von leluno (Gast)


Angehängte Dateien:

Lesenswert?

Dirk K. schrieb:
> Auch, wenn keiner
> antwortet

Danke für die aufmunternden Worte. ich befinde mich wohl in der 
LPC-Diaspora. Beim Stichwort STM-DSM wird erhält man sicher mehr 
Unterstützung. Wahrscheinlich auch beim Stichwort DSM-Bascom - aus 
Neugier, Schadenfreude oder Mitleid.

LPCs hat kaum jemand. Dass es DMA gibt, habe ich vor einer Woche nicht 
gewusst. LPC-Anwender mit DMA-Kenntnissen sind wohl sehr selten.

Im Prinzip ist es für die Aufgabebstellung OV7670 aber egal, ob man mit 
LPC oder Stm arbeitet. Gleicher Arm-Kern und ähnliche Eclipse-Ide. Ich 
habe hier noch ein StmF103-board rumliegen, an welches ich mich bislang 
nicht herangetraut habe, einfach weil ich mit LPC sehr zufrieden bin. 
Vielleicht löte ich dafür aber jetzt den Anschluss an eine 
2o-Pin-Streifenrasterplatine zusammen, dann kann die Cam auch an dieses 
board.

Jedenfalls lese ich ideinen thread immer mit. Ich denke, wir können uns 
bei der Umsetzung der OV7670 Aufgabe gegenseitig unterstützen, da wir 
beide das Problem der Umsetzung des parallelen Dateneingangs auf seriell 
haben.

von leluno (Gast)


Angehängte Dateien:

Lesenswert?

Ein kleiner Teilerfolg, das erste brauchbare Foto auf dem Display- noch 
auf dem Atmega1284.

Der code ist soweit optimiert, dass mit einem Prescaler 17 (->knapp 
1Mhz) alle Pixel gelesen werden können:
1
#if fifo==0
2
//++++==================================================
3
void get_picture(u8 teilbild){
4
u16 zl_row=0,zl_offset,gw_offset;
5
u8 n,i,offset;
6
u32 index,zl_pix=0;
7
8
lgw(1,1,"xxx ");
9
zl_row=15;
10
11
if(!vsync){while(!vsync);}
12
zl_pix=12799;
13
zl_row=0;
14
while(vsync);
15
//lw("1");
16
17
18
while(zl_row<20*teilbild){
19
while(!href);//Warten auf href_h
20
while(href);
21
zl_row++;
22
}
23
24
25
26
zl_row=20;
27
//start frame
28
while(zl_row){
29
while(!href);//Warten auf href_h
30
while(href){;
31
// +++ px ++++
32
  while(!pclk);
33
    buffer[zl_pix]=PINA;
34
    zl_pix--;
35
  while(pclk);
36
}//zl_row
37
zl_row--;
38
}//end frame
39
40
41
42
lgi(19,16,zl_row);lw("/");li(zl_pix+1);lw("/");li(ov_prescaler);
43
//alle Pixel erkannt:153600pix
44
if (zl_pix<153600 && ov_prescaler<32){
45
//ov_prescaler++;
46
// CLKRC register: Prescaler = 0-31 15
47
48
//n = wr_OV7670(0x11, (registerValue & 0b11000000) | ov_prescaler);
49
}
50
//wait;
51
52
53
//Bild malen:
54
//  for(i=0;i<max_y;i++){
55
56
  lcd_setcursor(0,20*teilbild);
57
  lcd_write_index(0x0022);
58
   cs_lcd_off;
59
  LCD_Write_Data_Start();
60
61
62
//  240x320x2=153600 bytes
63
u16 tmp;
64
  for( index = 12799; index < 12800; index-=2 )  {
65
    lcd_write_data_only((buffer[index]<<8)+buffer[index-1]);
66
  }
67
cs_lcd_on;
68
}//end sub
69
//----===================================================
70
#else

von leluno (Gast)


Lesenswert?

Der spi-Durchbruch:
1
zl_row=240;
2
//start frame
3
while(zl_row){
4
while(!href);//Warten auf href_h
5
while(href){;
6
// +++ px ++++
7
  while(!pclk);
8
9
//    buffer[zl_pix]=PINA;
10
//    zl_pix--;
11
12
SPDR=PINA;
13
//while(!(SPSR & (1<<SPIF)));
14
  
15
  while(pclk);
16
}//zl_row
17
zl_row--;
18
}//end frame

statt in den buffer zu schreiben, wird das spi-tft direkt beschrieben. 
Das TFT ist im Prinzip ein Speicher mit 240x320x2=153.600byte. Statt des 
TFTs könnte man sicher auch andere spi-speicher nehmen.

Die Geschwindigkeit geht jetzt beim 16Mhz-Atmega hoch bis prescaler-12 
(ca. 1,5Mhz)

von leluno (Gast)


Angehängte Dateien:

Lesenswert?

das Programm ließ sich relativ einfach vom avr auf den arm übertragen. 
Auch der arm liefert inzwischen ein brauchbares Bild.

Überaschende Erkenntnis: Die Verarbeitungsgeschwindigkeit ist beim 
100Mhz-Arm nur halb so groß, wie beim AVR. Unter Prescaler 32 liefert 
der Arm kein brauchbares Bild.

Kann es sein, dass die Verarbeitungsgeschwindigkeit des AVR-Spi/Spi-Takt 
8Mhz größer ist als beim  Arm-SSP/Takt 25Mhz?

von Dirk K. (dekoepi)


Lesenswert?

Auf meinen stm32f0/1 ist SPI zwar eine Zicke, ich bin jedoch schneller 
als auf dem AVR (ATmega328p). Mit 18 MHz SPI erreiche ich im Polling von 
der Kamera mit umschalten und neu-adressieren sauber etwa 500 kByte/s.

Das DMA-Initial-Setup compiliert ohne Fehler, jetzt muss ich 'nur noch' 
den Transfer darauf umstellen. Sehe noch nicht, wie das klappen soll, 
dass der DMA auf PCLK wartet, um das nächste Byte zu übertragen. Aber 
kommt langes Wochenende, kommt Rat ;-)

: Bearbeitet durch User
von leluno (Gast)


Lesenswert?

Dirk K. schrieb:
> Warten auf PCLK

Meine Überlegung dazu:
pixelweise in einen fifo-buffer schreiben lassen, wenn 512 byte 
geschrieben sind, durch einen software-single-request die 
DMA-Übertragung auslößen...

von leluno (Gast)


Angehängte Dateien:

Lesenswert?

Als Problem beim Speichern auf sd-Karte stellte sich heraus, dass eine 
gleichzeitige Steuerung des Pixelinputs und des Fat-Überbaus 
erforderlich ist. Ich wollte den Pixelinput über Interrupt steuern, das 
verlangsamt aber das Programm. Zwischenzeitlich durfte ich beim 
Hauptzollamt das AL422-Fifo-Modul abholen.

Das Fifo-Modul läuft inzwischen auf dem Atmega. Der Fifo ist eher 
schwieriger zu programmieren, da die Rücksetzung des Speichers nicht 
ganz einfach ist. Das Modul läuft mit Prescaler 30=>400khz sehr stabil. 
Der Datentransfer erfolgt zur Zeit aber noch innerhalb des vsync-Zyclus, 
so dass im Prinzip nur eine Zeile im Fifo zwischengespeichert wird. Das 
hat den Vorteil, dass der Code zukünftig für ein Mt9d112-Modul genutzt 
werden kann.

von leluno (Gast)


Lesenswert?

Der Durchbruch mit dem Fifo-Modul:

Das Schreiben in den Fifo wird über einen vsync-Interrupt beendet. 
Dadurch entfällt die Überwachung der Zeilen beim Lesen des Fifos. Die 
write-Geschwindigkeit darf so hoch sein, dass zum Zeitpunkt vsync 50% 
des Bildes gelesen sind - der Rest passt in den Fifo. Sie muss höher als 
die Lesegeschwindigkeit sein.

Nach Umsetzung im Programm schafft der AVR wahrscheinlich auch das 
Schreiben eines vollständigen Bildes in eine sd-Karte. Die bekommt er 
als nächsten Schritt verpasst.

Durch das parallele Arbeiten mit AVR und ARM ist darüberhinaus ein für 
beide Systeme einsetzbarer Code entstanden.

von Grundschüler (Gast)


Angehängte Dateien:

Lesenswert?

Das Programm schreibt jetzt QVGA auf die sd-karte. Die Bitmap-Datei wird 
nach dem Schreiben geöffnet und in das Lcd eingelesen.

Beim Wechsel auf VGA kommt es zu unerklärlichen Problemen. Dies auch bei 
einem Format von 640x120 was eigentlich programmtechnisch keinen 
Unterschied  zu 320x240 machen dürfte. Die Bilder werden zerstückelt.

von Dirk K. (dekoepi)


Lesenswert?

Püh, habe ich nicht alleine Probleme mit dem 640x480-Modus - beruhigt 
mich zumindest etwas.

Gestern kam meine OV5642 an. Werde wohl die RAM-Bank und die olle OV7670 
auf Halde für seeeehr gelangweilte Tage legen und mich langsam mit 
SD-Karte auseinandersetzen. Da soll das fertige JPEG aus der Kamera 
direkt hin.

Nicht die optimale Lösung, aber die OV7670 raubt mir irgendwie nur die 
Nerven ;)

von Grundschüler (Gast)


Lesenswert?

Dirk K. schrieb:
> 640x480-Modus

Der ov7670 ist nicht das Problem. Mit
1
#if H_PX==320
2
#define ovregCOM7 0b00010100 //QVGA
3
#else
4
#define ovregCOM7 0b00000100//VGA
5
#endif
kann man zwischen den Formaten schalten.
Es werden rund 420 Zeilen auf dem Lcd einwandfrei angezeigt(jedes 
2.Pixel jede 2.Zeile).

Das Problem ist der AL422, der mit meinem code nicht wirklich 
zwischenspeichert. Laut Datenblatt braucht er durchgehenden Schreibe- 
und Lesetakt von >1Mhz. Das passt mit meinem Code bislang nicht.

von Dirk K. (dekoepi)


Lesenswert?

Ach siehste, OV7670 mit AL422B habe ich ja auch noch rumfliegen. Der 
nächste graue Herbst/Winter kommt bestimmt ... ;)

Kannst du die CLKs für R/W nicht einfach via XCLK abgreifen?

von Grundschüler (Gast)


Lesenswert?

Dirk K. schrieb:
> XCLK

das habe ich gerade gemacht - wck mit pck verbunden. Das Problem ist 
dass ich jetzt den schnellen Modul-Takt überwachen muss, während ich 
bislang die Lesegeschwindigkeit auf die SD-Karte/lcd angepasst hatte. 
Wenn ich deine relativ schnellen Speichermodule hätte, wäre ich schon 
fertig. Hauptproblem ist die langsame Geschwindigkeit der sd-karte.

von Grundschüler (Gast)


Lesenswert?

Grundschüler schrieb:
> pck

muss rck heißen

von Dirk K. (dekoepi)


Lesenswert?

Der FIFO soll doch deshalb von Vorteil sein, weil du dir die Bytes 
raustaktest, wie es grade passt? Also FIFO-PCK togglen, Byte lesen. Der 
Speichertakt ist doch nur RAM Refresh und hat mit der 
Ausgabegeschwindigkeit nichts zu tun?
Missverstehe ich am FIFO etwas?

von Grundschüler (Gast)


Lesenswert?

Dirk K. schrieb:
> Missverstehe ich am FIFO etwas?

Ich fürchte leider ja. Mein bisheriger code beruhte auf genau dieser 
Annahme. Das funktioniert auch, solange man direkt nach dem Schreiben 
liest.

zl_hrref =300;
while(zl_hrref);//Zeilen Vorlauf
al422_writeStop();
al422_readStart_cyc();
müsste eigentlich 300 Zeilen in den Fifo laden und dann mit dem Lesen 
beginnen. Ein brauchbares Bild bekomme ich aber nur, wenn der Vorlauf 
zl_hrref sehr klein ist. D.h. es werden nur wenige Zeilen 
zwischengespeichert. Es reicht, um QVGA-Bild auf sd-Karte zu speichern, 
mehr nicht.

==>Der Beitrag scheint Spam zu enthalten: "hrref="

von Dirk K. (dekoepi)


Lesenswert?

Ich würde mich hierdran orientieren - das funktioniert ja offenbar ;)
http://www.haoyuelectronics.com/Attachment/OV7670%20+%20AL422B(FIFO)%20Camera%20Module(V2.0)/OV7670%20+%20AL422B(FIFO)%20Camera%20Module(V2.0)%20Schematic.pdf

Die RCK steuerst du dann anstatt PCK, hatte ich oben falsch. Der AL422B 
nimmt sich ja den höheren Takt von WCK/RCK, sodass WCK über den 
OV7670-PCK ausreichend versorgt ist. Zumindest als Idee vielleicht mal 
probieren.

von Grundschüler (Gast)


Angehängte Dateien:

Lesenswert?

Stillleben mit morgendlicher Kaffeetasse im 640x480 Format. Nicht 
wirklich befriedigend, denn es erfolgt eine Zerlegung in drei 
Teilbilder:
1
#define lcd0_sd1 1
2
3
   void get_picture(){//
4
   u32 n,i,j,offset=0, timespan,ii,zl_tbild;
5
   u32 index,zl_pix=0,zl_pix8=0,zl_pixx,restzeit=0;
6
   //zl_px=153600;//2x240x320
7
   extern_int_hr_ef_init();
8
   extern_int_hr_ef_enable();
9
   //++
10
 //lcd_streamwrite_start();
11
 #if (!lcd0_sd1)//{
12
 lcd_streamwrite_start(0);
13
 #else//}else{
14
 f_write_Bmpheader(H_PX,V_ZL*3);
15
 //SysTick->CTRL &= ~NVIC_SYSTICK_ENABLE;
16
  #endif//}//
17
// TimeTick=0;
18
   //1. rücksetzen///////////////
19
20
 zl_tbild=0;
21
 for(zl_tbild=0;zl_tbild<3;zl_tbild++){//3 teilbilder
22
23
 al422_writeReset();
24
25
   while(!(vsync));while(vsync);
26
27
   if(zl_tbild>0){
28
 zl_hr_ef=160*zl_tbild;
29
 while(zl_hr_ef);
30
  }
31
   al422_writeStart();
32
33
 // zlsum_hr_ef=0;
34
zl_hr_ef=1;
35
 while(zl_hr_ef);
36
37
38
39
  led1_on;
40
fifo_wren_high;
41
//al422_writeStop();
42
  // while(!vsync);
43
 zl_hr_ef=1;
44
 while(zl_hr_ef);
45
46
47
 al422_readStart_cyc();
48
   /////////////// read schleife ///////////////
49
 for(i=0;i<(V_ZL/ZEILEN);i++){
50
          zl_px=BLOCK;
51
           zl_hr_ef=ZEILEN;
52
           while(zl_px){//buffer-modus
53
           al422_readData_cyc();
54
           buf_fifo[BLOCK-zl_px]=Value_Datenbus;
55
           //buf_fifo[BLOCK-zl_px]=RED;//Value_Datenbus;
56
         zl_px--;
57
         //zl_px--;
58
           }//zl_px
59
60
 #if (!lcd0_sd1)//{
61
           if(H_PX==640){
62
    lcd_streamwrite_px4(buf_fifo,BLOCK);
63
           }else{
64
    lcd_streamwrite_px(buf_fifo,BLOCK);
65
           }
66
    #else//}else{
67
     f_write(&Fil, buf_fifo, BLOCK, &bw);
68
 #endif//}//
69
 //         lcd_streamwrite_px(buf_fifo,BLOCK);//22-3-394122
70
  //             DMA_transfer_SSP0x(0);//lcd tested ok
71
72
 //           restzeit=0;
73
 //          while(zl_hr_ef)restzeit++;//Anpassung Geschwindigkeit cmos
74
 }//for
75
 }//zl_tbild
76
77
 //fifo_wren_high;//ende write
78
 // fifo_rden_high;
79
 al422_writeStop();
80
 al422_readStop();
81
82
 #if (!lcd0_sd1)//{
83
 lcd_streamwrite_stop();
84
 lgw(20,5,"LCD");li(restpixel);lw("-");li(zl_Bild);
85
 lcd_pixel(120,120,YELLOW);
86
 lcd_pixel(121,121,YELLOW);
87
 lcd_pixel(122,122,YELLOW);
88
 lcd_pixel(123,123,YELLOW);
89
 #else//}else{
90
   f_close(&Fil);
91
   wait;
92
93
   bmp_to_lcd(0);
94
   lgw(20,5,"SD ");li(restpixel);lw("-");li(zl_Bild);
95
   wait;
96
   wait;
97
   wait;
98
   wait;
99
   wait;
100
 #endif//}//
101
   wait;
102
103
   }//end sub get_picture

Problem bleibt der AL422 Fifo. Er scheint seinen Speicherinhalt zu 
verlieren, wenn nicht unmittelbar nach dem Schreiben gelesen wird. Fifo 
vollschreiben und dann in Ruhe lesen geht nicht. Laut Datenblatt sollen 
beide  Taktzyklen kontinuierlich mindestens 1MHZ betragen. Bei 1MHz 
W-Clock werden die Bilder jedoch zerstückelt.

Auch der Versuch, W+R-Clock zu verbinden und den Fifo mittels 
RClock-Interrupt auszulesen war nicht erfolgreich. Der Fifo funktioniert 
nicht wie er soll und ich weiß nicht wirklich woran es liegt.

Immerhin klappt das Speichern auf SD-Karte mit den Teilbildern recht 
ordentlich. Ich werde jetzt erst einmal versuchen, das Programm in einen 
Webserver zu integrieren.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.