STM32 Sin-Wave generator

OP #6069001
Lesenswert?

Moin Leute,
ich habe mir einen Sinus Generator zur Aufgabe gemacht. Umgesetzt wird 
das auf einem STM32 NUCLEO Board L053R8. Meine Werte für den Sinus 
speichere ich in einem Array und gebe die Werte per DMA auf den DAC. Der 
Sinus hat eine Frequenz von 1.4kHz und Sp-Sp 3.3V. Soweit so gut, nun 
möchte ich aber gerne die Frequenz erhöhen, 40kHz wäre das Ziel. Als 
Trigger Source benutzt der DAC den DAC_TRIGGER_T6_TRGO vom Timer6. 
Dennoch bekomme ich durch Änderung des prescalers und der Periode keine 
Frequenz höher als 1.4kHz generiert. Habe ich die SysClock richtig 
eingestellt? Vielleicht hat ja jemand ein Tip?
1
/* Includes ------------------------------------------------------------------*/
2
#include "main.h"
3
#include "math.h"
4

5
/* Private variables ---------------------------------------------------------*/
6
DAC_HandleTypeDef hdac;
7
DMA_HandleTypeDef hdma_dac_ch1;
8

9
TIM_HandleTypeDef htim6;
10

11
/* USER CODE BEGIN PV */
12
float  pi = 3.14159265359;
13
uint16_t dacVal [1024];
14
int i;
15

16
/* USER CODE END PV */
17

18
/* Private function prototypes -----------------------------------------------*/
19
void SystemClock_Config(void);
20
static void MX_GPIO_Init(void);
21
static void MX_DMA_Init(void);
22
static void MX_DAC_Init(void);
23
static void MX_TIM6_Init(void);
24

25
int main(void)
26
{
27
  for(i=0; i<= 1023; i++)
28
   {
29
     dacVal[i]= 2048*(sin(i*2*pi/1023)+1);
30
   }     
31

32
  HAL_Init();
33

34
  /* Configure the system clock */
35
  SystemClock_Config();
36

37
  /* Initialize all configured peripherals */
38
  MX_GPIO_Init();
39
  MX_DMA_Init();
40
  MX_DAC_Init();
41
  MX_TIM6_Init();
42

43
  /* USER CODE BEGIN 2 */
44
  HAL_TIM_Base_Start(&htim6);
45
  HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
46
  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)dacVal, 1024, DAC_ALIGN_12B_R);
47
  /* USER CODE END 2 */
48

49
  while (1)
50
  {
51

52
  }
53
}
54

55
void SystemClock_Config(void)
56
{
57
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
58
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
59

60
  /**Configure the main internal regulator output voltage 
61
  */
62
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
63
  /**Initializes the CPU, AHB and APB busses clocks 
64
  */
65
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
66
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
67
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
68
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
69
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
70
  {
71
    Error_Handler();
72
  }
73
  /**Initializes the CPU, AHB and APB busses clocks 
74
  */
75
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
76
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
77
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
78
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
79
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
80
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
81

82
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
83
  {
84
    Error_Handler();
85
  }
86
}
87

88
static void MX_DAC_Init(void)
89
{
90

91
  DAC_ChannelConfTypeDef sConfig = {0};
92

93
  hdac.Instance = DAC;
94
  if (HAL_DAC_Init(&hdac) != HAL_OK)
95
  {
96
    Error_Handler();
97
  }
98

99
  /**DAC channel OUT1 config*/
100
  sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
101
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
102
  if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK)
103
  {
104
    Error_Handler();
105
  }
106
}
107

108
static void MX_TIM6_Init(void)
109
{
110
  TIM_MasterConfigTypeDef sMasterConfig = {0};
111

112
  /* USER CODE END TIM6_Init 1 */
113
  htim6.Instance = TIM6;
114
  htim6.Init.Prescaler = 0;
115
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
116
  htim6.Init.Period = 10;
117
  htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
118
  if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
119
  {
120
    Error_Handler();
121
  }
122
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
123
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
124
  if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
125
  {
126
    Error_Handler();
127
  }
128
}
129

130
static void MX_DMA_Init(void) 
131
{
132
  /* DMA controller clock enable */
133
  __HAL_RCC_DMA1_CLK_ENABLE();
134

135
  /* DMA interrupt init */
136
  /* DMA1_Channel2_3_IRQn interrupt configuration */
137
  HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0, 0);
138
  HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
139

140
}
141

142
static void MX_GPIO_Init(void)
143
{
144
  /* GPIO Ports Clock Enable */
145
  __HAL_RCC_GPIOA_CLK_ENABLE();
146

147
}
148

149
void Error_Handler(void)
150
{
151
  /* USER CODE BEGIN Error_Handler_Debug */
152
  /* User can add his own implementation to report the HAL error return state */
153

154
  /* USER CODE END Error_Handler_Debug */
155
}
Gast #6069240
Lesenswert?

Muss das unbedingt ein Controller sein? Für eine Sinusausgabe braucht 
man einen Taktgenerator, einen Adresszähler, einen Speicher für die 
Amplitudenwerte und einen DAC, das kann man für fast beliebige 
Frequenzen aus wenigen Standard-ICs konstruieren. Nur der Lerneffekt ist 
dann natürlich ein anderer.

Georg
Persönliche Seite #6157038
Lesenswert?

Für 1024 Stufen und 1.4kHz müsste dein Timer auf knapp 700ns Interrupts 
generieren :-)
1.4k * 1024 = ~1.5MHz -> 698ns

Das wird eng, daher: Stufen runter.
Du könntest das ganze dynamisch gestalten, alle x kHz nimmst du nur noch 
die Hälfte der Werte des arrays, also counter +1, +2, +4, +8, ...
Ggf. Mittelwert aus den übersprungenen Werten bilden (decimation).
OP #6157114
Lesenswert?

Ich komme meinem Ziel immer näher. Ich habe die erforderliche Schwingung 
in der richtigen Frequenz. Meinen DAC starte ich im DMA modus.
1
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)dacVal, 25, DAC_ALIGN_12B_R);
Die 25 Werte entsprechen der positiven Halbwelle meines Sinus Signal. 
Die Frequenz ist bei 40 kHz, also genau so wie ich sie benötige. Der 
oben genannte befehl übergibt einmal die 25 Werte an meinen DAC, wenn 
ich das richtig verstanden habe. Nun möchte ich aber bestimmen wie oft 
ich diese positive Halbwelle ausgebe. Funktioniert das im DMA modus?
1
      HAL_Delay(20);
2
      HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)dacCosVal, 25, DAC_ALIGN_12B_R);
3
      HAL_DAC_Stop_DMA(&hdac, DAC1_CHANNEL_1);
4
      HAL_Delay(20);
Das hier endet in einem undefinierbaren chaos :/
1
    for(i=0;i<=25;i++)
2
    {
3
      HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dacVal[i]);
4
  
5
    }
Und diese Version dauert 4 mal länger-
#6157130
Lesenswert?

Guest schrieb:
> Also der STM schafft ja wohl locker mehr als 1.4kHz .....

Aber nicht mit so hoher zeitlicher Auflösung.

Walt N. schrieb:
> Wenn ich die anzahl der Werte nicht verringern möchte, ist der
> andere weg doch die Systemclock zu erhöhen, oder?

Erstmal müsstest du nur den Takt für den DAC erhöhen. Viel mehr als 1MHz 
schafft der Analogteil aber nicht, siehe Bild. Also reichen die 16MHz 
aus dem HSI locker.

> Kann ich das durch hinzufügen eines externen oscillators erreichen?

Das lohnt sich nicht, selbst wenn der DAC mehr könnte. Es ist egal, ob 
der Takt extern oder intern per PLL und HSI erzeugt wird, Systemtakt 
bleibt Systemtakt. Einen externen Takt brauchst du (nur), wenn der HSI 
nicht stabil genug ist, z.B. für CAN oder weil es bei -40°C 
funktionieren muss. Oder wenn du unbedingt eine krumme Frequenz 
brauchst.
Angehängte Dateien:
(Firma: www.harerod.de) Persönliche Seite #6157242
Lesenswert?

Bauform B. schrieb:
...
> Erstmal müsstest du nur den Takt für den DAC erhöhen. Viel mehr als 1MHz
> schafft der Analogteil aber nicht, siehe Bild. Also reichen die 16MHz
> aus dem HSI locker.
Der verlinkte Ausschnitt aus dem Datenblatt zeigt die Crux des 
STM32-DAC:
- full-scale Sprungantworten brauchen bis zu 12µs für Einschwingen auf 1 
LSB
- eine Änderung um 1 LSB geht in 1µs

Das sind einige der Limitierungen dieser eingebauten Peripherie (für 
weitere Überraschungen das komplette Kapitel genau lesen, siehe auch 
min/max-Werte).

Schon bei moderaten Anforderungen ist mit einem externen Wandler 
deutlich besser bedient.
OP #6166174
Lesenswert?

Marcus H. schrieb:
> Schon bei moderaten Anforderungen ist mit einem externen Wandler
> deutlich besser bedient.

Ich habe nun einen externen Wandler angeschlossen. Per SPI sende ich 
meine Daten an den mcp4922. Funktioniert einwandfrei! Meine SPI 
Schnittstelle habe ich wie folgt initialisiert:
1
static void MX_SPI1_Init(void)
2
{
3
  hspi1.Instance = SPI1;
4
  hspi1.Init.Mode = SPI_MODE_MASTER;
5
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
6
  hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
7
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
8
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
9
  hspi1.Init.NSS = SPI_NSS_SOFT;
10
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
11
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
12
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
13
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
14
  hspi1.Init.CRCPolynomial = 10;
15
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
16
  {
17
    Error_Handler();
18
  }
19
}

Die Wertetabelle für den Sinus habe ich so aufgestellt:
1
uint16_t sine_wave[] =
2
{
3
0x800,0xcb3,0xf9b,0xf9b,0xcb3,0x800,0x34c,0x64,
4
0x64,0x34c,0x800,
5
};

Und die Funktion für die Übertragung an den mcp4922 ist diese hier:
1
void MCP4922_SendByte(uint16_t dac_data)
2
{
3
  data[1] = ((dac_data >> 8) & 0x0F) | 0x30;
4

5
  data[0] = dac_data;    
6
  
7
  HAL_GPIO_WritePin(GPIOA, SPI1_CS_Pin, 0);
8
  
9
  HAL_SPI_Transmit(&hspi1,data, 1, HAL_MAX_DELAY);
10
  
11
  HAL_GPIO_WritePin(GPIOA, SPI1_CS_Pin, 1);
12
}
Das Problem besteht immer noch darin dass ich den Sinus mit 40KHz 
generieren möchte. Mit dieser kleinen Wertetabelle komme ich auf einen 
recht kantigen Sinus von 4,2KHz. Bekomme ich die SPI übertragung noch 
schneller hin? Kann ich vielleicht dieses
1
HAL_MAX_DELAY
 noch ändern um etwas an geschwindigkeit zu gewinnen? Bin über jeden 
Tipp und Hinweis dankbar!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren