Отговори на тема  [ 6 мнения ] 
STM32F030 DMA питане 
Автор Съобщение
Ранг: Ориентиран
Ранг: Ориентиран

Регистриран на: Нед Фев 25, 2007 12:41 am
Мнения: 276
Местоположение: София
Мнение STM32F030 DMA питане
Здравейте,

Някой да има опит със следното...

Правя четене от външна SPI FLASH памет чрез DMA.
т.1 - Един DMA канал обслужва изпращането на байтове от RAM буфер към SPI периферия.
т.2 - Друг DMA канал приемането от SPI периферията към RAM буфер.
До тук е ясно.

Искам това което е прочетено от SPI периферията да го метна в друга периферия, в случая на един таймер в регистъра TIMx->CCR2
Тоест в т.2 DMA-то да осъществи трансфер от периферия към периферия.
Обаче не става.

Чета из документацията че уж можело, но не е обяснено достатъчно ясно или аз нещо не мога го разбера.
В този документ липсва възможност за периферия към периферия.
https://www.st.com/resource/en/applicat ... ronics.pdf

Не че няма да го направя да мине през RAM буфера - и с още един трети DMA канал да дръпна от буфера и да прехвърля към таймера, ама си е разправия.


Съб Юни 27, 2020 12:24 am
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Яну 26, 2005 1:01 pm
Мнения: 1952
Местоположение: Варна
Мнение Re: STM32F030 DMA питане
speedblue написа:
Чета из документацията че уж можело, но не е обяснено достатъчно ясно или аз нещо не мога го разбера.

По скоро четеш пожелателно-оптимистично. От повече от 3 години не съм пипвал STM32, но по някакъв смътен спомен няма възможност за DMA трансфер между периферии с този DMA контролер.
В апликейшън нотата, към която си дал линк е казано в прав текст - само два типа трансфер:
памет <> периферия
памет > памет

_________________
Най-опасният враг на истината и свободата е мнозинството.


Съб Юни 27, 2020 2:56 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог

Регистриран на: Нед Фев 26, 2006 5:52 pm
Мнения: 10368
Местоположение: Добрич
Мнение Re: STM32F030 DMA питане
По мои спомени пък, в терминологията на ST под "периферия" за ДМА се има предвид животно, което да командва трансфера. Иначе обикновено има една картинка и табличка с всички мастери/роби на шините. Съответно там се вижда какво може и не може да адресира ДМА-то. Трябва да се провери, но по принцип мисля че всички или поне много ДМА можеха да адресират APB шините, където са перифериите.

В случая с SPI е малко по-особен, тъй като стартирането на транзакция става с пращане на байт по шината и първия байт някой трябва да го метне. Нормално първо се пуска SPI-a в режим ДМА и после самото ДМА като се пусне тръгва. При всеки изпратен байт обратно се получава един и получаването му задейства пък другия ДМА канал (ако е на ДМА).

С две думи това дето се иска технически не би трябвало да има проблем. Трябва да си бачка. Друг е въпросът какъв е смисъла, щото пак казвам SPI-a се пуска ръчно, от там насетне работи със скоростта с която той може и вади някакви данни дето ще отиват в таймер, който явно трябва да е много по-бърз иначе не виждам смисъл...


Пон Юни 29, 2020 5:17 pm
Профил
Ранг: Ориентиран
Ранг: Ориентиран

Регистриран на: Нед Фев 25, 2007 12:41 am
Мнения: 276
Местоположение: София
Мнение Re: STM32F030 DMA питане
Тръгна най-накрая!
Както Миро каза - няма начин да не може. И аз така четох, но нещо не ми се получаваше. APB шината я дават двупосочна, но нещо в документацията са пре-оптимизирали и няма детайли.
Пак рових рових и ... провидях.

Нещото което спъваше нещата е (TIM15_CCR2 is 16bit register) - този детайл го пропусках упорито.
В случая едното DMA чете от 8 битов регистър и трябва да метне в 16 битов. Преди самия звук да потече се прехвърлят други неща от 8 бита към 8 бита и някак не съм обърнал достатъчно внимание. DMA-то грешка не дава разбира се, но на изхода вместо честота се получава права линия.

:partyman:

Код:
/*
STM32F0 - Peripheral to Peripheral DMA transactions

Reading WAVE file from NAND FLASH and playing on PWM

On every ~62us (16000Hz)
  1. Get 'dummyByte' from RAM
  2. Send it to NAND FLASH
  3. Get respond byte from NAND FLASH
  4. Write it to TIMx_CCR2 register -> PWM on output pin
*/

// SPI send a byte triggered from TIM3 on every ~62us (16000Hz)
SPI1->CR2 &= ~SPI_CR2_TXDMAEN;                          // Disable TX DMA
SPI1->CR2 |= SPI_CR2_RXDMAEN;                           // Enable RX DMA

// TX DMA
DMA1_Channel3->CPAR = (uint32_t)(&(SPI1->DR));          // MEM->PER : Set peripheral address
DMA1_Channel3->CMAR = (uint32_t)(&(dummyByte));         // MEM->PER : Set memory address
DMA1_Channel3->CNDTR = dataLen;                         // Number of data to transfer
DMA1_Channel3->CCR |= DMA_CCR_DIR;                      // Data transfer direction 0:P->M, 1:M->P
DMA1_Channel3->CCR &= ~DMA_CCR_MEM2MEM;                 // Disable memory to memory transfer
DMA1_Channel3->CCR &= ~DMA_CCR_CIRC;                    // Disable circular mode
DMA1_Channel3->CCR &= ~DMA_CCR_PSIZE;                   // Set peripheral size to 8bit
DMA1_Channel3->CCR &= ~DMA_CCR_MSIZE;                   // Set memory size to 8bit
DMA1_Channel3->CCR &= ~DMA_CCR_PINC;                    // Do NOT increment peripheral address
DMA1_Channel3->CCR &= ~DMA_CCR_MINC;                    // Do NOT increment memory address

// RX DMA
DMA1_Channel2->CPAR = (uint32_t)(&(SPI1->DR));          // PER->MEM : Set peripheral address
DMA1_Channel2->CMAR = (uint32_t)(&(TIM15->CCR2));       // PER->MEM : Set memory address
DMA1_Channel2->CNDTR = dataLen;                         // Number of data to transfer
DMA1_Channel2->CCR &= ~DMA_CCR_DIR;                     // Data transfer direction 0:P->M, 1:M->P
DMA1_Channel2->CCR &= ~DMA_CCR_MEM2MEM;                 // Disable memory to memory transfer
DMA1_Channel2->CCR &= ~DMA_CCR_CIRC;                    // Disable circular mode
DMA1_Channel2->CCR &= ~DMA_CCR_PSIZE;                   // Set peripheral size to 8bit
DMA1_Channel2->CCR &= ~DMA_CCR_MSIZE;                   // Set memory size to 8bit
DMA1_Channel2->CCR |= DMA_CCR_MSIZE_0;           // ! ---> Set memory size to 16bit (TIM15_CCR2 is 16bit register)
DMA1_Channel2->CCR &= ~DMA_CCR_PINC;                    // Do NOT increment peripheral address
DMA1_Channel2->CCR &= ~DMA_CCR_MINC;                    // Do NOT increment memory address


void Timer_SoundSampling_Init() {
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;                 // Enable clock (f_PCLK=24MHz)
    TIM3->CR1 |= TIM_CR1_ARPE;                          // Auto-reload preload enable
    TIM3->PSC = 0;                                      // Prescaler = 1, f_TIMx=f_PCLK/(0+1)=24MHz
    TIM3->CNT = 0xFFFF;
    // 24MHz / 44.1kHz  = 544 approx.
    // 24MHz / 22.05kHz = 1088 approx.
    // 24MHz / 16kHz    = 1502 approx.
    TIM3->ARR = 1502;                                   // Set auto-reload value
    TIM3->DIER |= TIM_DIER_UDE;                         // Enable timer update DMA request
    TIM3->CR1 |= TIM_CR1_CEN;                           // Enable timer
}


Сря Юли 01, 2020 4:28 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог

Регистриран на: Нед Фев 26, 2006 5:52 pm
Мнения: 10368
Местоположение: Добрич
Мнение Re: STM32F030 DMA питане
Чудех се как правиш семплинт рейта, щото говореше за ДМА на SPI...
Реално доколкото разбирам имаш един таймер и неговото ДМА пише по SPI и това стартира трансферите. А пък при получаване ДМА на SPI-a пише полученото в PWM-a ?


Сря Юли 01, 2020 6:13 pm
Профил
Ранг: Ориентиран
Ранг: Ориентиран

Регистриран на: Нед Фев 25, 2007 12:41 am
Мнения: 276
Местоположение: София
Мнение Re: STM32F030 DMA питане
Точно така го ползвам.

Ето малко по-подробно.

a. WAVE файла се конвертира към 8bit MONO.
b. По някакъв начин WAVE файла се записва в NAND FLASH чип (в случая с JFlashSPI.exe).
c. SPI модула на процесора се настройва (пинове, алтернативни функции, с каква честота ще говори с чипа и т.н.).
d. TIM15 се настройва за PWM изход

******* WAVE file header

1. Трябва да се прочете 'WAVE file header'. SPI модула се настройва за TX и RX да праща заявка към DMA.
Код:
    SPI1->CR2 |= SPI_CR2_TXDMAEN;                   // TX with DMA - when TXE (TXEmpty) flag is set
    SPI1->CR2 |= SPI_CR2_RXDMAEN;                   // RX with DMA - when RXNE (RXNotEmpty) flag is set
   
    SPI модула все още не се разрешава !

-> При празен SPI TX буфер ще извиква DMA-то да му даде байт, прочетен от някъде си - това е DMA1_Channel3
-> При получен байт в SPI RX буфера ще извиква DMA-то да запише този байт някъде си - това е DMA1_Channel2

2. DMA1_Channel3 се настройва да сочи към правилни адреси за Memory=RAM_txBuff и Peripheral=&(SPI1->DR), указва се посоката на данни и т.н.
3. DMA1_Channel2 се настройва да сочи към правилни адреси за Memory=RAM_rxBuff и Peripheral=&(SPI1->DR), указва се посоката на данни и т.н.
4. Посочва се брой байтове за преточване
5. Разрешава се DMA-то
6. Разрешава се SPI
-> В този момент SPI модула вдига флага TXE (TXEmpty) и DMA1_Channel3 взима байт от RAM_txBuff и го записва в SPI1->DR
-> Това предизвиква физическо изпращане на този байт към NAND FLASH чипа
-> Той отговоря на свой ред през MISO пина
-> И когато се получи целия байт в SPI1->DR вдига флага RXNE (RXNotEmpty) предизвиква заявка по DMA1_Channel2, взима байта и го записва в RAM_rxBuff
-> Когато брояча на байтове се извърти - въртележката спира
7. С това е прочетен 'WAVE file header' на максимално висока скорост според настройките, без участието на потребителски фърмуер.

******* WAVE file play

От хедър файла се гледат някои неща и числа (по желание е).

1. Трябва да се просвири самия WAVE файл. SPI модула се настройва само при RX байт да праща заявка към DMA.
Код:
    SPI1->CR2 &= ~SPI_CR2_TXDMAEN;                  // Изключваме тази заявка -> TX with DMA - when TXE (TXEmpty) flag is set
    SPI1->CR2 |= SPI_CR2_RXDMAEN;                   // RX with DMA - when RXNE (RXNotEmpty) flag is set
   
    SPI модула все още не се разрешава !

-> При празен SPI TX буфер нищо няма да прави
-> При получен байт в SPI RX буфера ще извиква DMA-то да запише този байт някъде си - това е DMA1_Channel2

2. Подготвя се един таймер който да замести изключената SPI TX заявка. В случая на всеки ~62us (16000Hz) да завърта въртележката
-> Този таймер периодично ще задейства точно DMA1_Channel3 което ще предизвиква изпращането на dummyByte към NAND FLASH чипа
-> Той пък ще отговори с полезен байт от WAVE поредицата
Код:
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;                 // Enable clock (f_PCLK=24MHz)
    TIM3->CR1 |= TIM_CR1_ARPE;                          // Auto-reload preload enable
    TIM3->PSC = 0;                                      // Prescaler = 1, f_TIMx=f_PCLK/(0+1)=24MHz
    TIM3->CNT = 0xFFFF;
    // 24MHz / 16kHz    = 1502 approx.
    TIM3->ARR = 1502;                                   // Set auto-reload value
    TIM3->DIER |= TIM_DIER_UDE;                         // Enable timer update DMA request
    TIM3->CR1 &= ~TIM_CR1_CEN;                          // Do NOT Enable timer

3. DMA1_Channel3 се настройва да сочи към правилни адреси за Memory=RAM_dummyByte и Peripheral=&(SPI1->DR), указва се посоката на данни и т.н.
4. DMA1_Channel2 се настройва да сочи към правилни адреси за Memory=&(TIM15->CCR2) и Peripheral=&(SPI1->DR), указва се посоката на данни и т.н.
5. Посочва се брой байтове за преточване
6. Разрешава се DMA-то
7. Разрешава се SPI
8. Разрешава се TIM3
-> Според брояча (първия път е след няколко такта) таймера прави DMA заявка, DMA1_Channel3 взима байт от RAM_dummyByte и го записва в SPI1->DR
-> Това предизвиква физическо изпращане на този байт към NAND FLASH чипа
-> Той отговоря на свой ред през MISO пина
-> И когато се получи целия байт в SPI1->DR вдига флага RXNE (RXNotEmpty) предизвиква заявка по DMA1_Channel2, взима байта и го записва в TIM15->CCR2
-> Когато брояча на байтове се извърти - въртележката спира
9. С това е просвирен целия WAVE файл или фрагмент от него
-> без участието на потребителски фърмуер или някакви междинни RAM буфери
-> процесора е напълно свободен за други дейности през цялото време

******* DMA interrupts

На самото DMA се активират някои прекъсвания за да се извършват необходими действия при приключване на въртележките
Код:
    DMA1_Channel3->CCR |= DMA_CCR_TEIE;          // Transmit Error
    DMA1_Channel3->CCR |= DMA_CCR_TCIE;          // Transmit Complete


Сря Юли 01, 2020 11:00 pm
Профил
Покажи мненията от миналия:  Сортирай по  
Отговори на тема   [ 6 мнения ] 

Кой е на линия

Потребители разглеждащи този форум: 0 регистрирани и 4 госта


Вие не можете да пускате нови теми
Вие не можете да отговаряте на теми
Вие не можете да променяте собственото си мнение
Вие не можете да изтривате собствените си мнения
Вие не можете да прикачвате файл

Търсене:
Иди на:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.
Хостинг и Домейни