Форум программистов, компьютерный форум, киберфорум
locm
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

STM32F103C8T6 - Timer DMA GPIO

Запись от locm размещена 06.03.2018 в 23:58
Показов 23009 Комментарии 5
Метки dma, embitz, gpio, stm32

У модуля DMA довольно простая задача - копировать данные из одного места в другое с заданными настройками. Запустить копирование можно несколькими способами, как программно, так и аппаратно событием от периферии. Сейчас пойдет речь о запуске DMA по событию от таймера. Для примера сделаем полностью аппаратную "мигалку светодиодом" подключенную к порту PC13.
Код.
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include "stm32f10x_conf.h"
 
extern uint32_t SystemCoreClock;
volatile uint16_t DmaBuff[2] = {0, 1<<13};
 
void Config(void)
{
 
    // Настройка GPIO.
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // Вкл. порт GPIOC,
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  // и модуль альтернативных функций.
    GPIO_SetBits(GPIOC, GPIO_Pin_13);
 
    {
        GPIO_InitTypeDef GPIO;
 
        GPIO.GPIO_Pin = GPIO_Pin_13;
        GPIO.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO.GPIO_Speed = GPIO_Speed_50MHz;
 
        GPIO_Init(GPIOC, &GPIO); // Настройка GPIOC.13 в режим выхода.
    }
 
   // Настройка DMA.
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // Вкл. модуль DMA1.
   {
        DMA_InitTypeDef Dma;
 
        Dma.DMA_MemoryBaseAddr = (uint32_t) DmaBuff;           // Источник данных массив DmaBuff.
        Dma.DMA_PeripheralBaseAddr = (uint32_t) &(GPIOC->ODR); // Приемник - регистр ODR порта GPIOC.
        Dma.DMA_DIR = DMA_DIR_PeripheralDST;                   // Периферия является приемником данных.
        Dma.DMA_BufferSize = 2;                                // Размер данных в "словах".
        Dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;     // Не изменять адрес приемника.
        Dma.DMA_MemoryInc = DMA_MemoryInc_Enable;              // Увеличивать адрес памяти.
        Dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // Размер "слова" - 2 байта.
        Dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;         // Размер "слова" - 2 байта.
        Dma.DMA_Mode = DMA_Mode_Circular;                      // Циклическая работа.
        Dma.DMA_Priority = DMA_Priority_Low;                   // Приоритет канала DMA.
        Dma.DMA_M2M = DMA_M2M_Disable;                         // Не копирование из памяти в память.
        DMA_Init(DMA1_Channel2, &Dma);
   }
 
   // Настройка таймера.
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // Вкл. таймер TIM2.
 
   {
        TIM_TimeBaseInitTypeDef Tim;
        TIM_TimeBaseStructInit(&Tim);
 
        Tim.TIM_Prescaler = SystemCoreClock / 10000 - 1; // Настройка предделителя таймера.
        Tim.TIM_Period = 10000 - 1;
 
        TIM_TimeBaseInit(TIM2, &Tim);
    }
 
    TIM_GenerateEvent(TIM2, TIM_EventSource_Update); // Генерация события при переполнении таймера.
    TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE);        // Разрешаем запуск DMA по событию переполнения.
    DMA_Cmd(DMA1_Channel2, ENABLE);                  // Разрешаем работу 2 канала модуля DMA1.
    TIM_Cmd(TIM2, ENABLE);                           // Разрешаем работу таймера TIM2.
}
 
int main(void)
{
    SystemCoreClockUpdate();
    Config();
 
    while(1)
    {
    }
}
В массиве DmaBuff находятся данные выводимые в порт через DMA. В нулевом элементе массива находится 0, а в первом - установлен 13 бит который соответствует выводу порта PC13. Поочередная запись нулевого и первого элемента массива в порт приведет к изменению логического уровня на выводе PC13.
В функции Config сперва настраивается порт PC13 для работы на выход. Далее второй канал модуля DMA1 конфигурируется таким образом чтобы при запуске каждой транзакции DMA, данные копировались из массива DmaBuff в регистр ODR порта GPIOC. Работа производится циклически и как только будет достигнут конец массива, копирование начнется сначала. После настраивается таймер TIM2 таким образом чтобы он переполнялся раз в секунду и разрешается генерация события от переполнения. Затем разрешается запуск транзакций DMA от события переполнения таймера TIM2 и разрешаются работа второго канала модуля DMA1 и таймера. После всех этих действий, таймер начинает работать и генерировать события каждую секунду, от которого запускается работа DMA и копирование данных из массива в порт.
Вот таким довольно простым методом можно сделать полностью аппаратную "мигалку светодиодом". Светодиод мигает, а процессор занимается своими делами.
Но у этого метода есть существенный недостаток - запись в регистр ODR влияет на весь порт, а не на отдельные выходы. Чтобы избавится от этого недостатка, нужно использовать вместо ODR, регистр BSRR. Это 32-ух битный регистр, в котором старшие 16 бит используются установки логического 0 на выводах порта, а младшие 16 бит - для установки логической 1.
Такой метод позволяет изменять состояние одного или нескольких выводов порта не затрагивая остальные.
Для этого в код нужно внести небольшие изменения. Объявление массива.
C
1
volatile uint32_t DmaBuff[2] = {1<<13<<16, 1<<13}; // Настройка DMA.
Настройка DMA.
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   // Настройка DMA.
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // Вкл. модуль DMA1.
   {
        DMA_InitTypeDef Dma;
 
        Dma.DMA_MemoryBaseAddr = (uint32_t) DmaBuff;           // Источник данных массив DmaBuff.
        Dma.DMA_PeripheralBaseAddr = (uint32_t) &(GPIOC->BSRR); // Приемник - регистр BSRR порта GPIOC.
        Dma.DMA_DIR = DMA_DIR_PeripheralDST;                   // Периферия является приемником данных.
        Dma.DMA_BufferSize = 2;                                // Размер данных в "словах".
        Dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;     // Не изменять адрес приемника.
        Dma.DMA_MemoryInc = DMA_MemoryInc_Enable;              // Увеличивать адрес памяти.
        Dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // Размер "слова" - 4 байта.
        Dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;         // Размер "слова" - 4 байта.
        Dma.DMA_Mode = DMA_Mode_Circular;                      // Циклическая работа.
        Dma.DMA_Priority = DMA_Priority_Low;                   // Приоритет канала DMA.
        Dma.DMA_M2M = DMA_M2M_Disable;                         // Не копирование из памяти в память.
        DMA_Init(DMA1_Channel2, &Dma);
   }
А теперь о том где это может пригодится на практике. Мигать светодиодом не имеет большого смысла. Проще добиться того же результата таймером в ШИМ режиме. Применять подобный метод целесообразно например для аппаратной реализации динамической индикации, т. е. без прерываний.
Подобным методом можно не только выводить данные в порт, но и считывать из него, что позволит аппаратно реализовать работу например с 1-Wire, DHT22 и др устройствами.

Программа тестировалась на "китайской" плате.
Нажмите на изображение для увеличения
Название: STM32F103C8T6.jpg
Просмотров: 1691
Размер:	58.9 Кб
ID:	4715
Вложения
Тип файла: 7z STM32F103C8T6_DMA_GPIO.7z (197.2 Кб, 787 просмотров)
Метки dma, embitz, gpio, stm32
Размещено в Микроконтроллеры
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 5
Комментарии
  1. Старый комментарий
    Подобным методом можно не только выводить данные в порт, но и считывать из него
    Если можно намекните как это сделать
    желательно по внешнему прерыванию...
    Запись от Khludenkov размещена 12.03.2018 в 09:01 Khludenkov вне форума
  2. Старый комментарий
    Необходимо заменить адрес регистра ODR, на IDR.
    Указать что источник данных - периферия, заменив DMA_DIR_PeripheralDST на DMA_DIR_PeripheralSRC.
    Дальнейшие действия зависят от того что в итоге нужно получить. Возможно таймер в режиме захвата совместно с DMA лучше подойдет для этой задачи.
    Запись от locm размещена 12.03.2018 в 16:26 locm вне форума
  3. Старый комментарий
    Спасибо!
    Т.е. можно сделать тактирование таймера от внешнего вывода и на каждый его такт делать прерывание...
    Запись от Khludenkov размещена 13.03.2018 в 08:53 Khludenkov вне форума
  4. Старый комментарий
    Напишите (лучше на форуме в разделе микроконтроллеров) максимально подробно что требуется. Потому что при такой постановке вопроса, наиболее подходящим будет внешние прерывание (без использования таймера).
    Запись от locm размещена 13.03.2018 в 13:23 locm вне форума
  5. Старый комментарий
    Спасибо, locm.

    Тема:
    https://www.cyberforum.ru/arm/... st12185937
    Запись от Khludenkov размещена 13.03.2018 в 15:19 Khludenkov вне форума
 
Новые блоги и статьи
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
Перемещение строк из ТЧ в другой документ с учетом текущего пробега
Maks 17.08.2026
Реализация из решения ниже выполнена на примере нетипового документа "Автозапчасти", с ТЧ "Шины". За основу взят алгоритм отсюда: https:/ / www. cyberforum. ru/ blogs/ 359708/ 10838. html Задача: . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru