199 lines
7.0 KiB
Arduino
199 lines
7.0 KiB
Arduino
// ARDUINO DUE: ROBUSTNI DMA RADAR (ODSTRANITEV DC + PRILAGOJEN ADC)
|
|
// Strojna oprema: Signal na pinu A1 (ADC kanal 6)
|
|
|
|
#include "arduinoFFT.h"
|
|
|
|
// --- KONFIGURACIJA ---
|
|
#define SAMPLES 1024 // Število vzorcev
|
|
#define SAMPLING_FREQUENCY 2048 // Frekvenca vzorčenja v Hz
|
|
|
|
|
|
// --- TABELA ---
|
|
const uint16_t FM_FREQ=2;
|
|
const uint16_t FM_SAMPLES=4096; // 12 bitov
|
|
uint16_t FM_table_pos[FM_SAMPLES];
|
|
uint16_t FM_table_neg[FM_SAMPLES];
|
|
|
|
int DAC_working=0;
|
|
// --- MEDPOMNILNIKI (BUFFERS) ---
|
|
volatile uint16_t bufferA[SAMPLES];
|
|
volatile uint16_t bufferB[SAMPLES];
|
|
volatile bool bufferA_Ready = false;
|
|
volatile bool bufferB_Ready = false;
|
|
|
|
float vReal[SAMPLES];
|
|
float vImag[SAMPLES];
|
|
|
|
ArduinoFFT<float> FFT = ArduinoFFT<float>(vReal, vImag, SAMPLES, SAMPLING_FREQUENCY);
|
|
|
|
void setup() {
|
|
// 1. Postavimo serijsko komunikacijo
|
|
SerialUSB.begin(115200);
|
|
while (!SerialUSB); // Počakaj na povezavo z računalnikom
|
|
delay(5000);
|
|
|
|
// 2. Poračunamo wavetable za FM signal
|
|
for (uint16_t i=0; i<FM_SAMPLES; i++) {
|
|
FM_table_pos[i] = i; // Napolnimo tabelo z linearnim sweepom navzgor
|
|
FM_table_neg[i] = 4095-i; // Napolnimo tabelo z linearnim sweepom navzdol
|
|
}
|
|
|
|
// 3. DACC - Digitalno-Analogni pretvornik
|
|
// Krmiljenje VCO Tune signala
|
|
|
|
pmc_enable_periph_clk(ID_DACC); // Vklopi clock za DACC
|
|
|
|
DACC->DACC_CR = DACC_CR_SWRST; // Resetiraj DACC
|
|
|
|
|
|
DACC->DACC_MR = DACC_MR_TRGSEL(2)| // Proženje na Timer Channel 1
|
|
DACC_MR_WORD_HALF| // Prenos po 16-bitov naenkrat
|
|
DACC_MR_STARTUP_512| // 512-bitni startup cikel
|
|
DACC_MR_USER_SEL_CHANNEL0| // Izhod na DAC0
|
|
DACC_MR_REFRESH(0)| // Brez refresha, ker se signal spremenja hitreje od Sample&Hold trajanja
|
|
DACC_MR_TAG_DIS| // brez TAGanja, ker vedno pišemo DAC0
|
|
DACC_MR_MAXS_NORMAL; // Normalni, ne Max Speed mode
|
|
|
|
DACC->DACC_CHER = DACC_CHER_CH0; // Enablaj DAC0 izhod
|
|
DACC->DACC_PTCR = DACC_PTCR_TXTDIS; // Disablaj DACC DMA kontroler
|
|
DACC->DACC_TPR = (uint32_t) FM_table_pos; // Najprej začni z pozitivnim sweepom
|
|
DACC->DACC_TNPR = (uint32_t) FM_table_neg; // Zatem z negativnim
|
|
DACC->DACC_TCR = FM_SAMPLES; // Dolžina sweep tabele
|
|
DACC->DACC_TNCR = FM_SAMPLES;
|
|
DACC->DACC_PTCR = DACC_PTCR_TXTEN; // Enablaj DACC DMA kontroler
|
|
|
|
|
|
NVIC_EnableIRQ(DACC_IRQn); // Enablaj prekinitev za DACC
|
|
DACC->DACC_IDR = DACC_IDR_TXRDY | DACC_IDR_EOC; // Izklopi TXRDY, EOC prekinitve
|
|
DACC->DACC_IER = DACC_IER_ENDTX; // Vklopi ENDTX prekinitev (ko DAC preleti eno tabelo)
|
|
|
|
DACC->DACC_MR |= DACC_MR_TRGEN_EN; // Vklopi proženje DACC
|
|
DACC->DACC_CDR = FM_table_pos[0]; // Naloži prvo vrednost v DACC
|
|
|
|
// 4. ADC - Analogno-Digitalni pretvornik
|
|
// Vzorčenje povratnega signala v osnovnem pasu
|
|
pmc_enable_periph_clk(ID_ADC);
|
|
adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
|
|
|
|
// TRACKTIM(15): Poveča čas vzorčenja in zadrževanja (daljše čakanje, da se napetost ustali)
|
|
// TRANSFER(3): Optimizira čas prenosa med analognim in digitalnim delom
|
|
// TRGEN / TRGSEL: Še vedno uporablja proženje s Timerjem 0
|
|
ADC->ADC_MR = ADC_MR_TRGEN |
|
|
ADC_MR_TRGSEL_ADC_TRIG1 |
|
|
ADC_MR_TRACKTIM(15) |
|
|
ADC_MR_TRANSFER(3);
|
|
|
|
ADC->ADC_CHER = 0x40; // Omogoči kanal 6 (Pin A1)
|
|
|
|
// ADC DMA (PDC)
|
|
ADC->ADC_PTCR = ADC_PTCR_RXTDIS; //
|
|
ADC->ADC_RPR = (uint32_t)bufferA; // Kazalec na trenutni medpomnilnik
|
|
ADC->ADC_RCR = SAMPLES; // Število vzorcev za prejem
|
|
ADC->ADC_RNPR = (uint32_t)bufferB; // Kazalec na naslednji medpomnilnik
|
|
ADC->ADC_RNCR = SAMPLES; // Število vzorcev za naslednji prejem
|
|
ADC->ADC_PTCR = ADC_PTCR_RXTEN; // Omogoči DMA prejem
|
|
|
|
// ADC DMA prekinitev (Interrupt)
|
|
NVIC_EnableIRQ(ADC_IRQn);
|
|
ADC->ADC_IER = ADC_IER_ENDRX; // Prekinitev ob koncu prejema medpomnilnika
|
|
|
|
// 5. Timer Counterja TC0 in TC1
|
|
// TC0: 2048 Hz: krmili vzorčenje signala v osnovnem pasu
|
|
// TC1: VAR Hz: krmili frekvenčni prelet VCO Tune signala
|
|
pmc_enable_periph_clk(ID_TC0); // Clock TC0
|
|
pmc_enable_periph_clk(ID_TC1); // Clock TC1
|
|
|
|
// Channel 0 (ADC trigger) - unchanged
|
|
TC_Configure(TC0, 0, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC |
|
|
TC_CMR_TCCLKS_TIMER_CLOCK2 |
|
|
TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET);
|
|
const uint32_t rc_adc = 5127; // 2048 Hz
|
|
TC_SetRA(TC0, 0, rc_adc / 2);
|
|
TC_SetRC(TC0, 0, rc_adc);
|
|
|
|
// Channel 1 (DAC trigger) - use CLOCK1
|
|
TC_Configure(TC0, 1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC |
|
|
TC_CMR_TCCLKS_TIMER_CLOCK1 |
|
|
TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET);
|
|
|
|
const uint32_t dac_period = 84000000/2/4096/FM_FREQ;
|
|
TC_SetRA(TC0, 1, dac_period / 2);
|
|
TC_SetRC(TC0, 1, dac_period);
|
|
|
|
// Sinhronizacija Timer bloka
|
|
TC0->TC_BCR = TC_BCR_SYNC;
|
|
|
|
// Zagon timerja
|
|
TC_Start(TC0, 0);
|
|
TC_Start(TC0, 1);
|
|
|
|
pinMode(2, OUTPUT);
|
|
digitalWrite(2, HIGH); // Vklop VCO-ja
|
|
}
|
|
|
|
void loop() {
|
|
if (bufferA_Ready || bufferB_Ready) {
|
|
// Izberi pripravljen medpomnilnik
|
|
volatile uint16_t* inputBuffer = bufferA_Ready ? bufferA : bufferB;
|
|
|
|
// --- KORAK 1: IZRAČUN DC ODMIKA (OFFSET) ---
|
|
// Seštejemo vse vzorce, da najdemo povprečno napetost
|
|
float dcBias = 0;
|
|
for (int i = 0; i < SAMPLES; i++) {
|
|
dcBias += inputBuffer[i];
|
|
}
|
|
dcBias = dcBias / SAMPLES;
|
|
|
|
// --- KORAK 2: POLNJENJE FFT POLJ IN ODSTRANITEV DC ---
|
|
// Odštevanje dcBias
|
|
for (int i = 0; i < SAMPLES; i++) {
|
|
vReal[i] = (float)inputBuffer[i] - dcBias;
|
|
vImag[i] = 0.0;
|
|
}
|
|
|
|
// --- KORAK 3: IZRAČUN FFT ---
|
|
FFT.windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD); // HAMMING-ovo okno
|
|
FFT.compute(FFT_FORWARD);
|
|
FFT.complexToMagnitude();
|
|
|
|
// --- KORAK 4: POŠILJANJE PODATKOV ---
|
|
// Pošljemo samo prvo polovico spektra (SAMPLES / 2)
|
|
SerialUSB.write((uint8_t*)vReal, (SAMPLES / 2) * sizeof(float));
|
|
// Ponastavi zastavico
|
|
if (bufferA_Ready) bufferA_Ready = false;
|
|
else bufferB_Ready = false;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
// Prekinitveni program (ISR) za ADC
|
|
void ADC_Handler() {
|
|
if ((ADC->ADC_ISR & ADC_ISR_ENDRX) == ADC_ISR_ENDRX) {
|
|
// Preveri, kateri medpomnilnik je ADC pravkar napolnil
|
|
// Opomba: RPR kaže na TRENUTNEGA, kar pomeni, da je prejšnji končan
|
|
if (ADC->ADC_RPR == (uint32_t)bufferB) {
|
|
bufferA_Ready = true;
|
|
ADC->ADC_RNPR = (uint32_t)bufferA; // Pripravi Buffer A kot naslednjega
|
|
ADC->ADC_RNCR = SAMPLES;
|
|
} else {
|
|
bufferB_Ready = true;
|
|
ADC->ADC_RNPR = (uint32_t)bufferB; // Pripravi Buffer B kot naslednjega
|
|
ADC->ADC_RNCR = SAMPLES;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DACC_Handler(void) {
|
|
uint32_t status = DACC->DACC_ISR; // Beri status
|
|
if (status & DACC_ISR_ENDTX) {
|
|
if(DACC->DACC_TPR == (uint32_t)FM_table_pos) { // Izberemo nasprotno smer preleta, ko pridemo do konca tabele
|
|
DACC->DACC_TNPR = (uint32_t)FM_table_pos;
|
|
DACC->DACC_TNCR = FM_SAMPLES;
|
|
} else {
|
|
DACC->DACC_TNPR = (uint32_t)FM_table_pos;
|
|
DACC->DACC_TNCR = FM_SAMPLES;
|
|
}
|
|
|
|
}
|
|
} |