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


von Frank B. (fbergemann)


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
1
  // write data
2
  for(i=0;i<MMC_SECTOR_SIZE;i++)
3
  {
4
    spi_write(SD_MMC_SPI,*_ram++);
5
  }
...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)


Lesenswert?

Das hier will leider nicht funktionieren
1
#if BOARD==EVK1100
2
#define AVR32_PDCA_CHANNEL_USED_RX AVR32_PDCA_PID_SPI1_RX
3
#define AVR32_PDCA_CHANNEL_USED_TX AVR32_PDCA_PID_SPI1_TX
4
#elif BOARD==EVK1101
5
#define AVR32_PDCA_CHANNEL_USED_RX AVR32_PDCA_PID_SPI_RX
6
#define AVR32_PDCA_CHANNEL_USED_TX AVR32_PDCA_PID_SPI_TX
7
#elif BOARD==EVK1105
8
#define AVR32_PDCA_CHANNEL_USED_RX AVR32_PDCA_PID_SPI0_RX
9
#define AVR32_PDCA_CHANNEL_USED_TX AVR32_PDCA_PID_SPI0_TX
10
#else
11
# error No known AVR32 board defined
12
#endif
13
#define AVR32_PDCA_CHANNEL_SPI_TX 1 // In the example we will use the pdca channel 1. // TODO: should be #0?
14
15
volatile avr32_pdca_channel_t* pdca_channeltx;
16
// Used to indicate the end of PDCA transfer
17
volatile Bool end_of_transfer;
18
19
/* interrupt handler to notify if the Data reception from flash is
20
 * over, in this case lunch the Memory(ram_buffer) to USART transfer and
21
 * disable interrupt*/
22
 #if __GNUC__
23
 __attribute__((__interrupt__))
24
 #elif __ICCAVR32__
25
 __interrupt
26
 #endif
27
 static void pdca_int_handler(void)
28
 {
29
   // Disable all interrupts.
30
   Disable_global_interrupt();
31
   
32
   // Disable unnecessary channel
33
   pdca_disable(AVR32_PDCA_CHANNEL_SPI_TX);
34
35
   // Enable all interrupts.
36
   Enable_global_interrupt();
37
38
   end_of_transfer = true;
39
 }
40
 
41
void local_pdca_init(void * adr)
42
{
43
  // this channel is used to activate the clock of the SPI by sending a dummy variables
44
  pdca_channel_options_t pdca_options_SPI_TX ={ // pdca channel options
45
46
    .addr = (void *)adr, // memory address.
47
    .size = 512, // transfer counter: here the size of the string
48
    .r_addr = NULL, // next memory address after 1st transfer complete
49
    .r_size = 0, // next transfer counter not used here
50
    .pid = AVR32_PDCA_CHANNEL_USED_TX, // select peripheral ID - data are on reception from SPI1 RX line
51
    .transfer_size = PDCA_TRANSFER_SIZE_BYTE // select size of the transfer: 8,16,32 bits
52
  };
53
54
  // Init PDCA transmission channel
55
  pdca_init_channel(AVR32_PDCA_CHANNEL_SPI_TX, &pdca_options_SPI_TX);
56
57
  INTC_register_interrupt(&pdca_int_handler, AVR32_PDCA_IRQ_1, AVR32_INTC_INT1); // pdca_channel_spi1_RX = 0
58
}
59
60
//! @brief This function writes one MMC sector from a ram buffer
61
//!
62
//!         DATA FLOW is: RAM => SD/MMC
63
//!
64
//!
65
//! NOTE (please read) :
66
//!   - First call (if sequential write) must be preceded by a call to the sd_mmc_spi_write_open() function
67
//!   - An address error will not detected here, but with the call of sd_mmc_spi_get_status() function
68
//!   - The program exits the functions with the memory card busy !
69
//!
70
//! @param ram         pointer to ram buffer
71
//!
72
//! @return bit
73
//!   The write succeeded   -> true
74
//!   The write failed      -> false
75
//!
76
bool sd_mmc_spi_write_sector_from_ram(const void *ram)
77
{
78
  const uint8_t *_ram = ram;
79
  uint16_t i;
80
81
  // wait for MMC not busy
82
  if (false == sd_mmc_spi_wait_not_busy())
83
    return false;
84
85
  spi_selectChip(SD_MMC_SPI, SD_MMC_SPI_NPCS);    // select SD_MMC_SPI
86
87
  // issue command
88
  if(card_type == SD_CARD_2_SDHC) {
89
    r1 = sd_mmc_spi_command(MMC_WRITE_BLOCK, gl_ptr_mem>>9);
90
  } else {
91
    r1 = sd_mmc_spi_command(MMC_WRITE_BLOCK, gl_ptr_mem);
92
  }
93
94
  // check for valid response
95
  if(r1 != 0x00)
96
  {
97
    spi_unselectChip(SD_MMC_SPI, SD_MMC_SPI_NPCS);
98
    return false;
99
  }
100
  // send dummy
101
  spi_write(SD_MMC_SPI,0xFF);   // give clock again to end transaction
102
103
  // send data start token
104
  spi_write(SD_MMC_SPI,MMC_STARTBLOCK_WRITE);
105
  
106
 #if 0
107
  // write data
108
  for(i=0;i<MMC_SECTOR_SIZE;i++)
109
  {
110
    spi_write(SD_MMC_SPI,*_ram++);
111
  }
112
#else
113
  local_pdca_init((void const *)ram);
114
  pdca_load_channel(
115
  AVR32_PDCA_CHANNEL_SPI_TX,
116
  (void *)ram,
117
  512); 
118
  end_of_transfer = false;
119
  pdca_enable_interrupt_transfer_complete(AVR32_PDCA_CHANNEL_SPI_TX);
120
  pdca_channeltx =(volatile avr32_pdca_channel_t*) pdca_get_handler(AVR32_PDCA_CHANNEL_SPI_TX); // get the correct PDCA channel pointer
121
  pdca_channeltx->cr = AVR32_PDCA_TEN_MASK; // enable TX PDCA transfer
122
123
  while(!end_of_transfer);
124
  
125
  // pdca_disable_interrupt_transfer_complete(AVR32_PDCA_CHANNEL_SPI_TX);
126
#endif 
127
128
  spi_write(SD_MMC_SPI,0xFF);    // send CRC (field required but value ignored)
129
  spi_write(SD_MMC_SPI,0xFF);
130
131
  // read data response token
132
  r1 = sd_mmc_spi_send_and_read(0xFF);
133
  if( (r1&MMC_DR_MASK) != MMC_DR_ACCEPT)
134
  {
135
    spi_write(SD_MMC_SPI,0xFF);    // send dummy bytes
136
    spi_write(SD_MMC_SPI,0xFF);
137
    spi_unselectChip(SD_MMC_SPI, SD_MMC_SPI_NPCS);
138
    return false;         // return ERROR byte
139
  }
140
141
  spi_write(SD_MMC_SPI,0xFF);    // send dummy bytes
142
  spi_write(SD_MMC_SPI,0xFF);
143
144
  // release chip select
145
  spi_unselectChip(SD_MMC_SPI, SD_MMC_SPI_NPCS);  // unselect SD_MMC_SPI
146
  gl_ptr_mem += 512;        // Update the memory pointer.
147
148
  // wait card not busy after last programming operation
149
  i=0;
150
  while (false == sd_mmc_spi_wait_not_busy())
151
  {
152
    i++;
153
    if (i == 10)
154
      return false;
155
  }
156
157
  return true;                  // Write done
158
}

von Jim M. (turboj)


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:

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)


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)


Lesenswert?

"pdca" mein' ich natürlich.

von Frank B. (fbergemann)


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:

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:

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
1
  if (1)
2
//  if (!pdca_activated)

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

: Bearbeitet durch User
von Frank B. (fbergemann)


Angehängte Dateien:

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...

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.