Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2741
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

Управление DMA

248

Таблица 4: Отображение каналов/запросов к DMA в микроконтроллере STM32L053

9.2. Модуль HAL_DMA

После долгих разговоров пришло время начать писать код.

Строго говоря, программирование контроллера DMA довольно простое, особенно если понятно, как контроллер DMA работает с теоретической точки зрения. Кроме того, CubeHAL предназначен для абстрагирования большинства базовых аппаратных подробностей.

Все функции HAL, связанные с манипулированием DMA, спроектированы таким образом, что они принимают в качестве первого параметра экземпляр структуры Си DMA_HandleTypeDef. Эта структура немного отличается от HAL CubeF2/F4/F7 и других CubeHAL из-за другой реализации контроллера DMA, как было показано в предыдущих параграфах. По этой причине мы покажем их отдельно.

Управление DMA

249

9.2.1. DMA_HandleTypeDef в HAL для F0/F1/F3/L0/L1/L4

Структура DMA_HandleTypeDef определена в HAL CubeF0/F1/F3/L1 следующим образом:

typedef struct {

 

 

 

 

DMA_Channel_TypeDef

*Instance;

/*

Базовый адрес регистров

*/

DMA_InitTypeDef

Init;

/*

Параметры связи через DMA

*/

HAL_LockTypeDef

Lock;

/*

Блокировка объекта DMA

*/

__IO HAL_DMA_StateTypeDef State;

/*

Состояние работы DMA

*/

void

*Parent;

/*

Состояние родительского объекта

*/

void

(* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);

 

void

(* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);

void

(* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);

 

__IO uint32_t

ErrorCode;

/*

Код ошибки DMA

*/

} DMA_HandleTypeDef;

 

 

 

 

Давайте более подробно рассмотрим наиболее важные поля данной структуры.

Instance (экземпляр): это указатель на дескриптор пары DMA/канал, который мы

будем использовать. Например, DMA1_Channel5 обозначает пятый канал DMA1. Помните, что каналы связаны с периферийными устройствами во время проектирования микроконтроллера, поэтому обратитесь к техническому описанию вашего микроконтроллера, чтобы узнать канал, связанный с периферийным устройством, которое вы хотите использовать в режиме DMA.

Init: является экземпляром структуры Си DMA_InitTypeDef, которая используется

для конфигурации пары DMA/канал. Мы рассмотрим ее более подробно в ближайшее время.

Parent (родитель): данный указатель используется HAL для отслеживания пери-

ферийных дескрипторов, связанных с текущей парой DMA/канал. Например, если мы используем UART в режиме DMA, это поле будет указывать на экземпляр UART_HandleTypeDef. Скоро мы увидим, как периферийные дескрипторы «связаны» с этим полем.

XferCpltCallback, XferHalfCpltCallback, XferErrorCallback: это указатели на функ-

ции, используемые в качестве обратных вызовов для осведомления пользовательского кода о том, что передача через DMA завершена, завершена наполовину или произошла ошибка. Они автоматически вызываются HAL при срабатывании прерываний DMA функцией HAL_DMA_IRQHandler(), как мы увидим далее.

Все действия по конфигурации DMA/канал выполняются с использованием экземпляра структуры Си DMA_InitTypeDef, которая определена следующим образом:

typedef struct { uint32_t Direction; uint32_t PeriphInc; uint32_t MemInc;

uint32_t PeriphDataAlignment; uint32_t MemDataAlignment; uint32_t Mode;

uint32_t Priority; } DMA_InitTypeDef;

Управление DMA

250

Direction: задает направление передачи через DMA и может принимать одно из

значений, указанных в таблице 5.

PeriphInc: как было сказано в предыдущих параграфах, контроллер DMA имеет

один периферийный порт, используемый для указания адреса периферийного регистра, участвующего в передаче памяти (например, для интерфейса UART адрес его Регистра данных (Data Register, DR)). Поскольку передача памяти через DMA обычно включает в себя несколько байт, DMA может быть запрограммирован на автоматическое увеличение периферийного регистра для каждого переданного байта. Это справедливо как для передачи типа память-в-память, так и для периферийных устройств, предоставляющих байтовую, полусловную и пословную адресации (как внешняя память SRAM). В этом случае поле принимает значение

DMA_PINC_ENABLE, в противном случае DMA_PINC_DISABLE.

MemInc: это поле имеет то же значение, что и поле PeriphInc, но оно предполагает

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

PeriphDataAlignment: размеры передаваемых данных периферийного устройства и памяти полностью программируются через это поле и следующее. Оно может принимать значение из таблицы 6. Контроллер DMA спроектирован так, чтобы автоматически выполнять выравнивание данных (упаковку/распаковку) при различии в размерах данных источника и пункта назначения. Эта тема выходит за рамки данной книги. Пожалуйста, обратитесь к справочному руководству по вашему микроконтроллеру.

MemDataAlignment: задает размер данных, передаваемых из памяти, и может при-

нимать значение из таблицы 7.

Mode: контроллер DMA в микроконтроллерах STM32 имеет два режима работы:

DMA_NORMAL и DMA_CIRCULAR. В обычном режиме (normal mode) DMA отправляет ука-

занный объем данных из источника в порт назначения и останавливает транзакции. Он должен быть снова переподготовлен, чтобы сделать еще одну передачу. В циклическом режиме (circular mode) в конце передачи он автоматически сбрасывает счетчик передачи и начинает передачу снова с первого байта буфера источника (то есть он рассматривает буфер источника как кольцевой буфер). Данный режим также называется непрерывным режимом (continuous mode), и это единственный способ достичь действительно высокой скорости передачи в некоторых периферийных устройствах (например, высокоскоростных устройствах SPI).

Priority: еще одна важная особенность контроллера DMA – способность назна-

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

Управление DMA

 

 

 

251

Таблица 5: Доступные направления передачи через DMA

 

Направление передачи через DMA

Описание

 

 

 

 

 

DMA_PERIPH_TO_MEMORY

 

Направление передачи из периферии в память

 

DMA_MEMORY_TO_PERIPH

 

Направление передачи из памяти в периферию

 

DMA_MEMORY_TO_MEMORY

 

Направление передачи из памяти в память

 

Таблица 6: Размер данных периферийного устройства при передаче через DMA

 

Размер периферийных данных

Описание

 

 

 

 

 

DMA_PDATAALIGN_BYTE

 

Выравнивание периферийных данных: побайтно

 

DMA_PDATAALIGN_HALFWORD

 

Выравнивание периферийных данных: полусловно

DMA_PDATAALIGN_WORD

 

Выравнивание периферийных данных: пословно

 

Таблица 7: Размер данных в памяти при передаче через DMA

 

Размер данных в памяти

Описание

 

 

 

 

 

DMA_MDATAALIGN_BYTE

 

Выравнивание данных в памяти: байт

 

DMA_MDATAALIGN_HALFWORD

 

Выравнивание данных в памяти: полуслово

 

DMA_MDATAALIGN_WORD

 

Выравнивание данных в памяти: слово

 

Таблица 8: Доступные приоритеты канала DMA

 

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

 

Описание

 

 

 

 

 

DMA_PRIORITY_LOW

 

Уровень приоритета: низкий

 

DMA_PRIORITY_MEDIUM

 

Уровень приоритета: средний

 

DMA_PRIORITY_HIGH

 

Уровень приоритета: высокий

 

DMA_PRIORITY_VERY_HIGH

 

Уровень приоритета: очень высокий

 

9.2.2. DMA_HandleTypeDef в HAL для F2/F4/F7

 

Структура DMA_HandleTypeDef определена в HAL CubeF2/F4/F7 следующим образом:

 

typedef struct {

 

 

 

 

DMA_Stream_TypeDef

*Instance;

/*

Базовый адрес регистров

*/

DMA_InitTypeDef

Init;

/* Параметры связи через DMA

*/

HAL_LockTypeDef

Lock;

/*

Блокировка объекта DMA

*/

__IO HAL_DMA_StateTypeDef State;

/*

Состояние работы DMA

*/

void

*Parent;

/*

Состояние родительского объекта

*/

void

(* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);

 

void

(* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);

void

(* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma);

void

(* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);

__IO uint32_t

ErrorCode;

/*

Код ошибки DMA

*/

uint32_t

StreamBaseAddress; /* Базовый адрес потока DMA

*/

uint32_t

StreamIndex;

/* Индекс потока DMA

*/

} DMA_HandleTypeDef;

 

 

 

 

Давайте более подробно рассмотрим наиболее важные поля данной структуры.

Управление DMA

252

Instance (экземпляр): указатель на дескриптор потока, который мы будем исполь-

зовать. Например, DMA1_Stream6 обозначает седьмой13 поток DMA1. Помните, что поток должен быть связан с каналом, прежде чем его можно будет использовать. Это достигается через поле Init, как мы увидим через некоторое время. Помните также, что каналы связаны с периферийными устройствами во время проектирования микроконтроллера, поэтому обратитесь к техническому описанию вашего микроконтроллера, чтобы увидеть канал, связанный с периферийным устройством, которое вы хотите использовать в режиме DMA.

Init: это экземпляр структуры Си DMA_InitTypeDef, которая используется для кон-

фигурации тройки DMA/канал/поток. Мы рассмотрим ее более подробно в ближайшее время.

Parent (родитель): этот указатель используется HAL для отслеживания перифе-

рийных дескрипторов, связанных с текущей парой DMA/канал. Например, если мы используем UART в режиме DMA, это поле будет указывать на экземпляр UART_HandleTypeDef. Скоро мы увидим, как периферийные дескрипторы «связаны» с этим полем.

XferCpltCallback, XferHalfCpltCallback, XferM1CpltCallback, XferErrorCallback: это

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

произошла ошибка. Они автоматически вызываются HAL при срабатывании прерывания DMA функцией HAL_DMA_IRQHandler(), как мы увидим далее.

Все действия по конфигурации DMA/канал выполняются с использованием экземпляра структуры Си DMA_InitTypeDef, которая определена следующим образом:

typedef struct { uint32_t Channel; uint32_t Direction; uint32_t PeriphInc; uint32_t MemInc;

uint32_t PeriphDataAlignment; uint32_t MemDataAlignment; uint32_t Mode;

uint32_t Priority; uint32_t FIFOMode; uint32_t FIFOThreshold; uint32_t MemBurst; uint32_t PeriphBurst;

} DMA_InitTypeDef;

Channel: задает канал DMA, используемый для выбранного потока. Может прини-

мать значения от DMA_CHANNEL_0, DMA_CHANNEL_1 до DMA_CHANNEL_7. Помните, что пе-

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

13 Отсчет потоков начинается с нуля.

Управление DMA

253

Direction: задает направление передачи DMA и может принимать одно из значе-

ний, указанных в таблице 5.

PeriphInc: как было сказано в предыдущих параграфах, контроллер DMA имеет

один периферийный порт, используемый для указания адреса периферийного регистра, участвующего в передаче памяти (например, для интерфейса UART адрес его Регистра данных (Data Register, DR)). Поскольку передача памяти через DMA обычно включает в себя несколько байт, DMA может быть запрограммирован на автоматическое увеличение периферийного регистра для каждого переданного байта. Это справедливо как для передачи типа память-в-память, так и для периферийных устройств, предоставляющих байтовую, полусловную и пословную адресации (как внешняя память SRAM). В этом случае поле принимает значение

DMA_PINC_ENABLE, в противном случае DMA_PINC_DISABLE.

MemInc: это поле имеет то же значение, что и поле PeriphInc, но оно предполагает

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

PeriphDataAlignment: размеры передаваемых данных периферийного устройства и памяти полностью программируются через это поле и следующее. Оно может принимать значение из таблицы 6. Контроллер DMA спроектирован так, чтобы автоматически выполнять выравнивание данных (упаковку/распаковку) при различии в размерах данных источника и пункта назначения. Эта тема выходит за рамки данной книги. Пожалуйста, обратитесь к справочному руководству по вашему микроконтроллеру.

MemDataAlignment: задает размер данных, передаваемых из памяти, и может при-

нимать значение из таблицы 7.

Mode: контроллер DMA в микроконтроллерах STM32 имеет два режима работы:

DMA_NORMAL и DMA_CIRCULAR. В обычном режиме (normal mode) DMA отправляет ука-

занный объем данных из источника в порт назначения и останавливает транзакции. Он должен быть снова переподготовлен, чтобы сделать еще одну передачу. В циклическом режиме (circular mode) в конце передачи он автоматически сбрасывает счетчик передачи и начинает передачу снова с первого байта буфера источника (то есть он рассматривает буфер источника как кольцевой буфер). Данный режим также называется непрерывным режимом (continuous mode), и это единственный способ достичь действительно высокой скорости передачи в некоторых периферийных устройствах (например, высокоскоростных устройствах SPI).

Priority: одна важная особенность контроллера DMA – способность назначать

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

FIFOMode: используется для включения/отключения в DMA режима FIFO при по-

мощи макросов DMA_FIFOMODE_ENABLE/DMA_FIFOMODE_DISABLE. В микроконтроллерах

STM32F2/F4/F7 каждый поток имеет независимый буфер FIFO из 4 слов (4*32 бита). Буфер FIFO используется для временного хранения данных, поступающих от источника, до их передачи в пункт назначения. При отключении используется прямой режим, англ. direct mode (это «обычный» режим, доступный в других микроконтроллерах STM32).

Управление DMA

254

Режим FIFO предоставляет несколько преимуществ: он сокращает доступ к SRAM и, таким образом, дает больше времени другим ведущим устройствам для доступа к шинной матрице без дополнительного параллелизма; он позволяет программному обеспечению выполнять пакетные транзакции (burst transactions), которые оптимизируют пропускную способность передачи (подробнее об этом чуть позже); также он позволяет упаковывать/распаковывать данные для адаптации размеров данных источника и получателя без дополнительного доступа к DMA. Если в DMA включен режим FIFO, можно использовать упаковку/распаковку данных и/или режим пакетной передачи. Буфер FIFO автоматически очищается в соответствии с пороговым уровнем. Этот уровень конфигурируется программно в диапазонах 1/4, 1/2, 3/4 буфера или его полный размер.

FIFOThreshold: задает пороговый уровень буфера FIFO и может принимать значе-

ние из таблицы 9.

MemBurst: алгоритм планирования Round Robin управляет доступом к потоку DMA,

прежде чем он сможет передать последовательность байтов по шине AHB. Это «замедляет» операции передачи, и для некоторых высокоскоростных периферийных устройств это может стать узким местом. Пакетная передача позволяет потоку DMA неоднократно передавать данные, не проходя все этапы, необходимые для передачи каждого фрагмента данных в отдельной транзакции. Режим пакетной передачи (burst mode) работает в сочетании с буфером FIFO и ничего не говорит о количестве переданных байт. Он основан на конфигурации поля MemDataAlignment (когда мы выполняем передачу типа память-в-периферию). MemBurst указывает количество транзакций, выполненных потоком, и состоит из байтов, полуслов и слов в зависимости от конфигурации источника. Поле MemBurst может принимать одно из значений таблицы 10.

PeriphBurst: это поле означает то же, что и предыдущее, но оно связано с переда-

чами типа периферия-в-память. Может принимать значение из таблицы 11.

Таблица 9: Доступные пороговые уровни FIFO

Пороговые уровни FIFO

Описание

 

 

DMA_FIFO_THRESHOLD_1QUARTERFULL

Конфигурация порога в 1/4 буфера FIFO

DMA_FIFO_THRESHOLD_HALFFULL

Конфигурация порога в 1/2 буфера FIFO

DMA_FIFO_THRESHOLD_3QUARTERSFULL

Конфигурация порога в 3/4 буфера FIFO

DMA_FIFO_THRESHOLD_FULL

Конфигурация порога в полный буфер FIFO

Таблица 10: Доступные режимы DMA пакетной передачи памяти

Режимы пакетной передачи памяти

Описание

 

 

DMA_MBURST_SINGLE

Одиночный пакет

DMA_MBURST_INC4

Разрыв на 4 пакета (Burst of 4 beats)

DMA_MBURST_INC8

Разрыв на 8 пакетов

DMA_MBURST_INC16

Разрыв на 16 пакетов

Таблица 11: Доступные режимы DMA пакетной передачи периферийных данных

Режимы пакетной передачи периферийных данных

Описание

 

 

 

DMA_PBURST_SINGLE

Одиночный пакет

DMA_PBURST_INC4

Разрыв на 4

пакета

DMA_PBURST_INC8

Разрыв на 8

пакетов

DMA_PBURST_INC16

Разрыв на 16 пакетов