Kampis Elektroecke

XADC

Der ZYNQ verfügt über einen leistungsstarken 12-Bit 1 MSPS Analog-/Digital-Wandler mit 17 verschiedenen internen (z. B. die Versorgungsspannung für den RAM) und externen Analogeingängen (die Kanäle AD0 bis AD15), der es einer nachgeschalteten Logik oder dem ARM Prozessor ermöglichen verschiedene analoge Spannungen zu messen und zu überwachen. In diesem Artikel möchte ich zeigen, wie der XADC des ZYNQ genutzt werden kann um unipolare und bipolare Spannungen zu messen, wobei die zu messende Spannungen über einen Spannungsteiler erzeugt werden.


Hinweis:

Das ZYBO verwendet eine 1 V Referenzspannung für den XADC, wodurch die maximal messbare Spannung ebenfalls bei 1 V liegt.


Zudem soll der Over Temperature Alarm und der User Temperature Alarm des XADC verwendet werden. Beide Alarme werden zur Visualisierung auf je eine LED gelegt. Für die beiden Alarme sollen folgende Grenzen gelten:

  Over Temperature Alarm User Temperature Alarm
Trigger 50 °C 40 °C
Reset 45 °C 35 °C
LED M14 M15

Der XADC besitzt die Möglichkeit mit Hilfe des Channel Sequencer automatisch alle aktivierten Kanäle durchzuwechseln und für den jeweiligen Kanal eine Messung anzustoßen, wodurch der Overhead für eine Messung und das Auslesen mehrerer Kanäle drastisch reduziert wird. Dazu wird der XADC folgendermaßen konfiguriert:

Option Auswahl
Basic Startup Channel Selection Channel Sequencer
ADC SetupSequencer Mode Continouous
ADC SetupChannel Averaging 16
ADC SetupADC Calibration ADC Offset and Gain Calibration
ADC SetupSupply Sensor Calibration Sensor Offset and Gain Calibration
Channel Sequencer → Channel Enable Temperature, 14, 15

Das Blockschaltbild wird jetzt synthetisiert und ein Bitstream erzeugt. Die fertige Hardware wird anschließend für die Vitis Entwicklungsumgebung exportiert.

Für die Software wird ein neues Application Projekt erstellt und der entsprechende Header für den XADC eingefügt. 

#include "xadcps.h"

Bevor der XADC verwendet werden kann, muss er erst initialisiert werden:

ConfigPtr = XAdcPs_LookupConfig(XPAR_XADCPS_0_DEVICE_ID);
if(ConfigPtr == NULL)
{
	xil_printf("[ERROR] Invalid XADC configuration!\n\r");
	return XST_FAILURE;
}

XAdcPs_CfgInitialize(&XAdc, ConfigPtr, ConfigPtr->BaseAddress);

Als erstes wird ein Zeiger auf die XADC Konfiguration mit dem Index XPAR_XADCPS_0_DEVICE_ID erstellt:

ConfigPtr = XAdcPs_LookupConfig(XPAR_XADCPS_0_DEVICE_ID);

Dieser Zeiger wird dann genutzt um den XADC zu konfigurieren und das entsprechende XAdcPs-Objekt zu initialisieren.

Status = XAdcPs_CfgInitialize(&XAdc, ConfigPtr, ConfigPtr->BaseAddress);
if(Status != XST_SUCCESS)
{
	xil_printf("[ERROR] Can not initialize XADC!\n\r");
	return XST_FAILURE;
}

Jeder weiteren Funktionsaufruf für den XADC erwartet eine Referenz auf das jeweilige XAdcPs-Objekt, wodurch, im Falle von mehreren identischen IP-Blöcken, der entsprechende Block identifiziert werden kann.


Hinweis:

Der Prozessor eines SoC kann auf über zwei verschiedene Interfaces mit dem XADC kommunizieren:

  • Über ein 32-Bit breites PS-XADC Interface (wird in diesem Beispiel genutzt)
  • Über einen PL AXI Master

Für eine schnelle Datenerfassung wird die Verwendung eines AXI Masters empfohlen, da die Daten, die über das PS-XADC Interface gesendet werden, in FIFOs gepuffert werden, wodurch die Datenübertragungsrate ausgebremst wird. Der Vorteil ist allerdings, dass das PS-XADC Interface keine FPGA Ressourcen benötigt.


Gemäß der Aufgabenstellung soll der Eingang 15 die Spannung an einem Widerstand in der Kette messen. Da es sich bei dieser Spannung um eine negative Spannung handelt, muss hier der bipolare Eingangsmodus verwendet werden. Der Eingang 14 soll eine positive Spannung messen, weshalb hier der unipolare Modus ausreicht.

XAdcPs_SetSequencerMode(&XAdc, XADCPS_SEQ_MODE_SAFE);
XAdcPs_SetSeqInputMode(&XAdc, XADCPS_SEQ_CH_AUX15);
XAdcPs_SetSequencerMode(&XAdc, XADCPS_SEQ_MODE_CONTINPASS);

Damit der Eingangsmodus umgestellt werden kann, muss der Channel Sequencer deaktiviert werden, indem die Funktion XAdcPs_SetSequencerMode() mit dem Parameter XADCPS_SEQ_MODE_SAFE aufgerufen wird. Die Funktion XAdcPs_SetSeqInputMode() erwartet eine Bitmaske, welche die ausgewählten Analogeingänge maskiert, wobei die externen Analogeingänge mit der Bitmaske 0x00010000 beginnen. Nachdem der Eingangsmodus definiert wurde, wird der Channel Sequencer wieder aktiviert.

Weiter geht es mit dem Hauptprogramm, wo die Werte der einzelnen Analogkanäle ausgelesen, konvertiert und angezeigt werden.

while(1)
{
	Temperature = XAdcPs_RawToTemperature(XAdcPs_GetAdcData(&XAdc, XADCPS_CH_TEMP));

	RawData = XAdcPs_GetAdcData(&XAdc, XADCPS_CH_AUX_MIN + 14);
	Channel14 = RawData * 1.0 / 65536.0;

	RawData = XAdcPs_GetAdcData(&XAdc, XADCPS_CH_AUX_MIN + 15);
	if(RawData & 0x8000)
	{
		RawData = (~RawData) + 0x01;
		Channel15 = RawData * -1.0 / 65536.0;
	}
	else
	{
		Channel15 = RawData * 1.0 / 65536.0;
	}

	printf("[INFO] XADC results\n\r");
	printf("	Temperature: %f Degree\r\n", Temperature);
	printf("	Channel %d: %f V\r\n", 14, Channel14);
	printf("	Channel %d: %f V\r\n", 15, Channel15);

	for(u32 i = 0x00; i < 0xFFFFFFF; i++) {};
}

Zuerst lese ich die Temperatur des On-Chip Temperatursensors aus und konvertiere den ausgelesenen Wert mit dem Makro XAdcPs_RawToTemperature in einen Temperaturwert als Gleitpunktzahl.

Anschließend werden die Werte der beiden externen Analogkanäle ausgelesen und konvertiert. Auch hierfür wird von Xilinx ein entsprechendes Makro bereitgestellt, welches aber nicht verwendet werden kann, da das Makro eine Referenzspannung von 3 V vorsieht und den bipolaren Betriebsmodus nicht berücksichtigt.

/****************************************************************************/
/**
*
* This macro converts XADC/ADC Raw Data to Voltage(volts).
*
* @param	AdcData is the XADC/ADC Raw Data.
*
* @return 	The Voltage in volts.
*
* @note		C-Style signature:
*		float XAdcPs_RawToVoltage(u32 AdcData);
*
*****************************************************************************/
#define XAdcPs_RawToVoltage(AdcData) 					\
	((((float)(AdcData))* (3.0f))/65536.0f)

Aus diesem Grund muss die Umrechnung der Messwerte für die externen Kanäle händisch erfolgen. Bei dem Kanal 15 muss zudem das letzte Bit geprüft werden, da es sich hier um den bipolaren Kanal handelt.

Die berechneten Werte werden dann am Schluss noch in der Konsole ausgegeben:

Zum Schluss können noch die beiden Alarme getestet werden, indem das FPGA vorsichtig erwärmt wird (z. B. vorsichtig mit einem Lötkolben). Sobald die Temperatur die Grenze von 50 °C überschreitet sollte die LED an M14 angehen und erlöschen, sobald die Temperaturgrenze von 45 °C unterschritten wird. Selbiges gilt für den User Temperature Alarm.


Achtung:

Damit der jeweilige Alarm funktioniert, muss der entsprechende Kanal im Channel Sequencer aktiviert sein!


Das komplette Projekt steht in meinem GitLab-Repository zum Download bereit.

Schreibe einen Kommentar

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