Микроконтролери и електроника
http://mcu-bg.com/mcu_site/

STM32F030 DMA питане
http://mcu-bg.com/mcu_site/viewtopic.php?f=3&t=17423
Страница 1 от 1

Автор:  speedblue [ Съб Юни 27, 2020 1:24 am ]
Заглавие:  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 канал да дръпна от буфера и да прехвърля към таймера, ама си е разправия.

Автор:  Zdrav [ Съб Юни 27, 2020 3:56 pm ]
Заглавие:  Re: STM32F030 DMA питане

speedblue написа:
Чета из документацията че уж можело, но не е обяснено достатъчно ясно или аз нещо не мога го разбера.

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

Автор:  miro_atc [ Пон Юни 29, 2020 6:17 pm ]
Заглавие:  Re: STM32F030 DMA питане

По мои спомени пък, в терминологията на ST под "периферия" за ДМА се има предвид животно, което да командва трансфера. Иначе обикновено има една картинка и табличка с всички мастери/роби на шините. Съответно там се вижда какво може и не може да адресира ДМА-то. Трябва да се провери, но по принцип мисля че всички или поне много ДМА можеха да адресират APB шините, където са перифериите.

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

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

Автор:  speedblue [ Сря Юли 01, 2020 5:28 pm ]
Заглавие:  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
}

Автор:  miro_atc [ Сря Юли 01, 2020 7:13 pm ]
Заглавие:  Re: STM32F030 DMA питане

Чудех се как правиш семплинт рейта, щото говореше за ДМА на SPI...
Реално доколкото разбирам имаш един таймер и неговото ДМА пише по SPI и това стартира трансферите. А пък при получаване ДМА на SPI-a пише полученото в PWM-a ?

Автор:  speedblue [ Чет Юли 02, 2020 12:00 am ]
Заглавие:  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

Страница 1 от 1 Часовете са според зоната UTC + 2 часа [ DST ]
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/