Forum: Mikrocontroller und Digitale Elektronik STM32 DMA 8bit Memory buffer


von Anderle K. (anderl)


Lesenswert?

Hallo Hallo,

ich benutze einen STM32F4 und einen externen ADC der mir über I2S 24 bit 
Werte liefert, die dann in ein 32bit register geschrieben werden. Diese 
Daten will ich dann über TCP an meinen PC senden. netTCP_Send benötigt 
dafür einen 8bit Buffer in dem die zu sendenden Daten gespeichert sind.

Um das ganze zu testen nehme ich erstmal den on board ADC. Dieser 
schreibt die gewandelten Daten in ein 16bit register. Jetzt will ich die 
Daten mittels DMA in einen Buffer (size=1000) schreiben und sie dann 
über TCP versenden. Die DMA verlangt das es sich um einen 32 bit buffer 
handeln soll. Ich hätte aber gerne einen 8 bit buffer.


!---------------------------------------------------!
Ich stell mir das so vor das die DMA das höherwertige byte des ersten 
samples in die erste Adresse des buffers schreibt und das 
niederwertigere byte in die zweite. Dann das höherwertige byte des 
zweiten samples in die dritte, niederwertigees in die vierte und so 
weiter.
!---------------------------------------------------!


Wenn der buffer halb voll ist sende ich über TCP und beschreibe die 
zweite Hälfte. Versende sie und fange zyklisch wieder am Anfang an. DMA 
und ADC funktionieren gut, nur leider werden die Daten halt in ein 32 
bit register geschrieben und ich kann sie damit nicht per TCP versenden, 
da sonst die höherwertigen 8 bit verloren gehen. Falls die DMA das nicht 
kann habe ich mir auch schon was mit Schiebeoperatoren überlegt. 
Allerdings weis ich nicht wie ich ds schnell mit einem ganzen array 
machen sollte.


Kann mir bitte jemand helfen die DMA so einzustllen, dass die Werte wir 
oben beschrieben in einen 8bit Memorybuffer geschrieben werden.

Vielen  vielen Dank schon mal.

von Little B. (lil-b)


Lesenswert?

Ich verstehe deine Problematik nicht...

Ob du einen Buffer nun Byteweise beschreibst, oder immer 4 Byte auf 
einmal, ist doch völlig Banane.

Ob du in deinem C-Programm nun uint8_t Buffer[4] oder uint32_t Buffer[1] 
definierst, ist nur in deinem Programm relevant. Der DMA sieht in beiden 
Fällen nur 4 Byte Speicher, die er beschreiben kann.

Das einzige, was sich ändert, ist die Anzahl der Transfers.
AnzahlTransfers = sizeof(Buffer) / BytesPerTransfer;

von Anderle K. (anderl)


Lesenswert?

So sieht die DMA initialisierung im Moment aus.

    uint32_t ADCBuffer[2000];

    hdma_adc1.Instance = DMA2_Stream0;
    hdma_adc1.Init.Channel = DMA_CHANNEL_0;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    //hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    //hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    hdma_adc1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_adc1.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_adc1.Init.PeriphBurst = DMA_PBURST_SINGLE;
    HAL_DMA_Init(&hdma_adc1);

    __HAL_LINKDMA(&hadc1,DMA_Handle,hdma_adc1);

    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

    HAL_ADC_Start_DMA(&hadc1,ADCBuffer, 2000);

ADCBuffer muss 32bit haben (ist in HAL_ADC_Start_DMA vorgegen)
Ich hätte aber gerne einen 8bit buffer der wie folgt aufgebaut ist
ADCbuffer[0]=höherwertiges byte von sample 1
ADCbuffer[1]=niederwertiges byte von sample 1
ADCbuffer[2]=höherwertiges byte von sample 2
ADCbuffer[3]=niederwertiges byte von sample 2
ADCbuffer[4]=höherwertiges byte von sample 3
.
.
.
.

Leider steh ich gerade auf dem Schlauch und weis nicht wie ich das 
hinbekommen soll. Leider kann ich mit deinem Tip noch nicht soviel 
anfangen. Bitte, bitte konkreter.

vielen Dank schon mal

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

Wie sieht denn Dein
1
ADC_HandleTypeDef DMA_Handle
aus?

von Anderle K. (anderl)


Lesenswert?

ADC_HandleTypeDef hadc1;
    DMA_HandleTypeDef hdma_adc1;

    void ADC_Initisalize (void) {


    ADC_ChannelConfTypeDef sConfig;
    GPIO_InitTypeDef GPIO_InitStruct;



    /* Peripheral clock enable */
    __ADC1_CLK_ENABLE();
    /* GPIO Ports Clock Enable */
    __GPIOA_CLK_ENABLE();
    /* DMA controller clock enable */
    __DMA2_CLK_ENABLE();


    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);



  /**Configure the global features of the ADC (Clock, Resolution, Data 
Alignment and number of conversion)
    */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV8;
  hadc1.Init.Resolution = ADC_RESOLUTION12b;
  hadc1.Init.ScanConvMode = DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.EOCSelection = EOC_SINGLE_CONV;
  HAL_ADC_Init(&hadc1);

    /**Configure for the selected ADC regular channel its corresponding 
rank in the sequencer and its sample time.
    */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);


Dann kommt die DMA config. Der ADC tut was er tun soll. Dass passt. Die 
DMA auch, ausser dass sie alle 16bit samples in einen 32 bit buffer 
schreibt. Ich bräuchte einen acht bit buffer der die samples in low und 
high trennt und immmer wechseln speichert.

Danke

von Little B. (lil-b)


Lesenswert?

Anderle K. schrieb:
>     hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
>     hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
>     hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

Das funktioniert nicht!

Für unterschiedliche Datengrößen brauchst du den FIFO! Ansonsten wird 
die Größe der Peripherie auch für Memory übernommen.

von Anderle K. (anderl)


Angehängte Dateien:

Lesenswert?

Also ich habe das mit der FIFO ausprobiert. Die initialisierung der DMA 
sieht jetzt so aus.


    hdma_adc1.Instance = DMA2_Stream0;
    hdma_adc1.Init.Channel = DMA_CHANNEL_0;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    //hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    //hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_adc1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
    hdma_adc1.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_adc1.Init.PeriphBurst = DMA_PBURST_SINGLE;
    HAL_DMA_Init(&hdma_adc1);

    __HAL_LINKDMA(&hadc1,DMA_Handle,hdma_adc1);


    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);



   HAL_ADC_Start_DMA(&hadc1,ADCBuffer, 2000);

 Im Anhang habe ich einen Screenshot von meinem Buffer gelegt. Ich denke 
dass jetzt pro 32bit Bufferzeile zwei 16 bit samples sind, die wiederrum 
in jeweils 2byte aufgeteilt sind. Ich hätte aber gerne einen buffer der 
in jeder Zeile genau ein byte hat, da TCP_send einen 8bit buffer 
benötigt. Wenn ich der Funktion meinen 32bit ADCbuffer gebe, werden die 
höherwertigen 24bit ja einfach abgeschnitten. So sieht der TCP 
Sendevorgang aus wenn die DMA sagt dass sie die Hälfte der Daten 
übertragen hat.

osSignalWait(2U, osWaitForever);    // Wait for signal
LED_On (1);
if (netTCP_SendReady (tcp_sock)) {
LED_On (3);
// The socket is ready to send the data
maxlen  = netTCP_GetMaxSegmentSize (tcp_sock);
//hier NDTR auslesen und checken obs auch die Hälfte ist dann mit 
maxlen/1000 ersetzen

sendbuf = netTCP_GetBuffer (1000);
memcpy (sendbuf, &ADCBuffer[0], 1000);
netTCP_Send (tcp_sock, sendbuf, sizeof(sendbuf));

LED_On (0);

Vllt kann man den 32bit buffer ja auch so übergeben das er behandelt 
wird als wäre es ein 8bit buffer. Das sozusagen die Zeilen durchgescant 
werden. Leider weis ich nicht wie sowas funktioniert. Ich bin mir sicher 
dass ich wieder irgendwas recht einfaches nicht checke.

Ich würde mich sehr um Hilfe freuen.

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.