Forum: Mikrocontroller und Digitale Elektronik Fragen zu DMA im Controller


von Owen S. (senmeis)


Lesenswert?

Hallo,

ich möchte einen Entwicklungsboard Discovery von ST zum Laufen bringen. 
Nun bin ich überfordert von einem Beispiel mit USART und DMA. Der Code 
wird unten dargestellt.

Die Beschreibung hier:
The USART sends a predefined TxBuffer to the HyperTerminal and keep 
waiting data to be entered by user (The number of bytes to receive is 
defined by "DATA_TO_RECEIVE" constant). Each character sent by user is 
transferred using USART DMA receiver request to RxBuffer, when RxBuffer 
is filled the DMA transfer complete flag is set and LD1..LD4 start 
toggling in an infinite loop.

Frage 1: Welche Zeile startet die USART Kommunikation? Soweit ich 
verstehe sollte die zweite while() Schleife nur ausgeführt werden wenn 
die erste fertig ist, aber das ist der Fall. Der Controller sendet immer 
Daten egal ob Daten empfangen werden.

Frage 2: Was ist der Unterschied zwischen DMA und Interrupts? Ich meine, 
mit Interrupts können Tasks auch im Hintergrund laufen.

Code:
1
/* Private typedef -----------------------------------------------------------*/
2
/* Private define ------------------------------------------------------------*/
3
#ifdef USE_STM8L1526_EVAL
4
/* define the DMA parameters related to USART1 since USE_STM8L1526_EVAL is used */
5
#define USART_DMA_CHANNEL_RX   DMA1_Channel2
6
#define USART_DMA_CHANNEL_TX   DMA1_Channel1
7
#define USART_DMA_FLAG_TCRX    (uint16_t)DMA1_FLAG_TC2
8
#define USART_DMA_FLAG_TCTX    (uint16_t)DMA1_FLAG_TC1
9
#define USART_DR_ADDRESS       (uint16_t)0x5231  /* USART1 Data register Address */
10
#else /* USE_STM8L1528_EVAL */
11
/* define the DMA parameters related to USART2 since USE_STM8L1528_EVAL is used */
12
#define USART_DMA_CHANNEL_RX   DMA1_Channel3
13
#define USART_DMA_CHANNEL_TX   DMA1_Channel0
14
#define USART_DMA_FLAG_TCRX    (uint16_t)DMA1_FLAG_TC3
15
#define USART_DMA_FLAG_TCTX    (uint16_t)DMA1_FLAG_TC0
16
#define USART_DR_ADDRESS       (uint16_t)0x53E1  /* USART2 Data register Address */
17
#endif /* USE_STM8L1526_EVAL */
18
19
#define USART_BAUDRATE         (uint32_t)9600
20
#define DATA_TO_TRANSFER       (countof(TxBuffer) - 1)
21
#define DATA_TO_RECEIVE        (uint8_t) 0x05
22
23
/* Private macro -------------------------------------------------------------*/
24
#define countof(a)            (sizeof(a) / sizeof(*(a)))
25
26
/* Private variables ---------------------------------------------------------*/
27
uint8_t TxBuffer[] = "\n\rUSART Example:USART-Hyperterminal communication using DMA.\nEnter your Text\n\r";
28
uint8_t RxBuffer[DATA_TO_RECEIVE] = {0};
29
30
/* Private function prototypes -----------------------------------------------*/
31
static void CLK_Config(void);
32
static void DMA_Config(void);
33
static void USART_Config(void);
34
void Delay (uint32_t nCount);
35
/* Private functions ---------------------------------------------------------*/
36
37
/**
38
  * @brief  Main program.
39
  * @param  None
40
  * @retval None
41
  */
42
void main(void)
43
{
44
   /* CLK configuration -------------------------------------------*/
45
  CLK_Config();
46
  
47
  /* Initialize LEDs mounted on STM8L152X-EVAL board */
48
  STM_EVAL_LEDInit(LED1);
49
  STM_EVAL_LEDInit(LED2);
50
  STM_EVAL_LEDInit(LED3);
51
  STM_EVAL_LEDInit(LED4);
52
53
   /* USART configuration -------------------------------------------*/
54
  USART_Config();
55
  
56
   /* DMA configuration -------------------------------------------*/
57
  DMA_Config();
58
59
  /* USART Enable */
60
  USART_Cmd(EVAL_COM1, ENABLE);
61
62
  /* Wait the USART DMA Tx\Rx transfer complete */
63
  while (DMA_GetFlagStatus((DMA_FLAG_TypeDef)USART_DMA_FLAG_TCRX) == RESET);
64
  while (DMA_GetFlagStatus((DMA_FLAG_TypeDef)USART_DMA_FLAG_TCTX) == RESET);
65
  
66
  while (1)
67
  {
68
    STM_EVAL_LEDToggle(LED1);
69
    STM_EVAL_LEDToggle(LED2);
70
    STM_EVAL_LEDToggle(LED3);
71
    STM_EVAL_LEDToggle(LED4);
72
    Delay((uint32_t)0xFFFFF);
73
  }
74
}
75
76
/**
77
  * @brief  Configure peripherals Clock   
78
  * @param  None
79
  * @retval None
80
  */
81
static void CLK_Config(void)
82
{
83
  /*High speed external clock prescaler: 1*/
84
  CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1);
85
86
  /*Enable DMA clock */
87
  CLK_PeripheralClockConfig(CLK_Peripheral_DMA1, ENABLE);
88
}
89
90
/**
91
  * @brief  Configure DMA peripheral  
92
  * @param  None
93
  * @retval None
94
  */
95
static void DMA_Config(void)
96
{
97
  /* Deinitialize DMA channels */
98
  DMA_GlobalDeInit();
99
100
  DMA_DeInit(DMA1_Channel1);
101
  DMA_DeInit(DMA1_Channel2);
102
103
  /* DMA channel Rx of USART Configuration */
104
  DMA_Init(USART_DMA_CHANNEL_RX, (uint16_t)RxBuffer, (uint16_t)USART_DR_ADDRESS,
105
           DATA_TO_RECEIVE, DMA_DIR_PeripheralToMemory, DMA_Mode_Normal,
106
           DMA_MemoryIncMode_Inc, DMA_Priority_Low, DMA_MemoryDataSize_Byte);
107
108
  /* DMA channel Tx of USART Configuration */
109
  DMA_Init(USART_DMA_CHANNEL_TX, (uint16_t)TxBuffer, (uint16_t)USART_DR_ADDRESS,
110
           DATA_TO_TRANSFER, DMA_DIR_MemoryToPeripheral, DMA_Mode_Normal,
111
           DMA_MemoryIncMode_Inc, DMA_Priority_High, DMA_MemoryDataSize_Byte);
112
113
  /* Enable the USART Tx/Rx DMA requests */
114
  USART_DMACmd(EVAL_COM1, USART_DMAReq_TX, ENABLE);
115
  USART_DMACmd(EVAL_COM1, USART_DMAReq_RX, ENABLE);
116
117
  /* Global DMA Enable */
118
  DMA_GlobalCmd(ENABLE);
119
120
  /* Enable the USART Tx DMA channel */
121
  DMA_Cmd(USART_DMA_CHANNEL_TX, ENABLE);
122
  /* Enable the USART Rx DMA channel */
123
  DMA_Cmd(USART_DMA_CHANNEL_RX, ENABLE);         
124
}
125
126
/**
127
  * @brief  Configure USART peripheral  
128
  * @param  None
129
  * @retval None
130
  */
131
static void USART_Config(void)
132
{
133
  /* EVAL COM (USARTx) configuration -----------------------------------------*/
134
  /* USART configured as follow:
135
        - BaudRate = USART_BAUDRATE baud  
136
        - Word Length = 8 Bits
137
        - One Stop Bit
138
        - No parity
139
        - Receive and transmit enabled
140
        - USART Clock disabled
141
  */
142
  STM_EVAL_COMInit(COM1, (uint32_t)USART_BAUDRATE, USART_WordLength_8b, USART_StopBits_1,
143
                   USART_Parity_No, (USART_Mode_TypeDef)(USART_Mode_Tx | USART_Mode_Rx));
144
145
  /* USART Disable */
146
  USART_Cmd(EVAL_COM1, DISABLE);
147
}
148
149
/**
150
  * @brief  Delay.
151
  * @param  nCount
152
  * @retval None
153
  */
154
void Delay(uint32_t nCount)
155
{
156
  /* Decrement nCount value */
157
  while (nCount != 0)
158
  {
159
    nCount--;
160
  }
161
}

Gruss
Owen

von Reinhard B. (brainstorm)


Lesenswert?

Hallo!

Also ich bin zwar auf dem STM32 unterwegs, aber die Peripherie ist ja 
recht ähnlich.

> Frage 1: Welche Zeile startet die USART Kommunikation? Soweit ich
> verstehe sollte die zweite while() Schleife nur ausgeführt werden wenn
> die erste fertig ist, aber das ist der Fall. Der Controller sendet immer
> Daten egal ob Daten empfangen werden.

Der USART wird mit der Zeile USART_Cmd(EVAL_COM1, ENABLE) gestartet.

Ich nehme an du beziehst dich auf diese zwei whiles:
>  while (DMA_GetFlagStatus((DMA_FLAG_TypeDef)USART_DMA_FLAG_TCRX) == RESET);
>  while (DMA_GetFlagStatus((DMA_FLAG_TypeDef)USART_DMA_FLAG_TCTX) == RESET);

Der Effekt dieser Zeilen ist der, dass der Code danach erst ausgeführt 
wird, wenn die DMA Transfers für RX und TX fertig sind. DMA Transfer 
hier nicht mit UART Transfer verwechseln!

Ich habe mit DMA selbst noch nichts gemacht, aber ich sehe den Ablauf 
so:
Durch das Enablen des UARTS mit USART_Cmd(...) setzt die UART Peripherie 
das TXE Bit. Deshalb schreibt der DMA das erste Byte ins DR und die 
Sache läuft. Die LEDs sollten meinem Verständnis nach erst blinken, wenn 
du zusätzlich auch noch DATA_TO_RECEIVE Bytes an den Controller gesendet 
hast, aber vielleicht übersehe ich da was.

> Frage 2: Was ist der Unterschied zwischen DMA und Interrupts? Ich meine,
> mit Interrupts können Tasks auch im Hintergrund laufen.

DMA kopiert Daten von einer Speicherstelle auf eine andere ohne Zutun 
der CPU. DMA führt also keine Befehle im Sinn von CPU-Instruktionen aus.

Der Handler eines Interrupts ist einfach ein Programmabschnitt, der von 
der CPU abgearbeitet wird. Da können Daten kopiert werden, aber auch 
andere Dinge geschehen.

mfg

von Owen S. (senmeis)


Lesenswert?

Vielen Dank für Deine Hilfe.

Nun habe ich versucht, den Code so zu ändern, dass der Controller Daten 
zyklisch sendet. Das funktioniert soweit mit der folgenden Main Methode. 
Das ist irgendwie nicht effizient denn DMA_Config() und USART_Cmd() 
befinden sich in der while Schleife. Wenn diese aber herausgeholt werden 
funktioniert das Senden nicht mehr.

Ich bin dankbar für jeden Vorschlag.
1
void main(void)
2
{
3
   /* CLK configuration -------------------------------------------*/
4
  CLK_Config();
5
  
6
   /* USART configuration -------------------------------------------*/
7
  USART_Config();
8
   
9
  while (1)
10
  {
11
    /* DMA configuration -------------------------------------------*/
12
    DMA_Config();
13
  
14
    /* USART Enable */
15
    USART_Cmd(EVAL_COM1, ENABLE);
16
    
17
    /* Wait the USART DMA Tx transfer complete */
18
    while (DMA_GetFlagStatus((DMA_FLAG_TypeDef)USART_DMA_FLAG_TCTX) == RESET);
19
  
20
    Delay((uint32_t)0xFFFF);
21
  }
22
}

Gruss
Owen

von Reinhard B. (brainstorm)


Lesenswert?

Owen Senmeis schrieb:
> Das ist irgendwie nicht effizient denn DMA_Config() und USART_Cmd()
> befinden sich in der while Schleife. Wenn diese aber herausgeholt werden
> funktioniert das Senden nicht mehr.

Wohin herausholen? Du müsstest entweder den DMA für den circular mode 
konfigurieren, oder den DMA in seiner ISR neu konfigurieren. Das erneute 
'enablen' des UART sollte nicht notwendig sein.

Du solltest dir den Abschnitt über DMA im Reference Manual mal von vorne 
bis hinten durchlesen.

mfg

von Owen S. (senmeis)


Lesenswert?

In diesem Beispiel ist ein Makro zu sehen:
1
#define USART_DR_ADDRESS       (uint16_t)0x5231  /* USART1 Data register Address */

Diese Adresse wird aber im Manual nicht gefunden, nur im File stm8l15x.h 
steht:
1
#define USART1_BASE                 (uint16_t)0x5230

Ist diese Adresse frei wählbar?

Gruss
Owen

von Reinhard B. (brainstorm)


Lesenswert?

Owen Senmeis schrieb:
> Ist diese Adresse frei wählbar?

Nein, das ist die Adresse der UART Peripheral - bzw. des DR des UARTs. 
Steht scheinbar nicht im RM, aber im Datasheet (Seite 37,  Fig. 9).

Frei wählen kannst du (natürlich nur in sinnvollem Kontext) TxBuffer und 
RxBuffer.

mfg

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.