Jump to content
    

STM32F4xx. Последовательность нескольких SPI-транзакций без участия CPU.

57 minutes ago, jcxz said:

Не понимаю - чего вы пытаетесь доказать всеми этими намалёванными красными линиями? Тем более теми, на которых показаны шины к AHB-периферии? Где в теме речь шла об AHB-периферии?

Букварь вы может и читали, но похоже - не поняли его....

Успокойтесь уже - вопрос уже решён.

Я предельно спокоен. APB сидит на AHB через мост. И на схеме видно, что у DMA1 нет пересечения с AHB периферии (отсутствует точка) а вторая шина подключена эксклюзивно к APB1. В то время как у DMA2 есть эти пересечения на обоих шинах, даже на выделенной связи с APB2. Это картинка про отношения шин. Всех шин. А вы так и не поняли этого посыла. Печально. То, что вопрос решён я видел, просто хотел дополнить ваш ответ о найденной причине...

Share this post


Link to post
Share on other sites

21 minutes ago, HardWareMan said:

просто хотел дополнить ваш ответ о найденной причине...

Зачем ?

Share this post


Link to post
Share on other sites

49 minutes ago, x893 said:

Зачем ?

Потому что утверждение

Quote

Странно, что говорит об этом только картинка (Figure 23 RM)

не совсем верное. Ну да ладно, всем пофиг же, да?

Share this post


Link to post
Share on other sites

9 minutes ago, HardWareMan said:

Потому что утверждение

Забейте

Share this post


Link to post
Share on other sites

@jcxz, Вы на макетке отлаживаете? Просто интересно, как Вы так лихо вывод таймера заменили.

Вспомнил тут, как сравнительно давно тоже делал похожий алгоритм, но промахнулся с распределением ресурсов и идея не взлетела.

Share this post


Link to post
Share on other sites

Вместо выхода таймера можно использовать запись в GPIO, тогда ограничений по выбору пина для CS нет. Делал так, но без привязки к событиям SPI, все по событиям таймера.

Edited by amaora

Share this post


Link to post
Share on other sites

2 часа назад, Arlleex сказал:

@jcxz, Вы на макетке отлаживаете? Просто интересно, как Вы так лихо вывод таймера заменили.

Это нога PB.0. На неё в F401 выходит и TIM1_CH1N и TIM3_CH2. Поэтому - заменить было не сложно. 

А исходники я стараюсь так писать, чтобы из "#define PIN_RFM_CS  B, 0" по максимуму вычислялись все возможные смещения нужных полей в регистрах типа:

  timrcsU->CCER = (B0 | B1) << CH_IX_TIM_PIN(nTIM_rfmcs, PIN_RFM_CS) * 4 + CH_NEG_TIM_PIN(nTIM_rfmcs, PIN_RFM_CS) * 2;

Здесь и менять ничего не приходится - макросы сами всё пересчитывают и в нужные биты пишут. Даже если бы пришлось ногу менять - должно всё нормально пересчитаться автоматом.

2 часа назад, Arlleex сказал:

Вспомнил тут, как сравнительно давно тоже делал похожий алгоритм, но промахнулся с распределением ресурсов и идея не взлетела.

После последнего моего поста вылезла ещё одна проблема:

Так как запуски DMA-передач, пишущих в SPI_DR, происходят от таймера, и на картинке (в конце) видно начало одного лишнего кадра. Но длина последовательности (общее число SPI-кадров) задана длиной блока DMA. То получается, что генерится один лишний DMA-запрос. Когда блок уже исчерпан. Этот DMA-запрос повисает необслуженным на отключенном DMA-потоке. И если его не выфлушить, вылезает при запуске следующей последовательности и всё ломает. Причём - этот запрос не сбрасывается ничем: ни выкл. DMA-потока, ни выкл.  генератора запросов (таймер). Опять - маты в сторону убогой периферии STM32 и её создателей: они не предусмотрели штатного способа сброса DMA-запросов! Как так можно???:negative:   Или я про него не знаю? Если кто знает - подскажите, плиз.

У XMC4xxx есть спец.регистр, через который можно сбросить ненужные DMA-запросы перед активацией DMA-канала, если они почему-либо остались висеть после предыдущего использования. И процедура сброса описана в документации (она элементарная).

Почему о таком не задумались инженеры STM - не знаю.  :unknw:

Вобщем - пришлось городить ещё выфлушивание зависших DMA-запросов перед запуском новой последовательности кадров. Посредством выполнения фейковой DMA-передачи. Теперь картинка выглядит так:

image.thumb.png.74ad0d6e661011c86822955b1a581c53.png

Здесь 2 последовательности (цепочки) SPI-кадров. Алгоритм запускает 1-ю последовательность кадров. После неё - IRQ и в его ISR - обработка принятых данных из DMA-буфера. Затем - запуск 2-й последовательности. Как видно на картинке - теперь и 2-я последовательность корректная, как 1-я.

Красными стрелками показаны места формирования лишних DMA-запросов. Которые лежат за пределами блоков передаваемых данных. Если их не выфлушить, то следующая последовательность ломается. Но здесь уже всё корректно флушится.

Возможно есть какой-то способ, чтобы вообще исключить этот последний лишний импульс CS? :umnik2:  Нужно проглядеть возможности сцепки таймеров. В STM32 конечно все эти возможности очень убогие (на XMC4xxx куча возможностей сцепок сигналов разных периферийных блоков, в разных комбинациях), но возможно получится что-то придумать и здесь. А может забью и оставлю текущий вариант с выфлушиванием лишнего DMA-запроса.

1 час назад, amaora сказал:

Вместо выхода таймера можно использовать запись в GPIO, тогда ограничений по выбору пина для CS нет.

Наверное можно, но потребуется ещё больше DMA-потоков задействовать. Причём - потоков на контроллере DMA2. А там уже их дефицит. Ведь DMA1 как я понимаю - не имеет доступа к AHB-периферии (GPIO). А генерация CS при помощи таймера, экономит DMA-каналы. Полноценный получается только DMA2. DMA1 - ограниченный.

1 час назад, amaora сказал:

Делал так, но без привязки к событиям SPI, все по событиям таймера.

Считаю это рискованным. Из-за джиттера, возникающего из-за непредсказуемой занятости шины. Потому и делал с обязательной привязкой к событиям SPI.RX.

Share this post


Link to post
Share on other sites

DMA.LIFSR/HIFSR чистите после завершения всех передач? Может (если не чистите) DMA не раздупляется правильно, хз.

И еще. У таймера передерните регистр DIER (запомните, что в нем было, потом сбросьте в 0, потом снова восстановите), потом можете запустить следующий цикл с DMA.

Share this post


Link to post
Share on other sites

16 часов назад, Arlleex сказал:

DMA.LIFSR/HIFSR чистите после завершения всех передач? Может (если не чистите) DMA не раздупляется правильно, хз.

Естественно я их чищу. Только не после, а перед инициализацией для новой передачи.

Что-ж вы меня за совсем чайника считаете? :mda:  С STM (разными) я уже довольно много работал, и DMA использую во всех проектах.

Да и какое отношение эти регистры имеют к DMA-запросам? Это регистры флагов DMA, выставляемых по результатам выполнения передач. Про запросы там флагов нет.

16 часов назад, Arlleex сказал:

И еще. У таймера передерните регистр DIER (запомните, что в нем было, потом сбросьте в 0, потом снова восстановите), потом можете запустить следующий цикл с DMA.

Это я тоже пробовал - не помогает. Да и этот регистр - он отвечает за генерацию новых DMA-запросов. Как я понимаю - после генерации DMA-запроса, он защёлкивается и хранится в триггере. Поэтому сброс источника генерации запроса на него уже не влияет.

В XMC4xxx кстати организовано аналогично - каждый DMA-запрос защёлкивается и хранится в триггере. Но там об этом явно сказано в документации. И описан способ сброса этих триггеров (есть спец.регистр). В STM32F4xx возможно тоже есть какой-то способ сброса этого триггера, но в мануале ничего не нашёл об этом. И ЧатГПТ тоже не знает.  :sad:

В NXP (LPC17xx) тоже запросы защёлкиваются на триггерах. Там тоже приходилось выфлушивать зависшие DMA-запросы подобным же образом. Вот в Tiva, там если не изменяет склероз - обошлись без триггеров, и если причина DMA-запроса снимается до её захвата DMA-каналом, то DMA-запрос теряется.

Share this post


Link to post
Share on other sites

40 минут назад, jcxz сказал:

Это я тоже пробовал - не помогает. Да и этот регистр - он отвечает за генерацию новых DMA-запросов. Как я понимаю - после генерации DMA-запроса, он защёлкивается и хранится в триггере. Поэтому сброс источника генерации запроса на него уже не влияет.

Вот тут чел похожую проблему озвучивал и решал: https://community.st.com/t5/stm32-mcus-products/how-to-clear-pending-dma-request/td-p/551646.

Share this post


Link to post
Share on other sites

17 минут назад, Arlleex сказал:

Вот тут чел похожую проблему озвучивал и решал: https://community.st.com/t5/stm32-mcus-products/how-to-clear-pending-dma-request/td-p/551646.

Ну ок - позже попробую ещё раз пообнулять DIER. Может действительно что-то не так делал.

Share this post


Link to post
Share on other sites

3 часа назад, jcxz сказал:

Естественно я их чищу. Только не после, а перед инициализацией для новой передачи.

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

Касательно проблемы - я ловил ровно то же самое, только с модулем UART. И да, оно полечилось ровно таким же способом, как по ссылке с таймером.

Share this post


Link to post
Share on other sites

18 минут назад, Arlleex сказал:

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

Нет, не чищу. Я же писал. :wink:  Если не верите - вот ISR того DMA-потока:

//Должно быть на том же уровне приоритета, что и IsrSPI.
extern "C" void concat(IsrDMA, nDMASTR_rfmcs_R)()
{
  AtomicXorI(&GPIO[concat(PIOIX_, PORT(PIN_RFM_CS))].MODER, 3u << PIN(PIN_RFM_CS) * 2);
  if (s0dmaU->ISR[nDMASTR_rfmcs_R >> 2 & 1] & (B2 | B3) << (nDMASTR_rfmcs_R & 3) * 6 << (nDMASTR_rfmcs_R & B1) * 2) trap(TRAP_DMA, TRAPR_SPI);
  timrcsU->CR[0] = 0;
  spiU->CR[0] = RFM_SPI_CR0 | B11;
  IntDis(concat(NVIC_DMA, concat(DMASTR, nDMASTR_rfmcs_R)));
  OsFlagSet(&evTask);
  IsrExit();
}

Код рабочий, с его помощью получены осциллограммы выше.

Я в ISR делаю запрет прерываний от данного вектора (в NVIC). И всё. Запрет - 5 строка кода ISR, проверка флагов DMA-потока - 2 строка.

Если найдёте где в этом ISR я чищу флаги DMA - обязуюсь что-нить съесть.... :sarcastic: например этот код. в напечатанном виде :bad:

18 минут назад, Arlleex сказал:

, а вот зачем их чистить перед инициализацией другой передачи - не понятно. Но да ладно.

Всегда перед инитом любой операции предварительно чищу все флаги. Поэтому - зачем чистить после, если чистится перед? Хотя можно конечно в ISR очистить флаги в периферии, а в NVIC - не трогать. Так тоже иногда делаю. Без разницы. Как удобнее, так и делаю.

18 минут назад, Arlleex сказал:

Касательно проблемы - я ловил ровно то же самое, только с модулем UART. И да, оно полечилось ровно таким же способом, как по ссылке с таймером.

Дьявол как известно - кроется в деталях. Посмотрим.

Share this post


Link to post
Share on other sites

20 часов назад, Arlleex сказал:

Вот тут чел похожую проблему озвучивал и решал: https://community.st.com/t5/stm32-mcus-products/how-to-clear-pending-dma-request/td-p/551646.

Да, сброс битов разрешения генерации DMA-запросов - помог. Код:

  timrcsU->DIER = 0;
  RDMB(timrcsU->DIER);
  timrcsU->DIER = B9 << nTIMCH_rfmcs_R | B9 << nTIMCH_rfmcs_F;

сбрасывает DMA-запросы от DIER. (где RDMB - обратное чтение соответствующего регистра)

Странно - я точно пробовал даже полный сброс в дефолт и переинициализацию всех регистров таймера перед новой передачей - не помогало. Возможно было что-то ещё, так как код был в разработке.

Спасибо! Так теперь код старта стал короче на несколько команд (чем в варианте выфлушивания дополнительной DMA-передачей).

 

PS: Для полной ляпоты, осталось только придумать способ избавиться полностью от финального лишнего импульса CS=0. Тогда и очистка лишних DMA-запросов не нужна будет. :smile:

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...