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

STM32F103C8T6 - Timer DMA GPIO

Запись от locm размещена 06.03.2018 в 23:58
Показов 23073 Комментарии 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
Просмотров: 1705
Размер:	58.9 Кб
ID:	4715
Вложения
Тип файла: 7z STM32F103C8T6_DMA_GPIO.7z (197.2 Кб, 799 просмотров)
Метки 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 вне форума
 
Новые блоги и статьи
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru