12.30.2013

ADC podstawy EK-TM4C123GXL



Procesor TM4C1233H6PM posiada dwa identyczne przetworniki ADC. Każdy przetwornik może pobierać próbki z 12 analogowych wejść, ma 12 bitową rozdzielczość. Maksymalne próbkowanie million samples/second Analogowe wejscia mogą byc typu "Single-ended" (jeden pin z sygnałem, odnieienie do GND) lub "differential-input" (wykorzystuje dwa piny, sygnał ujemny i dodatni względem GND). Procesor ma wbudowany czujnik temperatury który może być odczytany przez ADC.

Aby ADC działał poprawnie musi być włączony PLL ze zgodnym oscylatorem lub sam oscylator 16 MHz.
Przetwornik działa z zegarem 16MHz, jeżeli działa PLL to jego częstotliwość jest dzielona przez 25 (400/16=16).


Jeżeli będziemy korzystali z analogowych wejść procesora, należy skonfigurować piny które wykorzystamy jako AIN-analog in.
 
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);//PE3-AIN0

Następnie uruchamiamy sprzętowy ADC, do wyboru są dwa przetworniki ADC0 i ADC1.
Można ustawić szybkość próbkowania, domyślnie jest ustawione 125K samples/second. Można wybrać jeszcze 250K, 500K i 1M samples/second. Funkcja SysCtlADCSpeedGet() służy do pobierania prędkości ADC, zwraca wartość w formie 32 bitowego słowa "000000000000000000000111100000000" (dla 1M samples/s) w którym bity 8:9 ADC0 i 10:11 ADC1 reprezentują wartości speed. Ponieważ funkcja SysCtlADCSpeedSet ustawia oba rejestry (ADC 0 i ADC1) jednakowo, do odczytu wartości prętkości można wykorzystać zapis:
speed=(SysCtlADCSpeedGet()>>10); // 3=1M, 2=500K, 1=250K, 0=125K (0 is default)   

Dodatkowo istnieje opcja sprzętowego uśredniania sampli przed ich zapisem do "sequencers", w takiej opcji każdy pojedyńczy sampel w "sequencer" składa się ze średniej kilku pomiarów. Może to być 2, 4, 8 ,16, 32, 64 pomiarów przed uśrednieniem. Zabieg uśredniania zwiększa precyzję pomiaru, ale zmniejsza przepustowość (prędkość pobierania próbek należy podzielić przez liczbę uśrednianych pomiarów).

SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);  // ADC0
SysCtlADCSpeedSet(SYSCTL_ADCSPEED_125KSPS); // speed 125
ADCHardwareOversampleConfigure(ADC0_BASE, 64); // hardware averaging


Następnie wyłączamy "Sample Sequencers", należy to uczynić przed konfiguracją. Konfigurujemy źródło wyzwolenia pomiaru, może to być program (funkcja ADCProcessorTrigger), comparator, timer, GPIO. Każdy przetwornik posiada 4 sprzętowe "Sample Sequencers" w których są przechowywane sample z pomiarów, zasada FIFO.
"Sample Sequencers" w zależności od swojego numeru może przyjąć 8 (SS0),4 (SS1 i SS2), lub 1 (SS3) sampel. Po wybraniu numeru "Sample Sequencers" należy skonfigurować każdy krok, dla SS0 będzie to osiem kroków. W każdym należy wybrać źródło pomiaru, można zaznaczyć czy ma być wywołane przerwanie, można wybrać tryb "Single-ended" lub "differential-input", można wybrać comparator. Aby zaznaczyć, że dany krok jest końcowy wpisuje się informację ADC_CTL_END. Następnie należy aktywować "Sample Sequencers".

 ADCSequenceDisable(ADC0_BASE, 3);  //sequence 3 - depth FIFO= 1
 ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0); // software trigger
 //sequence 3 has one step
 ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE|ADC_CTL_END); // ch0,interrupt,end
 ADCSequenceEnable(ADC0_BASE,3);






Poniżej prosty kod programu wykorzystujący odczyt jednego pinu AIN0 ADC0. Program na podstawie wartości napięcia podawanego na pin PE3 zmienia ustawienia PWM na trzech diodach (kolor niebieski dla 0V do czerwonego dla 3,3V). ADC jest traktowane liniowo i kolory diod zmieniają się dosyć nagle gdyż ich intensywność świecenia nie jest liniowa względem wypełnienia sygnału. Do ustawiania napięcia posłużył mały potencjometr 4,7 kR, jedna końcówka do złącza GND, druga do zworki z napięciem 3,3V. Środkowe wyprowadzenie do pinu procesora, powstał dzielnik napięcia od 0V do 3,3V. Taki zakres zostanie przekształcony w liczby od 0 do 4095 (pełne 12 bit).




#include "inc/hw_types.h"

#include "inc/hw_types.h"

#include "driverlib/sysctl.h"

#include "driverlib/adc.h"

#include "driverlib/pin_map.h"

#include "driverlib/gpio.h"

#include "driverlib/timer.h"

#include "inc/hw_memmap.h"



int main(void)

{

 unsigned long ADC33[1]; // ADC buffer 0-3,3v, 0-4095

 unsigned long ledBlPWM, ledGrPWM, ledRePWM;  // led pwm TimerMatchSet value 

 unsigned long period=1024; // PWM frequency = 40000000/1024 = 3,9 kHz



 SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ); // 40 MHz



 // AIN0 = PE3

 SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

 GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);



 // ADC Config

 SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);  // ADC0

 SysCtlADCSpeedSet(SYSCTL_ADCSPEED_125KSPS); // speed 125

 ADCHardwareOversampleConfigure(ADC0_BASE, 64); // hardware averaging



 ADCSequenceDisable(ADC0_BASE, 3);  //sequence 3 - depth FIFO= 1

 ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0); // software trigger

 //sequence 3 has one step

 ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE|ADC_CTL_END); // ch0,interrupt,end

 ADCSequenceEnable(ADC0_BASE,3);







 // Configure PWM Pins

 SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

 //Configure PF1 as T0CCP1 Timer0 PWM1 RED LED

 GPIOPinConfigure(GPIO_PF1_T0CCP1 );

 GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_1);

 //Configure PF2 as T1CCP0 Timer1 PWM0 BLUE LED

 GPIOPinConfigure(GPIO_PF2_T1CCP0 );

 GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_2);

 //Configure PF3 as T1CCP1 Timer1 PWM1 GREEN LED

 GPIOPinConfigure(GPIO_PF3_T1CCP1 );

 GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_3);





 // Enable Peripheral Timer0 and Timer1

 SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0 | SYSCTL_PERIPH_TIMER1);



 // Configure Timers as PWM

 TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_B_PWM); //RED LED

 TimerConfigure(TIMER1_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM|TIMER_CFG_B_PWM); //BLUE and GREEN LEDS



 TimerLoadSet(TIMER0_BASE, TIMER_B, period+1); // 39 kHz

 TimerLoadSet(TIMER1_BASE, TIMER_A, period+1);

 TimerLoadSet(TIMER1_BASE, TIMER_B, period+1);







 TimerEnable(TIMER0_BASE,TIMER_B);

 TimerEnable(TIMER1_BASE,TIMER_BOTH);







 while(1){

  ADCIntClear(ADC0_BASE, 3);



  TimerMatchSet(TIMER0_BASE, TIMER_B, ledRePWM);

  TimerMatchSet(TIMER1_BASE, TIMER_A, ledBlPWM);

  TimerMatchSet(TIMER1_BASE, TIMER_B, ledGrPWM);





  ADCProcessorTrigger(ADC0_BASE, 3);





  while(!ADCIntStatus(ADC0_BASE, 3, false)){

   }



  ADCSequenceDataGet(ADC0_BASE, 3, ADC33);





  // turn off LED

  ledRePWM=1023;

  ledBlPWM=1023;

  ledGrPWM=1023;





  // PWM LED depending on the ADC

  if(ADC33[0]<1024){

   ledBlPWM=0;

   ledGrPWM=1024-ADC33[0];

  }

  else if (ADC33[0]<2048) {

   ledGrPWM=0;

   ledBlPWM=ADC33[0]-1024;

  }

  else if (ADC33[0]<3072) {

   ledGrPWM=0;

   ledRePWM=3071-ADC33[0];

  }

  else{

   ledRePWM=0;

   ledGrPWM=ADC33[0]-3072;

  }

 }

}


Zmiany wartości zmiennych podczas pracy debuggera: