Forum: Mikrocontroller und Digitale Elektronik STM32 PWM One Pulse Mode mit variablen Puleweiten


von Väinö (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich versuche auf einem STM32F4 Discovery Board ein PWM Signal (siehe 
Anhang) mit 16 Wiederholungen zu erzeugen. Dabei sollen die Pulsweiten 
einzeln vorgegeben werden. Hier meine Timer Initialisierung:
1
void Timer_Config(void) {
2
  TIM_OCInitTypeDef  TIM_OCInitStructure;
3
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
4
  /* PWM-Frq = TIM_CLK/(Period+1)/(Prescaler+1) */
5
  TIM_TimeBaseStructure.TIM_Period = 141 - 1; // 595.2 kHz Takt Dshot600
6
  TIM_TimeBaseStructure.TIM_Prescaler = 0; //84MHz
7
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
8
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
9
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
10
  /* PWM Mode configuration */
11
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
12
  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
13
  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;
14
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
15
  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
16
  /* PWM1 Mode configuration: Channel1 */
17
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
18
  TIM_OCInitStructure.TIM_Pulse = 100;
19
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
20
  TIM_OC1Init(TIM3, &TIM_OCInitStructure);
21
  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
22
  /* PWM2 Mode configuration: Channel2 */
23
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
24
  TIM_OCInitStructure.TIM_Pulse = 100;
25
  TIM_OC2Init(TIM3, &TIM_OCInitStructure);
26
  TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
27
  TIM_ARRPreloadConfig(TIM3, ENABLE);
28
29
  /* One Pulse Mode selection */
30
  TIM_SelectOnePulseMode(TIM3, TIM_OPMode_Repetitive);
31
  /* Generate an update event to reload the Prescaler and the repetition counter value immediately */
32
  TIM3->EGR = TIM_EGR_UG;
33
  TIM3->RCR = 16-1;  //Set the Repetition counter value  
34
  /* TIM3 enable counter */
35
  TIM_Cmd(TIM3, ENABLE);
36
}
Mit dieser Initialisierung wird nur ein ganz normales PWM Signal mit der 
Frequenz von 595kHz erzeugt ohne Unterbrechung obwohl das Register RCR 
auf 15 gesetzt ist.

Ich weiss nicht wie die Pulsbreite einzeln vorgegeben werden kann.
Weiss jemand zufällig wie man das macht?

Danke!

von Väinö (Gast)


Lesenswert?

hat jemand keine Idee?

von Ingo L. (corrtexx)


Lesenswert?

Versuch mal statt:
1
TIM_SelectOnePulseMode(TIM3, TIM_OPMode_Repetitive);
das hier:
1
TIM_SelectOnePulseMode(TIM3, TIM_OPMode_Single);
Sonst bleibt der Timer nicht stehen, nach deinen 16 Impulsen

: Bearbeitet durch User
von Ingo L. (corrtexx)


Lesenswert?

Väinö schrieb:
> Ich weiss nicht wie die Pulsbreite einzeln vorgegeben werden kann.
> Weiss jemand zufällig wie man das macht?
Einzeln kannst du die nicht vorgeben. Du kann halt [RepetitionCounter] x 
Impulse mit festem Tastgrad erzeugen.
Für deinen Fall:
Repetition Counter = 1 => Tastgrad 75%
Repetition Counter = 5 => Tastgrad 25%
Repetition Counter = 1 => Tastgrad 75%
Repetition Counter = 1 => Tastgrad 25%
usw... Du musst es quasi halb zu Fuß machen!

von Christopher J. (christopher_j23)


Lesenswert?

Es geht mit Hilfe von DMA auch mit variabler Periodendauer. Guck mal in 
AN4776 ("general purpose timer cookbook") unter "arbitrary waveform 
generator".

von Ingo L. (corrtexx)


Lesenswert?

Sehr interessant, gleich mal gespeichert die AppNote, danke!
Dann kann man damit Neopixel ohne CPU Auslastung befeuern? Genial!!!
Gerade gesehen, man könnte sogar den Prescaler gleich mit ändern. Da 
sind die Möglichkeiten ja grenzenlos!

: Bearbeitet durch User
von Väinö (Gast)


Lesenswert?

Christopher J. schrieb:
> Es geht mit Hilfe von DMA auch mit variabler Periodendauer. Guck
> mal in
> AN4776 ("general purpose timer cookbook") unter "arbitrary waveform
> generator".

Danke für die Antworten. Ich versuche vergeblich diese zu 
implementieren. Leider klappt es bei mir nicht. Ich sehe nur ein ganz 
normales PWM Signal, ohne Unterbrechung. Wenn ich OPMode auf 
TIM_OPMode_Single setze, sehe ich am PWM Ausgang nichts.

Weiss jemand zufällig woran es liegt?
Danke im Voraus.
1
void RCC_Config(void) {
2
  /* GPIO Takt freigeben */
3
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE);
4
5
  /* TIM1 clock enable */
6
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
7
8
  /* USART1 clock enable */
9
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
10
11
}
12
13
void Timer_Config(void) {
14
  TIM_OCInitTypeDef  TIM_OCInitStructure;
15
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
16
  /* PWM-Frq = TIM_CLK/(Period+1)/(Prescaler+1) */
17
  TIM_TimeBaseStructure.TIM_Period = 141 - 1; // 595.2 kHz Takt Dshot600
18
  TIM_TimeBaseStructure.TIM_Prescaler = 1; //84MHz
19
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
20
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
21
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
22
  /* PWM Mode configuration */
23
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
24
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
25
26
  /* Enable TIM1 Preload register on ARR */
27
  TIM_ARRPreloadConfig(TIM1, ENABLE);
28
29
  /* PWM1 Mode configuration: Channel1 */
30
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
31
  TIM_OCInitStructure.TIM_Pulse = 100;
32
33
  TIM_OC1Init(TIM1, &TIM_OCInitStructure);
34
  TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
35
36
  /* PWM2 Mode configuration: Channel3 */
37
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
38
  TIM_OCInitStructure.TIM_Pulse = 100;
39
40
  TIM_OC3Init(TIM1, &TIM_OCInitStructure);
41
  TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
42
43
  /* One Pulse Mode selection */
44
  TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Repetitive);
45
  // TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single);
46
  /* Generate an update event to reload the Prescaler and the repetition counter value immediately */
47
  TIM1->EGR = TIM_EGR_UG;
48
  TIM1->RCR = 16 - 1;  //Set the Repetition counter value
49
50
  TIM_DMAConfig(TIM1, TIM_DMABase_CCR1, TIM_DMABurstLength_16Transfers);
51
  TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);
52
  /* TIM1 enable counter */
53
  TIM_Cmd(TIM1, ENABLE);
54
55
  /* Main Output Enable */
56
  TIM_CtrlPWMOutputs(TIM1, ENABLE);
57
58
}
59
60
uint32_t TIM1_PWM_Values[16] = {140, 130, 130, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10};
61
/* DMA2 Channel 0 Stream 6 */
62
void DMA_Config(void) {
63
  DMA_InitTypeDef DMA_InitStructure;
64
65
  /* DMA2 Stream6 channel0 configuration */
66
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
67
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM1->CCR1;
68
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&TIM1_PWM_Values;
69
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
70
  DMA_InitStructure.DMA_BufferSize = 16;
71
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
72
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
73
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
74
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
75
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
76
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
77
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
78
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
79
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
80
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
81
  DMA_Init(DMA2_Stream6, &DMA_InitStructure);
82
83
  /* DMA2_Stream6 enable */
84
  DMA_Cmd(DMA2_Stream6, ENABLE);
85
}

von Stm M. (stmfresser)


Lesenswert?

vielleicht den MA_PeripheralDataSize auf Byte setzen?

von Stm M. (stmfresser)


Lesenswert?

wie Ingo L. vorgeschlagen, TIM_OPMode_Single Mode nehmen.

Bei Init TIM_TimeBaseStructure.TIM_RepetitionCounter = 15 setzen. Gilt 
nur für Timer 1 und 8, Nicht am Ende der Init an den Registern fummeln

von Väinö (Gast)


Lesenswert?

Vielen Dank für den Hinweis. Ich kann jetzt gewünschte 16 
Rechtecksignale generieren. Allerdings klappt es mir nicht die einzelnen 
PWM Duty Cycles zu setzen.
1
void RCC_Config(void) {
2
  /* DMA GPIO Takt freigeben */
3
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);
4
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
5
  /* TIM1 USART1 clock enable */
6
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
7
}
8
9
void GPIO_Config(void) {
10
  GPIO_InitTypeDef GPIO_InitStructure;
11
12
  /* PWM Pins konfigurieren PA8 PA10 */
13
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_10;
14
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
15
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
16
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
17
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
18
  GPIO_Init(GPIOA, &GPIO_InitStructure);
19
20
  /* Connect TIM1 pins to AF */
21
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
22
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_TIM1);
23
}
24
25
void Timer_Config(void) {
26
  TIM_OCInitTypeDef  TIM_OCInitStructure;
27
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
28
  /* PWM-Frq = TIM_CLK/(Period+1)/(Prescaler+1) */
29
  TIM_TimeBaseStructure.TIM_Period = 141 - 1; // 595.2 kHz Takt Dshot600
30
  TIM_TimeBaseStructure.TIM_Prescaler = 1; //84MHz
31
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
32
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
33
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 15;
34
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
35
  /* PWM Mode configuration */
36
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
37
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
38
39
  TIM_GenerateEvent(TIM1, TIM_EventSource_CC1);
40
  /* Enable TIM1 Preload register on ARR */
41
  TIM_ARRPreloadConfig(TIM1, ENABLE);
42
43
  /* PWM1 Mode configuration: Channel1 */
44
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
45
  TIM_OCInitStructure.TIM_Pulse = 10;
46
47
  TIM_OC1Init(TIM1, &TIM_OCInitStructure);
48
  TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
49
50
  /* PWM2 Mode configuration: Channel3 */
51
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
52
  TIM_OCInitStructure.TIM_Pulse = 10;
53
54
  TIM_OC3Init(TIM1, &TIM_OCInitStructure);
55
  TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
56
57
  /* One Pulse Mode selection */
58
  TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single);
59
60
  TIM_DMAConfig(TIM1, TIM_DMABase_CCR1, TIM_DMABurstLength_1Transfer);
61
  TIM_DMACmd(TIM1, TIM_DMA_CC1, ENABLE);
62
  /* TIM1 enable counter */
63
  TIM_Cmd(TIM1, ENABLE);
64
65
  /* Main Output Enable */
66
  TIM_CtrlPWMOutputs(TIM1, ENABLE);
67
68
}
69
70
uint32_t TIM1_PWM_Values[16] = {120, 120, 120, 120, 120, 120, 120, 50, 120, 120, 120, 120, 120, 120, 120, 120};
71
/* DMA2 Channel 0 Stream 6 */
72
void DMA_Config(void) {
73
  DMA_InitTypeDef DMA_InitStructure;
74
75
  /* DMA2 Stream6 channel0 configuration */
76
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
77
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM1->CCR1;
78
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TIM1_PWM_Values;
79
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
80
  DMA_InitStructure.DMA_BufferSize = 1;
81
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
82
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
83
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
84
  DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
85
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
86
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
87
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
88
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
89
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
90
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
91
  DMA_Init(DMA2_Stream6, &DMA_InitStructure);
92
93
  /* DMA2_Stream6 enable */
94
  DMA_Cmd(DMA2_Stream6, ENABLE);
95
}

von Frank B. (f-baer)


Lesenswert?

Der Datentyp deines Arrays und der Datentyp in MemoryDataSize passen 
schonmal nicht zusammen.

von Väinö (Gast)


Lesenswert?

Frank B. schrieb:
> Der Datentyp deines Arrays und der Datentyp in MemoryDataSize
> passen
> schonmal nicht zusammen.

Vielen Dank für den Hinweis. Ich hab es geändert, das bringt leider 
keine Verbesserung. :(
1
void DMA_Config(void) {
2
  DMA_InitTypeDef DMA_InitStructure;
3
4
  /* DMA2 Stream6 channel0 configuration */
5
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
6
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM1->CCR1;
7
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TIM1_PWM_Values;
8
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
9
  DMA_InitStructure.DMA_BufferSize = 1;
10
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
11
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
12
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
13
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
14
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
15
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
16
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
17
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
18
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
19
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
20
  DMA_Init(DMA2_Stream6, &DMA_InitStructure);
21
22
  /* DMA2_Stream6 enable */
23
  DMA_Cmd(DMA2_Stream6, ENABLE);
24
}

von Darth Moan (Gast)


Lesenswert?

Moin,

TIM1->CCR1 ist 16bit gross, weil TIM 1&8 16bit Timer sind.
Dein Array ist 32bittig.

Aus dem RM:
17.2 TIM1&TIM8 main features
TIM1&TIM8 timer features include:
• 16-bit up, down, up/down auto-reload counter.
• 16-bit programmable prescaler allowing dividing (also “on the fly”) 
the counter clock
frequency either by any factor between 1 and 65536.
• Up to 4 independent channels for:
– Input Capture
– Output Compare
– PWM generation (Edge and Center-aligned Mode)
– One-pulse mode output

Läuft denn der DMA durch?

Den HAL benutze ich nicht. Wo stellt man denn hier die Anzahl an
DMA Transfers ein?

von Andreas S. (igel1)


Lesenswert?

Darth Moan schrieb:
> Den HAL benutze ich nicht. Wo stellt man denn hier die Anzahl an
> DMA Transfers ein?

... er benutzt den HAL ebenfalls nicht :-)
Das sind SPL-Aufrufe ...  (SPL = standard peripheral library)

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

Zusätzlich zu dem, was Darth Moan geschrieben hat, müßte dann auch 
folgende Zeile angepaßt werden:
1
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
2
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;

ändern in:
1
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
2
  DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;

Außerdem würde ich den FIFO-Mode deaktivieren:
1
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;

Und wenn Du ganz genau wissen willst, wie's funktioniert, so lies Dir 
das Codebeispiel aus der Peripheral Library durch.

Es macht exakt das, was Du umsetzen möchtest: variable Duty-Zyklen, 
realisiert via DMA-Schreibvorgängen in das CCR-Register.

Das Beispiel findet sich gemeinerweise nicht im DMA-Beispiel-Ordner, 
sondern im TIM-Beispielordner:
STM32F4xx_DSP_StdPeriph_Lib_V1.8.0\Project\STM32F4xx_StdPeriph_Examples\ 
TIM\TIM_DMA

Viele Grüße

Igel1

von Väinö (Gast)


Lesenswert?

Andreas S. schrieb:

> Und wenn Du ganz genau wissen willst, wie's funktioniert, so lies Dir
> das Codebeispiel aus der Peripheral Library durch.
>
> Es macht exakt das, was Du umsetzen möchtest: variable Duty-Zyklen,
> realisiert via DMA-Schreibvorgängen in das CCR-Register.

Hallo Andreas,

Vielen Dank!!!. Die Beispiele der Standard Peripherie Lib sind bei mir 
aus dem Jahr 2011 :D. Da sind keine solche Beispiele vorhanden.

Gruss,
Väinö

von Christopher J. (christopher_j23)


Lesenswert?

Dann lade dir halt mal die aktuelle Version runter: 
http://www.st.com/resource/en/firmware/stm32f4_dsp_stdperiph_lib.zip

Da sind zwei Beispiele drin:
TIM_DMA verändert nur den Dutycycle per DMA und die Periodendauer bleibt 
konstant
TIM_DMABurst verändert sowohl Dutycycle, als auch Periodendauer

von Andreas S. (igel1)


Lesenswert?

Uppps - kleine Korrektur:

Andreas S. schrieb:
> Zusätzlich zu dem, was Darth Moan geschrieben hat, müßte dann auch
> folgende Zeile angepaßt werden:
>
>
1
>   DMA_InitStructure.DMA_PeripheralDataSize = 
2
> DMA_PeripheralDataSize_HalfWord;
3
>   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
4
>
>
> ändern in:
>
1
>   DMA_InitStructure.DMA_PeripheralDataSize = 
2
> DMA_PeripheralDataSize_HalfWord;
3
>   DMA_InitStructure.DMA_MemoryDataSize = 
4
> DMA_PeripheralDataSize_HalfWord;
5
>
>

Das war knapp daneben.
Richtig wäre:
1
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
2
DMA_InitStructure.DMA_MemoryDataSize = DMA_Memory_DataSize_HalfWord;


Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

Ach ja, und noch etwas:

Du hast diese Zeile in Deinem Code:
1
  TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

Damit wird Dein CCR1 immer erst nach einem Update Event [UEV] neu mit 
dem Preload-Wert befüllt. Das ist im Normalfall auch völlig korrekt, 
denn Du willst CCR1 ja immer erst neu setzen, wenn der Counter CNT 
einmal bis zum ARR-Wert durchgelaufen ist (sprich: das neue CCR1 soll 
erst im nächsten Zyklus gültig werden).

Das Problem: bei gesetztem Repetition-Register (was bei Dir 15 statt 1 
lauten müßte (siehe "Buffer"), damit die DMA 16 Werte übertragen werden) 
passiert so ein Update Event erst nachdem das TIMx_RCR-Register auf 0 
geht.

Will sagen: auch wenn Dein DMA schön in das CCR1-Register schreibt - es 
landet alles nur im Preload-CCR1-Register und wird erst am Ende der 
OnePulse-Sequenz in das echte CCR1-Register geschrieben.

Also Preload abschalten via:
1
  TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Disable);

Dann funktioniert's zwar, aber Du hast nun ein anderes Problem:
Wenn Du z.B. aufsteigende Werte per DMA in CCR1 schreibst (z.B. 50, 
120), so wird Dein DMA 2x in einem Timerlauf getriggert, bevor der 
Timer-Counter den Auto Reload Register-Wert erreichen kann: nämlich bei 
50 und kurz darauf bei 120). Du kannst also nur streng monoton fallende 
Duty-Cyclen realisieren (im Falle von PWM2-Mode) oder streng monoton 
steigende Duty-Cyclen im Falle von PWM1-Mode.

Will sagen:  der verfolgte Ansatz mit dem OnePulse-Mode und dem 
Repetition-Register ist vermutlich ungeeignet für Dein Ziel.

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

Hier ein Code, der funktioniert:
1
/* Includes ------------------------------------------------------------------*/
2
#include "stm32f4xx.h"
3
4
5
TIM_OCInitTypeDef  TIM_OCInitStructure;
6
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
7
8
uint32_t ndtrnew;
9
uint32_t ndtrold;
10
uint32_t counter;
11
uint32_t checkpar;
12
uint32_t checkm0ar;
13
uint32_t checkccr1;
14
15
16
void RCC_Config(void);
17
void GPIO_Config(void);
18
void Timer_Config(void);
19
void DMA_Config(void);
20
21
/*********************************************************************
22
 *
23
 *  main()
24
 *********************************************************************/
25
void main()
26
{
27
    RCC_Config();
28
    GPIO_Config();
29
    DMA_Config();
30
31
    ndtrold = DMA2_Stream6->NDTR;           // for debugging purposes
32
    checkpar = DMA2_Stream6->PAR;           // for debugging purposes
33
    checkm0ar = DMA2_Stream6->M0AR;         // for debugging purposes
34
                                            // DMA2_Stream6->M0AR value will not change - it's the base address of memory location
35
    Timer_Config();
36
37
    ndtrold = DMA2_Stream6->NDTR;           // for debugging purposes
38
    checkpar = DMA2_Stream6->PAR;           // for debugging purposes
39
40
    while(1){
41
        ndtrnew = DMA2_Stream6->NDTR;       // for debugging purposes
42
        checkccr1 = TIM1->CCR1;             // for debugging purposes
43
        if(ndtrnew != ndtrold){             // for debugging purposes
44
            ndtrold = ndtrnew;              // set breakpoint here to monitor when DMA counter NDTR is decremented
45
            counter++;                      // for debugging purposes
46
        }
47
    }
48
}
49
50
51
void RCC_Config(void) {
52
  /* DMA GPIO Takt freigeben */
53
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);
54
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
55
  /* TIM1 USART1 clock enable */
56
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
57
}
58
59
60
void GPIO_Config(void) {
61
  GPIO_InitTypeDef GPIO_InitStructure;
62
63
  /* PWM Pins konfigurieren PA8 PA10 */
64
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_10;
65
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
66
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
67
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
68
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
69
  GPIO_Init(GPIOA, &GPIO_InitStructure);
70
71
  /* Connect TIM1 pins to AF */
72
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);       // PA8  = TIM1_CH1    (connect PA8 -> PD12 via wire)
73
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_TIM1);      // PA10 = TIM1_CH3
74
}
75
76
77
void Timer_Config(void) {
78
79
  /* PWM-Frq = TIM_CLK/(Period+1)/(Prescaler+1) */
80
  TIM_TimeBaseStructure.TIM_Prescaler = 16800 - 1;              // 10 kHz
81
  TIM_TimeBaseStructure.TIM_Period = 10000 - 1;                 // 1 Hz
82
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
83
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
84
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 16 - 1;
85
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
86
87
88
  /* PWM Mode configuration */
89
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
90
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
91
  //TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;    // igel1
92
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
93
  TIM_OCInitStructure.TIM_Pulse = 9100;
94
95
  TIM_OC1Init(TIM1, &TIM_OCInitStructure);
96
  TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Disable);               // igel1 - it's important to switch off preload register CCR1
97
98
  /*
99
  17.3.3 Repetition counter
100
    Section 17.3.1: Time-base unit describes how the update event (UEV) is generated with
101
    respect to the counter overflows/underflows. It is actually generated only when the repetition
102
    counter has reached zero. This can be useful when generating PWM signals.
103
    This means that data are transferred from the preload registers to the shadow registers
104
    (TIMx_ARR auto-reload register, TIMx_PSC prescaler register, but also TIMx_CCRx
105
    capture/compare registers in compare mode) every N+1 counter overflows or underflows,
106
    where N is the value in the TIMx_RCR repetition counter register.
107
  */
108
109
  TIM_GenerateEvent(TIM1, TIM_EventSource_CC1);         //igel1
110
111
  /* Enable TIM1 Preload register on ARR */
112
  TIM_ARRPreloadConfig(TIM1, ENABLE);
113
114
  /* TIM1 enable counter */
115
  TIM_Cmd(TIM1, ENABLE);
116
117
  DMA_Cmd(DMA2_Stream6, ENABLE);
118
119
  TIM_DMACmd(TIM1, TIM_DMA_CC1, ENABLE);
120
121
  /* Main Output Enable */
122
  TIM_CtrlPWMOutputs(TIM1, ENABLE);
123
124
125
  /* PWM2 Mode configuration: Channel3 */
126
  //TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
127
  //TIM_OCInitStructure.TIM_Pulse = 1000;
128
129
  //TIM_OC3Init(TIM1, &TIM_OCInitStructure);
130
  //TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
131
132
  /* One Pulse Mode selection */
133
  TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single);
134
135
  //TIM_DMAConfig(TIM1, TIM_DMABase_CCR1, TIM_DMABurstLength_1Transfer);
136
137
138
139
140
}
141
142
// Warning: only decreasing series will work with this design
143
uint16_t TIM1_PWM_Values[16] = {9000, 8001, 7002, 6003, 5004, 4005, 3006, 2007, 1008, 909, 810, 711, 612, 513, 414, 315};
144
//uint16_t TIM1_PWM_Values[16] = {90, 101, 202, 303, 404, 505, 606, 707, 808, 909, 1010, 1111, 1212, 1313, 1414, 1515};
145
146
uint32_t ValAddr = (uint32_t)TIM1_PWM_Values;           // for debugging purposes
147
148
149
/* DMA2 Channel 0 Stream 6 */
150
void DMA_Config(void) {
151
  DMA_InitTypeDef DMA_InitStructure;
152
153
  uint32_t testvar = (uint32_t)&TIM1->CCR1;
154
155
  DMA_DeInit(DMA2_Stream6);
156
  /* DMA2 Stream6 channel0 configuration */
157
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
158
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM1->CCR1;
159
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TIM1_PWM_Values;
160
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
161
  DMA_InitStructure.DMA_BufferSize = 15;
162
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
163
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
164
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
165
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
166
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
167
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
168
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
169
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
170
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
171
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
172
173
  DMA_Init(DMA2_Stream6, &DMA_InitStructure);
174
175
  /* DMA2_Stream6 enable */
176
}

Das Timer-Signal liegt am Ausgang PA8 an.
Auf dem STM32F4 Board einfach PA8 mit PD12 verbinden - dann zeigt die 
grüne LED die Pulse an.

Periode der Pulse ist 1s.
Der Duty-Cycle wird über das Array "TIM1_PWM_Values[16]" bestimmt.

Entgegen meiner Voraussage aus dem vorigen Post, funktioniert das 
Programm auch mit aufsteigenden oder gar springenden Array-Werten 
(jedenfalls auf den ersten Blick). Einen Reim kann ich mir gerade nicht 
darauf machen. Müßte ich mir dem dem Oszi nochmals näher angucken - 
allein mir fehlt gerade der "Schnuff".

Viele Grüße

Igel1

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.