STM32 Часть 10–Контроллер ПДП + Таймер 15 в режиме ШИМ
На данный момент приостановились мои работы с STM32, так как пока приоритет у STM8L, но по просьбе пользователя LeftRadio, сегодня расскажу о контроллере прямого доступа к памяти (более привычно DMA) и в качестве примера рассмотрим работу контроллера ПДП с таймером 15 в режиме ШИМ.
Попутно приглашаю всех принять участие в викторинах проводимых терраэлетроникой, мне удалось выиграть с четвертого раза :) :
Контроллер прямого доступа к памяти (ПДП)
Особенности:
ul> <li>семь независимых каналов <li>аппаратный приоритет для каналов <li>программный четырех уровневый приоритет для каждого канала <li>возможность цикличной работы <li>поддержка пересылки из памяти в память <li>число данных для передачи от 1 до 65536 <li>и т. д. </li></ul>
Функциональная схема:
Наибольший интерес представляют две таблицы. Первая таблица описывает все возможные форматы передачи между источником и приемником:
Вторая описывает соответствие сигналов периферийных модулей и каналов ПДП:
Как видим не так уж и гибко можно настраивать передачу данных (в LPC1768 более гибкий контроллер ПДП), но и этого достаточно при умелом использовании.
Работать с контроллером очень просто. Я да же не поверил что все работает как нужно когда описал и прошил :)
Согласно документации основные действия при конфигурировании любого канала:
- задать адрес приемника данных
- задать адрес источника данных
- указать количество данных для пересылки
- задать приоритет
- задать режим работы
- разрешить работу</ol> Мне кажется не плохо бы указывать первым пунктом разрешение работы модуля.
Как видим все очень просто.
Рассмотрим в качестве примера работу контроллера ПДП совместно с таймером 15.
Пример
И так цель: формировать произвольный ШИМ сигнал.
Таймер 15 может работать только в режиме “PWM edge-aligned mode” по “нашему” ОШИМ (односторонний ШИМ), т.е. изменение ширины импульса происходит только в одну сторону, а не от центра импульса как при обычном ШИМ.
Период (частота) определяется значением регистра автоперезагрузки TIMx_ARR (напомню этот регистр определяет значение при котором счетчик обнуляется) длительность импульса определяется значением в регистре TIMx_CCRx.
Имеется возможность настраивать полярность ШИМ сигнала и ещё много чего (см. доку).
Кратенько работу аналогичного таймера я уже описывал, поэтому не долго думаю и переношу код инициализации (с дополнениями):
void mcu_tim15_init(void)
{
// разрешаем татирование модуля
RCC->APB2ENR |= RCC_APB2ENR_TIM15EN;
// устанавливаем предделитель
TIM15->PSC = 10 - 1;
// задаем период ШИМ
TIM15->ARR = 255;
// начальную длительность импульса
TIM15->CCR1 = 1;
// задаем режим работы ОШИМ
TIM15->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;
// разрешаем выходной сигнал
TIM15->CCER = TIM_CCER_CC1E;
TIM15->BDTR = TIM_BDTR_MOE;
// разрешаем запросы к ПДП
TIM15->DIER = TIM_DIER_CC1DE;
// разрешаем работу и автоперезагрузку
TIM15->CR1 = TIM_CR1_CEN | TIM_CR1_ARPE;
}
Примечание: обычно я так сильно не комментирую код, но по просьбам трудящихся...
По умолчанию выходной сигнал будет подключен к линии ввода-вывода CH1/PA2, но можно сделать ремап на CH1/PB14.
Настроим линию ввода-вывода:
#define TIM15_PWM1 A, 2, HIGH, ALTERNATE_OUTPUT_PUSH_PULL, SPEED_50MHZ
PIN_CONFIGURATION(TIM15_PWM1);
Ну вот осталось настроить ПДП:
Что касается режимов:
* DMA_CCR1_MINC – инкрементировать адрес источника
* DMA_CCR1_CIRC – использовать цикличный режим, т.е. передавать данные по кругу
* DMA_CCR1_DIR – направление данных “чтение из памяти”
* DMA_CCR1_PSIZE = 1 – размер приемника данных 16 бит (см. доку)
Логика работы проста: периодично по запросу таймера выбираем данные из буфера dma_buffer и помещаем их в регистр TIM15->CCR1 отвечающий за длительность импульса ШИМ, формируя тем самым последовательность импульсов с заданной длительностью.
Конечно можно не использовать ПДП, а просто по соответствующему событию в обработчике прерывания изменять значение длительности, но тут как говориться все зависит от задачи.
Код основной программы, для проверки:
int main(void)
{
PIN_CONFIGURATION(TIM15_PWM1);
mcu_dma_channel5_init();
mcu_tim15_init();
while (1)
{
}
return 0;
}
Исходники не выкладываю так как они были ранее:
2011-01-02_tim17_interrupt
Необходимо только исправить в майкфале неточность (спасибо читателю):
FLAGS_LD = -Xlinker -Map=target/target.map
FLAGS_LD += -Wl,--gc-sections
Если что не понятно спрашивайте.
Дополнение от 2011-06-27
Полный пример к приведенной выше статье:
2011-06-27-stm32-pwm-dma-demo
comments powered by Disqus