Table of Contents
Introduction
The ADC module is a successive approximation (SAR) style ADC with a selectable resolution of either 16 bits or 12 bits. The ADC is composed of a core and a wrapper. The core is composed of the analogue circuits which include the channel select MUX, the sample-and-hold (S/H) circuit, the successive approximation circuits, voltage reference circuits, and other analogue support circuits. The wrapper is composed of the digital circuits that configure and control the ADC. These circuits include the logic for programmable conversions, result registers, interfaces to analogue circuits, interfaces to the peripheral buses, post-processing circuits, and interfaces to other on-chip modules.
Each ADC module consists of a single sample-and-hold (s/h) circuit. The ADC module is designed to be duplicated multiple times on the same chip, allowing simultaneous sampling or independent operation of multiple ADCs. The ADC wrapper is start-of-conversion (SOC) based (see Section 11.5).
ADC Features
- Each ADC has the following features:
- Selectable resolution of 12 bits or 16 bits
- Ratiometric external reference set by VREFHI and VREFLO pins
- Differential signal conversions (16-bit mode only)
- Single-ended signal conversions (12-bit mode only)
- Input multiplexer with up to 16 channels (single-ended) or 8 channels (differential)
- 16 configurable SOCs
- 16 individually addressable result registers
- Multiple trigger sources
- – S/W – software immediate start
- – All ePWMs – ADCSOC A or B
- – GPIO XINT2
- – CPU Timers 0/1/2 (from each C28x core present)
- – ADCINT1/2
- Four flexible PIE interrupts
- Burst mode
- Four post-processing blocks, each with:
- Saturating offset calibration
- Error from setpoint calculation
- High, low, and zero-crossing compare, with interrupt and ePWM trip capability
- Trigger-to-sample delay capture
Clock Configuration
The base ADC clock is provided directly by the system clock (SYSCLK). This clock is used to generate the ADC acquisition window. The register ADCCTL2 has a PRESCALE field which determines the ADCCLK. The ADCCLK is used to clock the converter.
In 16-bit mode, the core requires approximately 29.5 ADCCLK cycles to process a voltage into a conversion result, while in 12-bit mode, this process requires approximately 10.5 ADCCLK cycles. The choice of resolution will also determine the necessary duration of the acquisition window, see Section 11.15.2.
Resolution
The resolution of the ADC determines how finely the analog range is quantized into digital values. This ADC supports a configurable resolution of 16 bits or 12 bits. The resolution should be configured by using either the AdcSetMode() or ADC_setMode() functions,
depending on the header files used, provided in C2000ware in F2837xD_Adc.c.
These functions ensure that the correct trim is loaded into the ADC trim registers and must be called at least once after a device reset. Do not configure the resolution by directly writing to the ADCCTL2 register.
The resolution can be changed at any time when the ADC is idle (no active or pending SOCs). No wait time is necessary after changing the resolution before conversions can be initiated. If SOCs are active or pending when the resolution is changed, those SOCs may produce incorrect conversion results.
Signal Mode
The ADC supports two signal modes: single-ended and differential.
In single-ended mode, the input voltage to the converter is sampled through a single pin (ADCINx), referenced to VREFLO.
In differential signalling mode, the input voltage to the converter is sampled through a pair of input pins, one of which is the positive input (ADCINxP) and the other is the negative input (ADCINxN). The actual input voltage is the difference between the two (ADCINxP – ADCINxN).
NOTES:
- In 16-bit differential signaling mode, VREFLO must be connected to VSSA.
- • In differential signal mode, the common mode voltage is VCM = (ADCINxP + ADCINxN)/2
- The datasheet for a particular device will place some requirements on how close this voltage needs to be to (VREFHI + VREFLO)/2
- Note: The above condition is not met by connecting the negative input to VSSA or VREFLO.
- Differential signaling mode is advantageous because noise encountered on both inputs will be largely cancelled. The effect can be maximized by routing the positive and negative traces for the same differential input as close together as possible and keeping them symmetrical with respect to the signal reference.
The signal mode should be configured by using either the AdcSetMode() or DC_setMode() function provided in C2000ware in F2837xD_Adc.c. These functions ensure that the correct trim is loaded into the ADC trim registers. These functions must be called at least once after a device reset. The signal mode should not be configured by writing to the ADCCTL2 register directly.
Expected Conversion Results
SOC Principle of Operation
The ADC triggering and conversion sequencing is accomplished through configurable start-of-conversions (SOCs). Each SOC is a configuration set defining the single conversion of a single channel. In that set, there are three configurations: the trigger source that starts the conversion, the channel to convert, and the acquisition (sample) window duration. Upon receiving the trigger configured for a SOC, the wrapper will ensure that the specified channel is captured using the specified acquisition window duration.
Multiple SOCs can be configured for the same trigger, channel, and/or acquisition window as desired. Configuring multiple SOCs to use the same trigger will allow the trigger to generate a sequence of conversions. Configuring multiple SOCs to use the same trigger and channel will allow for oversampling.
SOC Configuration
Each SOC has its own configuration register, ADCSOCxCTL. Within this register, SOCx can be configured for trigger source, channel to convert, and acquisition (sample) window duration.
Trigger Operation
Each SOC can be configured to start on one of many input triggers. The primary trigger selected for SOCx is in the ADCSOCxCTL.TRIGSEL register, which can select between:
- Disabled (software only)
- CPU Timers 0/1/2 (from each C28x core present)
- GPIO: Input X-Bar INPUT5
- ADCSOCA or ADCSOCB from each ePWM module
In addition, each SOC can also be triggered when the ADCINT1 flag or ADCINT2 flag is set. This is achieved by configuring the ADCINTSOCSEL1 register (for SOC0 to SOC7) or the ADCINTSOCSEL2 register (for SOC8 to SOC15). This is useful for creating continuous conversions.
ADC Acquisition (Sample and Hold) Window
External signal sources vary in their ability to drive an analog signal quickly and effectively. In order to achieve rated resolution, the signal source needs to charge the sampling capacitor in the ADC core to within 0.5LSBs of the signal voltage. The acquisition window is the amount of time the sampling capacitor is allowed to charge and is configurable for SOCx by the ADCSOCxCTL.ACQPS register.
ACQPS is a 9-bit register that can be set to a value between 0 and 511, resulting in an acquisition window duration of:
Acquisition window = (ACQPS + 1)∙(System Clock (SYSCLK) cycle time)
• The acquisition window duration is based on the System Clock (SYSCLK), not the ADC clock(ADCCLK).
• The selected acquisition window duration must be at least as long as one ADCCLK cycle.
• The datasheet will specify a minimum acquisition window duration (in nanoseconds). The user is responsible for selecting an acquisition window duration that meets this requirement.
Channel Selection
Each SOC can be configured to convert any of the ADC channels. This behaviour is selected for SOCx by the ADCSOCxCTL.CHSEL register. Depending on the signal mode, the election is different. For single-ended signal mode, the value in CHSEL selects a single pin as the input. For differential signal mode, the value in CHSEL selects an even-odd pin pair to be the positive and negative inputs. This is summarized in Table 11-6.
Multiple Conversions from CPU Timer Trigger
This example will show how to sample multiple signals with different acquisition window requirements. CPU1 Timer 2 will be used to generate the trigger. To see how to configure the CPU timer, see the System Control and Interrupts chapter.
A good first step when designing a sampling scheme with many signals is to list out the signals and their required acquisition window. From this, calculate the necessary number of SYSCLK cycles for each signal, then the ACQPS register setting. This is shown in Table 11-7, where an SYCLK of 200MHz is assumed ( 5ns cycle time).
Software Triggering of SOCs
At any point, whether or not the SOCs have been configured to accept a specific trigger, a software trigger can set the SOCs to be converted. This is accomplished by writing bits in the ADCSOCFRC1 register.
Software triggering of the previous example without waiting for the CPU1 Timer 2 to generate the trigger could be accomplished by the statement:
AdcaRegs.ADCSOCFRC1.all = 0x000F; //set SOC flags for SOC0 to SOC3
CODE
ConfigADC
void ConfigADC(uint32_t ADC_BASE)
{
EALLOW;
ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);
#if(EX_ADC_RESOLUTION == 12)
{
ADC_setMode(ADC_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
}
#elif(EX_ADC_RESOLUTION == 16)
{
ADC_setMode(ADCA_BASE, ADC_RESOLUTION_16BIT, ADC_MODE_DIFFERENTIAL);
}
#endif
ADC_setInterruptPulseMode(ADC_BASE, ADC_PULSE_END_OF_CONV);
ADC_enableConverter(ADC_BASE);
DEVICE_DELAY_US(1000);
EDIS;
}
initADC_SOC
void initADC_SOC(void)
{
#if(EX_ADC_RESOLUTION == 12)
{
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_SW_ONLY, ADC_CH_ADCIN0, 15);
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 15);
}
#elif(EX_ADC_RESOLUTION == 16)
{
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_SW_ONLY, ADC_CH_ADCIN0,64);
}
#endif
ADC_setInterruptSource(ADCA_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER0);
ADC_enableInterrupt(ADCA_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
}
Adc_init
void adc_init(void)
{
EALLOW; // Enable writing to the EALLOW protected areas
ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);
ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
ADC_setInterruptPulseMode(ADCARESULT_BASE, ADC_PULSE_END_OF_CONV);
ADC_enableConverter(ADCA_BASE);
DEVICE_DELAY_US(1000);
EDIS;
EALLOW;
AdcaRegs.ADCCTL2.bit.PRESCALE=4; // 200/4 50 MHz
AdcaRegs.ADCCTL2.bit.RESOLUTION=0; // 12 bit mode
AdcaRegs.ADCCTL2.bit.SIGNALMODE=0;// 0 Single-ended 1 Differential
AdcaRegs.ADCCTL1.bit.INTPULSEPOS=1;// Occurs at the end of the conversion
AdcaRegs.ADCCTL1.bit.ADCPWDNZ=1; // 7 ADC Power Down
EDIS;
}
soc_init
void soc_init(void)
{
// Select the channels to convert and the end of conversion flag
//
EALLOW;
AdcaRegs.ADCSOC0CTL.bit.CHSEL=0; // SOC0 ==> ADCINA0// 0:A0 1:A1 2:A2 3:A3
// 4:A4 5:A5 6:A6 7:A7
// 8:A8 9:A9 A:A10 B:A11
// C:A12 D:A13 E:A14 F:A15
// AdcaRegs.// Sample window is 10 SYSCLK cycles
}
UART_Communication.c
/*
* UART_Communication.c
*
* Created on: 12-Aug-2021
* Author: Admin
*/
#include "F28x_Project.h"
#include <UART_Communication.h>
#include "stdio.h"
#include "string.h"
#define EMPTY_LOOP
#define ENDLESS 1
void UART_Config()
{
EALLOW;
GPIO_SCIA_Init();
SCI_A_init();
EDIS;
}
//Initialization SCIA corresponds to GPIO
void GPIO_SCIA_Init()
{
// GPIO43 is the SCI Rx pin.
GPIO_setMasterCore(43, GPIO_CORE_CPU1);
GPIO_setPinConfig(GPIO_43_SCIRXDA);
GPIO_setDirectionMode(43, GPIO_DIR_MODE_IN);
GPIO_setPadConfig(43, GPIO_PIN_TYPE_STD);
GPIO_setQualificationMode(43, GPIO_QUAL_ASYNC);
//
// GPIO42 is the SCI Tx pin.
//
GPIO_setMasterCore(42, GPIO_CORE_CPU1);
GPIO_setPinConfig(GPIO_42_SCITXDA);
GPIO_setDirectionMode(42, GPIO_DIR_MODE_OUT);
GPIO_setPadConfig(42, GPIO_PIN_TYPE_STD);
GPIO_setQualificationMode(42, GPIO_QUAL_ASYNC);
}
void SCI_A_init()
{
//SCI0 initialization
SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_RXFF | SCI_INT_TXFF | SCI_INT_FE | SCI_INT_OE | SCI_INT_PE | SCI_INT_RXERR | SCI_INT_RXRDY_BRKDT | SCI_INT_TXRDY);
SCI_clearOverflowStatus(SCIA_BASE);
SCI_resetTxFIFO(SCIA_BASE);
SCI_resetRxFIFO(SCIA_BASE);
SCI_resetChannels(SCIA_BASE);
SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 9600, (SCI_CONFIG_WLEN_8|SCI_CONFIG_STOP_ONE|SCI_CONFIG_PAR_NONE));
SCI_disableLoopback(SCIA_BASE);
SCI_performSoftwareReset(SCIA_BASE);
SCI_setFIFOInterruptLevel(SCIA_BASE, SCI_FIFO_TX0, SCI_FIFO_RX0);
SCI_enableFIFO(SCIA_BASE);
SCI_enableModule(SCIA_BASE);
}
void UARTA_Transmit_String(char *Tx_string, uint16_t size)
{
uint16_t ch;
uint16_t temp=0;
while(temp++<size) // 0<5
{
ch =*Tx_string;
SciaRegs.SCITXBUF = ch;
while (SciaRegs.SCICTL2.bit.TXRDY == 0)
{
EMPTY_LOOP
}
Tx_string++;
}
}
void UARTA_Transmit_Byte(uint16_t c)
{
while (SciaRegs.SCICTL2.bit.TXRDY == 0) {EMPTY_LOOP}
SciaRegs.SCITXBUF = c;
}
UART_Communication.h
/*
* UART_Communication.h
*
* Created on: 12-Aug-2021
* Author: Admin
*/
#ifndef UART_COMMUNICATION_H_
#define UART_COMMUNICATION_H_
#include "driverlib.h"
#include "device.h"
void UART_Config();
void GPIO_SCIA_Init();
void SCI_A_init();
void UARTA_Transmit_Byte(uint16_t c);
void hal_PutsRS232(char * s);
void UARTA_Transmit_String(char *Tx_string, uint16_t size);
void hal_PutcUART(uint16_t c);
void hal_PutsUART(char * s);
#endif /* UART_COMMUNICATION_H_ */
Final code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "F2837xD_device.h"
#include "F28x_Project.h"
#include "F2837xD_Examples.h"
#include "driverlib.h"
#include "device.h"
#include "asysctl.h"
#include "cputimer.h"
#include "adc.h"
#include "UART_Communication.h"
#define EX_ADC_RESOLUTION 12
#define RESULTS_BUFFER_SIZE 15 //buffer for storing conversion results
// Globals
//
uint16_t adcAResult0;
char adcAResults[RESULTS_BUFFER_SIZE];
void ConfigADC(uint32_t ADC_BASE);
void initADC_SOC(void);
void main(void)
{
Device_init();
Device_initGPIO();
// Initialize PIE and clear PIE registers. Disables CPU interrupts.
//
Interrupt_initModule();
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
//
Interrupt_initVectorTable();
// Interrupt_register(INT_ADCA1, &adcA1ISR);
//
// Set up the ADC and the ePWM and initialize the SOC
//
ConfigADC(ADCA_BASE);
initADC_SOC();
UART_Config();
EINT;
ERTM;
UARTA_Transmit_String("..............................................................................\r\n", sizeof("..............................................................................\r\n"));
UARTA_Transmit_String("\t\t HBL POWER SYSTEM Application Start Serial Port_A\r\n", sizeof("\t\t HBL POWER SYSTEM Application Start Serial Port_A\r\n"));
UARTA_Transmit_String("..............................................................................\r\n", sizeof("..............................................................................\r\n"));
while(1)
{
// Convert, wait for completion, and store results
ADC_forceSOC(ADCA_BASE, ADC_SOC_NUMBER0);
// Wait for ADCA to complete, then acknowledge flag
while(ADC_getInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1) == false)
{
}
ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
//
// Store results
//
adcAResults[0] = ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER0);
float voltage;
char msg2[25];
voltage =adcAResults[0]*(3.3/4096);
UARTA_Transmit_String("Voltage :",sizeof("Voltage :"));
sprintf(msg2,"%g",voltage);
UARTA_Transmit_String(msg2,4);
UARTA_Transmit_String("\n",sizeof("\n"));
}
}
void ConfigADC(uint32_t ADC_BASE)
{
EALLOW;
ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);
#if(EX_ADC_RESOLUTION == 12)
{
ADC_setMode(ADC_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
}
#elif(EX_ADC_RESOLUTION == 16)
{
ADC_setMode(ADCA_BASE, ADC_RESOLUTION_16BIT, ADC_MODE_DIFFERENTIAL);
}
#endif
ADC_setInterruptPulseMode(ADC_BASE, ADC_PULSE_END_OF_CONV);
ADC_enableConverter(ADC_BASE);
DEVICE_DELAY_US(1000);
EDIS;
}
void initADC_SOC(void)
{
#if(EX_ADC_RESOLUTION == 12)
{
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_SW_ONLY, ADC_CH_ADCIN0, 15);
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 15);
}
#elif(EX_ADC_RESOLUTION == 16)
{
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_SW_ONLY, ADC_CH_ADCIN0,64);
}
#endif
ADC_setInterruptSource(ADCA_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER0);
ADC_enableInterrupt(ADCA_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
}
void adc_init(void)
{
EALLOW; // Enable writing to the EALLOW protected areas
ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);
ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
ADC_setInterruptPulseMode(ADCARESULT_BASE, ADC_PULSE_END_OF_CONV);
ADC_enableConverter(ADCA_BASE);
DEVICE_DELAY_US(1000);
EDIS;
EALLOW;
AdcaRegs.ADCCTL2.bit.PRESCALE=4; // 200/4 50 MHz
AdcaRegs.ADCCTL2.bit.RESOLUTION=0; // 12 bit mode
AdcaRegs.ADCCTL2.bit.SIGNALMODE=0;// 0 Single-ended 1 Differential
AdcaRegs.ADCCTL1.bit.INTPULSEPOS=1;// Occurs at the end of the conversion
AdcaRegs.ADCCTL1.bit.ADCPWDNZ=1; // 7 ADC Power Down
EDIS;
}
void soc_init(void)
{
// Select the channels to convert and the end of conversion flag
//
EALLOW;
AdcaRegs.ADCSOC0CTL.bit.CHSEL=0; // SOC0 ==> ADCINA0// 0:A0 1:A1 2:A2 3:A3
// 4:A4 5:A5 6:A6 7:A7
// 8:A8 9:A9 A:A10 B:A11
// C:A12 D:A13 E:A14 F:A15
// AdcaRegs.// Sample window is 10 SYSCLK cycles
}
ADC Data Reading With Resistor
/*
* main.c
*
* Created on: 05-Apr-2022
* Author: Admin
*/
#include "F28x_Project.h"
#include "F2837xD_device.h"
#include "F2837xD_Examples.h"
#include "device.h"
#include "driverlib.h"
void ADC_init();
uint16_t adc_result,i=0;
void main(void)
{
Device_init();
Device_initGPIO();
EALLOW;
GpioCtrlRegs.GPBMUX2.bit.GPIO52 = 0; // 0=GPIO, 1=EPWM1A, 2=Resv, 3=Resv
GpioCtrlRegs.GPBDIR.bit.GPIO52 = 1; // 1=OUTput, 0=INput
GpioCtrlRegs.GPBCSEL3.bit.GPIO52 = 0; // xx00: CPU1 selected
// xx01: CPU1.CLA1 selected
// xx10: CPU2 selected
// xx11: CPU2.CLA1 selected
EDIS;
ADC_init();
while(1)
{
AdcaRegs.ADCSOCFRC1.bit.SOC0 = 1; // software force start SOC0 to SOC7
adc_result = AdcaResultRegs.ADCRESULT0; // store results
GpioDataRegs.GPBTOGGLE.bit.GPIO52 = 1;
}
}
void ADC_init()
{
EALLOW;
AdcaRegs.ADCCTL2.bit.PRESCALE = 6; // set ADCCLK divider to /4
AdcaRegs.ADCCTL2.bit.RESOLUTION = 0; // 12 bit mode
AdcaRegs.ADCCTL2.bit.SIGNALMODE = 0; // 0 Single-ended 1 Differential
AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1; // power up the ADC/
DEVICE_DELAY_US(10000);
AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = 0; // TRIGGER-SOFTWRAE/HARDWARE
// 00h ADCTRIG0 - Software only
// 01h ADCTRIG1 - CPU1 Timer 0, TINT0n
// 02h ADCTRIG2 - CPU1 Timer 1, TINT1n
// 03h ADCTRIG3 - CPU1 Timer 2, TINT2n
AdcaRegs.ADCSOC0CTL.bit.CHSEL = 0; // SOC0 ==> ADCINA0// 0:A0 1:A1 2:A2 3:A3
// 4:A4 5:A5 6:A6 7:A7
// 8:A8 9:A9 A:A10 B:A11
// C:A12 D:A13 E:A14 F:A15
AdcaRegs.ADCSOC0CTL.bit.ACQPS = 15; // 000h Sample window is 1 system clock cycle wide
// 001h Sample window is 2 system clock cycles wide
// 002h Sample window is 3 system clock cycles wide
// 1FFh Sample window is 512 system clock cycles wide
EDIS;
}
[…] Analog-to-Digital Converter using (ADC) TMS320F28379D How to read from multiple channels of the ADC on a TMS320F28379D? […]