В статье рассматривается процесс создания программы для инициализации и управления микросхемой ad9363. Описанный подход применим для всего семейства ad936x. Всё реализовано под клон ADALM-Pluto с помощью Vivado 2021 и Vitis 2021 на Си.

Я не эксперт в области программирования ad936x. Это та область, которую я хочу изучить. Я пытаюсь разобраться в основах и мне проще учиться, когда я вижу, как это работает. Вот почему я это делаю. Не потому, что я уже всё знаю об ad936x. И если Вы тоже чему-то научитесь, прочитав мою статью, это будет здорово. Но пожалуйста, не полагайтесь на мои статьи, чтобы понять теорию. Есть множество отличных книг и других источников по этой теме, где Вы сможете найти более подробную информацию. Я надеюсь, что благодаря этой статье Вы научитесь применять некоторые базовые принципы программирования ad936x в реальных системах. Если Вы заметите ошибку или что-то, что можно улучшить, пожалуйста, напишите мне в комментариях. Тогда мы сможем учиться вместе, и, возможно, я смогу исправить это и показать в новых статьях.

Рис. 1. Общий вид сверху клона ADALM-PLUTO
Рис. 1. Общий вид сверху клона ADALM-PLUTO

Итак, перед нами клон достаточно популярного SDR под названием ADALM-PLUTO и нам необходимо написать для него свою прошивку. Этот клон отличается от оригинальной платы тем, что использована немного другая ПЛИС — XC7Z010-CLG400. Что это может значить для разработчика прошивки? А то, что придётся связаться с продавцом и попросить схему. В этом случае повезло, и продавец поделился файлом .xdc.

Рис. 2. Фрагмент предоставленного .xdc файла
Рис. 2. Фрагмент предоставленного .xdc файла

Путей для создания прошивок для ad936x и PlutoSDR, в частности, много. Здесь предлагается пройти по одному из самых простых. Есть такой ресурс https://wiki.analog.com. Он очень может помочь при разработке подобного софта. Например, есть страница https://wiki.analog.com/resources/fpga/docs/build. Давайте пройдёмся по этим шагам.

Рис 3. Скриншот страницы Building HDL
Рис 3. Скриншот страницы Building HDL

В этой статье разработка будет вестись на системе Windows, поэтому в CYGWIN выполним команду

export PATH=$PATH:/cygdrive/C/Xilinx/Vivado/2021.1/bin

Обратите внимание, что path_to заменён на C и указана версия Vivado. После выполнения команды which vivado возвращает правильный путь до Vivado.

Рис. 4. Скриншот терминала Cygwin
Рис. 4. Скриншот терминала Cygwin

Теперь можно клонировать репозиторий, и переключиться на ветку hdl_2021_r1, потому что в этой статье разработка ведётся с помощью Vivado 2021.1.

Рис. 5. Скриншот Git Bash терминала с результатом клонирования и переключения на ветку.
Рис. 5. Скриншот Git Bash терминала с результатом клонирования и переключения на ветку.

Мы используем клон с другой ПЛИС, поэтому необходимо подправить исходники. Первым делом заменим .xdc на предоставленный.

Рис. 6. Скриншот фрагмента оригинального файла .xdc
Рис. 6. Скриншот фрагмента оригинального файла .xdc
Рис. 7. Скриншот фрагмента подменного файла .xdc
Рис. 7. Скриншот фрагмента подменного файла .xdc

На двух изображениях выше видно, что назначения различаются. Например, rx_clk_in был на L12, а стал на U18. В файле system_bd.tcl на 41 строке есть параметр, значение которого необходимо заменить в соответствии с используемой на плате клона ПЛИС.

Рис. 8. Скриншот фрагмента файла system_bd.tcl с изменённым параметром
Рис. 8. Скриншот фрагмента файла system_bd.tcl с изменённым параметром

В файле system_project.tcl на 6 строке необходимо заменить модель ПЛИС.

Рис. 9. Скриншот фрагмента файла system_project.tcl с изменённой моделью ПЛИС.
Рис. 9. Скриншот фрагмента файла system_project.tcl с изменённой моделью ПЛИС.

Это все изменения, которые касаются железа в части сборки HDL дизайна.

Рис. 10. Скриншот страницы Building HDL
Рис. 10. Скриншот страницы Building HDL

Теперь на странице WIKI предлагается перейти в необходимый проект и выполнить команду make. Обратите внимание, что утилита make должна быть установлена в Cygwin и на команду which make возвращать правильный путь, например,

$ which make
/usr/bin/make
Рис. 11. Скриншот терминала Cygwin в процессе выполнения команды make в директории проекта pluto.
Рис. 11. Скриншот терминала Cygwin в процессе выполнения команды make в директории проекта pluto.

Процесс этот может быть долгим. Относительно, например, мой старенький ноутбук 2018 года, который тогда был с железом выше среднего, теперь делает это около 20 минут, а PC с Ryzen9 со старой работы собирал проект fmcomms2 под kc705 или zc706 за 10 минут. Кстати, процесс выполнения make тогда внезапно и по неизвестной причине завершался с error, тогда make можно просто запустить снова и всё хорошо собиралось дальше.

Рис. 12. Скриншот терминала Cygwin с результатами выполнения make
Рис. 12. Скриншот терминала Cygwin с результатами выполнения make

Утилита завершила свою работу, и теперь можно открыть проект с помощью Vivado.

Проект действительно собран под необходимую ПЛИС.

Рис. 13. Скриншот окна Vivado с используемой в проекте ПЛИС.
Рис. 13. Скриншот окна Vivado с используемой в проекте ПЛИС.

И новые назначения используются в соответствии с предоставленным файлом .xdc.

Рис. 14. Скриншот окна Vivado.
Рис. 14. Скриншот окна Vivado.

На этом этапе можно устроить промежуточную проверку и собрать простое приложение Hello World, которое выведет информацию через UART. Таким способом можно убедиться, что HDL дизайн подходит для этой платы. Для приложения Vitis необходимо экспортировать hardware с помощью File->Export->Export Hardware... Тогда в указанной директории появится необходимый файл .xsa

Рис. 15. Процесс Export Hardware
Рис. 15. Процесс Export Hardware

Обратите внимание на опцию Include bitstream, это важно.

Рис. 16. Необходимо выбрать правильную опцию
Рис. 16. Необходимо выбрать правильную опцию

Теперь можно открыть Vitis. Создать новое приложение с помощью File->New->Application Project...

Рис. 17. Создаём новое приложение
Рис. 17. Создаём новое приложение

На следующих этапах многие вещи понятны интуитивно. Главное — необходимо выбрать созданный .xsa файл:

Рис. 18. Необходимо перейти на вкладку создания из .xsa файла
Рис. 18. Необходимо перейти на вкладку создания из .xsa файла
Рис. 19. И выбрать созданный .xsa файл
Рис. 19. И выбрать созданный .xsa файл
Рис. 20 Создаём Hello World
Рис. 20 Создаём Hello World
Рис. 21. Выполняем Build
Рис. 21. Выполняем Build

Когда приложение будет готово, перед нами встанет вопрос о том, как загрузить прошивку в плату. На wiki.analog есть страничка https://wiki.analog.com/university/tools/pluto/devs/fpga, но там написано что-то невнятное. В любом случае на плате есть отверстия под разъём FTSH-105-01-L-D. Это оказался достаточно редкий разъём, и в продаже его найти не удалось. Описание контактов есть на схеме, которая доступна на https://wiki.analog.com/university/tools/pluto/hacking/hardware. Там несколько ревизий, но JTAG везде одинаковый. Кстати, для программирования Xilinx можно использовать сравнительно дешёвый программатор от Waveshare.

Рис. 21. Один из возможных программаторов
Рис. 21. Один из возможных программаторов
Рис. 22. Схема подключения программатора
Рис. 22. Схема подключения программатора
Рис. 23. Фото клона PlutoSDR с припаянным разъёмом
Рис. 23. Фото клона PlutoSDR с припаянным разъёмом

Подключаем программатор, потом крайний microUSB PlutoSDR, при этом STATUS светодиод на программаторе засветится зелёным, на плате засветится крайний светодиод, а в диспетчере устройств появится новый COM-порт. Необходимо подключиться любым удобным терминалом к этому COM-порту, чтобы увидеть UART вывод запрограммированной платы.

Рис. 24. PlutoSDR занимает COM-порт под номером 4
Рис. 24. PlutoSDR занимает COM-порт под номером 4

Когда всё подключено, необходимо вернуться к Vitis и войти в меню Run Configurations.

Рис. 25. Открываем меню Run Configurations.
Рис. 25. Открываем меню Run Configurations.

Два раза кликаем на System Project Debug и запускаем Run.

Рис 26. Заливаем прошивку в плату.
Рис 26. Заливаем прошивку в плату.

Тогда начнётся загрузка прошивки, а когда это будет сделано, то в терминале появится сообщение.

Рис. 27. Результат выполнения программы.
Рис. 27. Результат выполнения программы.

Подводя промежуточный итог, имеется рабочий HDL дизайн, который можно использовать для создания приложений и их успешного выполнения. Приступим к созданию приложения для инициализации и управления работой микросхемы ad936x.

Путей создания программы для микропроцессора, который будет управлять работой микросхемы, как и в случае с созданием HDL дизайна, несколько. Даже микропроцессоров «из коробки» доступно как минимум два. Это MicroBlaze и Zynq ARM ядра. В ADALM-PLUTO используется Zynq, как и в клоне, описываемом в этой статье. Обратимся к странице https://wiki.analog.com/resources/no-os/build. Откроем GIT BASH от имени администратора и пропишем путь до VItis с помощью команды,

$ export PATH=$PATH:"/c/Xilinx/Vitis/2021.1/bin"

Теперь команда which должна возвращать путь,

$ which vitis
/c/Xilinx/Vitis/2021.1/bin/vitis

Потребуется компилятор. На момент написания статьи получилось зайти и скачать последнюю доступную версию по ссылке. Если устанавливалось по пути по умолчанию, то прописать путь можно командой

$ export PATH=$PATH:"/c/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.10/bin"

Для проверки можно посмотреть версию

$ arm-none-eabi-gcc --version

Репозиторий no-OS надо клонировать рекурсивно

git clone --recursive https://github.com/analogdevicesinc/no-OS

У меня это заняло сравнительно значительное время. Не забудьте переключиться на ветку 2021_R1 или необходимую Вам. После этого можно скопировать созданный ранее .xsa файл в корневой каталог проекта ad9361, причём .xsa файл лучше сразу переименовать в system_top.xsa, чтобы потом не словить ошибку из-за особенностей готовых makefile. Файл app_config.h необходимо отредактировать. На 47 строке есть define, который выберет используемую нами микросхему. Но если Вам повезёт, то можно оставить define на 45 строке. Об этом рассказывается на странице https://wiki.analog.com/university/tools/pluto/hacking/hardware. В этом эксперименте останется define для ad9361, несмотря на то, что припаяна микросхема ad9363. Таким образом, этот исходный код годится для всего семейства ad936x.

У меня уже получилось с помощью этих исходников запустить и ad9361, и ad9364, и теперь ad9363. Необходимо только менять настройки под конкретную микросхему, например, ad9364 имеет по одному каналу на приём и передачу. Это работает.

После идёт блок define, которые отвечают за определённые примеры. Для проверки нашей работы мы будем анализировать не только debug информацию, которая печатается в терминале. Мы включим рядом передатчик, который транслирует в эфир синусоиду, примем этот сигнал на Pluto с bare-metal приложением, напечатаем в терминал отсчёты с ацп и построим из этих отсчётов график. Для этого раскомментируем строки 55 с ADC_DMA_EXAMPLE , 57 с DAC_DMA_EXAMPLE и 59 с TDD_SWITCH_STATE_EXAMPLE. Дальше переходим к файлу main.c. В этом файле нас больше всего пока интересует структура default_init_param. На строке 186 отключим режим FDD (0,      //frequency_division_duplex_mode_enable *** adi,frequency-division-duplex-mode-enable). На строке 223 включим тактирование от внешнего опорного источника тактового сигнала (1,      //xo_disable_use_ext_refclk_enable *** adi,xo-disable-use-ext-refclk-enable). На строке 352 поменяем местами порты 0 и 1 (1,      //swap_ports_enable *** adi,swap-ports-enable). На строке 354 отключим режим LVDS (0,      //lvds_mode_enable *** adi,lvds-mode-enable). На строке 357 включим опцию full_port_enable (1,      //full_port_enable *** adi,full-port-enable).

Вся структура целиком
AD9361_InitParam default_init_param = {
	/* Device selection */
	ID_AD9361,	// dev_sel
	/* Reference Clock */
	40000000UL,	//reference_clk_rate
	/* Base Configuration */
	1,		//two_rx_two_tx_mode_enable *** adi,2rx-2tx-mode-enable
	1,		//one_rx_one_tx_mode_use_rx_num *** adi,1rx-1tx-mode-use-rx-num
	1,		//one_rx_one_tx_mode_use_tx_num *** adi,1rx-1tx-mode-use-tx-num
	0,		//frequency_division_duplex_mode_enable *** adi,frequency-division-duplex-mode-enable
	0,		//frequency_division_duplex_independent_mode_enable *** adi,frequency-division-duplex-independent-mode-enable
	0,		//tdd_use_dual_synth_mode_enable *** adi,tdd-use-dual-synth-mode-enable
	0,		//tdd_skip_vco_cal_enable *** adi,tdd-skip-vco-cal-enable
	0,		//tx_fastlock_delay_ns *** adi,tx-fastlock-delay-ns
	0,		//rx_fastlock_delay_ns *** adi,rx-fastlock-delay-ns
	0,		//rx_fastlock_pincontrol_enable *** adi,rx-fastlock-pincontrol-enable
	0,		//tx_fastlock_pincontrol_enable *** adi,tx-fastlock-pincontrol-enable
	0,		//external_rx_lo_enable *** adi,external-rx-lo-enable
	0,		//external_tx_lo_enable *** adi,external-tx-lo-enable
	5,		//dc_offset_tracking_update_event_mask *** adi,dc-offset-tracking-update-event-mask
	6,		//dc_offset_attenuation_high_range *** adi,dc-offset-attenuation-high-range
	5,		//dc_offset_attenuation_low_range *** adi,dc-offset-attenuation-low-range
	0x28,	//dc_offset_count_high_range *** adi,dc-offset-count-high-range
	0x32,	//dc_offset_count_low_range *** adi,dc-offset-count-low-range
	0,		//split_gain_table_mode_enable *** adi,split-gain-table-mode-enable
	MAX_SYNTH_FREF,	//trx_synthesizer_target_fref_overwrite_hz *** adi,trx-synthesizer-target-fref-overwrite-hz
	0,		// qec_tracking_slow_mode_enable *** adi,qec-tracking-slow-mode-enable
	/* ENSM Control */
	0,		//ensm_enable_pin_pulse_mode_enable *** adi,ensm-enable-pin-pulse-mode-enable
	0,		//ensm_enable_txnrx_control_enable *** adi,ensm-enable-txnrx-control-enable
	/* LO Control */
	2400000000UL,	//rx_synthesizer_frequency_hz *** adi,rx-synthesizer-frequency-hz
	2400000000UL,	//tx_synthesizer_frequency_hz *** adi,tx-synthesizer-frequency-hz
	1,				//tx_lo_powerdown_managed_enable *** adi,tx-lo-powerdown-managed-enable
	/* Rate & BW Control */
	{983040000, 245760000, 122880000, 61440000, 30720000, 30720000},// rx_path_clock_frequencies[6] *** adi,rx-path-clock-frequencies
	{983040000, 122880000, 122880000, 61440000, 30720000, 30720000},// tx_path_clock_frequencies[6] *** adi,tx-path-clock-frequencies
	18000000,//rf_rx_bandwidth_hz *** adi,rf-rx-bandwidth-hz
	18000000,//rf_tx_bandwidth_hz *** adi,rf-tx-bandwidth-hz
	/* RF Port Control */
	0,		//rx_rf_port_input_select *** adi,rx-rf-port-input-select
	0,		//tx_rf_port_input_select *** adi,tx-rf-port-input-select
	/* TX Attenuation Control */
	10000,	//tx_attenuation_mdB *** adi,tx-attenuation-mdB
	0,		//update_tx_gain_in_alert_enable *** adi,update-tx-gain-in-alert-enable
	/* Reference Clock Control */
	1,		//xo_disable_use_ext_refclk_enable *** adi,xo-disable-use-ext-refclk-enable
	{8, 5920},	//dcxo_coarse_and_fine_tune[2] *** adi,dcxo-coarse-and-fine-tune
	CLKOUT_DISABLE,	//clk_output_mode_select *** adi,clk-output-mode-select
	/* Gain Control */
	2,		//gc_rx1_mode *** adi,gc-rx1-mode
	2,		//gc_rx2_mode *** adi,gc-rx2-mode
	58,		//gc_adc_large_overload_thresh *** adi,gc-adc-large-overload-thresh
	4,		//gc_adc_ovr_sample_size *** adi,gc-adc-ovr-sample-size
	47,		//gc_adc_small_overload_thresh *** adi,gc-adc-small-overload-thresh
	8192,	//gc_dec_pow_measurement_duration *** adi,gc-dec-pow-measurement-duration
	0,		//gc_dig_gain_enable *** adi,gc-dig-gain-enable
	800,	//gc_lmt_overload_high_thresh *** adi,gc-lmt-overload-high-thresh
	704,	//gc_lmt_overload_low_thresh *** adi,gc-lmt-overload-low-thresh
	24,		//gc_low_power_thresh *** adi,gc-low-power-thresh
	15,		//gc_max_dig_gain *** adi,gc-max-dig-gain
	0,		//gc_use_rx_fir_out_for_dec_pwr_meas_enable *** adi,gc-use-rx-fir-out-for-dec-pwr-meas-enable
	/* Gain MGC Control */
	2,		//mgc_dec_gain_step *** adi,mgc-dec-gain-step
	2,		//mgc_inc_gain_step *** adi,mgc-inc-gain-step
	0,		//mgc_rx1_ctrl_inp_enable *** adi,mgc-rx1-ctrl-inp-enable
	0,		//mgc_rx2_ctrl_inp_enable *** adi,mgc-rx2-ctrl-inp-enable
	0,		//mgc_split_table_ctrl_inp_gain_mode *** adi,mgc-split-table-ctrl-inp-gain-mode
	/* Gain AGC Control */
	10,		//agc_adc_large_overload_exceed_counter *** adi,agc-adc-large-overload-exceed-counter
	2,		//agc_adc_large_overload_inc_steps *** adi,agc-adc-large-overload-inc-steps
	0,		//agc_adc_lmt_small_overload_prevent_gain_inc_enable *** adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable
	10,		//agc_adc_small_overload_exceed_counter *** adi,agc-adc-small-overload-exceed-counter
	4,		//agc_dig_gain_step_size *** adi,agc-dig-gain-step-size
	3,		//agc_dig_saturation_exceed_counter *** adi,agc-dig-saturation-exceed-counter
	1000,	// agc_gain_update_interval_us *** adi,agc-gain-update-interval-us
	0,		//agc_immed_gain_change_if_large_adc_overload_enable *** adi,agc-immed-gain-change-if-large-adc-overload-enable
	0,		//agc_immed_gain_change_if_large_lmt_overload_enable *** adi,agc-immed-gain-change-if-large-lmt-overload-enable
	10,		//agc_inner_thresh_high *** adi,agc-inner-thresh-high
	1,		//agc_inner_thresh_high_dec_steps *** adi,agc-inner-thresh-high-dec-steps
	12,		//agc_inner_thresh_low *** adi,agc-inner-thresh-low
	1,		//agc_inner_thresh_low_inc_steps *** adi,agc-inner-thresh-low-inc-steps
	10,		//agc_lmt_overload_large_exceed_counter *** adi,agc-lmt-overload-large-exceed-counter
	2,		//agc_lmt_overload_large_inc_steps *** adi,agc-lmt-overload-large-inc-steps
	10,		//agc_lmt_overload_small_exceed_counter *** adi,agc-lmt-overload-small-exceed-counter
	5,		//agc_outer_thresh_high *** adi,agc-outer-thresh-high
	2,		//agc_outer_thresh_high_dec_steps *** adi,agc-outer-thresh-high-dec-steps
	18,		//agc_outer_thresh_low *** adi,agc-outer-thresh-low
	2,		//agc_outer_thresh_low_inc_steps *** adi,agc-outer-thresh-low-inc-steps
	1,		//agc_attack_delay_extra_margin_us; *** adi,agc-attack-delay-extra-margin-us
	0,		//agc_sync_for_gain_counter_enable *** adi,agc-sync-for-gain-counter-enable
	/* Fast AGC */
	64,		//fagc_dec_pow_measuremnt_duration ***  adi,fagc-dec-pow-measurement-duration
	260,	//fagc_state_wait_time_ns ***  adi,fagc-state-wait-time-ns
	/* Fast AGC - Low Power */
	0,		//fagc_allow_agc_gain_increase ***  adi,fagc-allow-agc-gain-increase-enable
	5,		//fagc_lp_thresh_increment_time ***  adi,fagc-lp-thresh-increment-time
	1,		//fagc_lp_thresh_increment_steps ***  adi,fagc-lp-thresh-increment-steps
	/* Fast AGC - Lock Level (Lock Level is set via slow AGC inner high threshold) */
	1,		//fagc_lock_level_lmt_gain_increase_en ***  adi,fagc-lock-level-lmt-gain-increase-enable
	5,		//fagc_lock_level_gain_increase_upper_limit ***  adi,fagc-lock-level-gain-increase-upper-limit
	/* Fast AGC - Peak Detectors and Final Settling */
	1,		//fagc_lpf_final_settling_steps ***  adi,fagc-lpf-final-settling-steps
	1,		//fagc_lmt_final_settling_steps ***  adi,fagc-lmt-final-settling-steps
	3,		//fagc_final_overrange_count ***  adi,fagc-final-overrange-count
	/* Fast AGC - Final Power Test */
	0,		//fagc_gain_increase_after_gain_lock_en ***  adi,fagc-gain-increase-after-gain-lock-enable
	/* Fast AGC - Unlocking the Gain */
	0,		//fagc_gain_index_type_after_exit_rx_mode ***  adi,fagc-gain-index-type-after-exit-rx-mode
	1,		//fagc_use_last_lock_level_for_set_gain_en ***  adi,fagc-use-last-lock-level-for-set-gain-enable
	1,		//fagc_rst_gla_stronger_sig_thresh_exceeded_en ***  adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable
	5,		//fagc_optimized_gain_offset ***  adi,fagc-optimized-gain-offset
	10,		//fagc_rst_gla_stronger_sig_thresh_above_ll ***  adi,fagc-rst-gla-stronger-sig-thresh-above-ll
	1,		//fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en ***  adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable
	1,		//fagc_rst_gla_engergy_lost_goto_optim_gain_en ***  adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable
	10,		//fagc_rst_gla_engergy_lost_sig_thresh_below_ll ***  adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll
	8,		//fagc_energy_lost_stronger_sig_gain_lock_exit_cnt ***  adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt
	1,		//fagc_rst_gla_large_adc_overload_en ***  adi,fagc-rst-gla-large-adc-overload-enable
	1,		//fagc_rst_gla_large_lmt_overload_en ***  adi,fagc-rst-gla-large-lmt-overload-enable
	0,		//fagc_rst_gla_en_agc_pulled_high_en ***  adi,fagc-rst-gla-en-agc-pulled-high-enable
	0,		//fagc_rst_gla_if_en_agc_pulled_high_mode ***  adi,fagc-rst-gla-if-en-agc-pulled-high-mode
	64,		//fagc_power_measurement_duration_in_state5 ***  adi,fagc-power-measurement-duration-in-state5
	2,		//fagc_large_overload_inc_steps *** adi,fagc-adc-large-overload-inc-steps
	/* RSSI Control */
	1,		//rssi_delay *** adi,rssi-delay
	1000,	//rssi_duration *** adi,rssi-duration
	3,		//rssi_restart_mode *** adi,rssi-restart-mode
	0,		//rssi_unit_is_rx_samples_enable *** adi,rssi-unit-is-rx-samples-enable
	1,		//rssi_wait *** adi,rssi-wait
	/* Aux ADC Control */
	256,	//aux_adc_decimation *** adi,aux-adc-decimation
	40000000UL,	//aux_adc_rate *** adi,aux-adc-rate
	/* AuxDAC Control */
	1,		//aux_dac_manual_mode_enable ***  adi,aux-dac-manual-mode-enable
	0,		//aux_dac1_default_value_mV ***  adi,aux-dac1-default-value-mV
	0,		//aux_dac1_active_in_rx_enable ***  adi,aux-dac1-active-in-rx-enable
	0,		//aux_dac1_active_in_tx_enable ***  adi,aux-dac1-active-in-tx-enable
	0,		//aux_dac1_active_in_alert_enable ***  adi,aux-dac1-active-in-alert-enable
	0,		//aux_dac1_rx_delay_us ***  adi,aux-dac1-rx-delay-us
	0,		//aux_dac1_tx_delay_us ***  adi,aux-dac1-tx-delay-us
	0,		//aux_dac2_default_value_mV ***  adi,aux-dac2-default-value-mV
	0,		//aux_dac2_active_in_rx_enable ***  adi,aux-dac2-active-in-rx-enable
	0,		//aux_dac2_active_in_tx_enable ***  adi,aux-dac2-active-in-tx-enable
	0,		//aux_dac2_active_in_alert_enable ***  adi,aux-dac2-active-in-alert-enable
	0,		//aux_dac2_rx_delay_us ***  adi,aux-dac2-rx-delay-us
	0,		//aux_dac2_tx_delay_us ***  adi,aux-dac2-tx-delay-us
	/* Temperature Sensor Control */
	256,	//temp_sense_decimation *** adi,temp-sense-decimation
	1000,	//temp_sense_measurement_interval_ms *** adi,temp-sense-measurement-interval-ms
	0xCE,	//temp_sense_offset_signed *** adi,temp-sense-offset-signed
	1,		//temp_sense_periodic_measurement_enable *** adi,temp-sense-periodic-measurement-enable
	/* Control Out Setup */
	0xFF,	//ctrl_outs_enable_mask *** adi,ctrl-outs-enable-mask
	0,		//ctrl_outs_index *** adi,ctrl-outs-index
	/* External LNA Control */
	0,		//elna_settling_delay_ns *** adi,elna-settling-delay-ns
	0,		//elna_gain_mdB *** adi,elna-gain-mdB
	0,		//elna_bypass_loss_mdB *** adi,elna-bypass-loss-mdB
	0,		//elna_rx1_gpo0_control_enable *** adi,elna-rx1-gpo0-control-enable
	0,		//elna_rx2_gpo1_control_enable *** adi,elna-rx2-gpo1-control-enable
	0,		//elna_gaintable_all_index_enable *** adi,elna-gaintable-all-index-enable
	/* Digital Interface Control */
	0,		//digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode
	0,		//digital_interface_tune_fir_disable *** adi,digital-interface-tune-fir-disable
	1,		//pp_tx_swap_enable *** adi,pp-tx-swap-enable
	1,		//pp_rx_swap_enable *** adi,pp-rx-swap-enable
	0,		//tx_channel_swap_enable *** adi,tx-channel-swap-enable
	0,		//rx_channel_swap_enable *** adi,rx-channel-swap-enable
	1,		//rx_frame_pulse_mode_enable *** adi,rx-frame-pulse-mode-enable
	0,		//two_t_two_r_timing_enable *** adi,2t2r-timing-enable
	0,		//invert_data_bus_enable *** adi,invert-data-bus-enable
	0,		//invert_data_clk_enable *** adi,invert-data-clk-enable
	0,		//fdd_alt_word_order_enable *** adi,fdd-alt-word-order-enable
	0,		//invert_rx_frame_enable *** adi,invert-rx-frame-enable
	0,		//fdd_rx_rate_2tx_enable *** adi,fdd-rx-rate-2tx-enable
	1,		//swap_ports_enable *** adi,swap-ports-enable
	0,		//single_data_rate_enable *** adi,single-data-rate-enable
	0,		//lvds_mode_enable *** adi,lvds-mode-enable
	0,		//half_duplex_mode_enable *** adi,half-duplex-mode-enable
	0,		//single_port_mode_enable *** adi,single-port-mode-enable
	1,		//full_port_enable *** adi,full-port-enable
	0,		//full_duplex_swap_bits_enable *** adi,full-duplex-swap-bits-enable
	0,		//delay_rx_data *** adi,delay-rx-data
	0,		//rx_data_clock_delay *** adi,rx-data-clock-delay
	4,		//rx_data_delay *** adi,rx-data-delay
	7,		//tx_fb_clock_delay *** adi,tx-fb-clock-delay
	0,		//tx_data_delay *** adi,tx-data-delay
#ifdef ALTERA_PLATFORM
	300,	//lvds_bias_mV *** adi,lvds-bias-mV
#else
	150,	//lvds_bias_mV *** adi,lvds-bias-mV
#endif
	1,		//lvds_rx_onchip_termination_enable *** adi,lvds-rx-onchip-termination-enable
	0,		//rx1rx2_phase_inversion_en *** adi,rx1-rx2-phase-inversion-enable
	0xFF,	//lvds_invert1_control *** adi,lvds-invert1-control
	0x0F,	//lvds_invert2_control *** adi,lvds-invert2-control
	/* GPO Control */
	0,		//gpo_manual_mode_enable *** adi,gpo-manual-mode-enable
	0,		//gpo_manual_mode_enable_mask *** adi,gpo-manual-mode-enable-mask
	0,		//gpo0_inactive_state_high_enable *** adi,gpo0-inactive-state-high-enable
	0,		//gpo1_inactive_state_high_enable *** adi,gpo1-inactive-state-high-enable
	0,		//gpo2_inactive_state_high_enable *** adi,gpo2-inactive-state-high-enable
	0,		//gpo3_inactive_state_high_enable *** adi,gpo3-inactive-state-high-enable
	0,		//gpo0_slave_rx_enable *** adi,gpo0-slave-rx-enable
	0,		//gpo0_slave_tx_enable *** adi,gpo0-slave-tx-enable
	0,		//gpo1_slave_rx_enable *** adi,gpo1-slave-rx-enable
	0,		//gpo1_slave_tx_enable *** adi,gpo1-slave-tx-enable
	0,		//gpo2_slave_rx_enable *** adi,gpo2-slave-rx-enable
	0,		//gpo2_slave_tx_enable *** adi,gpo2-slave-tx-enable
	0,		//gpo3_slave_rx_enable *** adi,gpo3-slave-rx-enable
	0,		//gpo3_slave_tx_enable *** adi,gpo3-slave-tx-enable
	0,		//gpo0_rx_delay_us *** adi,gpo0-rx-delay-us
	0,		//gpo0_tx_delay_us *** adi,gpo0-tx-delay-us
	0,		//gpo1_rx_delay_us *** adi,gpo1-rx-delay-us
	0,		//gpo1_tx_delay_us *** adi,gpo1-tx-delay-us
	0,		//gpo2_rx_delay_us *** adi,gpo2-rx-delay-us
	0,		//gpo2_tx_delay_us *** adi,gpo2-tx-delay-us
	0,		//gpo3_rx_delay_us *** adi,gpo3-rx-delay-us
	0,		//gpo3_tx_delay_us *** adi,gpo3-tx-delay-us
	/* Tx Monitor Control */
	37000,	//low_high_gain_threshold_mdB *** adi,txmon-low-high-thresh
	0,		//low_gain_dB *** adi,txmon-low-gain
	24,		//high_gain_dB *** adi,txmon-high-gain
	0,		//tx_mon_track_en *** adi,txmon-dc-tracking-enable
	0,		//one_shot_mode_en *** adi,txmon-one-shot-mode-enable
	511,	//tx_mon_delay *** adi,txmon-delay
	8192,	//tx_mon_duration *** adi,txmon-duration
	2,		//tx1_mon_front_end_gain *** adi,txmon-1-front-end-gain
	2,		//tx2_mon_front_end_gain *** adi,txmon-2-front-end-gain
	48,		//tx1_mon_lo_cm *** adi,txmon-1-lo-cm
	48,		//tx2_mon_lo_cm *** adi,txmon-2-lo-cm
	/* GPIO definitions */
	{
		.number = -1,
		.platform_ops = GPIO_OPS,
		.extra = GPIO_PARAM
	},		//gpio_resetb *** reset-gpios
	/* MCS Sync */
	{
		.number = -1,
		.platform_ops = GPIO_OPS,
		.extra = GPIO_PARAM
	},		//gpio_sync *** sync-gpios

	{
		.number = -1,
		.platform_ops = GPIO_OPS,
		.extra = GPIO_PARAM
	},		//gpio_cal_sw1 *** cal-sw1-gpios

	{
		.number = -1,
		.platform_ops = GPIO_OPS,
		.extra = GPIO_PARAM
	},		//gpio_cal_sw2 *** cal-sw2-gpios

	{
		.device_id = SPI_DEVICE_ID,
		.mode = NO_OS_SPI_MODE_1,
		.chip_select = SPI_CS,
		.platform_ops = SPI_OPS,
		.extra = SPI_PARAM
	},

	/* External LO clocks */
	NULL,	//(*ad9361_rfpll_ext_recalc_rate)()
	NULL,	//(*ad9361_rfpll_ext_round_rate)()
	NULL,	//(*ad9361_rfpll_ext_set_rate)()
#ifndef AXI_ADC_NOT_PRESENT
	&rx_adc_init,	// *rx_adc_init
	&tx_dac_init,   // *tx_dac_init
#endif
};

При этом чтобы вывести отсчёты в терминал, необходимо дописать в последний блок кода примера TDD_SWITCH_STATE_EXAMPLE строки, которые для удобства выведут в терминал уровень принимаемого сигнала, затем прочитают данные из DMA, данные проверятся и в цикле выведется 1024 отсчёта из необходимого нам канала. Это происходит в строке 21 в коде ниже. То есть всего 4 канала, i0, q0, i1, q1. А по этому условию будут напечатаны отсчёты из одного канала.

struct rf_rssi ch0;
struct rf_rssi ch1;

ad9361_get_rx_rssi(ad9361_phy, 0, &ch0);
ad9361_get_rx_rssi(ad9361_phy, 1, &ch1);
printf("ch0: %lu %lu %lu %ld %d\n", ch0.ant, ch0.symbol, ch0.preamble, ch0.multiplier, ch0.duration);
printf("ch1: %lu %lu %lu %ld %d\n", ch1.ant, ch1.symbol, ch1.preamble, ch1.multiplier, ch1.duration);

/* Read the data from the ADC DMA. */
axi_dmac_transfer_start(rx_dmac, &read_transfer);

/* Wait until transfer finishes */
status = axi_dmac_transfer_wait_completion(rx_dmac, 500);
if(status < 0)
    return status;

Xil_DCacheInvalidateRange((uintptr_t)adc_buffer, sizeof(adc_buffer));

//----------------------------------------------------------------------------------------------------
for(int i = 0; i < 4096; i++){
    if(i%4==0){
        int16_t sample = (int16_t)adc_buffer[i];
        printf("%d\n", sample);
    }
}
//----------------------------------------------------------------------------------------------------
Весь код для TDD_SWITCH_STATE_EXAMPLE
#ifdef TDD_SWITCH_STATE_EXAMPLE
//	uint32_t ensm_mode;
	struct no_os_gpio_init_param  gpio_init = {
		.platform_ops = GPIO_OPS,
		.extra = GPIO_PARAM
	};
	struct no_os_gpio_desc 	*gpio_enable_pin;
	struct no_os_gpio_desc 	*gpio_txnrx_pin;
	if (!ad9361_phy->pdata->fdd) {
		if (ad9361_phy->pdata->ensm_pin_ctrl) {
			gpio_init.number = GPIO_ENABLE_PIN;
			status = no_os_gpio_get(&gpio_enable_pin, &gpio_init);
			if (status != 0) {
				printf("no_os_gpio_get() error: %"PRIi32"\n", status);
				return status;
			}
			no_os_gpio_direction_output(gpio_enable_pin, 1);
			gpio_init.number = GPIO_TXNRX_PIN;
			status = no_os_gpio_get(&gpio_txnrx_pin, &gpio_init);
			if (status != 0) {
				printf("no_os_gpio_get() error: %"PRIi32"\n", status);
				return status;
			}
			no_os_gpio_direction_output(gpio_txnrx_pin, 0);
			no_os_udelay(10);
			ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
			printf("TXNRX control - Alert: %s\n",
			       ensm_mode == ENSM_MODE_ALERT ? "OK" : "Error");
			no_os_mdelay(1000);

			if (ad9361_phy->pdata->ensm_pin_pulse_mode) {
				while(1) {
					no_os_gpio_set_value(gpio_txnrx_pin, 0);
					no_os_udelay(10);
					no_os_gpio_set_value(gpio_enable_pin, 1);
					no_os_udelay(10);
					no_os_gpio_set_value(gpio_enable_pin, 0);
					ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
					printf("TXNRX Pulse control - RX: %s\n",
					       ensm_mode == ENSM_MODE_RX ? "OK" : "Error");
					no_os_mdelay(1000);

					no_os_gpio_set_value(gpio_enable_pin, 1);
					no_os_udelay(10);
					no_os_gpio_set_value(gpio_enable_pin, 0);
					ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
					printf("TXNRX Pulse control - Alert: %s\n",
					       ensm_mode == ENSM_MODE_ALERT ? "OK" : "Error");
					no_os_mdelay(1000);

					no_os_gpio_set_value(gpio_txnrx_pin, 1);
					no_os_udelay(10);
					no_os_gpio_set_value(gpio_enable_pin, 1);
					no_os_udelay(10);
					no_os_gpio_set_value(gpio_enable_pin, 0);
					ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
					printf("TXNRX Pulse control - TX: %s\n",
					       ensm_mode == ENSM_MODE_TX ? "OK" : "Error");
					no_os_mdelay(1000);

					no_os_gpio_set_value(gpio_enable_pin, 1);
					no_os_udelay(10);
					no_os_gpio_set_value(gpio_enable_pin, 0);
					ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
					printf("TXNRX Pulse control - Alert: %s\n",
					       ensm_mode == ENSM_MODE_ALERT ? "OK" : "Error");
					no_os_mdelay(1000);
				}
			} else {
				while(1) {
					no_os_gpio_set_value(gpio_txnrx_pin, 0);
					no_os_udelay(10);
					no_os_gpio_set_value(gpio_enable_pin, 1);
					no_os_udelay(10);
					ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
					printf("TXNRX control - RX: %s\n",
					       ensm_mode == ENSM_MODE_RX ? "OK" : "Error");
					no_os_mdelay(1000);

					no_os_gpio_set_value(gpio_enable_pin, 0);
					no_os_udelay(10);
					ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
					printf("TXNRX control - Alert: %s\n",
					       ensm_mode == ENSM_MODE_ALERT ? "OK" : "Error");
					no_os_mdelay(1000);

					no_os_gpio_set_value(gpio_txnrx_pin, 1);
					no_os_udelay(10);
					no_os_gpio_set_value(gpio_enable_pin, 1);
					no_os_udelay(10);
					ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
					printf("TXNRX control - TX: %s\n",
					       ensm_mode == ENSM_MODE_TX ? "OK" : "Error");
					no_os_mdelay(1000);

					no_os_gpio_set_value(gpio_enable_pin, 0);
					no_os_udelay(10);
					ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
					printf("TXNRX control - Alert: %s\n",
					       ensm_mode == ENSM_MODE_ALERT ? "OK" : "Error");
					no_os_mdelay(1000);
				}
			}
		} else {
//			int i = 1;
			while(1) {

				ad9361_set_en_state_machine_mode(ad9361_phy, ENSM_MODE_RX);
				ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
				printf("SPI control - RX: %s\n",
				       ensm_mode == ENSM_MODE_RX ? "OK" : "Error");
				no_os_mdelay(1000);

				struct rf_rssi ch0;
				struct rf_rssi ch1;

				ad9361_get_rx_rssi(ad9361_phy, 0, &ch0);
				ad9361_get_rx_rssi(ad9361_phy, 1, &ch1);
				printf("ch0: %lu %lu %lu %ld %d\n", ch0.ant, ch0.symbol, ch0.preamble, ch0.multiplier, ch0.duration);
				printf("ch1: %lu %lu %lu %ld %d\n", ch1.ant, ch1.symbol, ch1.preamble, ch1.multiplier, ch1.duration);

				/* Read the data from the ADC DMA. */
				axi_dmac_transfer_start(rx_dmac, &read_transfer);

				/* Wait until transfer finishes */
				status = axi_dmac_transfer_wait_completion(rx_dmac, 500);
				if(status < 0)
					return status;

				Xil_DCacheInvalidateRange((uintptr_t)adc_buffer, sizeof(adc_buffer));

				//----------------------------------------------------------------------------------------------------
				for(int i = 0; i < 4096; i++){
					if(i%4==2){
						int16_t sample = (int16_t)adc_buffer[i];
						printf("%d\n", sample);
					}
				}
				//----------------------------------------------------------------------------------------------------

				ad9361_set_en_state_machine_mode(ad9361_phy, ENSM_MODE_ALERT);
				ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
				printf("SPI control - Alert: %s\n",
				       ensm_mode == ENSM_MODE_ALERT ? "OK" : "Error");
				no_os_mdelay(1000);

				ad9361_set_en_state_machine_mode(ad9361_phy, ENSM_MODE_TX);
				ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
				printf("SPI control - TX: %s\n",
				       ensm_mode == ENSM_MODE_TX ? "OK" : "Error");
				no_os_mdelay(1000);

				ad9361_set_en_state_machine_mode(ad9361_phy, ENSM_MODE_ALERT);
				ad9361_get_en_state_machine_mode(ad9361_phy, &ensm_mode);
				printf("SPI control - Alert: %s\n",
				       ensm_mode == ENSM_MODE_ALERT ? "OK" : "Error");
				no_os_mdelay(1000);

			}
		}
	}
#endif

В Git Bash выполним команду мake в каталоге проекта ad9361.

Рис. 28. Скриншот терминала GIT BASH после выполнения make
Рис. 28. Скриншот терминала GIT BASH после выполнения make

В терминал будет выведена кое-какая отладочная информация о процессах сборки, линковки и компиляции проекта, и некоторые предупреждения, которые пока что можно проигнорировать. А в конце будет информация об успешном создании бинарного файла для нашего микропроцессора. Давайте посмотрим, что будет, если загрузить программу в ZYNQ. Для этого в терминал надо дописать команды:

export XSCT_REMOTE_HOST=127.0.0.1
export XSCT_REMOTE_PORT=3121

Об этом тоже написано на https://wiki.analog.com/resources/no-os/build в разделе Running/Debugging. Подключаем программатор, если уже успели его отключить, и питание отладочной платы. Проверяем COM-порт, на котором сидит клон PlutoSDR и подключаемся к com-порту любимым терминалом на скорости 115200. В Git Bash пишем команду make run.

При первой прошивке после подключения программатора он пару раз сам отключается и подключается к системе. А в Git Bash появляется информация о якобы успешной загрузке, хотя это не так. Я просто это игнорирую и запускаю команду ещё раз.

Кстати, если при экспорте из Vivado .xsa файла Вы назвали не system_top.xsa, то можете получить couldn't open <путь_до_Вашего_bit>: no such file or directory

Рис. 29. Скриншот терминала Git Bash, когда не получается найти system_top.bit
Рис. 29. Скриншот терминала Git Bash, когда не получается найти system_top.bit

Это не страшно, просто надо переименовать Ваш <название_Вашего>.bit в system_top.bit и запустить make run ещё раз. И на этот раз синий светодиод на отладочной плате вспыхнет ярким синим светом, а в COM-порт будет выведено заветное ad9361_init : AD936x Rev 2 successfully initialized. Но информация в терминале не вся.

Рис. 30. Скриншот COM-терминала с отладочной информацией от микросхемы ad936x.
Рис. 30. Скриншот COM-терминала с отладочной информацией от микросхемы ad936x.

Не вся, потому что при запуске одного только примера DMA_EXAMPLE в терминал должно быть напечатано DAC_DMA_EXAMPLE: address=0x161030 samples=65536 channels=4 bits=16. Это видно в исходном коде на строке 812

printf("DAC_DMA_EXAMPLE: address=%#lx samples=%lu channels=%u bits=%lu\n",
	       (uintptr_t)adc_buffer, NO_OS_ARRAY_SIZE(adc_buffer), rx_adc_init.num_channels,
	       8 * sizeof(adc_buffer[0]));
Рис. 31. Скриншот COM-терминала с информацией о выполненном примере TDD_SWITCH_STATE_EXAMPLE.
Рис. 31. Скриншот COM-терминала с информацией о выполненном примере TDD_SWITCH_STATE_EXAMPLE.

Я это заметил ещё когда только начинал ��аниматься программированием этих микросхем несколько лет назад. Отлаживаться без IDE мне показалось не очень удобно, и я перешёл в Vitis. С тех пор я так и не запускал сборку приложений в Git Bash терминале. И этот баг не искал, скорее всего, надо ковырять makefile. Может быть, кто-нибудь это сделает и поделится в комментариях.

Давайте соберём это приложение в Vitis. Начинается всё точно так же, как и Hello World, но создать надо empty приложение.

Рис. 32. Процесс  создания Empty приложения.
Рис. 32. Процесс создания Empty приложения.

А дальше необходимо добавить все необходимые исходные файлы. Они доступны в Github https://github.com/NSV47/for_article_11_on_Habr.git

Я добавил исходные файлы, которыми сам много пользовался в Github. Но их лучше перепроверить. Потому что где-то выводится лишняя отладочная информация.

Рис. 33. Импорт исходников в Vitis.
Рис. 33. Импорт исходников в Vitis.
Рис. 34. Импорт исходников в Vitis.
Рис. 34. Импорт исходников в Vitis.
Рис. 35. Импорт исходников в Vitis.
Рис. 35. Импорт исходников в Vitis.
Рис. 36. Импорт исходников в Vitis.
Рис. 36. Импорт исходников в Vitis.

После добавления необходимо выполнить Build, но IDE не очень нравятся пути, поэтому их необходимо переписывать.

Рис. 37. Ошибки из-за путей.
Рис. 37. Ошибки из-за путей.
Рис. 38. Ошибки из-за путей.
Рис. 38. Ошибки из-за путей.

Когда все пути исправлены, всё завершается без ошибок.

Рис. 39. Успешная сборка и компиляция.
Рис. 39. Успешная сборка и компиляция.

Вот теперь, если включить все необходимые примеры, о которых мы говорили раньше, в терминал будут печататься значения отсчётов по необходимому каналу.

Рис. 40. Уровень сигнала при выключенном генераторе синуса.
Рис. 40. Уровень сигнала при выключенном генераторе синуса.
Рис. 40. Уровень сигнала при включённом генераторе синуса на мощности 0 dBm.
Рис. 40. Уровень сигнала при включённом генераторе синуса на мощности 0 dBm.

Чтобы построить график из отсчётов можно их скопировать в файл и воспользоваться очень простым скриптом на Python.

import numpy as np
from matplotlib import pyplot as plt

# Читаем данные из файла
with open('data2.txt', 'r') as f:
    data = [int(line.strip()) for line in f.readlines()]

# Рисуем график
plt.figure(figsize=(12, 6))
plt.plot(data, 'b-', linewidth=0.5)
plt.xlabel('Отсчеты')
plt.ylabel('Значение')
plt.title('Данные из data1.txt')
plt.grid(True, alpha=0.3)
plt.axhline(y=0, color='r', linestyle='--', alpha=0.5)
plt.show()
Рис. 41. Какой-то шум на 2.4 ГГц при выключенном генераторе.
Рис. 41. Какой-то шум на 2.4 ГГц при выключенном генераторе.
Рис. 42. Синус от генератора на 2.4012 ГГц.
Рис. 42. Синус от генератора на 2.4012 ГГц.
Рис. 43. Синус от генератора на 2.4006 ГГц.
Рис. 43. Синус от генератора на 2.4006 ГГц.

Подведём итоги. Что же это всё значит? А это значит, что микросхема получила по SPI от ARM-ядра, которое мы с Вами успешно запрограммировали в свои регистры необходимые данные для инициализации и работы в режимах приёма и передачи. Встроенный конечный автомат управляет микросхемой по SPI и переводит её поочерёдно в режимы приёма и передачи. Ядро axi_ad9361 инициализировано и отдаёт данные, которые мы с Вами при данной реализации можем считать через DMA и напечатать. Про ad9361 спето очень много хвалебных песен, это микросхема заслужившая уважение, но порог вхождения без изучения примеров остаётся сравнительно высоким.

Меня на протяжении длительного времени преследовали ошибки инициализации ядра axi_ad9361. В структуре AD9361_InitParam есть параметр digital_interface_tune_skip_mode, который при определённом HDL дизайне необходимо активировать. Причём даже при эталонном дизайне можно наткнуться на неожиданные ошибки, например, при неверной настройке Digital Interface Control, в терминал напечатается сообщение под спойлером ниже.

Возможная ошибка
cf-ad9361-lpc: Successfully initialized (61445617 Hz)
SAMPL CLK: 30720000 tuning: RX
  0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:
0:# # # # # # # # # # # # # # # #
1:# # # # # # # # # # # # # # # #
ad9361_dig_tune_delay: Tuning RX FAILED!
ad9361_init : AD936x initialization error

И первое, что захочется сделать — это пропустить настройку цифрового интерфейса. Тогда даже пройдёт сообщение об инициализации микросхемы и HDL ядер, но это прямая дорога в тупик при разработке под ad9361. Ошибки при настройке цифрового интерфейса прямо указывают на проблемы передачи информации, и игнорировать их нельзя!

Эта статья — капля в океане регистров и моря возможностей ad9361. И если Вам будет интересно, мы могли бы поговорить в следующих статьях об использовании этой микросхемы для передачи различных данных, будь то символы для передачи сообщений или даже простая передача видеосигнала, используя bare-metal приложение.

Поэтому, пожалуйста, не стесняйтесь задавать свои вопросы в комментариях. Даже если я не смогу на них ответить, может быть, это увидят люди, которые более глубоко погрузились в использование этой микросхемы.

Спасибо.

С. Н.