mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Bare-Metal ATSAM


Autor: Rudolph R. (rudolph)
Datum:
Angehängte Dateien:

Bewertung
-2 lesenswert
nicht lesenswert
Gestern ist mir aufgefallen, dass auf dem Trinket M0 eine APA102 LED mit 
drauf ist, da ich mit so einem Ding sowieso rumspielen wollte, war das 
die Gelegenheit das Trinket M0 mal auszupacken. :-)

Da das Board aber keinen SWD Stecker hat (nur zwei Lötpads),
habe ich etwas im Arduino Framework rumgespielt mit Hilfe von vscode und 
PlatformIO.
Der Projekt-Ordner ist durch das PlatformIO etwas aufgebläht, daher 
hängt nur die .ino mit an.

Und tolle Dinger diese APA102, der SPI läuft hier gerade so mit 24MHz 
vor sich hin.

Als natives Projekt würde der Code um die APA102 LED zu beschreiben 
nicht anders aussehen.
Dazu kommen würde noch den Core-Takt auf 48MHz zu stellen und vielleicht 
den Systick Timer einzurichten um das Test-Blinken nicht per delay() zu 
machen.

Zum Zitieren hänge ich die paar Zeilen auch direkt mit in den Text.

- Was ist jetzt die Frage dazu?

Keine konkrete dazu.
Aber hast Du vielleicht noch mehr solche Beispiele?
Wie könnte man das um DMA erweitern?

- Warum ist das dann nicht unter "Projekte & Code"?

Das ist jetzt als Projekt für sich so etwas dünn und die Idee ist 
eigentlich noch mehr solche Schnipsel einzusammeln.
Die Beispiele für ATSAM ohne ASF sind etwas dünn gesäht...
Wenn ein Mod da jetzt anderer Meinung ist, bitte verschieben.

#include "sam.h"

void init_spi(void)
{
  /* configure SERCOM1 MOSI on PA00 and SERCOM1 SCK on PA01 */
  REG_PORT_WRCONFIG0 =
    PORT_WRCONFIG_WRPINCFG |
    PORT_WRCONFIG_WRPMUX |
    PORT_WRCONFIG_PMUX(3) |    /* SERCOM1 */
    PORT_WRCONFIG_DRVSTR |
    PORT_WRCONFIG_PINMASK(0x03) | /* PA00 + PA01 */
    PORT_WRCONFIG_PMUXEN;

  REG_PM_APBCMASK |= PM_APBCMASK_SERCOM1;
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_ID_SERCOM1_CORE; /* setup SERCOM1 to use GLCK0 -> 48MHz */
  
  REG_SERCOM1_SPI_CTRLA = 0x00; /* disable SPI -> enable config */
  REG_SERCOM1_SPI_CTRLA = SERCOM_SPI_CTRLA_MODE(3) | SERCOM_SPI_CTRLA_DOPO(0); /* MSB first, CPOL = 0, CPHA = 0, SPI frame, master mode, PAD0 = MISO, PAD2 = MOSI, PAD3 = SCK */
  
  REG_SERCOM1_SPI_BAUD = 0; /* 48 / (2 * (baudval + 1)) -> @48Mhz: 0 = 24MHz, 5 = 4MHz, 23 = 1MHz, 47 = 500kHz */
  
  REG_SERCOM1_SPI_CTRLB = 0x00; /* receiver disabled, no hardware select, 8-bit */
  REG_SERCOM1_SPI_CTRLA |= SERCOM_SPI_CTRLA_ENABLE; /* activate SERCOM1 */
  while(REG_SERCOM1_SPI_SYNCBUSY & SERCOM_SPI_SYNCBUSY_ENABLE); /* wait for SERCOM1 to be ready */
}

void spi_transmit(uint8_t data)
{
  REG_SERCOM1_SPI_DATA = data;
  while((REG_SERCOM1_SPI_INTFLAG & SERCOM_SPI_INTFLAG_TXC) == 0);
}

/* write out data to a *single* APA102 LED as RGB */
void apa_write(uint32_t color)
{
  /* start frame */
  spi_transmit(0x00);
  spi_transmit(0x00);
  spi_transmit(0x00);
  spi_transmit(0x00);

  spi_transmit(0xff); /* 0b11100000 + brightness */ 
  spi_transmit(color); /* blue */
  spi_transmit(color >> 8); /* green */
  spi_transmit(color >> 16); /* red */
 }

void setup()
{
  init_spi();
}

void loop()
{
  apa_write(0x400000);
  delay(300);
  apa_write(0x004000);
  delay(300);
  apa_write(0x000040);
  delay(300);
}

: Bearbeitet durch User
Autor: Rudolph R. (rudolph)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Das Interesse ist zwar offenbar recht dünn, aber da ich praktisch keine 
Beispiele im Netz finden konnte wie man bei den ATSAM den DMA benutzt, 
hängt hier mal mein Test-Projekt dazu an.
Das ist immer noch im Arduino Framework, als PlatformIO Projekt.

Bis auf delay() wird allerdings keine Arduino Funktion verwendet und 
auch keine Library vorausgesetzt, die eigentliche Funktion wird durch 
direktes Beschreiben der Register erreicht.

Das Adafruit Trinket M0 hat eine APA102 LED verbaut, das macht es für 
diese Spielerei perfekt.
Wenn da ein SWD Header dran wäre, dann hätte ich ein Atmel Studio 
Projekt erstellt.
Die Funktionen init_spi(), init_dma() und apa_dma_write() würden dann 
allerdings exakt genau so aussehen.


Ich hatte zwei wesentliche Probleme mit dem DMA.

Das erste war das mir zunächst nicht klar war, wie man einen Transfer 
überhaupt anschiebt.
Mit dem Trigger auf SERCOM1 TX sah es für mich zunächst so aus, als 
könnte ich den ersten Transfer nicht auslösen.
Der Transfer geht allerdings los wenn man im CHCTRLA Register das ENABLE 
bit setzt.

Das zweite war die Quell-Adresse.
Ich bin einfach blind davon ausgegangen, dass die Quell- und die 
Ziel-Adresse die Anfangs-Adressen sind von denen aus der DMAC weiter 
zählt.
Es sind aber die Adressen an denen der DMAC ankommen soll wenn der 
Transfer durch ist - aus welchem Grund auch immer.

"Bits 31:0 – SRCADDR[31:0] Transfer Source Address
This bit group holds the source address corresponding to the last beat 
transfer address in the block
transfer."

"Bits 31:0 – DSTADDR[31:0] Transfer Destination Address
This bit group holds the destination address corresponding to the last 
beat transfer address in the block
transfer."

In dem Fall Speicher zu Peripherie wird die Quell-Adresse hochgezählt, 
die Ziel-Adresse aber nicht.

Nach der Beschreibung würde ich allerdings die Adresse des letzten 
Elementes in einem Array angeben und nicht etwa die erste Adresse hinter 
dem Array.

Hier noch direkt die schlapp 90 Zeilen zum Zitieren um Fehler 
aufzuzeigen oder Fragen zu stellen:
#include "sam.h"

static DmacDescriptor dmadescriptor __attribute__((aligned(16))) SECTION_DMAC_DESCRIPTOR;
static DmacDescriptor dmawriteback __attribute__((aligned(16))) SECTION_DMAC_DESCRIPTOR;

#define DATA_LENGTH 12

uint8_t led_data[DATA_LENGTH] = {0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff};

void init_spi(void)
{
  /* configure SERCOM1 MOSI on PA00 and SERCOM1 SCK on PA01 */
  REG_PORT_WRCONFIG0 =
    PORT_WRCONFIG_WRPINCFG |
    PORT_WRCONFIG_WRPMUX |
    PORT_WRCONFIG_PMUX(3) |    /* SERCOM1 */
    PORT_WRCONFIG_DRVSTR |
    PORT_WRCONFIG_PINMASK(0x03) | /* PA00 + PA01 */
    PORT_WRCONFIG_PMUXEN;

  REG_PM_APBCMASK |= PM_APBCMASK_SERCOM1;
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_ID_SERCOM1_CORE; /* setup SERCOM1 to use GLCK0 -> 48MHz */
  
  REG_SERCOM1_SPI_CTRLA = 0x00; /* disable SPI -> enable config */
  REG_SERCOM1_SPI_CTRLA = SERCOM_SPI_CTRLA_MODE(3) | SERCOM_SPI_CTRLA_DOPO(0); /* MSB first, CPOL = 0, CPHA = 0, SPI frame, master mode, PAD0 = MISO, PAD2 = MOSI, PAD3 = SCK */
  
  REG_SERCOM1_SPI_BAUD = 0; /* 48 / (2 * (baudval + 1)) -> @48Mhz: 0 = 24MHz, 5 = 4MHz, 23 = 1MHz, 47 = 500kHz */
  
  REG_SERCOM1_SPI_CTRLB = 0x00; /* receiver disabled, no hardware select, 8-bit */
  REG_SERCOM1_SPI_CTRLA |= SERCOM_SPI_CTRLA_ENABLE; /* activate SERCOM1 */
  while(REG_SERCOM1_SPI_SYNCBUSY & SERCOM_SPI_SYNCBUSY_ENABLE); /* wait for SERCOM1 to be ready */
}

void init_dma(void)
{
  REG_PM_APBBMASK |= PM_APBBMASK_DMAC;
  REG_PM_AHBMASK |= PM_AHBMASK_DMAC;

  REG_DMAC_BASEADDR = (uint32_t) &dmadescriptor;
  REG_DMAC_WRBADDR = (uint32_t) &dmawriteback;
  REG_DMAC_PRICTRL0 = 0; /* all off, reset-default */

  REG_DMAC_CHCTRLB = DMAC_CHCTRLB_TRIGACT_BEAT | DMAC_CHCTRLB_TRIGSRC(SERCOM1_DMAC_ID_TX); /* beat-transfer, SERCOM1 TX Trigger, level 0, channel-event input / output disabled */
  REG_DMAC_CTRL = DMAC_CTRL_LVLEN0 | DMAC_CTRL_DMAENABLE; /* enable level 0 transfers, enable DMA */
  REG_DMAC_CHID = 0; /* select channel 0, reset-default */

  dmadescriptor.BTCTRL.reg = DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_VALID; /* increase source-address, beat-size = 8-bit */
  dmadescriptor.BTCNT.reg = DATA_LENGTH;
  dmadescriptor.SRCADDR.reg = (uint32_t) &led_data[DATA_LENGTH]; /* note: last entry in array + 1 */
  dmadescriptor.DSTADDR.reg = (uint32_t) &REG_SERCOM1_SPI_DATA;
  dmadescriptor.DESCADDR.reg = 0; /* no next descriptor */
}

/* write out data to APA102 LED as RGB */
void apa_dma_write(uint32_t color)
{
  if((REG_DMAC_CHCTRLA & DMAC_CHCTRLA_ENABLE) == 0)
  {
    led_data[5] = color; /* blue */
    led_data[6] = (color >> 8); /* green */
    led_data[7] = (color >> 16); /* red */
    REG_DMAC_CHCTRLA = DMAC_CHCTRLA_ENABLE;
  }
 }

void setup()
{
   init_spi();
   init_dma();
}

void loop()
{
  apa_dma_write(0x400000);
  delay(300);

  apa_dma_write(0x004000);
  delay(300);

  apa_dma_write(0x000040);
  delay(300);

  apa_dma_write(0x000000);
  delay(500);
}

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.

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