Dma transfer error

I use STM32F407VTG6 controller and try to receive data from SPI using DMA. Then I want to process data on DMA Complete Transfer Interrupt. But when Complete Transfer Interrupt is occurred I see tha...

I use STM32F407VTG6 controller and try to receive data from SPI using DMA. Then I want to process data on DMA Complete Transfer Interrupt. But when Complete Transfer Interrupt is occurred I see that TEIF (transfer error interrupt flag) is set. After this DMA can’t be started. This is part of my code:

static void DmaInit()
{
 DMA_InitTypeDef dma;

 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

 // DMA for Rx
 dma.DMA_Channel = DMA_Channel_3;
 dma.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
 dma.DMA_Memory0BaseAddr = 0; // will be set later
 dma.DMA_DIR = DMA_DIR_PeripheralToMemory;
 dma.DMA_BufferSize = 1; // will be set later
 dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
 dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
 dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
 dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
 dma.DMA_Mode = DMA_Mode_Normal;
 dma.DMA_Priority = DMA_Priority_High;
 dma.DMA_FIFOMode = DMA_FIFOMode_Disable;
 dma.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
 dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
 dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
 DMA_DeInit(DMA2_Stream2);
 DMA_Init(DMA2_Stream2, &dma);

 // Enable DMA Interrupt on complete transfer
 NVIC_EnableIRQ(DMA2_Stream2_IRQn);
 DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE);
}

// It run on external interrupt
static void DmaStart(uint32_t bufferSize, uint32_t* rxBuffer)
{ 
 // Start DMA for reading
 DMA2_Stream2->NDTR = bufferSize;
 DMA2_Stream2->M0AR = (uint32_t)rxBuffer;
 DMA_Cmd(DMA2_Stream2, ENABLE);
}

static void SpiInit()
{
  SPI_InitTypeDef spi;

  // Enable clock for SPI
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

  // SPI settings
  SPI_StructInit(&spi);
  spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  spi.SPI_Mode = SPI_Mode_Master;
  spi.SPI_DataSize = SPI_DataSize_8b;
  spi.SPI_CPOL = SPI_CPOL_Low;
  spi.SPI_CPHA = SPI_CPHA_2Edge;
  spi.SPI_NSS = SPI_NSS_Soft;
  spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
  spi.SPI_FirstBit = SPI_FirstBit_MSB;
  SPI_Init(SPI1, &spi);
  SPI_Cmd(SPI1, ENABLE);

  SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
}

Всем привет. Пишу программу для приема данных по USART через DMA, но пока что как-то не получается. Проблема в том, что как только начинают поступать данные, CR_EN сбрасывается из 1 в 0 и выставляется флаг LISR_TEIF «Stream x transfer error interrupt flag». Если его вручную сбросить, то CR_EN выставляется обратно в 1.

Как с этим бороться?

Для начальной инициализации использую Cube LL.

Настройка USART3 и DMA, функция сгенерированная Кубом.

Скрытый текст

void MX_USART3_UART_Init(void)
{
  LL_USART_InitTypeDef USART_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);

  LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOD);
  /**USART3 GPIO Configuration
  PD8   ------> USART3_TX
  PD9   ------> USART3_RX
  */
  GPIO_InitStruct.Pin = STLK_RX_Pin|STLK_TX_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
  LL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* USART3 DMA Init */

  /* USART3_RX Init */
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_0, LL_DMAMUX1_REQ_USART3_RX);

  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_0, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

  LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_0, LL_DMA_PRIORITY_LOW);

  LL_DMA_SetMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MODE_NORMAL);

  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_0, LL_DMA_PDATAALIGN_BYTE);

  LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_0, LL_DMA_MDATAALIGN_BYTE);

  LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_0);

  /* USART3 interrupt Init */
  NVIC_SetPriority(USART3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(USART3_IRQn);

  USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
  USART_InitStruct.BaudRate = 115200;
  USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
  USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
  USART_InitStruct.Parity = LL_USART_PARITY_NONE;
  USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
  USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
  USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
  LL_USART_Init(USART3, &USART_InitStruct);
  LL_USART_SetTXFIFOThreshold(USART3, LL_USART_FIFOTHRESHOLD_1_8);
  LL_USART_SetRXFIFOThreshold(USART3, LL_USART_FIFOTHRESHOLD_1_8);
  LL_USART_DisableFIFO(USART3);
  LL_USART_ConfigAsyncMode(USART3);

  /* USER CODE BEGIN WKUPType USART3 */

  /* USER CODE END WKUPType USART3 */

  LL_USART_Enable(USART3);

  /* Polling USART3 initialisation */
  while((!(LL_USART_IsActiveFlag_TEACK(USART3))) || (!(LL_USART_IsActiveFlag_REACK(USART3))))
  {
  }

}

В main(), сразу после инициализации, сделаны такие настройки:

  LL_USART_EnableDMAReq_RX(USART3); // DMA enable receiver
  LL_USART_EnableIT_IDLE(USART3); // включение прерывания по событию LINE IDLE
  LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_0);				// 
  while (LL_DMA_IsEnabledStream(DMA1, LL_DMA_STREAM_0));		// отключение стрима, на всякий случай
  LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_0, LL_USART_DMA_GetRegAddr(USART3, LL_USART_DMA_REG_DATA_RECEIVE)); // адрес периферии &USART3->RDR
  LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_0, (uint32_t)gps_data_input); // uint8_t gps_data_input[40] = {0};
  LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, sizeof(gps_data_input));
  LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0); // включение стрима

Осциллограммы. Желтый канал — USART_RX, синий — LINE IDLE, красный — DMA1 CR_EN.

Скрытый текст

photo5238209876356870971.thumb.jpg.08f378ca36a6dda009a8fa5079b49d50.jpgphoto5238209876356870970.thumb.jpg.4301b87ab520f92febd6f813054ed186.jpg


Изменено 8 мая, 2021 пользователем Behram

Code here is configuring DMA2 for transferring 160 samples each from two analog channels from ADC1 to memory.

The ADC,DMA,NVIC etc. seem properly configured but for some reason I am getting transfer errors exactly as the ADC is turned on.

More precisely, the TEIF0 flag for DMA2 and OVR flag for ADC1 is raised exactly after ADC_Cmd in the second snippet is called.

When looking for causes for Transfer errors in RM0090 all it states is:

Transfer error:
the transfer error interrupt flag (TEIFx) is set when:

– A bus error occurs during a DMA read or a write access

– A write access is requested by software on a memory address register in Double
buffer mode whereas the stream is enabled and the current target memory is the
one impacted by the write into the memory address register (refer to
Section 10.3.9: Double buffer mode)

The definition isn’t very informative. A link to a similar issue can be found here: https://www.mikrocontroller.net/topic/389574

is it possible that the issue may be coming from the startup code? The code here was ported from a project that originally uses is compiled on Linux system with GNU makefile. But the same code was successfully compiled and run on the same chip with Keil MDK and all other functionality like LEDs, GPIO ports seems to be running well.

Debugging the code with Keil I also have access to the register values if they’re needed.

Timer2 for external triggering of ADC is configured like so:

static void tim2_config(int fs_divisor)
{
  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;

  /* TIM2 Periph clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  /* Time base configuration */

  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
   TIM_TimeBaseStructure.TIM_Period = fs_divisor - 1;
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  /* TIM2 TRGO selection */

  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

  /* TIM2 enable counter */

  TIM_Cmd(TIM2, ENABLE);
}

The ADC and DMA are then configured like so:

void adc_configure(){
    ADC_InitTypeDef  ADC_init_structure;
    GPIO_InitTypeDef GPIO_initStructre;
    DMA_InitTypeDef  DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // Clock configuration

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1ENR_GPIOAEN,ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

    // Analog pin configuration ADC1->PA1, ADC2->PA2

    GPIO_initStructre.GPIO_Pin =  GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_initStructre.GPIO_Mode = GPIO_Mode_AN;
    GPIO_initStructre.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA,&GPIO_initStructre);

    // ADC structure configuration

    ADC_DeInit();
    ADC_init_structure.ADC_DataAlign = ADC_DataAlign_Left;
    ADC_init_structure.ADC_Resolution = ADC_Resolution_12b;
    ADC_init_structure.ADC_ContinuousConvMode = DISABLE;
    ADC_init_structure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
    ADC_init_structure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
    ADC_init_structure.ADC_NbrOfConversion = 2;
    ADC_init_structure.ADC_ScanConvMode = ENABLE;
    ADC_Init(ADCx,&ADC_init_structure);

    // Select the channel to be read from

    ADC_RegularChannelConfig(ADCx,ADC_Channel_1,1,ADC_SampleTime_144Cycles);
    ADC_RegularChannelConfig(ADCx,ADC_Channel_2,2,ADC_SampleTime_144Cycles);
    //ADC_VBATCmd(ENABLE);

    DMA_DeInit(DMA_STREAMx);
    DMA_InitStructure.DMA_Channel = DMA_CHANNELx;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADCx_DR_ADDRESS;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)adc_buf;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize = ADC_BUF_SZ;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA_STREAMx, &DMA_InitStructure);

    /* Enable DMA request after last transfer (Single-ADC mode) */

    ADC_DMARequestAfterLastTransferCmd(ADCx, ENABLE);

    /* Enable ADC1 DMA */

    ADC_DMACmd(ADCx, ENABLE);

    /* DMA2_Stream0 enable */

    DMA_Cmd(DMA_STREAMx, ENABLE);

    /* Enable DMA Half & Complete interrupts */

    DMA_ITConfig(DMA2_Stream0,DMA_IT_TC | DMA_IT_HT, ENABLE);

    /* Enable the DMA Stream IRQ Channel */

    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // Enable and start ADC conversion

    ADC_Cmd(ADC1,ENABLE);
    ADC_SoftwareStartConv(ADC1);
}

Hi Giovanni,
thanks for your fast response.

Are debug options enabled in chconf.h? those could give an hint about the problem.

I don’t use the Chibi-OS as Kernel. I’m working OS-less. I enabled OSAL_DBG_ENABLE_ASSERTS and OSAL_DBG_ENABLE_CHECKS in osalconf.h, but I didn’t get on with it until now.

I don’t know why CS takes so long, is the code compiled with optimizations? are clocks initialized correctly?

I don’t use code optimizations. SYSCLK is set to 168Mhz, APB1 is clocked with 42Mhz. With the used prescaler of 4, I get a SPI frequency of ~10Mhz.

Note that you should never call spi_lld_ functions directly, use the official interfaces

Yes, sorry for that. I meant function call to spiSelect().

I don’t understand why function call to spi_lld_serve_rx_interrupt() is executed. I don’t want to receive data, I just want to send data via SPI. Do you have an explanation for it? Might a transfer error occur due to timing problems (when I send 4 channels to DAC module instead of 1 each 50us, the transfer interrupt happens more likely)?

RM0453

Addressing AHB peripherals not supporting byte/half-word write transfers

When the DMA controller initiates an AHB byte or half-word write transfer, the data are

duplicated on the unused lanes of the AHB master 32-bit data bus (HWDATA[31:0]).

When the AHB slave peripheral does not support byte or half-word write transfers and does

not generate any error, the DMA controller writes the 32 HWDATA bits as shown in the two

examples below:

To write the half-word 0xABCD, the DMA controller sets the HWDATA bus to

0xABCDABCD with a half-word data size (HSIZE = HalfWord in AHB master bus).

To write the byte 0xAB, the DMA controller sets the HWDATA bus to 0xABABABAB

with a byte data size (HSIZE = Byte in the AHB master bus).

Assuming the AHB/APB bridge is an AHB 32-bit slave peripheral that does not take into

account the HSIZE data, any AHB byte or half-word transfer is changed into a 32-bit APB

transfer as described below:

An AHB byte write transfer of 0xB0 to one of the 0x0, 0x1, 0x2 or 0x3 addresses, is

converted to an APB word write transfer of 0xB0B0B0B0 to the 0x0 address.

An AHB half-word write transfer of 0xB1B0 to the 0x0 or 0x2 addresses, is converted to

an APB word write transfer of 0xB1B0B1B0 to the 0x0 address.

13.4.7

A DMA transfer error is generated when reading from or writing to a reserved address

space. When a DMA transfer error occurs during a DMA read or write access, the faulty

channel x is automatically disabled through a hardware clear of its EN bit in the

corresponding DMA_CCRx register.

The TEIFx bit of the DMA_ISR register is set. An interrupt is then generated if the TEIE bit of

the DMA_CCRx register is set.

The EN bit of the DMA_CCRx register can not be set again by software (channel x re-

activated) until the TEIFx bit of the DMA_ISR register is cleared (by setting the CTEIFx bit of

the DMA_IFCR register).

When the software is notified with a transfer error over a channel which involves a

peripheral, the software has first to stop this peripheral in DMA mode, in order to disable any

pending or future DMA request. Then software may normally reconfigure both DMA and the

peripheral in DMA mode for a new transfer.

Additionally, a security illegal access pulse signal is generated on an illegal non-secure

software access to a secure DMA register. This signal is routed to the secure interrupt

controller.

Direct memory access controller (DMA)

RM0453 Rev 1

465/1461

479

Direct memory access (DMA), или прямой доступ к памяти (ПДП) используется для быстрой передачи данных между памятью и периферийным устройством, памятью и памятью, или между двумя периферийными устройствами без участия процессора. В микроконтроллере STM32F103c8 доступен один контроллер DMA1 с 7-ю каналами. DMA2 присутствует только в микроконтроллерах high-density и XL-density. Предыдущая статья здесь, все статьи цикла можно посмотреть тут: http://dimoon.ru/category/obuchalka/stm32f1.

Содержание

  • Функциональное описание DMA
  • Приоритеты каналов DMA
  • Каналы DMA
  • Выравнивание данных разной разрядности
  • Особенности обращения к периферии AHB/APB
  • Кольцевой режим DMA (Circular mode)
  • Режим «Из памяти в память» (Memory-to-memory mode)
  • Прерывания DMA
  • Ошибки при передаче данных по DMA
  • Каналы DMA и периферия

Функциональное описание DMA

Контроллер DMA использует системную шину совместно с процессорным ядром. Если CPU и DMA обращаются к одной о той же области памяти или одной и той же периферии, то DMA может приостановить доступ CPU к системной шине, но при этом как минимум половина пропускной способности шины резервируется за CPU. Это означает, что даже при интенсивном обмене данными по DMA процессор не зависнет наглухо.

При возникновении определенного события, периферийное устройство отправляет сигнал запроса в контроллер DMA. После этого запускается процесс обмена данными, который состоит из 3-х шагов:

  1. Загрузка данных из регистра периферийного устройства (если направление передачи из периферии в память) или загрузка данных из памяти (если направление передачи из памяти в периферию);
  2. Сохранение загруженных данных в память (если из периферии в память) или в периферийное устройство (если из памяти в периферию);
  3. Уменьшение значения регистра DMA_CNDTRx на единицу. Как только этот регистр станет равен нулю, то передача данных завершится.

Приоритеты каналов DMA

DMA1 в микроконтроллерах STM32F103c8 имеет 7 каналов, причем в конкретный момент времени передача данных может осуществляться только по одному из них. Однако, если активно несколько каналов, то при одновременном возникновении запросов DMA передача будет запущена для того канала, приоритет которого выше. Приведу небольшой пример. Пусть у нас 3-й канал DMA1 настроен на передачу массива данных в SPI1, а 1-й канал на прием от ADC1. Установим приоритет канала 3 больше, чем у канала 1. В этом случае, если запросы от SPI1 и ADC1 возникнут одновременно, то сначала будет обработан запрос от SPI1 (3-й канал), а уже потом от ADC1 (1-й канал). То есть одновременно включать несколько каналов DMA можно, но одновременно вести передачу может только один из них.

Приоритеты можно настраивать программно, всего 4-е градации:

  • Very high priority (Очень высокий приоритет)
  • High priority (Высокий приоритет)
  • Medium priority (Средний приоритет)
  • Low priority (Низкий приоритет)

При одинаковом уровне приоритета (например, 1-й и 3-й канал настроили на Very high priority) канал с меньшим номером будет иметь приоритет над каналом с большим номером (канал 1 будет иметь бОльший приоритет)

Каналы DMA

Каждый канал DMA имеет следующие регистры:

  • DMA_CCRx — регистр конфигурации канала DMA. В нем содержатся биты конфигурации передачи данных, биты разрешения прерывания от канала и бит включения канала.
  • DMA_CNDTRx — сколько кадров данных подлежит передаче (0..65535). Я намеренно употребил слово «кадр», а не «байт». Дело в том, то есть возможность настроить количество байт данных, которые передаются за одну транзакцию (настраивается в регистре DMA_CCRx), об этом поговорим далее.
  • DMA_CMARx — адрес памяти. Если направление передачи из периферии в память, то сюда будем записывать данные, если обратно, то читать.
  • DMA_CPARx — адрес периферийного устройства. Если направление передачи из периферии в память, то отсюда будем читать данные, если обратно, то записывать. В режиме Memory-to-memory сюда так же записывается адрес памяти.

Так, вроде все понятно: имеем 4-е регистра, с помощью которых можно настроить пересылку данных туда-сюда.

Теперь самое время поговорить о такой вещи как инкремент адреса памяти и периферии. Все примеры буду приводить для SPI. Инкремент адреса периферии чаще всего не имеет смысла, а вот инкремент памяти очень полезен. Пусть в регистр DMA_CMARx занесен адрес нулевой ячейки массива, который мы ходим отправить в SPI (вспоминаем указатели Си). После каждой отправки данных в SPI внутренний указатель памяти канала DMA будет увеличиваться на 1 элемент массива. Тут стоит отметить один важный момент: инкремент производится внутреннего указателя, который недоступен программно для чтения или записи, регистр DMA_CMARx не меняет своего значения в процессе передачи данных. 

На примере SPI работать это будет вот так. В регистр DMA_CMARx занесли адрес нулевого элемента массива, который хотим отправить, в DMA_CPARx адрес регистра данных DR модуля SPI. В DMA_CNDTRx записали количество байт для передачи. Включили инкремент адреса памяти, в модуле SPI разрешили запрос к DMA на передачу данных и запустили процесс, установкой бита EN в регистре DMA_CCRx. В начальном состоянии передатчик SPI пуст, флаг пустого передатчика устанавливается в единицу, это провоцирует зарос DMA. DMA получает запрос от SPI, после этого читает байт данных из массива и записывает его в регистр DR интерфейса SPI, увеличивает внутренний указатель памяти на один элемент массива (1 байт) и уменьшает значение регистра DMA_CNDTRx на единицу. После того, как SPI выплюнет байт данных, процесс повторится. Все это будет продолжаться до тех пор, пока значение DMA_CNDTRx не станет равно нулю. После этого канал DMA завершит передачу и больше не будет реагировать на запросы от SPI.

Но это для случая, если нам надо передавать данные в периферию по одному байту. А что делать, если у нас разрядность массива 2 байта, и периферия хочет на вход 2 байта тоже?

Для таких случаев в регистре DMA_CCRx есть конфигурационные биты разрядности периферийного регистра (PSIZE) и разрядности данных в памяти (MSIZE). Они могут принимать следующие значения:

  • 00: 8-bits
  • 01: 16-bits
  • 10: 32-bits
  • 11: Reserved (эта комбинация не используется)

То есть, если мы поставим MSIZE=16 бит (2 байта), то за раз мы будем отправлять уже 2 байта, и указатель на адрес памяти будет увеличиваться на 2. А вот регистр DMA_CNDTRx все так же будет уменьшаться на единицу, так как он содержит не количество байт для передачи, а количество самих передач (транзакций). Получается, что MSIZE нужен для того, чтобы сказать DMA, на сколько байт увеличивать внутренний указатель на адрес памяти. Все верно, но MSIZE  используется и еще для одной вещи.

Выравнивание данных разной разрядности

Очень часто бывают ситуации, когда разрядность приемника данных не совпадает с разрядностью источника. Например, в модуле SPI разрядность регистра данных DR равна 16 бит (2 байта, или полуслово). Однако, SPI у нас может быть настроен на передачу 8-и бит за раз и мы имеем массив данных для передачи, с разрядностью 8 бит. DMA позволяет настроить независимо разрядность передатчика и приемника данных. Как было сказано выше, с помощью битов MSIZE мы задаем разрядность данных в памяти. Но есть еще биты PSIZE, которыми надо указать разрядность регистра периферийного устройства (8, 16 или 32 бита). Если PSIZE не равен MSIZE, то DMA будет производить автоматическое выравнивание данных по следующим правилам.

Пусть разрядность источника данных 8 бит, а приемника 16. Тогда при пересылке DMA добавит 8 незначащих нулей к данным из источника и запишет их в приемник: из источника прочитали, например, 0x13, а в приемник записали 0x0013. В случае, если разрядность источника больше разрядности приемника, то DMA обрежет лишние старшие биты у данных из источника, и в приемник запишет только младшие биты: если разрядность источника 32 бита, а приемника 8 бит, то DMA прочтет из источника значение, например, 0xABCDEF12, а в приемник попадет 0x12. В принципе, все как при присвоении значений переменным в Си.

В Reference manual на микроконтроллеры STM32F1xxx в разделе про DMA есть вот такая таблица:

Programmable data width and endian behavior

Рис. 1. Правила выравнивания данных DMA

Таблица из Reference manual-а может показаться довольно замысловатой (на самом деле это так и есть 😉 ).Давайте разберемся в ней на одном из случае. Например, источник данных у нас 32 бита, а приемник 16 бит:

Рис. 2. Преобразование 32-х битных значений к 16-и битным

Пусть мы пересылаем данные из одного массива в памяти в другой посредством DMA. Причем эти массивы разной разрядности. Массив-источник имеет 32 разряда и содержит следующие данные:

  • элемент 0: B3B2B1B0, смещение 0x00
  • элемент 1: B7B6B5B4, смещение 0x04
  • элемент 2: BBBAB9B8, смещение 0x08
  • элемент 3: BFBEBDBC, смещение 0x0C

Если непонятно что такое смещение и почему оно принимает именно эти значения, то необходимо почитать про организацию данных в памяти микроконтроллера и указатели языка Си.

В качестве массива это будет выглядеть вот так:

uint32_t Sourse[4] = 
{
  0xB3B2B1B0,
  0xB7B6B5B4,
  0xBBBAB9B8,
  0xBFBEBDBC
};

Ну и приемник данных:

uint16_t Destination[4];

А DMA будет выполнять вот такую операцию:

for(int i=0; i<4; i++)
  Destination[i] = Sourse[i];

Думаю, что для людей, знакомых с Си, будет понятен результат такой операции:

uint16_t Destination[4] = 
{
  0xB1B0,
  0xB5B4,
  0xB9B8,
  0xBDBC
};

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

Особенности обращения к периферии AHB/APB

Тут есть одна очень важная и не очевидная особенность архитектуры микроконтроллеров STM32. CPU в STM32 является 32-х разрядным, и для записи в память 8, 16 или 32-х бит существуют разные команды и разные запросы на запись. Для ОЗУ ни каких проблем не существует: мы можем выполнять 8, 16 и 32-х разрядные запросы к памяти. А вот к периферии AHB/APB можно обращаться только 32-х битными запросами. А если нам надо выполнить запись в регистр, который имеет разрядность меньше, чем 32 бита? Объясню на примере все того же SPI. Регистр данных DR у него имеет разрядность 16 бит, и старшие 16 бит 32-х разрядной шины просто не используются:

SPI register map and reset values

Рис. 3. Карта регистров SPI

Если в DMA мы настроим разрядность периферии PSIZE = 16 бит, и разрядность памяти MSIZE = 16 бит, то DMA продублирует младшие 16 бит в старшие и произведет 32-х битный запрос к периферии:

0xABCD -> 0xABCDABCD

Т.е. из 0xABCD DMA сделает 0xABCDABCD и это значение отправится в периферию. И так как старшие 16 бит регистра DR не используются (зарезервированы), то старшие 16 бит просто проигнорируются. Так же можно настроить PSIZE = 32 бита, и тогда в регистр DR будет занесено значение 0x0000ABCD. А вот если PSIZE установить 8 бит, то DMA сделает следующее преобразование:

0xAB -> 0xABABABAB

Таким образом, в DR будет занесено 0xABAB а не 0x00AB, как можно подумать, если не знать этих особенностей. Вот как раз из-за того, что к периферии можно обращаться только 32-битными запросами, все регистры в периферии выравнены по границе 32 бита (см. рис. 3).

Кольцевой режим DMA (Circular mode)

Думаю, все знакомы с кольцевым буфером. Его очень удобно использовать при непрерывном приеме/передаче данных. В DMA микроконтроллеров STM32 такой режим работы реализован аппаратно, и включается он битом CIRC в регистре управления DMA_CCRx. Если этот режим активирован, то после передачи всех данных по DMA (после того, как DMA_CNDTRx станет равно нулю), регистр DMA_CNDTRx заново перезагружается исходным значением и передача продолжается.

Режим «Из памяти в память» (Memory-to-memory mode)

В «обычном» режиме канал DMA ждет запроса на передачу данных от какого-либо периферийного модуля (SPI, ADC, таймер, и т.д.) Однако, канал DMA может работать и без запроса от периферии, т.е. передача начнется сразу после установки бита EN в регистре DMA_CCRx. Этот режим может использоваться для копирования одной области памяти в другую. Для этого необходимо в регистры DMA_CPARx и DMA_CMARx занести адреса массивов, над которыми необходимо выполнить операцию копирования, и установить бит MEM2MEM в регистре DMA_CCRx. Получается, что и регистру адреса периферии, и регистру адреса памяти присваиваются адреса массивов в памяти. При пересылке MEM2MEM можно использовать любой свободный канал DMA. А как выбирается направление передачи? Точно так же, как и при обмене данными с периферией: битом DIR регистра DMA_CCRx. Пример передачи из памяти в память будет в одной из следующих статей, там, где мы перейдем к практике. Стоит отметить, что нельзя использовать режим MEM2MEM одновременно с Circular mode.

Прерывания DMA

Каждый канал DMA имеет 3 прерывания:

  • Half-transferDMA передал половину данных, удобно при потоковой передаче данных совместно с кольцевым режимом: пока передаем одну половину массива, заполняем другую.
  • Transfer complete — прерывание о завершении передачи данных.
  • Transfer error — прерывание об ошибке передачи.

Ошибки при передаче данных по DMA

Ошибка DMA может возникнуть при чтении/записи в зарезервированное адресное пространство микроконтроллера STM32. При возникновении ошибки соответствующий канал DMA отключается (сбрасывается бит EN) и возникает прерывание Transfer error (если разрешено).

Каналы DMA и периферия

DMA1 в микроконтроллерах STM32F103C8 имеет 7 каналов передачи данных, причем на каждом канале висит своя периферия. Приведу таблицу из Reference manual, чтоб было понятнее:

Summary of DMA1 requests for each channel

Рис. 4. Каналы DMA и соответствующие им запросы от периферийных устройств

Например, 1-й канал может обслуживать запросы от ADC1, TIM2_CH3 и TIM4_CH1, а 2-й канал от SPI1_RX, USART3_TX, TIM1_CH1, TIM2_UP и TIM3_CH3. Стоит отметить, что сами запросы должны быть разрешены в регистрах периферийных устройств, при этом, если разрешить DMA-запрос от 2-х источников, то обмен данными будет запускаться от 2-х разных запросов. С ходу не смогу привести пример, где это может быть полезно, и скорее всего такая конфигурация не имеет смысла.

На этом пока все, в следующей статье будет описание регистров DMA, а уже потом перейдем к практике. Продолжение следует!!! 😉

Продолжение.

Прямой доступ к памяти (Direct Memory Access, DMA) используется для быстрой передачи данных между периферийными устройствами микроконтроллера и памятью (периферия-память) и между областями памяти (память-память). Данные можно быстро перемещать под управлением DMA, не нагружая этой операцией CPU, что освобождает его для других полезных действий (выполнение кода). Непонятные термины и сокращения см. в Словарике [3].

В контроллере DMA скомбинирована мощная архитектура двойной master-шины AHB с независимым стеком FIFO, чтобы оптимизировать полосу пропускания системы, на основе комплексной матричной архитектуры шины.

Два контроллера DMA всего поддерживают 16 потоков (8 для каждого контроллера), каждый предназначен для обработки доступа к памяти от одного или большего количества периферийных устройств. Каждый поток может иметь всего до 8 каналов (запросов). И у каждого есть арбитр для обработки приоритета между запросами DMA.

[Функции DMA]

• Архитектура с двойной master-шиной AHB, одна для доступа к памяти, другая для доступа к периферийным устройства микроконтроллера.

• Интерфейс программирования AHB slave, поддерживающий только 32-битный доступ.

• 8 потоков (stream) для каждого контроллера DMA, обрабатывающих до 8 каналов (запросов) на поток.

• Буферы памяти FIFO глубиной 32 ячейки на поток, которые можно использовать в режиме FIFO или direct-режиме:

– FIFO-режим: с программно выбираемым порогом: 1/4, 1/2 или 3/4 от размера FIFO.
– Direct-режим. Каждый запрос DMA немедленно инициирует передачу из памяти или в память. Когда сконфигурирован direct-режим (FIFO запрещен) для передачи данных память-периферия, DMA предварительно загружает только одну порцию информации из памяти во внутренний FIFO, чтобы обеспечить немедленную передачу данных, как только запрос DMA инициируется периферийным устройством.

• Каждый поток может быть сконфигурирован аппаратурой, чтобы представлять собой:

– regular-канал, который поддерживает передачи периферия-память, память-периферия и память-память.
– канал с двойным буферизированием, который также поддерживает двойную буферизацию на стороне памяти.

• Каждый из 8 потоков соединен с выделенными аппаратными каналами DMA (запросами).

• Приоритеты между запросами потока DMA выбираются программно (по 4 уровням very high, high, medium, low), или аппаратно в случае эквивалентного уровня приоритета (запрос 0 имеет приоритет перед запросом 1, и так далее).

• Каждый поток также поддерживает программный триггер (инициатор) для передач память-память (это доступно только для контроллера DMA2).

• Может быть выбран каждый запрос на потоке, всего может быть до 8 запросов канала. Этот выбор конфигурируется программно, и позволяет нескольким периферийным устройствам инициировать запросы DMA.

• Количеством передаваемых элементов данных можно управлять либо от контроллера DMA, либо от периферийного устройства:

– Управление потоком DMA: количество передаваемых элементов данных выбирается программно в диапазоне 1 .. 65535.
– Управление потоком от периферийного устройства: количество передаваемых элементов данных неизвестно, и управляется от источника или от периферийного устройства, который аппаратно сигнализирует об окончании передачи.

• Независимая ширина передачи источника и места назначения (байт, полуслово, слово): когда ширина данных источника и места назначения не одинаковые, DMA автоматически упаковывает/распаковывает необходимые передачи, чтобы оптимизировать полосу пропускания. Эта функция доступна только в режиме FIFO.

• Адресация с инкрементом или без инкремента для источника и места назначения данных.

• Поддерживаются инкрементальные burst-передачи 4, 8 или 16 битов. Размер burst конфигурируется программно, обычно для половины размера FIFO периферийного устройства.

• Каждый поток поддерживает управление кольцевым буфером.

• 5 флагов событий (DMA Half Transfer, DMA Transfer complete, DMA Transfer Error, DMA FIFO Error, Direct Mode Error) обычно соединяемых операцией ИЛИ для генерации одного запроса прерывания для каждого потока.

[Функциональное описание DMA]

STM32F4xx DMA block diagram fig32

Рис. 32. Блок-схема DMA.

Контроллер DMA выполняет прямые передачи по памяти: как AHB master он берет управление матрицей шины AHB для инициализации транзакций AHB. Он может выполнять следующие транзакции:

• периферия-память
• память-периферия
• память-память

Контроллер DMA предоставляет два master-порта AHB: порт памяти AHB, предназначенный для подключения к областям памяти и порт периферии AHB, предназначенный для подключения к периферийным устройствам микроконтроллера. Однако, чтобы реализовать передачи память-память, порт периферии AHB также должен иметь доступ к областям памяти.

AHB slave-порт используется для программирования контроллера DMA (он поддерживает только 32-битный доступ).

STM32F4xx DMA two controllers STM32F405xx 07xx and STM32F415xx 17xx fig33

Рис. 33. Реализация системы двух контроллеров DMA (STM32F405xx/07xx и STM32F415xx/17xx).

STM32F4xx DMA two controllers STM32F42xxx and STM32F43xxx fig34

Рис. 34. Реализация системы двух контроллеров DMA (STM32F42xxx и STM32F43xxx).

Примечание: AHB порт периферии контроллера DMA1 не подключен к матрице шины, как контроллер DMA2. В результате только потоки DMA2 могут выполнять передачи память-память.

Транзакции DMA. Транзакция состоит из последовательности указанного количества передач данных. Количество передаваемых элементов данных и их ширина (8, 16 или 32 бит) выбирается программно.

Каждая передача DMA состоит из трех операций:

• Загрузка из регистра данных периферии или ячейки памяти, адресованных регистрами DMA_SxPAR или DMA_SxM0AR.
• Сохранение данных, загруженных из регистра данных периферии или ячейки памяти, адресованных регистрами DMA_SxPAR или DMA_SxM0AR.
• Пост-декремент регистра DMA_SxNDTR, который содержит количество транзакций, которые еще должны быть выполнены.

После события периферия отправляет сигнал запроса контроллеру DMA. Контроллер DMA обрабатывает запрос в зависимости от приоритетов канала. Как только контроллер DMA осуществил доступ к периферийному устройству, контроллером DMA отправляется сигнал подтверждения (Acknowledge). Периферийное устройство снимает свой запрос, как только получит сигнал Acknowledge от контроллера DMA. Как только запрос снят периферией, контроллер DMA освобождает сигнал Acknowledge. Если больше нет запросов, периферия может инициировать следующую транзакцию.

Выбор канала. Каждый поток может быть ассоциирован с запросом DMA, с выбором до 8 возможных запросов канала. Выбор управляется битами CHSEL[2:0] регистра DMA_SxCR.

STM32F4xx DMA channel selection fig35

Рис. 35. Выбор канала.

8 запросов от периферийных устройств (TIM, ADC, SPI, I2C и т. д.) независимо подключаются к каждому каналу, и их соединение зависит от реализации продукта. См. следующие таблицы для примеров отображений запросов DMA.

Таблица 42. Отображение запроса DMA1.

Запросы периферии Поток 0 Поток 1 Поток 2 Поток 3 Поток 4 Поток 5 Поток 6 Поток 7
Канал 0 SPI3_RX SPI3_RX SPI2_RX SPI2_TX SPI3_TX SPI3_TX
Канал 1 I2C1_RX TIM7_UP TIM7_UP I2C1_RX I2C1_TX I2C1_TX
Канал 2 TIM4_CH1 I2S3_EXT_RX TIM4_CH2 I2S2_EXT_TX I2S3_EXT_TX TIM4_UP TIM4_CH3
Канал 3 I2S3_EXT_RX TIM2_UP
TIM2_CH3
I2C3_RX I2S2_EXT_RX I2C3_TX TIM2_CH1 TIM2_CH2
TIM2_CH4
TIM2_UP
TIM2_CH4
Канал 4 UART5_RX USART3_RX UART4_RX USART3_TX UART4_TX USART2_RX USART2_TX UART5_TX
Канал 5 UART8_TX(1) UART7_TX(1) TIM3_CH4
TIM3_UP
UART7_RX(1) TIM3_CH1
TIM3_TRIG
TIM3_CH2 UART8_RX(1) TIM3_CH3
Канал 6 TIM5_CH3
TIM5_UP
TIM5_CH4
TIM5_TRIG
TIM5_CH1 TIM5_CH4
TIM5_TRIG
TIM5_CH2 TIM5_UP
Канал 7 TIM6_UP I2C2_RX I2C2_RX USART3_TX DAC1 DAC2 I2C2_TX

Примечание (1): эти запросы доступны только на STM32F42xxx и STM32F43xxx.

Таблица 43. Отображение запроса DMA2.

Запросы периферии Поток 0 Поток 1 Поток 2 Поток 3 Поток 4 Поток 5 Поток 6 Поток 7
Канал 0 ADC1 SAI1_A(1) TIM8_CH1
TIM8_CH2
TIM8_CH3
SAI1_A(1) ADC1 SAI1_B(1) TIM1_CH1
TIM1_CH2
TIM1_CH3
Канал 1 DCMI ADC2 ADC2 SAI1_B(1) SPI6_TX(1) SPI6_RX(1) DCMI
Канал 2 ADC3 ADC3 SPI5_RX(1) SPI5_TX(1) CRYP_OUT CRYP_IN HASH_IN
Канал 3 SPI1_RX SPI1_RX SPI1_TX SPI1_TX
Канал 4 SPI4_RX(1) SPI4_TX(1) USART1_RX SDIO USART1_RX SDIO USART1_TX
Канал 5 USART6_RX USART6_RX SPI4_RX(1) SPI4_TX(1) USART6_TX USART6_TX
Канал 6 TIM1_TRIG TIM1_CH1 TIM1_CH2 TIM1_CH1 TIM1_CH4
TIM1_TRIG
TIM1_COM
TIM1_UP TIM1_CH3
Канал 7 TIM8_UP TIM8_CH1 TIM8_CH2 TIM8_CH3 SPI5_RX(1) SPI5_TX(1) TIM8_CH4
TIM8_TRIG
TIM8_COM

Примечание (1): эти запросы доступны только на STM32F42xxx и STM32F43xxx.

Арбитр. Арбитр управляет 8-ю запросами потока DMA, основываясь на их приоритете для каждого из двух master-портов AHB (порта памяти и порта периферии), и запускает последовательности доступа периферии/памяти.

Приоритеты обслуживаются в двух стадиях:

• Программно: приоритет каждого потока может быть сконфигурирован регистром DMA_SxCR. Имеется 4 уровня приоритета:

– Very high
– High
– Medium
– Low

• Аппаратно: если у двух запросов одинаковый уровень программного приоритета, то поток с меньшим номером получает приоритет над потоком с большим номером. Например, у Stream 2 приоритет выше, чем у Stream 4.

Потоки DMA. Каждый из 8 потоков контроллера DMA предоставляет однонаправленную передачу между источником (source) и местом назначения (destination) данных.

Каждый поток можно сконфигурировать для выполнения:

• Обычных транзакций: память-периферия, периферия-память и память-память.
• Транзакций с двойной буферизацией: передачи используют два указателя на память для памяти (в то время как DMA читает/записывает из одного буфера или в него, приложение может записывать/читать в другой буфер или из него).

Количество передаваемых данных (до 65535) программируется, и связано с шириной источника периферии, которая запрашивает передачу DMA, подключенную к AHB порту периферии. Регистр, который содержит количество передаваемых элементов данных, декрементируется после каждой транзакции.

Источник, место назначения и режимы передачи. И источник (source), и место назначения (destination) передачи может адресовать периферийные устройства и области памяти во всем адресном пространстве 4 гигабайта, по адресам от 0x00000000 до 0xFFFFFFFF.

Направление передач конфигурируется битами DIR[1:0] регистра DMA_SxCR: память-периферия, периферия-память или память-память. Таблица 44 показывает регистры, соответствующие адресу источника (source) и адресу места назначения (destination) данных.

Таблица 44. Адреса источника и места назначения данных и destination.

DIR[1:0](1) Направление Адрес источника Адрес назначения
00 Периферия — память DMA_SxPAR DMA_SxM0AR
01 Память — периферия DMA_SxM0AR DMA_SxPAR
10 Память — память DMA_SxPAR DMA_SxM0AR
11 зарезервировано

Примечание (1): DIR[1:0] это поле бит направления данных в регистре DMA_SxCR.

Когда ширина данных (программируемая полем бит PSIZE или MSIZE регистра DMA_SxCR) составляет половину слова (16 бит) или слово (32 бита) соответственно, то адрес периферии или адрес памяти, записанный в регистры DMA_SxPAR или DMA_SxM0AR/DMA_SxM1AR, должен быть выровнен на адрес полуслова или слова соответственно.

STM32F4xx DMA peripheral to memory mode fig36

Рис. 36. Режим перемещения данных из периферийного устройства в память.

Примечание (1): для режима двойной буферизации.

Режим FIFO. Когда разрешается этот режим (установкой бита EN в регистре DMA_SxCR), то каждый раз, когда возникает запрос от периферии, поток инициирует передачу из источника для заполнения FIFO.

Когда был достигнут порог заполнения FIFO, содержимое FIFO сливается и сохраняется по месту назначения.

Передача останавливается, как только содержимое регистра DMA_SxNDTR достигает нуля, когда периферийное устройство запрашивает конец передач (в случае управления потоком от периферии), или когда бит EN регистра DMA_SxCR сброшен кодом программы.

Режим Direct. В direct-режиме (когда значение DMDIS в регистре DMA_SxFCR равен 0’), уровень порога заполнения FIFO не используется: после каждого одиночного запроса данных из периферии в FIFO, соответствующие данные немедленно сливаются в место назначения.

Поток имеет доступ к порту источника AHB или порту назначения только в случае выигрыша арбитража. Этот арбитраж обслуживается на основе приоритета, определенного для каждого потока битами PL[1:0] регистра DMA_SxCR.

STM32F4xx DMA memory to peripheral mode fig37

Рис. 37. Режим перемещения данных из памяти в периферийное устройство.

Примечание (1): для режима двойной буферизации.

Режим FIFO. Когда разрешается этот режим (установкой бита EN в регистре DMA_SxCR), то поток немедленно инициирует передачу из источника до полного заполнения FIFO.

Каждый раз, когда возникает запрос периферийного устройства, содержимое FIFO сливается и сохраняется по месту назначения. Когда уровень заполнения FIFO становится меньше или равным предварительно заданному порогу, FIFO полностью перезагружается данными из памяти.

Передача останавливается, как только регистр DMA_SxNDTR достигает 0, когда периферия запрашивает конец передач (в случае управления потоком со стороны периферии) или когда бит EN в регистре DMA_SxCR очищается программой.

Режим Direct. В этом режиме (когда значение DMDIS в регистре DMA_SxFCR равно 0), уровень порога заполнения FIFO не используется. Как только поток разрешен, DMA предварительно загружает первые данные для передачи во внутренний FIFO. Как только периферия запросит передачу данных, DMA передает эту предварительно загруженную величину в сконфигурированное место назначения. Затем DMA снова загружает в пустой внутренний FIFO следующие данные для передачиr. Размер предварительно загруженных данных соответствует значению поля бит PSIZE регистра DMA_SxCR.

Поток имеет доступ к порту источника AHB или порту назначения только в случае выигрыша арбитража. Этот арбитраж обслуживается на основе приоритета, определенного для каждого потока битами PL[1:0] регистра DMA_SxCR.

Каналы DMA также работают без инициации по запросу от периферийного устройства. Это режим память-память, показанный на рис. 38.

STM32F4xx DMA memory to memory mode fig38

Рис. 38. Режим перемещения данных между областями памяти.

Примечание (1): для режима двойной буферизации.

Когда поток разрешен установкой бита EN в регистре DMA_SxCR, поток немедленно начинает заполнять FIFO до установленного порога заполнения. Когда этот порог достигнут, содержимое FIFO сливается и сохраняется по месту назначения.

Передача останавливается, как только регистр DMA_SxNDTR достигает 0, или когда бит EN в регистре DMA_SxCR очищается программой.

Поток имеет доступ к порту источника AHB или порту назначения только в случае выигрыша арбитража. Этот арбитраж обслуживается на основе приоритета, определенного для каждого потока битами PL[1:0] регистра DMA_SxCR.

Примечание: используется режим память-память, режимы circular и direct не дозволяются. Только контроллер DMA2 может выполнять передачи память-память.

Инкремент указателя. Указатели на периферию и память могут опционально автоматически инкрементироваться после транзакции (post-increment) или оставаться постоянными после каждой передачи, в зависимости от бит PINC и MINC регистра DMA_SxCR.

Запрет режима инкремента полезен, когда данные источника периферии или данные назначения находятся в одном регистре.

Если режим инкремента разрешен, то следующая передача будет использовать предыдущий адрес, инкрементированный на 1 (для байт), 2 (для полуслов) или 4 (для слов) в зависимости от данных, запрограммированных в битах PSIZE или MSIZE регистра DMA_SxCR.

Чтобы оптимизировать операцию упаковки, можно фиксировать размер смещения приращения для периферийного адреса независимо от размера данных, передаваемых по периферийному порту AHB. Бит PINCOS в регистре DMA_SxCR используется для выравнивания размера смещения приращения на размер данных периферийного порта AHB, или нв 32-битный адрес (тогда адрес инкрементируется на 4). Бит PINCOS влияет только на периферийный порт AHB.

Если бит PINCOS установлен, адрес следующей передачи это адрес предыдущей передачи, инкрементированный на 4 (автоматически выровненный на 32-битный адрес), независимо от размера PSIZE. Однако на порт памяти AHB эта операция не влияет.

Режим кольцевого буфера (circular mode). Этот режим режим доступен для обработки кольцевых (circular) буферов и непрерывных потоков данных (например, режим сканирования ADC). Эта функция может быть разрешена с помощью бита CIRC в регистре DMA_SxCR.

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

Примечание: в circular-режиме важно следовать следующему правилу в случае burst-режима, сконфигурированного для памяти:

DMA_SxNDTR = значение, нацело делящееся на ((Mburst beat) x (Msize)/(Psize)), где:

– (Mburst beat) = 4, 8 или 16 (в зависимости от бит MBURST в регистре DMA_SxCR)
– ((Msize)/(Psize)) = 1, 2, 4, 1/2 или 1/4 (Msize и Psize представлены битами MSIZE и PSIZE в регистре DMA_SxCR. Они зависят от байтов)
– DMA_SxNDTR = количество элементов данных для передаче на порту периферии AHB.

Например: Mburst beat = 8 (INCR8), MSIZE = ‘00’ (байт) и PSIZE = 01 (полуслово), в этом случае DMA_SxNDTR должно нацело делиться на (8 x 1/2 = 4).

Если не следовать этой формуле, то корректное поведение DMA и целостность данных не гарантируется.

NDTR также должно нацело делиться на размер пакета периферии (peripheral burst size), умноженный на размер данных периферии, иначе это приведет к неправильной работе DMA.

Режим двойного буфера. Этот режим доступен для всех потоков DMA1 и DMA2. Режим двойного буфера разрешается установкой бита DBM в регистре DMA_SxCR.

Поток двойного буфера работает как обычный поток (с одним буфером) с тем отличием, что здесь есть два указателя на память. Когда разрешен режим двойного буфера, автоматически разрешается кольцевой режим (бит CIRC в регистре DMA_SxCR не имеет значения) и на каждом завершении транзакции указатели на память переключаются (меняются местами).

В этом режиме контроллер DMA переключается от одной цели в памяти к другой по окончании транзакции DMA. Это дает возможность обрабатывать программе один буфер, пока другой буфер заполняется (или считывается, в зависимости от направления транзакции) передачей DMA. Поток двойного буфера может работать в двух направлениях (память может быть либо источником, либо местом назначения данных), как это описано в таблице 45 ниже.

Примечание: когда поток разрешен, в режиме двойного буфера можно обновлять базовый адрес для порта памяти AHB на лету (DMA_SxM0AR или DMA_SxM1AR), если выполнять следующие условия:

• Когда бит CT равен 0 в регистре DMA_SxCR, можно записывать регистр DMA_SxM1AR. Попытка записать в этот регистр, когда CT = 1, установит флаг ошибки (TEIF), и поток будет автоматически запрещен.
• Когда бит CT равен 1 в регистре DMA_SxCR, можно записывать регистр DMA_SxM0AR. Попытка записать в этот регистр, когда CT = 0, установит флаг ошибки (TEIF), и поток будет автоматически запрещен.

Чтобы избежать ситуации ошибки, рекомендуется менять базовый адрес, как только был выставлен флаг TCIF, потому что в этот момент целевой адрес должен поменяться от области памяти 0 на область памяти 1, или от 1 на 0, в зависимости от значения бита CT регистра DMA_SxCR. Само собой, изменение базового адреса должно выполняться с соблюдением двух вышеупомянутых условий.

Для всех других режимов (кроме режима двойного буфера), регистры адреса памяти защищены от записи, пока разрешен поток DMA.

Таблица 45. Регистры адреса источника (source) и места назначения (destination) данных в режиме двойного буфера (DBM=1).

DIR[1:0](1) Направление Адрес источника Адрес назначения
00 Периферия — память DMA_SxPAR DMA_SxM0AR / DMA_SxM1AR
01 Память — периферия DMA_SxM0AR / DMA_SxM1AR DMA_SxPAR
10 Не допускается(2)
11 зарезервировано

Примечания:

(1) DIR[1:0] это поле бит направления данных в регистре DMA_SxCR.
(2) Когда разрешен режим двойного буфера, автоматически разрешается circular режим. Поскольку режим память-память не совместим с режимом circular, то когда разрешен режим двойного буфера, нельзя конфигурировать режим память-память.

Программируемая ширина данных, упаковка/распаковка, порядок следования байт (endianess). Количество элементов данных для передачи должно быть запрограммировано в DMA_SxNDTR (количество элементов данных передачи, number of data items to transfer, NDT) перед тем, как разрешать поток (кроме ситуации, когда потоком управляет периферийное устройство, установлен бит PFCTRL в регистре DMA_SxCR).

Когда используется внутренний FIFO, ширина данных источника и места назначения программируется битовыми полями PSIZE и MSIZE регистра DMA_SxCR (ширина может быть 8, 16 или 32 бита).

Когда PSIZE и MSIZE не равны:

• Ширина данных количества передаваемых элементов, сконфигурированных в регистре DMA_SxNDTR, равна ширине периферийной шины (сконфигурированной битами PSIZE в регистре DMA_SxCR). Например, в случае передач периферия-память, память-периферия или память-память, и если биты PSIZE[1:0] сконфигурированы для полуслова, количество передаваемых байт равно 2 x NDT.
• Контроллер DMA копирует только с little-endian адресацией для источника и места назначения данных. Это описано в таблице 46 ниже.

Таблица 46. Поведение упаковки/распаковки и endian (бит PINC = MINC = 1).

Ширина порта AHB памяти
Ширина порта AHB периферии
Количество элементов данных для передачи (NDT)
Номер передачи памяти
Адрес порта памяти / прохождение байт     
Номер передачи периферии
Адрес порта периферии / прохождение байт
PINCOS = 1                       PINCOS = 0                      
8 8 4 1
2
3
4
0x0 / B0[7:0]
0x1 / B1[7:0]
0x2 / B2[7:0]
0x3 / B3[7:0]
1
2
3
4
0x0 / B0[7:0]
0x4 / B1[7:0]
0x8 / B2[7:0]
0xC / B3[7:0]
0x0 / B0[7:0]
0x1 / B1[7:0]
0x2 / B2[7:0]
0x3 / B3[7:0]
8 16 2 1
2
3
4
0x0 / B0[7:0]
0x1 / B1[7:0]
0x2 / B2[7:0]
0x3 / B3[7:0]
1

2

0x0 / B1|B0[15:0]

0x4 / B3|B2[15:0]

0x0 / B1|B0[15:0]

0x2 / B3|B2[15:0]

8 31 1 1
2
3
4
0x0 / B0[7:0]
0x1 / B1[7:0]
0x2 / B2[7:0]
0x3 / B3[7:0]
1 0x0 / B3|B2|B1|B0[31:0] 0x0 / B3|B2|B1|B0[31:0]
16 8 4 1

2

0x0 / B1|B0[15:0]

0x2 / B3|B2[15:0]

1
2
3
4
0x0 / B0[7:0]
0x4 / B1[7:0]
0x8 / B2[7:0]
0xC / B3[7:0]
0x0 / B0[7:0]
0x1 / B1[7:0]
0x2 / B2[7:0]
0x3 / B3[7:0]
16 16 2 1
2
0x0 / B1|B0[15:0]
0x2 / B1|B0[15:0]
1
2
0x0 / B1|B0[15:0]
0x4 / B3|B2[15:0]
0x0 / B1|B0[15:0]
0x2 / B3|B2[15:0]
16 32 1 1
2
0x0 / B1|B0[15:0]
0x2 / B3|B2[15:0]
1 0x0 / B3|B2|B1|B0[31:0] 0x0 / B3|B2|B1|B0[31:0]
32 8 4 1 0x0 / B3|B2|B1|B0[31:0] 1
2
3
4
0x0 / B0[7:0]
0x4 / B1[7:0]
0x8 / B2[7:0]
0xС / B3[7:0]
0x0 / B0[7:0]
0x1 / B1[7:0]
0x2 / B2[7:0]
0x3 / B3[7:0]
32 16 2 1 0x0 /B3|B2|B1|B0[31:0] 1
2
0x0 / B1|B0[15:0]
0x4 / B3|B2[15:0]
0x0 / B1|B0[15:0]
0x2 / B3|B2[15:0]
32 32 1 1 0x0 /B3|B2|B1|B0 [31:0] 1 0x0 /B3|B2|B1|B0[31:0] 0x0 / B3|B2|B1|B0[31:0]

Примечание: порт периферии может быть источником или местом назначения данных (он также может быть источником в памяти в случае передачи память-память).

Эта процедура упаковки/распаковки может вводить риск повреждения данных, когда операция была прервана до полной упаковки/распаковки. Таким образом, чтобы гарантировать когерентность данных, поток может быть сконфигурирован для burst-передач: в этом случае, каждая группа передач, принадлежащая burst, является неделимой (см. далее секцию «Передачи single и burst»).

В direct-режиме (DMDIS = 0 в регистре DMA_SxFCR), упаковка/распаковка данных невозможна. В этом случае не дозволяется иметь разную ширину данных для источника и места назначения: обе ширины одинаковые, и ширина определяется битами PSIZE регистра DMA_SxCR (биты MSIZE не имеют значения).

Поля PSIZE, MSIZE и NDT[15:0] должны конфигурироваться так, чтобы гарантировать, что последняя передача не была незавершенной. Это может произойти, когда ширина данных порта периферии (биты PSIZE) меньше, чем ширина данных порта памяти (биты MSIZE). Это ограничение обобщено в таблице 47.

Таблица 47. Ограничение на NDT по отношению к PSIZE и MSIZE.

PSIZE[1:0] регистра DMA_SxCR MSIZE[1:0] регистра DMA_SxCR NDT[15:0] регистра DMA_SxNDTR
00 (8 бит) 01 (16 бит) Должно нацело делиться на 2
00 (8 бит) 10 (32 бита) Должно нацело делиться на 4
01 (16 бит) 10 (32 бита) Должно нацело делиться на 2

Передачи single и burst. Контроллер DMA может генерировать одиночные передачи (single transfer) или инкрементальные burst-передачи 4, 8 или 16 beat-пакетов.

Размер burst конфигурируется программой независимо для двух портов AHB путем использования бит MBURST[1:0] и PBURST[1:0] регистра DMA_SxCR.

Размер burst показывает количество beat-ов в burst, а не количество передаваемых байт.

Чтобы гарантировать когерентность данных, каждая группа передач, которая формирует burst, является неделимой: передачи AHB блокируются и арбитр матрицы шины AHB не дает оказывать влияния на DMA master во время последовательности burst-передачи.

В зависимости от конфигурации single или burst, каждый запрос DMA инициирует разное количество передач на порту периферии AHB:

• Когда порт периферии AHB сконфигурирован для single передач, каждый запрос DMA генерирует передачу данных байта, полуслова или слова, в зависимости от бит PSIZE[1:0] в регистре DMA_SxCR.
• Когда порт периферии AHB сконфигурирован для burst, каждый запрос DMA генерирует 4, 8 или 16 передач beat-пакетов байт, полуслов или слов, в зависимости от бит PBURST[1:0] и PSIZE[1:0] в регистре DMA_SxCR.

То же самое, что было упомянуто выше, должно учитываться и для порта памяти AHB с битами MBURST и MSIZE.

В direct-режиме поток может генерировать только single-передачи и биты MBURST[1:0] и PBURST[1:0] принудительно устанавливаются аппаратно.

Указатели адреса (регистры DMA_SxPAR или DMA_SxM0AR) должны быть выбраны так, чтобы гарантировать, что все передачи в burst-блоке выравнены на границу, равную размеру передачи.

Конфигурация burst должна быть выбрана, чтобы соответствовать протоколу AHB, где пакеты burst не должны пересекать границы адреса 1 килобайт, потому что минимальное адресное пространство, которое можно выделить для single slave, составляет 1 килобайт. Это означает, что граница адреса 1 килобайт не должна пересекаться burst-передачей блока, иначе будет сгенерирована ошибка AHB, о которой не сообщается регистрами DMA.

[Структура FIFO]

FIFO используется для временного хранения данных, поступающих от источника, перед их передачей в место назначения.

У каждого потока есть FIFO глубиной 4 слова, и программно конфигурируемый уровень порога заполнения между 1/4, 1/2, 3/4 или полным заполнением.

Чтобы разрешить использование уровня порога FIFO, должен быть запрещен direct-режим установкой бита DMDIS в регистре DMA_SxFCR.

Структура FIFO может быть разной, в зависимости от ширины данных источника и места назначения, что показано на рис. 39.

STM32F4xx DMA FIFO structure fig39

Рис. 39. Структура FIFO.

[Порог FIFO и конфигурация burst]

Следует с осторожностью выбирать порог FIFO (значение поля бит FTH[1:0] в регистре DMA_SxFCR) и размер memory burst (поле бит MBURST[1:0] регистра DMA_SxCR): содержимое, на которое указывает порог FIFO, должно точно соответствовать целому числу передач memory burst. Если это не выполняется, то при разрешенном потоке будет генерироваться ошибка FIFO (флаг FEIFx в регистре DMA_HISR или DMA_LISR), после чего поток будет автоматически запрещен. Разрешенные и запрещенные конфигурации описаны в таблице 48.

Таблица 48. Конфигурации порога FIFO.

MSIZE Уровень заполнения FIFO MBURST = INCR4 MBURST = INCR8 MBURST = INCR16
Байт 1/4 1 burst из 4 порций запрещено запрещено
1/2 2 burst из 4 порций 1 burst из 8 порций
3/4 3 burst из 4 порций запрещено
Полностью 4 burst из 4 порций 2 burst из 8 порций 1 burst из 16 порций
Полуслово 1/4 запрещено запрещено запрещено
1/2 1 burst из 4 порций
3/4 запрещено
Полностью 2 burst из 4 порций 1 burst из 8 порций
Слово 1/4 запрещено запрещено
1/2
3/4
Полностью 1 burst из 4 порций

Во всех случаях размер burst, умноженный на размер данных, не должен превышать размера FIFO. Размер данных может быть: 1 (байт), 2 (полуслово) или 4 (слово).

Может произойти незавершенная пакетная передача (Incomplete Burst) по окончании передачи DMA, если будут присутствовать следующие условия:

• Для конфигурации порта периферии AHB: общее количество элементов данных (установленное в регистре DMA_SxNDTR) не делится нацело на размер burst, умноженный на размер данных.
• Для конфигурации порта памяти AHB: количество оставшихся элементов данных в FIFO для передачи в память не делится нацело на размер burst, умноженный на размер данных.

В таких случаях оставшиеся для передачи данные будут обслуживаться DMS в single-режиме, даже если была запрошена burst-транзакция во время конфигурации потока DMA.

Примечание: когда burst-передачи запрашиваются на порту периферии AHB, и используется FIFO (DMDIS = 1 в регистре DMA_SxCR), важно соблюдать следующее правило, чтобы избежать постоянных событий недогрузки (underrun) или переполнения (overrun) в зависимости от направления потока DMA:

Если (PBURST x PSIZE) = FIFO_SIZE (4 слова), то FIFO_Threshold = 3/4 запрещен при PSIZE = 1, 2 или 4, и PBURST = 4, 8 или 16.

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

[FIFO flush]

Когда поток запрещен сбросом бита EN в регистре DMA_SxCR, содержимое FIFO может быть сброшено (flush), и когда поток конфигурируется для поддержки передач периферия-память или память-память: если некоторые данные присутствуют в FIFO когда поток запрещен, то контроллер DMA продолжает передавать оставшиеся данных в место назначения (даже если поток фактически запрещен). Это и есть сброс данных буфера FIFO, flush. Когда сброс данных завершится, установится бит завершения передачи TCIFx в регистре DMA_LISR или DMA_HISR.

Счетчик оставшихся данных DMA_SxNDTR в этом случае сохранит свое значение, чтобы показать, сколько еще элементов данных доступно в памяти места назначения.

Обратите внимание, что если во время операции FIFO flush количество оставшихся данных в FIFO для передачи в память (в байтах) меньше, чем ширина данных памяти (например, 2 байта в FIFO, хотя MSIZE сконфигурировано для слова), то данные будут отправлены с шириной, установленной в битах MSIZE регистра DMA_SxCR. Это означает, что память будет записана нежелательным значением. Программа может прочитать регистр DMA_SxNDTR, чтобы определить область памяти, которая содержит хорошие данные (начальный адрес и последний адрес).

Если количество оставшихся элементов данных в FIFO меньше, чем размер burst (если биты MBURST в регистре DMA_SxCR установлены для конфигурирования потока, чтобы управлять burst на порту памяти AHB), будут генерироваться single-транзакции, чтобы выполнить FIFO flush.

[Режим Direct]

По умолчанию FIFO работает в direct-режиме (сброшен бит DMDIS в регистре DMA_SxFCR), и порог FIFO не используется. Этот режим полезен, когда системе нужна непосредственная и одиночная передача в память или из памяти после каждого запроса DMA.

Когда DMA сконфигурирован в режиме direct (FIFO запрещен), для передачи данных в режиме память-периферия, DMA предварительно загружает данные из памяти во внутренний FIFO, чтобы гарантировать немедленную передачу данных, как только запрос DMA будет инициирован периферийным устройством.

Чтобы избежать насыщения FIFO, рекомендуется сконфигурировать соответствующий поток с высоким приоритетом.

Этот режим ограничен передачами, в которых:

• Ширина источника и места назначения передачи одинаковая, и обе эти ширины определяются битами PSIZE[1:0] регистра DMA_SxCR (биты MSIZE[1:0] не имеют значения).
• Burst-передачи невозможны (биты PBURST[1:0] и MBURST[1:0] в регистре DMA_SxCR не имеют значения).

Direct-режим не должен использоваться, когда реализуются передачи память-память.

Завершение передачи DMA. Разные события могут генерировать завершение передачи путем установки бита TCIFx в регистре статуса DMA_LISR или DMA_HISR:

• В режиме управления потоком от DMA:

– Счетчик DMA_SxNDTR достиг нуля в режиме память-периферия.
– Поток сконфигурирован перед завершением передачи (очисткой бита EN в регистре DMA_SxCR) и (при передачах периферия-память или память-память) все оставшиеся данные сбрасываются (flush) из FIFO в память.

• Режим управления потоком от периферии:

– Последний внешний запрос burst или одиночный запрос был сгенерирован от периферии и (когда DMA работает в режиме периферия-память) оставшиеся данные переданы из FIFO в память.
– Поток запрещен программно, и (когда DMA работает в режиме периферия-память) оставшиеся данные переданы из FIFO в память.

Примечание: завершение передачи зависит от оставшихся данных в FIFO, подлежащих передач в память только в случае режима периферия-память. Это условие не применимо в режиме память-периферия.

Если поток сконфигурирован в не кольцевом режиме (noncircular mode), то после окончания передачи (когда количество передаваемых данных достигло нуля) DMA останавливается (бит EN в регистре DMA_SxCR очищается программно), и никакой запрос DMA не обслуживается если программа заново не запрограммирует поток и заново не разрешит его (установкой бита EN в регистре DMA_SxCR).

Приостановка передачи DMA. В любой момент времени передача DMA может быть приостановлена, чтобы быть возобновленной позже, или полностью быть запрещенной до завершения передачи.

Есть два случая:

• Поток запрещает передачу, чтобы позже его не перезапускать из точки, где была остановка. В этой ситуации ничего делать не нужно, кроме как очистить бит EN в регистре DMA_SxCR, чтобы запретить поток. Потоку может понадобиться время, чтобы перейти в запрещенное состояния (сначала должна завершится текущая передач). Флаг прерывания завершения передачи (бит TCIF а регистре DMA_LISR или DMA_HISR) установится, чтобы показать конец передачи. Значение бита EN в регистре DMA_SxCR теперь равно 0, чтобы подтвердить обрыв работы потока. Регистр DMA_SxNDTR содержит количество оставшихся элементов данных в момент, когда поток был установлен, так что программа может определить, сколько элементов данных было передано перед моментом, когда работа потока была прервана.

• Поток приостанавливает передачу до того, как достигнет 0 количество оставшихся элементов данных для передачи в регистре DMA_SxNDTR. Целью является перезапуск передачи после повторного разрешения потока. Чтобы перезапуск был с точки, где передача была остановлена, программа должна прочитать регистр DMA_SxNDTR после запрета потока записью бита EN в регистре DMA_SxCR (и затем проверить, что он стал равен 0), чтобы узнать количество данных, которое уже было собрано. Затем:

– Адрес периферии и/или памяти должен быть обновлен, чтобы подстроить указатели адреса.
– Регистр SxNDTR должен быть обновлен оставшимся количеством элементов данных, которое нужно передать (прочитанное значение, когда поток был запрещен).
– Затем поток может быть заново разрешен, чтобы передача перезапустилась с того места, где была остановлена.

Примечание: обратите внимание, что флаг установится прерывания завершения передачи (TCIF в регистре DMA_LISR или DMA_HISR), чтобы показать окончание передачи из-за обрыва работы потока.

Контроллер потока. Объект, который управляет количеством передаваемых данных, известен как контроллер потока (flow controller). Этот контроллер конфигурируется независимо для каждого потока с использованием бита PFCTRL в регистре DMA_SxCR.

Контроллером потока может быть:

• Контроллер DMA. В этом случае количество элементов данных для передачи устанавливает программа в регистре DMA_SxNDTR перед тем, как разрешается поток DMA.
• Периферия, которая является источником или местом назначения данных: это тот случай, когда количество передаваемых элементов данных неизвестно. Периферия аппаратно показывает контроллеру DMA момент переноса последних данных. Эта функция поддерживается только для периферийных устройств, которые могут сигнализировать об окончании передачи, т. е. только для SDIO.

Когда для определенного потока контроллером является периферия, то значение, записанное в регистр DMA_SxNDTR, не оказывает влияние на передачу DMA. В действительности, независимо от записанного значения, это значение принудительно установлено аппаратурой в 0xFFFF, как только поток был разрешен, чтобы соответствовать следующим схемам:

• Ожидаемое прекращение работы потока: бит EN в регистре DMA_SxCR сбрасывается в 0 программой, чтобы остановить поток до того, как аппаратный сигнал последних данных (single или burst) отправлен периферией. В этом случае поток выключается, и срабатывает сброс FIFO (flush) в случае передачи DMA периферия-память. Флаг TCIFx в регистре статуса соответствующего потока установится, чтобы показать завершение работы DMA. Чтобы узнать количество элементов данных, переданных во время передачи DMA, считывается регистр DMA_SxNDTR, и применяется следующая формула:

Количество переданных данных = 0xFFFF – DMA_SxNDTR

• Нормальное завершение работы потока из-за получения аппаратного сигнала последних данных: поток автоматически будет прерывает рабоу, когда периферия запрашивает последнюю передачу DMA (single или burst), и когда эта передача завершится. Установится флаг TCIFx в регистре статуса соответствующего потока, чтобы показать завершение передачи DMA. Чтобы узнать, сколько элементов данных передано, прочитайте регистр DMA_SxNDTR, и примените ту же самую формулу, показанную выше.

• Регистр DMA_SxNDTR достиг 0: установится флаг TCIFx в регистре статуса соответствующего потока, чтобы показать принудительное завершение передачи DMA. Поток автоматически отключается, даже если аппаратный сигнал последних данных (single или burst) еще не был выставлен. Уже переданные данные не потеряются. Это означает, что в одной транзакции блок DMA может обработать максимум 65535 элементов данных, даже когда потоком управляет периферия (peripheral flow control mode).

Примечание: когда сконфигурирован режим память-память, блок DMA всегда является контроллером потока, и аппаратура принудительно сбрасывает бит PFCTRL в 0.

Кольцевой режим (circular mode) запрещен, когда периферия управляет потоком (peripheral flow controller mode).

Обзор возможных конфигураций DMA. В таблице 49 суммарно показаны различные возможные конфигурации DMA.

Таблица 49. Возможные конфигурации DMA.

Режим передачи DMA Источник данных Место назначения данных Контроллер потока Режим кольцевого буфера Тип передачи Режим direct Режим двойного буфера
Периферия-память Порт AHB периферии Порт AHB памяти DMA Возможен Single Возможен Возможен
Burst Запрещено
Периферия Запрещено Single Возможен Запрещено
Burst Запрещено
Память-периферия Порт AHB памяти Порт AHB периферии DMA Возможен Single Возможен Возможен
Burst Запрещено
Периферия Запрещено Single Возможен Запрещено
Burst Запрещено
Память-память Порт AHB периферии Порт AHB памяти Только DMA Запрещено Single Запрещено Запрещено
Burst

Процедура конфигурации потока. Для конфигурирования DMA-потока x (здесь x номер потока) нужно выполнить следующую последовательность действий:

1. Если поток разрешен, запретите его сбросом бита EN в регистре DMA_SxCR, затем прочитайте этот бит, чтобы убедиться, что не происходит текущая операция на потоке. Запись этого бита в 0 не сразу дает эффект остановки потока (бит сбросится не сразу), поскольку он физически обнуляется как только завершатся все текущие передачи. Когда бит EN читается как 0, это означает, что поток остановлен и готов к конфигурированию. Поэтому нужно подождать, когда сбросится бит EN перед тем, как начать конфигурировать поток. Все установленные биты в регистре статуса  (DMA_LISR и DMA_HISR), отражающие предыдущую передачу блока данных DMA, должны быть очищены до того, как поток можно заново разрешить.

2. Установите адрес регистра порта периферии в регистре DMA_SxPAR. Данные будут перемещаться из этого регистра или в этот регистр (в зависимости от направления передачи), чтобы попасть в место назначения (из него) после события периферийного устройства (peripheral event).

3. Установите адрес памяти в регистре DMA_SxMA0R (и в регистре DMA_SxMA1R в случае режима двойной буферизации). Данные будут записаны в эту память или прочитаны из неё после события периферийного устройства.

4. Сконфигурируйте в регистре DMA_SxNDTR общее количество элементов данных для передачи. После события периферии или каждой порции (beat) передачи burst это значение декрементируется.

5. Выберите канал DMA (запрос), используя поле бит CHSEL[2:0] в регистре DMA_SxCR.

6. Если контроллером потока должна быть периферия, и если она поддерживает эту функцию, то установите бит PFCTRL в регистре DMA_SxCR.

7. Сконфигурируйте приоритет потока полем бит PL[1:0] регистра DMA_SxCR.

8. Сконфигурируйте использование FIFO (разрешен он или запрещен, а также порог заполнения при передаче и приеме).

9. Сконфигурируйте направление передачи данных периферии и памяти, режим incremented/fixed, транзакции single или burst, ширину данных периферии и памяти, кольцевой режим, режим двойного буфера и прерывания после половины и/или полной передачи, и/или прерывания при возникновении ошибок в регистре DMA_SxCR.

10. Активируйте поток установкой бита EN в регистре DMA_SxCR.

Как только поток разрешен, он может обслужить любой запрос DMA от периферии, подключенной к потоку.

Как только половина данных была передана на порту AHB места назначения, установится флаг половины передачи (half-transfer flag, HTIF), и сгенерируется прерывание, если установлен бит разрешения этого прерывания (HTIE). По окончании передачи установится флаг прерывания завершения передачи (transfer complete interrupt flag, TCIF), и будет сгенерировано прерывание, если оно разрешено (если установлен бит transfer complete interrupt enable, TCIE).

Предупреждение: для выключения периферии, подключенной к запросу потока DMA, важно сначала выключить поток DMA, к которому привязана периферия, затем подождать сброса в 0 бита EN. И только потом можно безопасно запретить периферийное устройство.

Управление обработкой ошибок. Контроллер DMA может детектировать следующие ошибки:

• Transfer error: установится флаг прерывания при ошибке передачи (transfer error interrupt flag, TEIFx), когда:

– Во время доступа DMA на чтение или запись произошла ошибка шины.
– Программа запросила доступ на запись через регистр адреса памяти в режиме двойной буферизации, в то время как поток разрешен, и текущая целевая память является той, на которую влияет запись в регистр адреса памяти (см. выше секцию «Режим двойного буфера»).

• Ошибка FIFO: установится флаг прерывания ошибки FIFO (FIFO error interrupt flag, FEIFx), если:

– Детектировано состояние недогрузки FIFO (FIFO underrun).
– Детектировано состояние переполнения FIFO (FIFO overrun). Это детектирование недоступно в режиме DMA память-память, потому что эти передачи обслуживаются внутри блока DMA.
– Поток разрешен, когда порог уровня заполнения FIFO (FIFO threshold level) несовместим с размером memory burst (см. таблицу 48 «Конфигурации порога FIFO»).

• Ошибка direct mode: флаг прерывания по ошибке direct-режима (direct mode error interrupt flag, DMEIFx) может только установиться в режиме периферия-память, когда работает direct-режим, и когда сброшен бит MINC в регистре DMA_SxCR. Этот флаг установится, когда произошел запрос DMA, в то время как предыдущие данные не были полностью переданы в память (из-за того, что не была предоставлена шина памяти). В этом случае флаг показывает, что 2 элемента данных были последовательно переданы по одному и тому же адресу назначения, что может вызвать проблему, если получатель не может управлять этой ситуацией.

В direct-режиме флаг FIFO error также может установиться в следующих ситуациях:

• В режиме периферия-память FIFO может быть насыщен (overrun, переполнение), если шина памяти не была предоставлена для некоторых запросов периферии.

• В режиме память-периферия, может произойти недогрузка (underrun, опустошения) если шина памяти не была предоставлена перед возникновением запроса периферии.

Если флаг TEIFx или FEIFx установлен из-за несовместимости размера burst и уровня порога заполнения FIFO, отказавший поток автоматически запрещается путем очистки аппаратурой бита EN в регистре конфигурации (DMA_SxCR) соответствующего потока.

Если флаг DMEIFx или FEIFx установится из-за ситуации overrun или underrun, отказавший поток не будет автоматически запрещен, и в области ответственности программы будет запретить или не запрещать поток сбросом бита EN в регистре DMA_SxCR. Это потому, что не было потери данных, когда произошла ошибка такого рода.

Когда установился флаг прерывания по ошибке потока (TEIF, FEIF, DMEIF) в регистре DMA_LISR или DMA_HISR, генерируется прерывание, если установлен соответствующий бит разрешения прерывания (TEIE, FEIE, DMIE) в регистре DMA_SxCR или DMA_SxFCR.

Примечание: когда произойдет переполнение или недогрузка FIFO (overrun или underrun), данные не теряются, потому что запрос периферии не подтверждается потоком, пока не будет очищено состояние переполнения или недогрузки (overrun или underrun). Если подтверждение занимает слишком много времени, периферия сама может детектировать переполнение или недогрузку своего внутреннего буфера, и данные могут быть потеряны.

[Прерывания DMA]

Для каждого потока DMA может быть сгенерировано прерывание при следующих событиях:

• Половина передачи (half-transfer).
• Завершение передачи (transfer complete).
• Ошибка передачи (transfer error).
• Ошибка FIFO (overrun, underrun или ошибка уровня заполнения FIFO).
• Ошибка direct-режима.

Есть отдельные управляющие биты разрешения этих прерываний, как показано в таблице 50.

Таблица 50. Запросы прерываний DMA.

Событие прерывания Флаг события Бит разрешения прерывания
Половина передачи (half-transfer) HTIF HTIE
Завершение передачи TCIF TCIE
Ошибка передачи TEIF TEIE
Переполнение/недогрузка FIFO (FIFO overrun/underrun) FEIF FEIE
Ошибка режима direct DMEIF DMEIE

Примечание: перед установкой в 1 бита управления разрешением прерывания должен быть очищен флаг соответствующего события прерывания, иначе может быть немедленно сгенерировано прерывание.

[Регистры DMA]

Регистрам DMA осуществляется 32-битный доступ.

Смещение адреса 0x00, значение после сброса 0x00000000.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
зарезервировано
TCIF3
r
HTIF3
r
TEIF3
r
DMEIF3
r
зарезерв. FEIF3
r
TCIF2
r
HTIF2
r
TEIF2
r
DMEIF2
r
зарезерв. FEIF2
r
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
зарезервировано
TCIF1
r
HTIF1
r
TEIF1
r
DMEIF1
r
зарезерв. FEIF1
r
TCIF0
r
HTIF0
r
TEIF0
r
DMEIF0
r
зарезерв. FEIF0
r

Биты 31:28, 15:12 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

TCIFx (биты 27, 21, 11, 5): флаг прерывания завершения передачи потока x (x = 3..0). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_LIFCR.

0: не было события завершения передачи на stream x.
1: было событие завершения передачи на stream x

HTIFx (биты 26, 20, 10, 4): флаг прерывания половины передачи потока x (half transfer interrupt flag, x=3..0). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_LIFCR.

0: не было события половины передачи на stream x.
1: было событие половины передачи на stream x.

TEIFx (биты 25, 19, 9, 3): флаг прерывания ошибки передачи потока x (transfer error interrupt flag, x=3..0). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_LIFCR.

0: не было ошибки передачи на stream x.
1: произошла ошибка передачи на stream x.

DMEIFx (биты 24, 18, 8, 2): прерывание ошибки direct-режима потока x (direct mode error interrupt flag, x=3..0). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_LIFCR.

0: не было ошибки direct-режима на stream x.
1: произошла ошибка direct-режима на stream x.

Биты 23, 17, 7, 1 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

FEIFx (биты 22, 16, 6, 0): флаг ошибки FIFO потока x (FIFO error interrupt flag, x=3..0). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_LIFCR.

0: не было события ошибки FIFO на stream x.
1: произошло событие ошибки FIFO на stream x.

Смещение адреса 0x04, значение после сброса 0x00000000.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
зарезервировано
TCIF7
r
HTIF7
r
TEIF7
r
DMEIF7
r
зарезерв. FEIF7
r
TCIF6
r
HTIF6
r
TEIF6
r
DMEIF6
r
зарезерв. FEIF6
r
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
зарезервировано
TCIF5
r
HTIF5
r
TEIF5
r
DMEIF5
r
зарезерв. FEIF4
r
TCIF4
r
HTIF4
r
TEIF4
r
DMEIF4
r
зарезерв. FEIF4
r

Биты 31:28, 15:12 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

TCIFx (биты 27, 21, 11, 5): флаг прерывания завершения передачи потока x (x = 7..4). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_HIFCR.

0: не было события завершения передачи на stream x.
1: было событие завершения передачи на stream x

HTIFx (биты 26, 20, 10, 4): флаг прерывания половины передачи потока x (half transfer interrupt flag, x=7..4). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_HIFCR.

0: не было события половины передачи на stream x.
1: было событие половины передачи на stream x.

TEIFx (биты 25, 19, 9, 3): флаг прерывания ошибки передачи потока x (transfer error interrupt flag, x=7..4). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_HIFCR.

0: не было ошибки передачи на stream x.
1: произошла ошибка передачи на stream x.

DMEIFx (биты 24, 18, 8, 2): прерывание ошибки direct-режима потока x (direct mode error interrupt flag, x=7..4). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_HIFCR.

0: не было ошибки direct-режима на stream x.
1: произошла ошибка direct-режима на stream x.

Биты 23, 17, 7, 1 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

FEIFx (биты 22, 16, 6, 0): флаг ошибки FIFO потока x (FIFO error interrupt flag, x=7..4). Этот бит устанавливается аппаратно, сбрасывается программно запись в 1 в соответствующий бит регистра DMA_HIFCR.

0: не было события ошибки FIFO на stream x.
1: произошло событие ошибки FIFO на stream x.

Смещение адреса 0x08, значение после сброса 0x00000000.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
зарезервировано
CTCIF3
w
CHTIF3
w
CTEIF3
w
CDMEIF3
w
зарезерв. CFEIF3
w
CTCIF2
w
CHTIF2
w
CTEIF2
w
CDMEIF2
w
зарезерв. CFEIF2
w
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
зарезервировано
CTCIF1
w
CHTIF1
w
CTEIF1
w
CDMEIF1
w
зарезерв. CFEIF1
w
CTCIF0
w
CHTIF0
w
CTEIF0
w
CDMEIF0
w
зарезерв. CFEIF0
w

Биты 31:28, 15:12 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

CTCIFx (биты 27, 21, 11, 5): бит очистки флага прерывания завершения передачи потока x (clear transfer complete interrupt flag, x = 3..0). Запись 1 в этот бит очищает соответствующий флаг TCIFx в регистре DMA_LISR.

CHTIFx (биты 26, 20, 10, 4): бит очистки флага прерывания половины передачи потока x (clear half transfer interrupt flag, x = 3..0). Запись 1 в этот бит очищает соответствующий флаг HTIFx в регистре DMA_LISR.

CTEIFx (биты 25, 19, 9, 3): бит очистки флага прерывания ошибки передачи потока x (clear transfer error interrupt flag, x = 3..0). Запись 1 в этот бит очищает соответствующий флаг TEIFx в регистре DMA_LISR.

CDMEIFx (биты 24, 18, 8, 2): бит очистки флага прерывания ошибки direct-режима потока x (clear direct mode error interrupt flag, x = 3..0). Запись 1 в этот бит очищает соответствующий флаг DMEIFx в регистре DMA_LISR.

Биты 23, 17, 7, 1 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

CFEIFx (биты 22, 16, 6, 0): бит очистки флага прерывания ошибки FIFO потока x (clear FIFO error interrupt flag, x = 3..0). Запись 1 в этот бит очищает соответствующий флаг CFEIFx в регистре DMA_LISR.

Смещение адреса 0x0C, значение после сброса 0x00000000.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
зарезервировано
CTCIF7
w
CHTIF7
w
CTEIF7
w
CDMEIF7
w
зарезерв. CFEIF7
w
CTCIF6
w
CHTIF6
w
CTEIF6
w
CDMEIF6
w
зарезерв. CFEIF6
w
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
зарезервировано
CTCIF5
w
CHTIF5
w
CTEIF5
w
CDMEIF5
w
зарезерв. CFEIF5
w
CTCIF4
w
CHTIF4
w
CTEIF4
w
CDMEIF4
w
зарезерв. CFEIF4
w

Биты 31:28, 15:12 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

CTCIFx (биты 27, 21, 11, 5): бит очистки флага прерывания завершения передачи потока x (clear transfer complete interrupt flag, x = 7..4). Запись 1 в этот бит очищает соответствующий флаг TCIFx в регистре DMA_HISR.

CHTIFx (биты 26, 20, 10, 4): бит очистки флага прерывания половины передачи потока x (clear half transfer interrupt flag, x = 7..4). Запись 1 в этот бит очищает соответствующий флаг HTIFx в регистре DMA_HISR.

CTEIFx (биты 25, 19, 9, 3): бит очистки флага прерывания ошибки передачи потока x (clear transfer error interrupt flag, x = 7..4). Запись 1 в этот бит очищает соответствующий флаг TEIFx в регистре DMA_HISR.

CDMEIFx (биты 24, 18, 8, 2): бит очистки флага прерывания ошибки direct-режима потока x (clear direct mode error interrupt flag, x = 7..4). Запись 1 в этот бит очищает соответствующий флаг DMEIFx в регистре DMA_HISR.

Биты 23, 17, 7, 1 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

CFEIFx (биты 22, 16, 6, 0): бит очистки флага прерывания ошибки FIFO потока x (clear FIFO error interrupt flag, x = 7..4). Запись 1 в этот бит очищает соответствующий флаг CFEIFx в регистре DMA_HISR.

Этот регистр используется для конфигурирования соответствующего потока.

Смещение адреса 0x10 + 0x18 * x (x номер потока 0..7), значение после сброса 0x00000000.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
зарезервировано
CHSEL[2:0]
rw
MBURST[1:0]
r
w
PBURST[1:0]
rw
зарезерв. CT
rw
DBM
rw
PL[1:0]
rw
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
PINCOS
rw
MSIZE[1:0]
rw

PSIZE[1:0]
rw

MINC
rw
PINC
rw
CIRC
rw
DIR[1:0]
rw
PFCTRL
rw
TCIE
rw
HTIE
rw
TEIE
rw
DMEIE
rw
EN
rw

Биты 31:28 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

CHSEL[2:0] (биты 27:25): выбор канала. Эти биты устанавливаются и сбрасываются программой. Они защищены, и их можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

000: выбран канал 0
001: выбран канал 1

111: выбран канал 7

MBURST[1:0] (биты 24:23): конфигурация burst-передачи памяти. Эти биты устанавливаются и сбрасываются программой. Они защищены, и их можно записать только если бит EN соответствующего потока равен 0 (поток запрещен). В режиме direct эти биты принудительно сбрасываются в 0 аппаратно, как только бит EN потока стал равным 1.

00: одиночная (single) передача
01: INCR4, инкрементальная burst-передача 4 порций (beats)
10: INCR8, инкрементальная burst-передача 8 порций (beats)
11: INCR16 инкрементальная burst-передача 16 порций (beats)

PBURST[1:0] (биты 22:21): конфигурация burst-передачи периферии. Эти биты устанавливаются и сбрасываются программой. Они защищены, и их можно записать только если бит EN соответствующего потока равен 0 (поток запрещен). В режиме direct эти биты принудительно сбрасываются в 0 аппаратно.

00: одиночная (single) передача
01: INCR4, инкрементальная burst-передача 4 порций (beats)
10: INCR8, инкрементальная burst-передача 8 порций (beats)
11: INCR16 инкрементальная burst-передача 16 порций (beats)

Биты 20 зарезервирован, его значение должно сохраняться в состоянии сброса (0).

CT (бит 19): текущий целевой буфер (Current Target). Бит применяется только в режиме двойной буферизации. Он устанавливается и очищается аппаратно. Программа также может записывать в этот бит.

0: текущий целевой буфер это Memory 0 (адресован указателем DMA_SxM0AR).
1: текущий целевой буфер это Memory 1 (адресован указателем DMA_SxM1AR).

Этот бит можно записать тольео если бит EN соответствующего потока равен 0, чтобы показать целевой буфер для первой передачи. Как только поток разрешен, этот бит работает как флаг статуса, показывая, в какой целевой буфер сейчас осуществляется передача DMA.

DBM (бит 18): режим двойной буферизации (Double Buffer Mode). Этот бит устанавливаются и сбрасываются программой. Он защищен, и его можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

0: по окончании передачи не происходит переключение буфера.
1: целевой буфер в памяти переключается по окончанию передачи DMA.

PL[1:0] (биты 17:16): уровень приоритета потока (Priority Level). Эти биты устанавливаются и сбрасываются программой. Они защищены, и их можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

00: Low (низкий приоритет)
01: Medium (средний приоритет)
10: High (высокий приоритет)
11: Very high (очень высокий приоритет)

PINCOS (бит 15): размер смещения инкремента периферии. Этот бит устанавливаются и сбрасываются программой. Он защищен, и его можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

0: вычисление размера смещения для адреса периферии привязано к PSIZE.
1: размер смещения для адреса периферии фиксировано и равно 4 (32-битное выравнивание адреса).

Этот бит не имеет значения, если бит PINC равен 0. Этот бит принудительно сбрасывается аппаратно, когда поток разрешен (бит EN равен 1), если выбран direct-режим, или значение PBURST отличается от 00.

MSIZE[1:0] (биты 14:13): ширина (разрядность) данных памяти. Эти биты устанавливаются и сбрасываются программой. Они защищены, и их можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

00: байт (8 бит)
01: полуслово (16 бит)
10: слово (32 бита)
11: зарезервировано

В direct-режиме MSIZE принудительно устанавливаются аппаратурой в то же значение, что и PSIZE, как только EN станет равным 1.

PSIZE[1:0] (биты 12:11): ширина (разрядность) данных периферийного устройства. Эти биты устанавливаются и сбрасываются программой. Они защищены, и их можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

00: байт (8 бит)
01: полуслово (16 бит)
10: слово (32 бита)
11: зарезервировано

MINC (бит 10): режим инкремента адреса памяти (Memory INCrement mode). Этот бит устанавливаются и сбрасываются программой. Он защищен, и его можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

0: значение указателя на память фиксировано.
1: адрес в указателе на память инкрементируется после каждой передачи данных (инкремент осуществляется в соответствии с полем бит MSIZE).

PINC (бит 9): режим инкремента адреса периферии (Peripheral INCrement mode). Этот бит устанавливается и сбрасывается программой. Он защищен, и его можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

0: значение указателя на регистр периферии фиксировано.
1: адрес в указателе на регистры периферии инкрементируется после каждой передачи данных (инкремент осуществляется в соответствии с полем бит PSIZE).

CIRC (бит 8): кольцевой режим (CIRCular mode). Этот бит устанавливается и сбрасывается программой, и может быть очищен аппаратно.

0: режим кольцевого буфера запрещен.
1: режим кольцевого буфера разрешен.

Когда контроллером потока установлена периферия (бит PFCTRL равен 1), и поток разрешен (бит EN равен 1), то этот бит автоматически сбрасывается в 0 аппаратурой. Он автоматически принудительно устанавливается аппаратурой в 1, если установлен бит DBM, как только поток стал разрешенным (бит EN равен 1).

DIR[1:0] (биты 7:6): направление передачи данных (data transfer DIRection). Эти биты устанавливаются и сбрасываются программой. Они защищены, и их можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

00: периферия-память
01: память-периферия
10: память-память
11: зарезервировано

PFCTRL (бит 5): контроллером потока установлена периферия (Peripheral Flow ConTRoLler). Этот бит устанавливается и сбрасывается программой. Он защищен, и его можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

0: контроллером потока является блок DMA.
1: контроллером потока является периферийное устройство.

Когда выбран режим память-память (DIR[1:0] = 10), то этот бит автоматически сбрасывается в 0 аппаратурой.

TCIE (бит 4): разрешение прерывания по завершению передачи (Transfer Complete Interrupt Enable). Этот бит устанавливается и сбрасывается программой.

0: прерывание TC запрещено.
1: прерывание TC разрешено.

HTIE (бит 3): разрешение прерывания на половине передачи (Half Transfer Interrupt Enable). Этот бит устанавливается и сбрасывается программой.

0: прерывание HT запрещено.
1: прерывание HT разрешено.

TEIE (бит 2): разрешение прерывания при ошибке передачи (Transfer Error Interrupt Enable). Этот бит устанавливается и сбрасывается программой.

0: прерывание TE запрещено.
1: прерывание TE разрешено.

DMEIE (бит 1): разрешение прерывания при ошибке direct-режима (Direct Mode Error Interrupt Enable). Этот бит устанавливается и сбрасывается программой.

0: прерывание DME запрещено.
1: прерывание DME разрешено.

EN (бит 0): разрешение потока и флаг готовности потока к конфигурированию, когда он читается как 0 (stream ENable). Этот бит устанавливается и сбрасывается программой.

0: поток запрещен, его можно конфигурировать.
1: поток разрешен и работает.

Этот бит может быть очищен аппаратно:

– По окончанию передачи DMA (поток готов к конфигурированию передачи).
– Если произошла ошибка передачи на шинах AHB master.
– Когда порог заполнения FIFO на памяти порта AHB несовместим с размером burst.

Когда этот бит читается как 0, коду программы разрешено запрограммировать в регистрах конфигурацию потока и биты FIFO. Запрещено записывать в эти регистры, когда бит EN читается как 1.

Примечание: перед установкой бита EN в 1 для запуска новой передачи, флаги событий в регистре DMA_LISR или DMA_HISR соответствующего потока должны быть сброшены.

Смещение адреса 0x14 + 0x18 * x (x номер потока 0..7), значение после сброса 0x00000000.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
зарезервировано
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
NDT[15:0]
rw

Биты 31:16 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

NDT[15:0]: количество элементов данных для передачи (Number of Data iTems), это количество может быть в диапазоне от 0 до 65535. Этот регистр можно записать только если поток запрещен (EN = 0). Когда поток разрешен (EN = 1), этот регистр работает только на чтение, показывая количество оставшихся данных для передачи. Этот регистр декрементируется после каждой передачи DMA.

Как только передача завершена, этот регистр может либо оставаться в 0 (когда поток в режиме normal) или может быть автоматически перезагружен ранее запрограммированным значением, что бывает в двух случаях:

– Когда поток запрограммирован в режиме кольцевого буфера (circular mode).
– Когда поток снова разрешен установкой бита EN в 1.

Если значение этого регистра равно 0, то никакая транзакция не может быть обработана, даже если поток разрешен.

Смещение адреса 0x18 + 0x18 * x (x номер потока 0..7), значение после сброса 0x00000000.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
PAR[31:16]
rw
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
PAR[15:0]
rw

PAR[31:0]: адрес регистра периферийного устройства. Базовый адрес регистра данных периферии, откуда/куда будут считываться/записываться данные. Этот регистр защищен, и его можно записать только если бит EN = 0 в соответствующем регистре потока DMA_SxCR.

Смещение адреса 0x1C + 0x18 * x (x номер потока 0..7), значение после сброса 0x00000000.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
M0A[31:16]
rw
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
M0A[15:0]
rw

M0A[31:0]: адрес буфера 0 в памяти (Memory 0 Address). Базовый адрес области памяти 0, из которой / в которую данные будут читаться/записываться. Эти биты защищены от записи. Их можно записать, только если:

– Поток запрещен (бит EN равен 0 в регистре DMA_SxCR), или
– поток разрешен (бит EN равен 1 в регистре DMA_SxCR), и бит CT = 1 в регистре DMA_SxCR (режим двойной буферизации).

Смещение адреса 0x20 + 0x18 * x (x номер потока 0..7), значение после сброса 0x00000000.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
M1A[31:16]
rw
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
M1A[15:0]
rw

M1A[31:0]: адрес буфера 1 в памяти (Memory 1 Address), используется в режиме двойной буферизации. Базовый адрес области памяти 1, из которой / в которую данные будут читаться/записываться. Этот регистр используется только в режиме двойной буферизации, и он защищены от записи. Его можно записать, только если:

– Поток запрещен (бит EN равен 0 в регистре DMA_SxCR), или
– поток разрешен (бит EN равен 1 в регистре DMA_SxCR), и бит CT = 0 в регистре DMA_SxCR.

Смещение адреса 0x24 + 0x24 * x (x номер потока 0..7), значение после сброса 0x00000021.

   31      30       29       28       27       26       25       24       23       22       21       20       19       18       17       16   
зарезервировано
   15       14       13       12       11       10       9       8       7       6       5       4       3       2       1       0   
зарезервировано FEIE
rw
зарезерв. FS[2:0]
r
DMDIS
rw
FTH[1:0]
rw

Биты 31:8 зарезервированы, их значение должно сохраняться в состоянии сброса (все нули).

FEIE (бит 7): разрешение прерывания при ошибке FIFO (FIFO Error Interrupt Enable). Этот бит устанавливается и сбрасывается программой.

0: прерывание FE запрещено.
1: прерывание FE разрешено.

Бит 6 зарезервирован, его значение должно сохраняться в состоянии сброса (0).

FS[2:0] (биты 5:3): статус FIFO. Эти биты работают только на чтение. 

000: 0 < fifo_level < 1/4
001: 1/4 < = fifo_level < 1/2
010: 1/2 < = fifo_level < 3/4
011: 3/4 < = fifo_level < full
100: FIFO пуст
101: FIFO заполнен

Другие значения не имеют смысла. Здесь fifo_level это уровень порога заполненности стека FIFO. Биты FS не имеют значения в direct-режиме (бит DMDIS сброшен в 0).

DMDIS (бит 2): запрет direct-режима (Direct Mode DISable). Этот бит устанавливается и сбрасывается программой, и может быть сброшен аппаратно. Бит защищен, и программа может его записать только если бит EN соответствующего потока сброшен в 0 (поток запрещен).

0: разрешен direct-режим.
1: direct-режим запрещен.

Этот бит может быть установлен аппаратно, если выбран режим память-память (биты DIR регистра DMA_SxCR в состоянии 10), и бит EN регистра DMA_SxCR в 1, потому что direct-режим недопустим в конфигурации память-память.

FTH[1:0] (биты 1:0): выбор порога заполненности стека FIFO (FIFO ThresHold). Эти биты устанавливаются и сбрасываются программой. Они защищены, и их можно записать только если бит EN соответствующего потока равен 0 (поток запрещен).

00: 1/4 от заполненного FIFO
01: 1/2 от заполненного FIFO
10: 3/4 от заполненного FIFO
11: полный FIFO

Эти биты не используются в direct-режиме, когда значение DMDIS сброшено в 0.

Таблица 51. Карта памяти регистров DMA. См. секцию 2.3 «Memory map» даташита [2] для информации по граничным адресам областей памяти.

STM32F4xx DMA register map

[Ссылки]

1. STM32F427xx STM32F429xx Datasheet site:st.com.
2. RM0090 Reference manual STM32F405/415, STM32F407/417, STM32F427/437 and STM32F429/439 advanced Arm®-based 32-bit MCUs site:st.com.
3. STM32: аббревиатуры и термины.
4. STM32: использование контроллера DMA.

Thank you for attention)

Could you please elaborate a little bit?

My dma init code right now:

#define TIM2_CCR1_Address ((unsigned int)0x40000000 + 0x34)
#define DMA_BUFFER_SIZE   8
uint32_t buffer[DMA_BUFFER_SIZE];
//^^----globally declared

static void my_DMA_init(void) {
  // enable DMA1 clocking
  RCC->AHB1ENR |= (0x1 << 0);
  // clear EN bit to 0
  DMA1_Stream0->CR &= ~(0x1 << 0);
  // safeguard EN bit reset
  while (DMA1_Stream0->CR & 0x1);
  // check LISR HISR registers
  if ((DMA1->HISR == 0) && (DMA1->LISR == 0))
    printf("status registers is clearrn");
  else
    printf("status register is not clear -- DMA wont startrn");
  // set peripheral address
  DMA1_Stream0->PAR = TIM2_CCR1_Address;
  // set memory address
  DMA1_Stream0->M0AR = (unsigned int)buffer;
  // set total number of data items
  DMA1_Stream0->NDTR = 10;

  // NOTE: configuration TIM2_CH1 interrupt route
  // set DMAMUX to route request (TIM2_CH1)
  DMAMUX1_Channel0->CCR |= 18U;

  // set DMA priority (very high)
  DMA1_Stream0->CR |= (0x3 << 16);
  // set memory data size (32)
  DMA1_Stream0->CR |= (0x2 << 13);
  // set peripheral data size (32)
  DMA1_Stream0->CR |= (0x2 << 11);
  // set memory address increment  (enable)
  DMA1_Stream0->CR |= (0x1 << 10);
  // set peripheral address increment (disable)
  DMA1_Stream0->CR |= (0x0 << 9);
  // set circular buffer mode (enable)
  DMA1_Stream0->CR |= (0x1 << 8);
  // set data transfer direction (peripheral to memory)
  DMA1_Stream0->CR |= (0x0 << 6);
  // set transfer complete interrupt
  DMA1_Stream0->CR |= (0x1 << 4);
  // set transfer error interrupt
  DMA1_Stream0->CR |= (0x1 << 2);
  // enable DMA1
  DMA1_Stream0->CR |= (0x1 << 0);
  // enable IRQ
  NVIC_EnableIRQ(DMA1_Stream0_IRQn);
  printf("DMA1_Stream0 %u rn", (DMA1_Stream0->CR & 0x1));
}

Понравилась статья? Поделить с друзьями:
  • Dma error fatal error system halted
  • Dma controller error
  • Dm8261 partition error 03
  • Dm1spn2 ошибка камаз
  • Dm verity error как исправить