Using ZYNQ's Analog to Digital Converter


The ZYNQ chip includes two 1MSPS, 12-bit analog-to-digital differential converters. These converters can read voltages from any one of several sources: the analog voltage present across two dedicated analog input pins (these two pins are called VP and VN); the voltage present across any one of 16 pairs of “accessory analog input” pins (an user-programmable analog multiplexor selects which pair of pins are used); the voltage produced by an on-chip temperature sensor; or the voltage present on any one of several power supply rails.

ADC circuits and sensors are contained in a special XADC circuit block in the FPGA. Since this IP block is in the FPGA, and not the ARM processing system, information about the block is in an FPGA-related user guide (the “XADC User Guide” UG480), and not in the ZYNQ TRM.

Figure 1. XADC Block

The XADC IP block is accessed through the AXI bus like any other peripheral. Because this block is not a permanent part of the ARM system (like the UART or SPI controller are), it’s register addresses are assigned by the user. This can be done “manually” by instantiating the XADC block in your Verilog source file and assigning the addresses within that block. To simplify things, Xilinx also provides an XADC configuration Wizard that connects all needed signals and assigns addresses to all XADC registers. The wizard was used in the creation of the Blackboard configuration file, and base address 0x43C50000 was defined.

(Note: A dedicated interface between the processing system and the XADC block also exists, and it can be used without configuring the FPGA. All XADC functions can be accessed through this interface, and it also offers read/data and write/command FIFOs. This interface is perhaps a bit more involved, but it may offer better performance in certain situations. You are free to use this interface if you wish – it is described in UG 480 and in the ZYNQ TRM.)

The XADC users guide UG 480 describes the IP block, and the LogiCORE IP Product Guide PG 091 describes the register interface.

A table on pages 16 through 23 of IP Product Guide (PG 091) describes all 83 registers that are available in the XADC block. Registers include reset functions, channel selection, various setup and control functions, and several data registers for the ADC conversion results and sensor outputs. (Note: channel selection refers to the 16-channel analog mux on the ADC input, which is included so that up to 16 signals can be digitized without the need for an external mux.) Only a few of the data registers are of interest here, and these are shown in the much smaller table below. The offset values shown in the table must be added to the assigned XADC base address of 0x43C50000.

Figure 2. Some XADC Registers

All data registers are 16 bits, but only the most significant 12 of the 16 bits are used. Since the ARM uses 32 bit address, these 12 unused bits are in the upper bits of the least-significant two bytes.

TEMP (0x0200): Die temperature

Data: Die temperature data: T (centigrade) = ((data x 503.975) / 4096) – 273.15

VCCINT (0x0204): FPGA VCCINT core voltage

Data: FPGA VCCINT voltage data: V = data/4096 x 3V

VP (0x020C): Bipolar voltage between VP and VN or unipolar voltage between VP and GND (set in CR0)

Data: VP voltage: V = data/4096

VCCINTPS (0x0234): ARM VCCINT core voltage

Data: ARM VCCINT voltage data: voltage = data/4096 x 3V

CR0 (0x0300): XADC Control Register 0

ADC input channel: 00000 for temp sensor; 00001 for VCCINT; 00010 for VCCAUX; 00011 for VP; etc. (see table 3-7 in UG 480)
AQ: ADC settling time (1 to increase by six ADCCLKs, 0 for no increase)
EC: Event-driven sampling (1 for event-driven, 0 for continuous)
BU: Bipolar input select (1 for bipolar inputs, 0 for unipolar)
M: External mux enable (1 to enable external mux, 0 to disable)
AVG: Samples averaged (00 for no averaging; 01 for 16 samples; 10 for 64 samples; 11 for 256 samples)
CA: Disable average for calibration (1 to disable, 0 to enable)

ADC Input Channels

The voltage on the main ADC input (the VP input) is available by reading register 0x20C. On the Blackboard, the VP input is connected to the sweeper on a 10K potentiometer (or “pot”). The pot terminals are connected between 0V and 1V, so as the pot is manually rotated, the sweeper voltage will move between 0 and 1 volt. The 12-bit ADC code represents the voltage in steps of 1/4096 volts.

The XADC supports automatic “oversampling” to create sample points that are the average of 16, 64, or 256 data points. Data samples created through automatic averaging will generally have lower frequency content, but higher accuracy. A two-bit field in control register 0 determines what (if any) averaging is used.

The XADC can sample an internal temperature sensor to measure the die temperature. The 12-bit temperature code can be read from register 0x200, and the data value must be mapped into the centigrade temperature range (-273 - +230) by multiplying the ADC code by a scalar (503 / 65536), and then subtracting 273 to shift the result to the proper range. For example, a code of 0x9770 (which is 38768 decimal) would equal 38768 * 503/65536 - 273, or about 25C.

The XADC can also sample all on-chip power supplies - see the register table in PG091 for more information.

To read data from a given ADC channel, write the channel number to CR0 and leave all other bits at 0 (for example, to read the pot voltage on the Blackboard, write a 3 to CR0). Once a channel is selectd, the corresponding register will hold the most recent sample data for that channel (for example, you can read location 0x43C5020C to read the pot voltage).

The function below shows how to configure the xadc to sample a desired input channel.

#define XADC_BASE 0x43C50000
#define XADC_CTL (*((uint32_t *)(XADC_BASE+0x300)))

#define CH_VCCINT	0
#define CH_TEMP		1
#define CH_VCCAUX	2
#define CH_VP		3

//configures the xadc to sample the given channel
void xadc_select_ch(uint8_t ch)
	uint32_t reg_temp;

	//mask to only get 5 bits

	reg_temp = XADC_CTL;

	//set [4:0] to the channel bits given
	reg_temp &= 0xFFE0;
	reg_temp |= ch;

	XADC_CTL = reg_temp;