STM32 с нуля. Использование DMA и USART.

Пришло время рассказать об одной потрясающей штуке под названием DMA(ПДП) – то есть прямой доступ к памяти (Direct Memory Access). Поясню что же это такое.

В двух словах – прямой доступ к памяти позволяет перемещать данные без(!) участия центрального процессора. То есть процессор работает себе преспокойненько, не отвлекается ни на что, а DMA в этот момент может пересылать огромные массивы данных, например, в USART. Думаю сразу понятно насколько это полезно, ведь процессору теперь не надо отвлекаться от основной полезной работы.

А в контроллерах STM32F10x даже не один, а два контроллера прямого доступа к памяти! И, соответственно, у каждого несколько каналов (у DMA1 – 7, а у DMA2 – 5). Вот как выглядит разделение каналов между периферийными устройствами:

Список каналов DMA1

Список каналов DMA2

Я когда то упорно пытался подключить USART не к тому каналу, к которому нужно, и совершенно не втыкал почему не работает. С тех пор очень люблю эти картинки )

Давайте-ка теперь рассмотрим процесс настройки в STM32 DMA. Открываем файлы stm32f10x_dma.h и stm32f10x_dma.c из SPL. В первом находим структуру:

typedef struct
{
  uint32_t DMA_PeripheralBaseAddr; 
  uint32_t DMA_MemoryBaseAddr;     
  uint32_t DMA_DIR;
  uint32_t DMA_BufferSize;
  uint32_t DMA_PeripheralInc;
  uint32_t DMA_MemoryInc;          
  uint32_t DMA_PeripheralDataSize; 
  uint32_t DMA_MemoryDataSize;     
  uint32_t DMA_Mode;               
  uint32_t DMA_Priority;           
  uint32_t DMA_M2M;                
}DMA_InitTypeDef;

В самом файле все довольно подробно откомментировано, правда, на английском. Итак:
uint32_t DMA_PeripheralBaseAddr – сюда мы должны записать адрес периферийного устройства, которое будет участвовать в обмене информацией
uint32_t DMA_MemoryBaseAddr – а сюда записываем адрес области памяти, где лежат данные (ну или куда их надо положить, в зависимости от направления обмена)
uint32_t DMA_DIR – тут устанавливаем, является ли периферия источником или местом назначения
uint32_t DMA_BufferSize – размер буфера для хранения данных
uint32_t DMA_PeripheralInc,
uint32_t DMA_MemoryInc – тут задаем надо ли инкрементировать указатели на данные в периферии и в памяти соответственно
Остановимся на этом поподробнее. Допустим, мы забираем данные из регистра данных АЦП и пишем их в массив в памяти. Так как мы хотим записывать результат преобразования в разные элементы массива, то необходимо инкрементировать указатель, следовательно DMA_MemoryInc = Enable. В то же время регистр данных АЦП у нас один и он никуда не перемещается, а что это значит? Правильно, uint32_t DMA_PeripheralInc = Disable )

Идем дальше:
DMA_PeripheralDataSize – размер единицы данных в периферии, аналогично – DMA_MemoryDataSize
Возможные значения этих полей:

DMA_PeripheralDataSize_Byte
DMA_PeripheralDataSize_HalfWord
DMA_PeripheralDataSize_Word
DMA_MemoryDataSize_Byte
DMA_MemoryDataSize_HalfWord
DMA_MemoryDataSize_Word

DMA_Mode – режим работы канала ПДП
DMA_Priority – приоритет для канала ПДП
DMA_M2M – используем ли передачу память->память

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

Пришло время немного поиграться ) Будем писать пример. Давайте объявим массив с данными в памяти и перекинем их с помощью прямого доступа к памяти в регистр данных USART, откуда они и вылетят во внешний мир.

/*******************************************************************/
#define BAUDRATE		9600
#define DMA_BUFFER_SIZE		16
 
/*******************************************************************/
GPIO_InitTypeDef port;
ADC_InitTypeDef adc;
USART_InitTypeDef usart;
uint16_t inputData;
uint16_t outputData;
DMA_InitTypeDef dma;
uint8_t dataBuffer[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
 
/*******************************************************************/

dataBuffer – массив с данными, которые надо отправить по USART’у. Не самый информативный набор данных в этом примере, но для тестирования покатит )
Инициализация:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	
 
DMA_StructInit(&dma);
dma.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
dma.DMA_MemoryBaseAddr = (uint32_t)&dataBuffer[0];
dma.DMA_DIR = DMA_DIR_PeripheralDST;
dma.DMA_BufferSize = DMA_BUFFER_SIZE;
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_Init(DMA1_Channel4, &dma);	
 
GPIO_StructInit(&port);
port.GPIO_Mode = GPIO_Mode_AF_PP;
port.GPIO_Pin = GPIO_Pin_9;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &port);
 
port.GPIO_Mode = GPIO_Mode_AF_PP;
port.GPIO_Pin = GPIO_Pin_10;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &port);
 
USART_StructInit(&usart);
usart.USART_BaudRate = BAUDRATE;
USART_Init(USART1, &usart);

Рассматриваем отдельно то, что касается DMA:

dma.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
это адрес регистра данных USART

dma.DMA_MemoryBaseAddr = (uint32_t)&dataBuffer[0];
аналогично ) адрес нулевого элемента массива

dma.DMA_DIR = DMA_DIR_PeripheralDST;
шлем в периферию, а не из нее

dma.DMA_BufferSize = DMA_BUFFER_SIZE;
буфер – 16 байт (#define DMA_BUFFER_SIZE 16)

dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
в периферии не инкрементируем, в памяти – инкрементируем, прям как в примере про АЦП чуть выше

dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
шлем байтами

DMA_Init(DMA1_Channel4, &dma);
Идем в таблицу с каналами – USART1_TX – 4 канал, все верно =)

int main()
{
    __enable_irq();
    initAll();
    USART_Cmd(USART1, ENABLE);
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    DMA_Cmd(DMA1_Channel4, ENABLE);
    while(1)
    {
 	__NOP();
    }
}

Тут все понятно:

USART_Cmd(USART1, ENABLE);
DMA_Cmd(DMA1_Channel4, ENABLE);
Включаем USART и прямой доступ к памяти DMA

USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
Активируем передачу в последовательный порт по запросу DMA

while(1)
{
    __NOP();
}

Тут могла быть ваша программа 😉 Процессор свободен.

Запускаем программу в отладчике:

DMA пример
Данные пришли туда, куда и требовалось =)

На этом, собственно, все, с прямым доступом к памяти (DMA) мы разобрались!

Понравилась статья? Поделись с друзьями!

STM32 с нуля. Использование DMA и USART.: 46 комментариев
  1. Т.е как я понимаю, у нас теперь процессор может не только одну инструкцию в один такт выполнять? Некое подобие ПЛИС что ли получается?

  2. Спасибо) Очень хорошая статья) Продолжайте в том же духе и мы с вами продолжать будем)) Ещё раз спасибо)

  3. Помогите плииз, не могу уйти в прерывание ни по одному событию, хотя флаги выставляются HTIF1 TCIF. Проект для STM32L-Discovery

    #include «stm32l1xx.h»
    #include «stm32l1xx_gpio.h»

    #define ADC1_DR_ADDRESS 0x40012458
    #define MEMORY_ARRAY_ADDRESS 0x20000000;

    #define Led_Port GPIOB
    #define Led_Blue GPIO_Pin_6
    #define Led_Green GPIO_Pin_7
    #define Led_High(a,b) a -> BSRRL = b
    #define Led_Low(a,b) a -> BSRRH = b

    void SetNVIC(void);
    void adc_init(void);
    void dma_init(void);
    void InitUART(void);
    void DMA1_Channel1_IRQHandler(void);
    void Delay(uint32_t time);

    int main()
    {
    SetNVIC();
    adc_init();
    dma_init();
    InitUART();
    ADC1->CR2 |= ADC_CR2_SWSTART;
    DMA1_Channel1->CCR |= DMA_CCR1_EN;
    while(1)
    {
    Led_High(Led_Port,Led_Blue);
    Delay(0x1FFFF);
    Led_Low(Led_Port,Led_Blue);
    Delay(0x1FFFF);
    Led_High(Led_Port,Led_Green);
    Delay(0x1FFFF);
    Led_Low(Led_Port,Led_Green);
    Delay(0x1FFFF);
    }
    }

    void adc_init(void)
    {

    RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;

    GPIOA->MODER |= GPIO_MODER_MODER15;
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5;

    GPIOB->MODER |= GPIO_MODER_MODER6_0;
    GPIOB->OTYPER &= ~GPIO_OTYPER_OT_6;
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6;

    GPIOB->MODER |= GPIO_MODER_MODER7_0;
    GPIOB->OTYPER &= ~GPIO_OTYPER_OT_7;
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7;

    RCC->CR |= RCC_CR_HSION;
    while(!(RCC->CR&RCC_CR_HSIRDY));

    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    ADC1->SQR1 &= ~ADC_SQR1_L;
    ADC1->SQR5 |= (ADC_SQR5_SQ1_0 | ADC_SQR5_SQ1_2);
    ADC1->CR1 |= ADC_CR1_SCAN;
    ADC1->CR2 |= ADC_CR2_CONT;
    ADC1->CR2 |= ADC_CR2_DELS_0;
    ADC1->CR1 &= ~ADC_CR1_RES;
    ADC1->CR2 &= ~ADC_CR2_ALIGN;
    ADC1->CR2 |= ADC_CR2_DMA;
    ADC1->CR2 |= ADC_CR2_ADON;

    while(!(ADC1->SR&ADC_SR_ADONS));
    }

    void dma_init(void)
    {
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;
    DMA1_Channel1->CPAR |= ADC1_DR_ADDRESS;
    DMA1_Channel1->CMAR |= MEMORY_ARRAY_ADDRESS;
    DMA1_Channel1->CCR &= ~DMA_CCR1_DIR;
    DMA1_Channel1->CNDTR = 10;
    DMA1_Channel1->CCR &= ~DMA_CCR1_PINC;
    DMA1_Channel1->CCR |= DMA_CCR1_MINC;
    DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0;
    DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0;
    DMA1_Channel1->CCR |= DMA_CCR1_PL;
    DMA1_Channel1->CCR |= DMA_CCR1_TCIE;
    DMA1_Channel1->CCR |= DMA_CCR1_HTIE;
    DMA1->IFCR |= DMA_IFCR_CGIF1;
    }

    void SetNVIC(void)
    {
    NVIC_EnableIRQ(DMA2_Channel1_IRQn);
    NVIC_SetPriority(DMA2_Channel1_IRQn,1);
    }

    void InitUART()
    {
    RCC->CR |= RCC_CR_HSION;
    while(!(RCC_CR_HSIRDY));
    RCC->CFGR |= RCC_CFGR_SW_HSI;
    RCC->CR &= ~RCC_CR_MSION;

    RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
    GPIOA->MODER |= GPIO_MODER_MODER9_1;
    GPIOA->OTYPER &= ~GPIO_OTYPER_OT_9;
    GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR9);
    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;

    GPIOA->AFR[1] |= (0x7<APB2ENR |= RCC_APB2ENR_USART1EN;
    USART1->CR1 |= USART_CR1_UE;
    USART1->CR1 &= ~USART_CR1_M;
    USART1->CR2 &= ~USART_CR2_STOP;
    USART1->BRR = 0x683;
    USART1->CR1 |= USART_CR1_TE;
    }

    void Delay(uint32_t time)
    {
    while(time>>0)
    {
    —time;
    }
    }
    void DMA1_Channel1_IRQHandler(void)
    {
    if (DMA1->ISR & DMA_ISR_HTIF1)
    {
    Led_High(Led_Port,Led_Blue);
    Delay(0x1FFFF);
    Led_Low(Led_Port,Led_Blue);
    Delay(0x1FFFF);
    Led_High(Led_Port,Led_Green);
    Delay(0x1FFFF);
    Led_Low(Led_Port,Led_Green);
    Delay(0x1FFFF);
    }
    if (DMA1->ISR & DMA_ISR_TCIF1)
    {
    Led_High(Led_Port,Led_Green);
    Delay(0x1FFFF);
    Led_Low(Led_Port,Led_Green);
    Delay(0x1FFFF);
    }
    }

  4. А если нужно периодически отсылать через USART посылки различной длины? Каждый раз инициализировать порт и канал DMA по-новому? Или можно как-то указать новую длину данных в буфере и снова инициализировать передачу?

  5. Снимаю шапку, в благодарность за то что Вы делаете.
    Все просто и почти понятно. Вопрос: В приведенном выше примере, зачем вот это ADC_InitTypeDef adc;
    И для чего включили прерывания ? __enable_irq();

  6. Почему не происходит overrun? Где здесь ожидание завеошения передачи байта по usart?

  7. Спасибо за доходчивую статью! Модуль DMA в связке с АЦП заносил в память одно значение, а остальные ячейки массива заполнял нулями. Помогло
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable у меня DMA модуль заносил в память

  8. В прямом. Из datasheet LPC1754 (NXP):
    «GPDMA supports the SSP, I2S, UART, A/D Converter, and D/A Converter peripherals. DMA can also be triggered by a timer match condition. Memory-to-memory transfers and transfers to or from GPIO are also supported.»
    В STM32 я такого не увидел.

    • Не смог придумать никакой ситуации, в которой может понадобиться что-то отправлять по dma в gpio.

  9. Ну, это Вы зря. Например многие LCD TFT небольших диагоналей имеют 8-16-ти битную шину. Есть два пути: брать LCD без контроллера и цеплять к нему МК со встроенным драйвером с кучей ног (как правило, от 144) или менее дорогой плюс МК с возможностью отправлять данные из памяти в порт.

  10. облазил оч. много сайтов.. и ни одного
    рабочего примера под stm32f10x
    если вас не затруднит. буду очень благодарен

  11. я где то делаю ошибку. и чегото недопанимаю.
    поправьте если что..
    1 настраиваем пины для уарта
    2 активируем уарт
    3 настраиваем дма из памяти в уарт с длиной передачи указанной нами.
    передача один раз.
    3 активируем дма.
    4 разрешаем прерывания для дма..

    1 вопрос а какой командой передача то осуществляется.
    2 для передачи данных второй раз что настраивать в дма.
    3 какие флаги надо скидывать. и когда.

    п.с. передо мной стоит задача получать данные по одному протоколу
    передавать данные в другой уарт по другому. может можно решить по другому как то..
    спс

    • Если надо всего лишь данные преобразовать и отправить, и процессор в принципе ничем не грузится, то особого смысла использовать DMA нет

  12. нашел следующее.
    Чтобы включить DMA набираем:

    DMA1_Channel4->CCR |= DMA_CCR1_EN;

    Если же вы хотите перезапустить DMA, то придётся несколько усложнить функцию:
    DMA1_Channel4->CCR &= (uint16_t)(~DMA_CCR1_EN);//Выключаем DMA.
    DMA1_Channel4->CNDTR = sizeof(text);//Устанавливаем размер передаваемого буфера заново.
    DMA1_Channel4->CCR |= DMA_CCR1_EN;//Включаем DMA

    После этих строк DMA, будет работать и в конце выдаст прерывание вида:

    void DMA1_Channel4_IRQHandler(void)
    {
    DMA1->IFCR |= DMA_ISR_TCIF4;//очистить флаг окончания обмена.
    }

    завтра буду пробовать

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

    Текст внутри спойлера

  14. dma.c(23): error: #79: expected a type specifier
    dma.c(23): error: #757: constant «ENABLE» is not a type name
    dma.c(23): warning: #77-D: this declaration has no storage class or type specifier
    dma.c(23): error: #147: declaration is incompatible with «void RCC_APB2PeriphClockCmd(uint32_t, FunctionalState)» (declared at line 693 of «.\STM32_SPL\STM32_SPL\inc\stm32f10x_rcc.h»)
    dma.c(24): error: #79: expected a type specifier
    dma.c(24): error: #757: constant «ENABLE» is not a type name
    dma.c(24): warning: #77-D: this declaration has no storage class or type specifier
    dma.c(25): error: #79: expected a type specifier
    dma.c(25): error: #757: constant «ENABLE» is not a type name
    dma.c(25): warning: #77-D: this declaration has no storage class or type specifier
    dma.c(25): error: #147: declaration is incompatible with «void RCC_AHBPeriphClockCmd(uint32_t, FunctionalState)» (declared at line 692 of «.\STM32_SPL\STM32_SPL\inc\stm32f10x_rcc.h»)
    dma.c(27): error: #79: expected a type specifier
    dma.c(27): warning: #77-D: this declaration has no storage class or type specifier
    dma.c(27): error: #147: declaration is incompatible with «void DMA_StructInit(DMA_InitTypeDef *)» (declared at line 412 of «.\STM32_SPL\STM32_SPL\inc\stm32f10x_dma.h»)
    dma.c(28): error: #77-D: this declaration has no storage class or type specifier
    dma.c(28): error: #147: declaration is incompatible with «DMA_InitTypeDef dma» (declared at line 17)
    dma.c(28): error: #65: expected a «;»
    dma.c(29): error: #77-D: this declaration has no storage class or type specifier
    dma.c(29): error: #65: expected a «;»
    dma.c(30): error: #77-D: this declaration has no storage class or type specifier
    dma.c(30): error: #65: expected a «;»
    dma.c(31): error: #77-D: this declaration has no storage class or type specifier
    dma.c(31): error: #65: expected a «;»
    dma.c(32): error: #77-D: this declaration has no storage class or type specifier
    dma.c(32): error: #65: expected a «;»
    dma.c(33): error: #77-D: this declaration has no storage class or type specifier
    dma.c(33): error: #65: expected a «;»
    dma.c(34): error: #77-D: this declaration has no storage class or type specifier
    dma.c(34): error: #65: expected a «;»
    dma.c(35): error: #77-D: this declaration has no storage class or type specifier
    dma.c(35): error: #65: expected a «;»
    dma.c(36): error: #79: expected a type specifier
    dma.c(36): error: #79: expected a type specifier
    dma.c(36): warning: #77-D: this declaration has no storage class or type specifier
    dma.c(36): error: #147: declaration is incompatible with «void DMA_Init(DMA_Channel_TypeDef *, DMA_InitTypeDef *)» (declared at line 411 of «.\STM32_SPL\STM32_SPL\inc\stm32f10x_dma.h»)

    Я так понимаю Кейл не может найти какие-то определения в файлах заголовках или я неправильно код собрал(

  15. нет, в четвертом, с большей частью разобрался, осталось только вот

    dma.c(70): warning: #128-D: loop is not reachable
    linking…
    .
    \usart_dma.axf: Error: L6218E: Undefined symbol assert_param (referred from stm32f10x_dma.o).

    .\usart_dma.axf» — 1 Errors, 1 Warning(s).

  16. Вы правы, я пропустил USE_STDPERIPH_DRIVER, все ошибок больше нет, пример ещё поковыряю, весьма не все понятно, спасибо большое.

    • /** @defgroup APB2_peripheral
      * @{
      */

      #define RCC_APB2Periph_AFIO ((uint32_t)0x00000001)
      #define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004)
      #define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)
      #define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010)

  17. Добрый день. У меня возник вот какой вопрос. Я хочу принимать данные с UART через DMA . Настраивал проект в MXCube (использую HAL ). Мне надо что бы прерывание void DMA1_Channel5_IRQHandler(void) вызывалось только после окончания передачи. Сейчас оно вызывается по половине и по окончанию передачи. Не нашел как и где это выставляется в кубе, ну или подскажите в каком месте ручками подправить.

    • Добрый день!

      По идее можно в функции HAL_DMA_Start_IT() в файле stm32f4xx_hal_dma (для STM32F4 к примеру) закомментировать __HAL_DMA_ENABLE_IT(hdma, DMA_IT_HT);

  18. пробовал комментировать строку
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt; в HAL_UART_Receive_DMA — не помогло.
    решил вопрос так:
    запустил выполнение в функции void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle).
    в принципе заработало. У меня идет обмен данными туда-сюда и периодически зависает прием, хотя данные поступают. Сейчас буду осваивать как просмотреть регистры, что бы увидеть на чем затыкается.

  19. Добрый день
    .
    По-поводу последнего комментария есть такой вопрос:
    Как организовать работу ПДП с помощью библиотеки HAL в режиме двойной буферизации. Задача — принять данные от периферии по I2C в буфер, обработать его и передать по SPI, а во время передачи записать данные во второй буфер.

    Как не пытался настроить в Cube, так и не удалось реализовать двойную буферизацию. Режим circular отказывается работать, а бит DBM, почему-то сбрасывается в функции стандартного обработчика.

  20. Доброго времени суток!
    У меня такой вопрос: допустим у меня есть огромный массив данных, который я хочу передать(например в uart), используя dma. При этом, идет постоянный расчет этих данных. Может ли получится так, что во время передачи данных, некоторые элементы массива перерасчетаются, и попадут в конечный буффер не со своей очередью. Если да, то как этого избежать, может быть есть встроенные механизмы, или все таки придется самому колхозить организацию доступа.? Кто не понял объясню на примере огромной светодиодной ленты . Например, в один момент у меня происходит дикий расчет, и вся лента должна загорется красным цветом, данные о цвете ленты мы передаем dma, dma что-то сам по себе делает, в это время мы опять проваливаемя в расчет цвета ленты, на этот раз лента должна окрасится в синий цвет, расчет по сути дела уже произошел, но данные о первом состоянии ленты еще не передались, но уже изменились, т.е. на выходе мы получим абрукадабру. Вот как грамотно действовать в таких случаях?

    • Просто новую запись в буфер производить только после отправки текущих значений. Иначе какой смысл, если половина данных теряется.

      • Так тогда смысл использования dma теряется, т.е. Пока данные не передадутся процессор будет курить, а хотелось бы чтобы и вычисления производились и передача данных была. кроме использования еще одного буфера мне в голову не приходит т.е. Пока происходит отправка данных производим преобразование со вторым буфером, как только передача произошла, передаем второй буфер, а с первым вычисляем, и.т.д.

        • Если данных не очень много, то, конечно, второй буфер надо добавить, хорошее решение. Правда смысл имеет в том случае, если вычисления тоже выполняются соизмеримое с передачей время. Если к примеру передача — 100 мс, а вычисления 1 мс, то использование двух буферов особо выигрыша не даст. С двумя буферами будет:
          — вычисления 1 (1 мс)
          — начало отправки 1
          — вычисления 2 (1 мс)
          И все — процессор 99 процентов времени свободен.

          • Ок, спасибо, за инфу! Просто только начинаю осваивать arm МК, думал что есть какие-нибудь хитрые механизмы для обработки таких ситуаций, а в инете в основном примеры как передать данные, про целосность и разгроничении доступа к передаваемым данным пока ничего не нашел..

  21. Здраствуйте,
    У меня такая задача:
    Принимаю масив даних с микрофона, каждую мили секунду заполняю буфер, обрабативаю даниые и тут надо их послат на SDIO и записат на карту памяти.
    для етова хачу сделот:
    2 буфера , один накапливаыет и пишет , пока первый пишет накапливаю второй.
    вопроса 2 :
    1. если предават через ДМА заполниный буфер то протсесор может далше обрабативат поралельно не мешая друг другу?
    2. Как склеет SDIO и DMA ? пробивала и через CUBE и через примери не получаытся чтото… может ест опыт работи или пример на ету тему?

    Спасибо зараниые и прошу прощение за ошибки — написано на транслите.

    • Hi!
      1. Yes, if you use DMA, the MCU is free, so it can be used for another operations.
      2. I tried to use DMA with fatfs and sd-card connected via SDIO and it worked fine… I’ve just generated the project with STM32Cube.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *