Аналого-цифровое преобразование ПЛИС DE1-SOC

У меня есть DE1-SOC (Rev. C) под управлением Linux. У меня проблемы с доступом к встроенному АЦП. Входом для всех 8 каналов является синусоидальный сигнал 3V Pk-Pk. Встроенный АЦП представляет собой 12-разрядный 8-канальный АЦП AD7928. В таблице данных указано, что АЦП может обрабатывать биполярные сигналы, и приведена следующая принципиальная схема:

Биполярная электрическая схема AD7928

Все восемь каналов должны быть дискретизированы постоянно. а в таблице данных DE1-SOC указано, что регистр первого канала должен быть установлен в 1, что активирует опцию автоматического обновления на АЦП. Вот моя первая попытка кода. Он компилируется и запускается, но значения неверны, так как тот же сигнал, который подается в АЦП, также измеряется моим осциллографом.

#include <inttypes.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/mman.h>

/* FPGA HPS BRIDGE BASE */
#define LW_BRIDGE_BASE (0xFF200000)
#define HW_REGS_BASE   (0xFF200000)
#define HW_REGS_SPAN   (0x00200000)
#define HW_REGS_MASK  ( HW_REGS_SPAN - 1 )

/* HPS-2-FPGA AXI Bridge */
#define ALT_AXI_FPGASLVS_OFST (0xC0000000) // axi_master
#define HW_FPGA_AXI_SPAN      (0x40000000) // Bridge span 1GB
#define HW_FPGA_AXI_MASK     ( HW_FPGA_AXI_SPAN - 1 )

/* ADC REGISTER SPAN */
#define ADC_BASE (0x00004000)

/* ADC CHANNEL & UPDATE REGISTERS */
#define ADC_CH0_UPDATE      (LW_BRIDGE_BASE+ADC_BASE)
#define ADC_CH1_AUTO_UPDATE (LW_BRIDGE_BASE+ADC_BASE+4) // Write 1 for continual ADC request
#define ADC_CH2             (LW_BRIDGE_BASE+ADC_BASE+8)
#define ADC_CH3             (LW_BRIDGE_BASE+ADC_BASE+12)
#define ADC_CH4             (LW_BRIDGE_BASE+ADC_BASE+16)
#define ADC_CH5             (LW_BRIDGE_BASE+ADC_BASE+20)
#define ADC_CH6             (LW_BRIDGE_BASE+ADC_BASE+24)
#define ADC_CH7             (LW_BRIDGE_BASE+ADC_BASE+28)

/* ADC REGISTER END */
#define ADC_END (0x0000001F)

int main() {

// Defining variables
void *virtual_base;
int fd;
volatile int *h2p_lw_adc_addr;
int i;

//Defining pointer for register
    if ((fd = open( "/dev/mem",(O_RDWR | O_SYNC ))) == -1) {
        printf("ERROR: could not open \"/dev/mem\"...\n");
        return(1);
    }

    virtual_base = mmap(NULL,HW_REGS_SPAN,(PROT_READ | PROT_WRITE),MAP_SHARED,fd,HW_REGS_BASE);
    if (virtual_base == MAP_FAILED) {
        printf("ERROR: mmap() failed...\n");
        close(fd);
        return(1);
    }

    h2p_lw_adc_addr = virtual_base + ((int)(LW_BRIDGE_BASE + ADC_BASE)&(int)(HW_REGS_MASK));

    float Vref = 5.0;
    float stepSize = Vref/4096.0;

    /* Heading & Calculating Step Size/Resolution */
    printf("*____________________________________*\n");
    printf("*     Setting up the AD7928 ADC      *\n");
    printf("*____________________________________*\n");
    printf("Resolution for 5V  Vref: %f[mV]\n", stepSize*1000);

    // Setting up the ADC for bipolar signal
    // ...

    // Auto-update all channels continuously
    *(int *)(h2p_lw_adc_addr + 4) = 1;

    // Sample a single channel
    // ...

    /* Data Collection Attempt #1 */
    int num = 5; // Number of samples?
    unsigned int samples[num];

    int channel = 16; // channel 4
    for (i = 0; i < num; i++){
        samples[i] = *(int *)(h2p_lw_adc_addr + channel);
    }

    if (munmap(virtual_base, HW_REGS_SPAN) != 0) {
        printf("ERROR: munmap() failed...\n");
        close(fd);
        return(1);
    }
close(fd);
return 0;
}

Он компилируется с помощью этого Makefile:

C_SRC := adc.c
CFLAGS := -g -O0 -Wall
LDFLAGS := -lm

CROSS_COMPILE := arm-linux-gnueabihf-
CC := $(CROSS_COMPILE)gcc
NM := $(CROSS_COMPILE)nm

ifeq ($(or $(COMSPEC),$(ComSpec)),)
RM := rm -rf
else
RM := cs-rm -rf
endif

ELF ?= adc
OBJ := $(patsubst %.c,%.o,$(C_SRC))

.c.o:
    $(CC) $(CFLAGS) -c $< -o $@

.PHONY: all
all: $(ELF)

.PHONY:
clean:
    $(RM) $(ELF) $(OBJ) $(OBJS) *.map *.objdump

$(ELF): $(OBJ) $(OBJS)
    $(CC) $(CFLAGS) $(OBJ) $(OBJS) -o $@ $(LDFLAGS)
    $(NM) $@ > [email protected]

Я новичок, когда дело касается АЦП и DSP, но в идеале я хотел бы иметь возможность постоянно измерять все восемь каналов, записывая pk-pk (амплитуду) входящих синусоидальных волн в каждом из них, которые в конечном итоге будут используется для постобработки.

На данный момент вывод для пяти отсчетов всегда равен 0, за исключением случаев, когда я выбираю канал 1, тогда все пять отсчетов равны 1, например:

Samples [0]: 1
Samples [1]: 1
Samples [2]: 1
Samples [3]: 1
Samples [4]: 1

Даже когда я увеличиваю количество сэмплов, оно всегда равно 1 для канала 1 и 0 для всех остальных каналов.

Я думаю, что моя проблема, вероятно, связана с комбинацией моего кода, а также, возможно, с отсутствием схемы буферизации? (Но я не обрабатываю биполярный вход только потому, что я могу установить смещение постоянного тока на своем генераторе сигналов, чтобы он был полностью положительным 3 В пик-пик.)

Vref на АЦП получает постоянный ток равным 5 В. Я сейчас довольно потерян, поэтому я буду очень признателен за любую помощь или указатели.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
475
1

Ответы 1

Готов поспорить, что ваша проблема в следующих строках:

> volatile int *h2p_lw_adc_addr;
> 
> *(int *)(h2p_lw_adc_addr + 4) = 1;
> 
> samples[i] = *(int *)(h2p_lw_adc_addr + channel);

Поскольку h2p_lw_adc_addr является указателем на int, вы получите неправильные адреса из двух последних строк.

Когда вы добавляете число N к указателю int, указатель результата на N * sizeof(int) больше, чем указатель int.

Измените тип h2p_lw_adc_addr на указатель char, чтобы получить быстрое исправление:

volatile char *h2p_lw_adc_addr;

Или в качестве альтернативы, вы можете изменить смещения:

 *(int *)(h2p_lw_adc_addr + 1) = 1;
 int channel = 4; // channel 4

Но в этом случае я предлагаю использовать int32_t или uint32_t вместо int:

Привет, СКИ. Я последовал вашему последнему предложению об изменении как смещений, так и типа на uint32_t, но получение аналогичного поведения ожидает, что теперь каналы 0 и 1 дают мне 1, в то время как другие каналы ничего не дают.

Daniel Pavlovsky 12.09.2018 15:51

Я думаю, что моя проблема на самом деле / ​​dev / mem, потому что AD7928 встроен через интерфейс SPI, но когда я перехожу в свою папку / dev /, там нет spidev0.0 или чего-то подобного. Я думаю, что ядро ​​не было скомпилировано с поддержкой SPI. Я githubbed ядро ​​linux-socfpga и перекомпилирую с некоторыми добавленными инструкциями, найденными здесь: wiki.analog.com/resources/tools-software/linux-drivers/iio-a‌ dc /…

Daniel Pavlovsky 12.09.2018 17:32

Другие вопросы по теме