Forum: Mikrocontroller und Digitale Elektronik ASF, sd_mmc_spi.c, Teilersetzung durch PDCA?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Frank B. (fbergemann)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich nutze ein altes EVK1100 als Referenz-board ("uralt" - ich weiß) für 
ein AVR32 Projekt. Dabei verwende ich file.c:file_write_buf(...).
Wenn ich mir da die Innereien vom ASF so ansehe, dann frißt das jede 
Menge CPU resourcen. Irgendwann lande ich in 
sd_mmc_spi.c:sd_mmc_spi_write_sector_from_ram(...).
Lohnt es sich da im Inneren den Block
  // write data
  for(i=0;i<MMC_SECTOR_SIZE;i++)
  {
    spi_write(SD_MMC_SPI,*_ram++);
  }
...durch PDCA zu ersetzen?
Davor und danach sind ja auch ein paar read's um die Kommandos 
abzusetzen und deren Bestätigung mitzubekommen. Daher nehme ich mal an, 
daß man nicht über die 512 Blocksize hinweg PDCA einsetzen kann? (ausser 
vielleicht ein paar FF bytes)
Kann man PDCA zentral hier unterbringen, ohne dass die aufrufende Stelle 
das mitbekommen muss?

von Frank B. (fbergemann)


Bewertung
0 lesenswert
nicht lesenswert
Das hier will leider nicht funktionieren
#if BOARD==EVK1100
#define AVR32_PDCA_CHANNEL_USED_RX AVR32_PDCA_PID_SPI1_RX
#define AVR32_PDCA_CHANNEL_USED_TX AVR32_PDCA_PID_SPI1_TX
#elif BOARD==EVK1101
#define AVR32_PDCA_CHANNEL_USED_RX AVR32_PDCA_PID_SPI_RX
#define AVR32_PDCA_CHANNEL_USED_TX AVR32_PDCA_PID_SPI_TX
#elif BOARD==EVK1105
#define AVR32_PDCA_CHANNEL_USED_RX AVR32_PDCA_PID_SPI0_RX
#define AVR32_PDCA_CHANNEL_USED_TX AVR32_PDCA_PID_SPI0_TX
#else
# error No known AVR32 board defined
#endif
#define AVR32_PDCA_CHANNEL_SPI_TX 1 // In the example we will use the pdca channel 1. // TODO: should be #0?

volatile avr32_pdca_channel_t* pdca_channeltx;
// Used to indicate the end of PDCA transfer
volatile Bool end_of_transfer;

/* interrupt handler to notify if the Data reception from flash is
 * over, in this case lunch the Memory(ram_buffer) to USART transfer and
 * disable interrupt*/
 #if __GNUC__
 __attribute__((__interrupt__))
 #elif __ICCAVR32__
 __interrupt
 #endif
 static void pdca_int_handler(void)
 {
   // Disable all interrupts.
   Disable_global_interrupt();
   
   // Disable unnecessary channel
   pdca_disable(AVR32_PDCA_CHANNEL_SPI_TX);

   // Enable all interrupts.
   Enable_global_interrupt();

   end_of_transfer = true;
 }
 
void local_pdca_init(void * adr)
{
  // this channel is used to activate the clock of the SPI by sending a dummy variables
  pdca_channel_options_t pdca_options_SPI_TX ={ // pdca channel options

    .addr = (void *)adr, // memory address.
    .size = 512, // transfer counter: here the size of the string
    .r_addr = NULL, // next memory address after 1st transfer complete
    .r_size = 0, // next transfer counter not used here
    .pid = AVR32_PDCA_CHANNEL_USED_TX, // select peripheral ID - data are on reception from SPI1 RX line
    .transfer_size = PDCA_TRANSFER_SIZE_BYTE // select size of the transfer: 8,16,32 bits
  };

  // Init PDCA transmission channel
  pdca_init_channel(AVR32_PDCA_CHANNEL_SPI_TX, &pdca_options_SPI_TX);

  INTC_register_interrupt(&pdca_int_handler, AVR32_PDCA_IRQ_1, AVR32_INTC_INT1); // pdca_channel_spi1_RX = 0
}

//! @brief This function writes one MMC sector from a ram buffer
//!
//!         DATA FLOW is: RAM => SD/MMC
//!
//!
//! NOTE (please read) :
//!   - First call (if sequential write) must be preceded by a call to the sd_mmc_spi_write_open() function
//!   - An address error will not detected here, but with the call of sd_mmc_spi_get_status() function
//!   - The program exits the functions with the memory card busy !
//!
//! @param ram         pointer to ram buffer
//!
//! @return bit
//!   The write succeeded   -> true
//!   The write failed      -> false
//!
bool sd_mmc_spi_write_sector_from_ram(const void *ram)
{
  const uint8_t *_ram = ram;
  uint16_t i;

  // wait for MMC not busy
  if (false == sd_mmc_spi_wait_not_busy())
    return false;

  spi_selectChip(SD_MMC_SPI, SD_MMC_SPI_NPCS);    // select SD_MMC_SPI

  // issue command
  if(card_type == SD_CARD_2_SDHC) {
    r1 = sd_mmc_spi_command(MMC_WRITE_BLOCK, gl_ptr_mem>>9);
  } else {
    r1 = sd_mmc_spi_command(MMC_WRITE_BLOCK, gl_ptr_mem);
  }

  // check for valid response
  if(r1 != 0x00)
  {
    spi_unselectChip(SD_MMC_SPI, SD_MMC_SPI_NPCS);
    return false;
  }
  // send dummy
  spi_write(SD_MMC_SPI,0xFF);   // give clock again to end transaction

  // send data start token
  spi_write(SD_MMC_SPI,MMC_STARTBLOCK_WRITE);
  
 #if 0
  // write data
  for(i=0;i<MMC_SECTOR_SIZE;i++)
  {
    spi_write(SD_MMC_SPI,*_ram++);
  }
#else
  local_pdca_init((void const *)ram);
  pdca_load_channel(
  AVR32_PDCA_CHANNEL_SPI_TX,
  (void *)ram,
  512); 
  end_of_transfer = false;
  pdca_enable_interrupt_transfer_complete(AVR32_PDCA_CHANNEL_SPI_TX);
  pdca_channeltx =(volatile avr32_pdca_channel_t*) pdca_get_handler(AVR32_PDCA_CHANNEL_SPI_TX); // get the correct PDCA channel pointer
  pdca_channeltx->cr = AVR32_PDCA_TEN_MASK; // enable TX PDCA transfer

  while(!end_of_transfer);
  
  // pdca_disable_interrupt_transfer_complete(AVR32_PDCA_CHANNEL_SPI_TX);
#endif 

  spi_write(SD_MMC_SPI,0xFF);    // send CRC (field required but value ignored)
  spi_write(SD_MMC_SPI,0xFF);

  // read data response token
  r1 = sd_mmc_spi_send_and_read(0xFF);
  if( (r1&MMC_DR_MASK) != MMC_DR_ACCEPT)
  {
    spi_write(SD_MMC_SPI,0xFF);    // send dummy bytes
    spi_write(SD_MMC_SPI,0xFF);
    spi_unselectChip(SD_MMC_SPI, SD_MMC_SPI_NPCS);
    return false;         // return ERROR byte
  }

  spi_write(SD_MMC_SPI,0xFF);    // send dummy bytes
  spi_write(SD_MMC_SPI,0xFF);

  // release chip select
  spi_unselectChip(SD_MMC_SPI, SD_MMC_SPI_NPCS);  // unselect SD_MMC_SPI
  gl_ptr_mem += 512;        // Update the memory pointer.

  // wait card not busy after last programming operation
  i=0;
  while (false == sd_mmc_spi_wait_not_busy())
  {
    i++;
    if (i == 10)
      return false;
  }

  return true;                  // Write done
}

von Jim M. (turboj)


Bewertung
0 lesenswert
nicht lesenswert
Der Code setzt nur den TX Teil auf, man sollte aber auch den RX-Teil 
bedienen, selbst wenn das beim Schreiben nur Dummy ist.

Dein Code hat nämlich das Problem, dass der DMA TX Transfer 
abgeschlossen ist bevor der SPI Transfer beendet wird. Würde man auf das 
RX Ende warten, dann wäre auch der SPI Transfer vollständig 
abgeschlossen.

Einen Geschwindigkeitszuwachs würde ich übrigens auch nicht ohne 
weiteres erwarten. Die Einzeilbeit Verarbeitung hat zwar ein paar Takte 
Latenz pro Byte, die DMA Variante hat dafür viel Latenz (den kompletten 
IRQ Handler) am Ende des Blocks. Das ergibt nur bei recht hoher 
Interrupt Last irgendwelche Vorteile.

von Frank B. (fbergemann)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich will auch nur einen Datenlogger haben.
Mit der angehängten Version geht es jetzt schon mal grob.
Ich muss aber noch aufräumen.
Sparen will ich vor allem CPU-Zeit.
Dafür muss ich das noch unter FreeRTOS einbinden und sehen, ob ich es 
mit einer Semaphore zwischen Hauptmodul und Interrupt-Handler 
hinbekomme.

von Frank Bergemann (Gast)


Bewertung
0 lesenswert
nicht lesenswert
> Dein Code hat nämlich das Problem, dass der DMA TX Transfer
> abgeschlossen ist bevor der SPI Transfer beendet wird. Würde man auf das
> RX Ende warten, dann wäre auch der SPI Transfer vollständig
> abgeschlossen.

versteh' ich nicht, sollte man RX/TX mit pcsa für den kompletten Dialog 
"verschachteln"?

von Frank Bergemann (Gast)


Bewertung
0 lesenswert
nicht lesenswert
"pdca" mein' ich natürlich.

von Frank B. (fbergemann)


Bewertung
0 lesenswert
nicht lesenswert
Wie lasse ich denn die Hauptroutine am effektivsten auf den interrupt 
warten? - ohne polling? Sowas wie CAS gibt's für den AVR32 nicht - oder?

von Frank B. (fbergemann)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
@Jim:
Bei 12MHz war die Version mit pdca (incl interrupt) 50% schneller als 
ohne pdca.
Aber wie gesagt: es ging mir primär um die Entlastung der CPU.
Anbei auch eine erste Version mit FreeRTOS support.
Die muss ich aber noch vermessen und sehen, was die semaphore kostet.
Ich werde dann doch noch support für READ einbauen - ist sonst nur eine 
halbe Sache.
Und es muss noch etwas aufgeräumt werden.

von Frank B. (fbergemann)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
SD Card WRITE mit pdca funktioniert.
Allerdings unter FreeRTOS mit Atmel FAT support bekomme ich es ohne 
file_flush() nicht hin, dass die Daten auch hinterher von der SD Karte 
zu lesen sind. Ich nehme an, die Daten werden zwar geschrieben, aber das 
FAT directory wird ohne file_flush() nicht aktualisiert(?).
(Es gibt noch eine FAT Implementierung von FreeRTOS selbst - vielleicht 
funktioniert die besser?)
Jetzt versuche ich, auch SD Card READ mit pdca zu machen. Ich dachte das 
geht analog. Aber das Programm hängt :-(
Hat das jmd. schon mal durchexerziert und kann mir sagen, wo der Fehler 
ist?

P.S.
in dem modifizierten sd_mmc_spi.c ist ein
  if (1)
//  if (!pdca_activated)

Das muß für's Testen ersetzt werden durch
  if (!pdca_activated)

: Bearbeitet durch User
von Frank B. (fbergemann)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Wer (im internet) lesen kann ist klar im Vorteil.
Zumindest sieht die jetzige Version so aus, als ob sie funktionieren 
WÜRDE.
Muss ich aber noch überprüfen...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.