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?
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?
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.
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.
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.
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.
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
|
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)
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?
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
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...
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.
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.
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.
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 ;)
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.
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?
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.
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?
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="
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.