DDS mit dem NUCLEO-H743ZI (Teil 1)

Das NUCLEO-Board mit dem STM32H743 gibt es in 2 Versionen:

  1. alte Version NUCLEO-H743ZI mit dem STM32H743 in der Version Y
  2. neue Version NUCLEO-H743ZI2 mit dem STM32H743 in der Version V

Die Unterschiede zwischen den beiden Version Y und V sind in der Application Note AN 5312 bei ST Microelectronics dokumentiert.

Der vielleicht auffälligste Unterschied ist, dass Version V mit einer Core-Clock von bis zu 480 MHz läuft, während die ältere Version Y nur bis 400 MHz spezifiziert ist.

Im Folgenden benutze ich jedenfalls die ältere Version mit 400 MHz, aber eigentlich sollten die Programmbeispiele genauso auf der neueren Version laufen.

In diesem Blog werden simple DDS-Programme zum Erzeugen von Sinusspannungen an den beiden DAC-Ausgängen, die der H7 zur Verfügung stellt, beschrieben.

Im Teil 1 dieses Blogs geht es um ein sehr simples Verfahren: Ein Timer (TIM6) erzeugt das Taktsignal für die beiden DAC-Kanäle.

Bei Ablauf des Timers werden an jedem DAC Ausgang neue Spannungswerte („samples“) zur Verfügung gestellt. Außerdem wird dadurch jedes Mal ein Interrupt ausgelöst.

In der zugehörigen Interrupt-Service-Routine (ISR) werden dann die beiden neuen Samples berechnet, die beim nächsten Ablauf des Timers vom DAC ausgegeben werden.

Wenn eine große Anzahl von Samples pro Zeiteinheit – genannt Sample-Rate – eingestellt wird, dann bekommt der H7 ordentlich zu tun. Leider muss sich der H7 in diesem Fall (Abarbeitung einer ISR für jedes Sample) aber mit ziemlich viel Programm-Overhead beschäftigen.

Mit Hilfe von DMA und Ringpuffern kann man den H7 deutlich entlasten, aber das ist das Thema von Teil 2 dieses Blogs

In diesem Repository befinden sich die verschiedenen Versionen https://github.com/papamidas/H7_DDSVariants

Version 1: https://github.com/papamidas/H7_DDSVariants/tree/master/Nucleo_H7_Simple_DDS_FPU

Weil der H7 eine extrem flotte Floating-Point-Unit besitzt, habe ich zuerst versucht, diese auch für den Phase-to-Amplitude-Converter zu benutzen (siehe https://dm1cr.de/dds-und-ncos-direct-digital-synthesis-and-numerically-controlled-oscillators).

In der ersten Version verwende ich in der ISR die Standard-Sinus-Version der C-Bibliothek:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
 	if(htim == &htim6){
      HAL_GPIO_WritePin(PROF0_GPIO_Port, PROF0_Pin, GPIO_PIN_SET);
	  uint32_t dhr12ld = (uint32_t)&hdac1.Instance->DHR12LD;
	  dds[0].phase += dds[0].phaseInc;
	  dds[1].phase += dds[1].phaseInc;
	  /*
	   *  write to right aligned dual channel DAC register:
	   */
	  double phase0 = dds[0].phase / pow(2,32) * 2.0 * M_PI;
	  double phase1 = dds[1].phase / pow(2,32) * 2.0 * M_PI;

	  uint16_t dacval1 = ((double)dds[0].amplitude * sin(phase0)) + (double)dds[0].offset;
	  uint16_t dacval2 = ((double)dds[1].amplitude * sin(phase1)) + (double)dds[1].offset;

	  *(__IO uint32_t *)dhr12ld = (uint32_t) dacval1 + ((uint32_t)dacval2 << 16);

	  HAL_GPIO_WritePin(PROF0_GPIO_Port, PROF0_Pin, GPIO_PIN_RESET);

	  return;
  }
  /* Prevent unused argument(s) compilation warning */
  UNUSED(htim);
}

Details zur Bedienung findet man in der zugehörigen README.md-Datei im Repository.

Version 1: Profiling-Pins PROF0 und PROF1

Unmittelbar am Anfang des TIM6_DAC_IRQHandler() wird vom Programm der Pin PROF0 gesetzt (obere Kurve) und kurz vor Verlassen der Funktion wieder zurückgesetzt.

Die gesamte ISR dauert also etwa 7 µs.

In der ISR wird geprüft, was der eigentliche Auslöser des Interrupts war und dann wird erst die Funktion HAL_TIM_PeriodElapsedCallback() aufgerufen, die im Code-Listing oben zu sehen ist.

Wie man an der Zeitdauer des „1“-Zustands von PROF1 sehen kann, dauert die Berechnung der beiden neuen Phasenwerte und die Umwandlung in Sinuswerte etwa 4.8 µs, wenn man dafür die sin()-Funktion der Standard-Bibliothek verwendet und keine weiteren Optimierungen einstellt.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.