Коротко: вы на правильном пути (идея «выталкивать 24 такта и считывать 3 байта через DMA» — рабочая), но реализация «SPI в slave + таймер вручную дергает SCK» — хрупкая и ограничивает скорость. Есть несколько надёжных путей решения — от чисто программных до аппаратных. Ниже перечислю варианты, плюсы/минусы и практические советы, что проверить прямо сейчас.
1) Самое простое и надёжное — сделать MCU SPI в master и читать 24 бита через DMA
- Поведение: по DRDY (EXTI/IRQ) включаете DMA на TX и RX и отправляете 3 «пустых» байта (0xFF/0x00) в SPI->DR, SPI выдаёт 24 такта и одновременно читает 24 бита в буфер DMA.
- Плюсы: полная аппаратная синхронизация SPI+DMA, очень высокая скорость, нет необходимости «изобретать» SCK таймерами.
- Минусы: задержка IRQ — обычно мелкая (µs) и не критична; если требуется нулевой jitter, смотреть аппаратные триггеры (ниже).
- Замечания: для 6 ADC — если ADC допускают совместный SCLK и отдельные CS, можно читать их поочередно одним SPI (или параллельно несколькими SPI). Часто ADS127xx поддерживают общий SCLK и разные CS/CSYNC.
2) Аппаратный запуск DMA без CPU (лучше для максимальной скорости и минимального jitter)
- На F7/H7 есть DMAMUX / периферийные триггеры: можно настроить, чтобы внешнее событие (таймер TRGO/EXTI) запускало DMA-канал, который пишет 3 байта в SPI->DR и одновременно читает. Тогда CPU почти не участвует.
- Плюсы: минимальная латентность/ jitter и высокая частота.
- Минусы: сложнее настройка, зависит от конкретной подсемейства MCU (какие источники триггеров доступны в DMAMUX).
3) Если хочется «таймер генерирует ровно N тактов» — используйте TIM + аппаратную связку таймеров (master/slave), а не repetition counter на каждом таймере
- Идея: один таймер генерирует SCK (TIMx PWM), второй таймер считает импульсы (работает в режиме внешнего счётчика или slave, считает фронты через ITR), при достижении ARR=24 он посылает сигнал (TRGO) который выключает генератор SCK (гейт/сброс). Это реализуемо аппаратно без CPU и не требует repetition counter на каждом таймере.
- Плюсы: аппаратный строго контролируемый Burst из N тактов.
- Минусы: чуть сложнее связать таймеры и правильно настроить внутр. маршрутизацию (ITR/TRGO/Slave mode). Требует чтения даташитов конкретного MCU.
4) Использовать внешний специализированный «генератор пачки тактов» — CPLD/FPGA или маленький контроллер
- Если точность и масштабируемость критичны, взять маленький CPLD (или FPGA) или отдельный маленький MCU/PLD, который по DRDY выдаёт ровно 24 такта SCLK и при этом может сигнализировать MCU о готовности данных.
- Плюсы: максимально надёжно, легко масштабировать на большое количество каналов.
- Минусы: добавляет компонент и немного прошивки/платы.
5) Аппаратные ограничения STM32F7, которые часто виноваты при падении скорости (> ~100—200 кHz)
- Кэш и DMA: на F7 присутствует D‑Cache. Если DMA пишет/читает в/из буфер в памяти, которая кэшируется, данные будут «теряться» или приходить с большим jitter’ом. Решение: размещать DMA-буферы в DTCM (uncached) или правильно делать Clean/Invalidate кэша (SCB_CleanDCache_by_Addr / SCB_Invalidate...). На это 80% похожих проблем с «работает до N, дальше — падает».
- Приоритет DMA и конфликт на шине (AXI): убедитесь в правильных настройках приоритета DMA Stream/Channel, используйте «direct mode» где нужно, отключите лишние прерывания.
- Выделяйте DMA RX/TX потоки с подходящими настроями (8-bit transfers для 24 бит — 3 байта) и избегайте больших ISR-обработок между пачками.
6) Почему SPI‑slave + таймер SCK может падать при высоких скоростях
- Синхронизация между «внешним» SCK (генерируемым таймером) и SPI периферией чувствительна: флаги TXE/RXNE, DMA-запуск и timing SPI internal sampling могут не совпадать идеально. Любая небольшая рассинхронизация даёт пропуски на высоких частотах.
- Использование SPI slave означает, что MCU не контролирует старт тактов, и нужно обеспечить, чтобы SPI был всегда «готов» — сложно при высокой частоте.
Практические рекомендации, что сделать прямо сейчас
1. Попробуйте реализовать чтение через SPI master + DMA (на одну ADS127L01 — три байта per DRDY). Проверьте работает ли это стабильно на 256kHz.
2. Если используете STM32F7: поместите DMA-буфер в DTCM_RAM или корректно обслуживайте кэш. Это часто решает падение при >100kHz.
3. Если важен полностью аппаратный запуск (без IRQ): изучите DMAMUX / hardware triggers на вашем конкретном варианте MCU и настройте DMA старт по EXTI/timer TRGO.
4. Если DRDY у всех АЦП синхронен и SCLK может быть общий — используйте один SPI master и поочередно выбирайте CS. Если DRDY индивидуален — либо 6 последовательных операций одним SPI, либо внешний логический блок/FPGA.
5. Если не хотите копаться в сложных таймерных связях — берите маленький CPLD/FPGA или второй дешевый MCU для генерации точных пачек SCK.
Если хотите, могу:
- показать шаблон настройки SPI master + DMA (RX+TX) + EXTI от DRDY для STM32F7 (пример кода HAL/LL);
- помочь составить аппаратную схему, если объясните как именно подключены 6 ADC (общий SCLK? отдельный DRDY? CS?);
- подсказать конкретные настройки DMA/кэша для вашей точной модели STM32F767.
Подытоживая: направление «hardware-driven пачки тактов + DMA для чтения» верно, но лучше переключиться на SPI-master+DMA или на аппаратный запуск DMA (DMAMUX/TIM TRGO) либо вынести генерацию пачек в отдельное устройство/CPLD. Это устранит проблемы выше 128 кГц и даст стабильность.