Продолжаем нашу работу с шиной USART контроллера STM32F1 с применением библиотеки CMSIS.
В данном уроке мы поработаем с возможностью использования периферии DMA при передаче данных по интерфейсу USART.
С применением DMA в работе с модулем USART мы уже знакомы из урока 15 и урока 164. Правда, тогда мы использовали при этом библиотеки HAL и LL, но тем не менее со спецификой применения DMA мы познакомились полностью. Также, как работать с DMA и как его настраивать при использовании библиотеки CMSIS, мы также знаем уже не понаслышке. Мы работали с данной периферией в уроке 176 и уроке 178.
Схема наша также со времён трёх прошлых уроков никаких изменений не претерпела.
Поэтому мы можем смело приступать к проекту, который был сделан из проекта прошлого урока с именем CMSIS_USART_INT и имя ему новое было дано CMSIS_USART_DMA.
Откроем наш проект в Keil и в файле main.c удалим вот эти две глобальные переменные
uint8_t fl=0;
uint8_t dt1;
Вместо них добавим два флага, один — для отслеживания окончания передачи пакета, другой — приёма
1 2 |
char rx_str[30], tx_str[30], tmp_str[10]; uint8_t fl_rx=0, fl_tx=0; |
Удалим также объявления символьных массивов
char rx_str[30], tx_str[30], tmp_str[10];
А добавим один инициализированный, другой — неинициализированный
1 2 3 |
__IO uint32_t SysTick_CNT = 0; char tx_str[] = "The universal synchronous asynchronous receiver transmitter (USART)\r\noffers a flexible means of full-duplex data exchange\r\nwith external equipment requiring an industry\nstandard NRZ asynchronous serial data format.\r\n"; uint8_t rx_str[256]; |
Добавим также функцию инициализации DMA над функцией USART1_Init
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//-------------------------------------------------------- static void DMA1_Init(void) { //DMA controller clock enable SET_BIT(RCC->AHBENR, RCC_AHBENR_DMA1EN); tmpreg = READ_BIT(RCC->AHBENR, RCC_AHBENR_DMA1EN); (void)tmpreg; //DMA1_Channel4_IRQn interrupt init NVIC_EnableIRQ(DMA1_Channel4_IRQn); //DMA1_Channel5_IRQn interrupt init NVIC_EnableIRQ(DMA1_Channel5_IRQn); } //-------------------------------------------------------- |
Так как в уроке 164 мы полностью проанализировали автоматически сгенерированную подобную функцию, то ничего нового тут для нас не будет.
Основная инициализация DMA будет уже в функции инициализации модуля USART1 — USART1_Init.
Для начала в данной функции мы удалим включение глобальных прерываний от данного модуля, так как прерывания мы уже будем использовать от DMA
//USART1 interrupt Init
NVIC_EnableIRQ(USART1_IRQn);
Вместо этих строк добавим код инициализации нашего DMA
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 |
GPIO_CRH_CNF10_0 | GPIO_CRH_MODE9 | GPIO_CRH_CNF9_1); //USART1 DMA Init //USART1_RX Init //Set transfer direction (Peripheral to Memory) CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_DIR | DMA_CCR5_MEM2MEM); //Set priority level CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_PL); //Transfer mode NORMAL CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_CIRC); //Set peripheral no increment mode CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_PINC); //Set memory increment mode SET_BIT(DMA1_Channel5->CCR, DMA_CCR5_MINC); //Set peripheral data width CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_PSIZE_1 | DMA_CCR5_PSIZE_0); //Set memory data width CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_MSIZE_1 | DMA_CCR5_MSIZE_0); //SPI1_TX Init //Set transfer direction (Memory to Peripheral) MODIFY_REG(DMA1_Channel4->CCR, DMA_CCR4_MEM2MEM, DMA_CCR4_DIR); //Set priority level CLEAR_BIT(DMA1_Channel4->CCR, DMA_CCR4_PL); //Transfer mode NORMAL CLEAR_BIT(DMA1_Channel4->CCR, DMA_CCR4_CIRC); //Set peripheral no increment mode CLEAR_BIT(DMA1_Channel4->CCR, DMA_CCR4_PINC); //Set memory increment mode SET_BIT(DMA1_Channel4->CCR, DMA_CCR4_MINC); //Set peripheral data width CLEAR_BIT(DMA1_Channel4->CCR, DMA_CCR4_PSIZE_1 | DMA_CCR4_PSIZE_0); //Set memory data width CLEAR_BIT(DMA1_Channel4->CCR, DMA_CCR4_MSIZE_1 | DMA_CCR4_MSIZE_0); |
Данную инициализацию мы тажке видели в уроке 164, здесь она просто переписана с использованием макросов библиотеки CMSIS. Практически такая же инициализация DMA была у нас в уроке 178, когда мы работали с модулем SPI.
Универсальную функцию USART_RX_TX_Str удалим вместе с телом.
В функции main вызовем функцию инициализации DMA
1 2 |
GPIO_Init(); DMA1_Init(); |
Удалим разрешение локальных прерываний от USART
SET_BIT(USART1->CR1, USART_CR1_RXNEIE);
SET_BIT(USART1->CR3, USART_CR3_EIE);
Вместо этого сначала отключим каналы DMA
1 2 3 4 |
Number_7219(87654321); //Disable DMA channels CLEAR_BIT(DMA1_Channel4->CCR, DMA_CCR4_EN); CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_EN); |
Очистим флаги прерываний каналов
1 2 3 4 5 6 7 8 9 10 11 12 13 |
CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_EN); //Clear Channel 4 global interrupt flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CGIF4); //Clear Channel 5 global interrupt flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CGIF5); //Clear Channel 4 transfer complete flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTCIF4); //Clear Channel 4 transfer error flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTEIF4); //Clear Channel 5 transfer complete flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTCIF5); //Clear Channel 5 transfer error flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTEIF5); |
Разрешим запросы DMA для USART1
1 2 3 4 5 |
WRITE_REG(DMA1->IFCR, DMA_IFCR_CTEIF5); //Enable DMA Mode for reception SET_BIT(USART1->CR3, USART_CR3_DMAR); //Enable DMA Mode for transmission SET_BIT(USART1->CR3, USART_CR3_DMAT); |
Разрешим прерывания по событиям ошибки и окончания передачи для каналов DMA
1 2 3 4 5 6 7 8 9 |
SET_BIT(USART1->CR3, USART_CR3_DMAT); //Enable Channel 4 Transfer complete interrupt SET_BIT(DMA1_Channel4->CCR, DMA_CCR4_TCIE); //Enable Channel 4 Transfer error interrupt SET_BIT(DMA1_Channel4->CCR, DMA_CCR4_TEIE); //Enable Channel 5 Transfer complete interrupt SET_BIT(DMA1_Channel5->CCR, DMA_CCR5_TCIE); //Enable Channel 5 Transfer error interrupt SET_BIT(DMA1_Channel5->CCR, DMA_CCR5_TEIE); |
Сконфигурируем в DMA адреса буферов источника и приёмника для передачи и приёма
1 2 3 4 5 |
//Configure the Source and Destination addresses WRITE_REG(DMA1_Channel5->CPAR, (uint32_t)&(USART1->DR)); WRITE_REG(DMA1_Channel5->CMAR, (uint32_t)&rx_str); WRITE_REG(DMA1_Channel4->CPAR, (uint32_t)&(USART1->DR)); WRITE_REG(DMA1_Channel4->CMAR, (uint32_t)&tx_str); |
Из бесконечного цикла пока всё удалим.
Обработчик прерываний USART1_IRQHandler удалим вместе с телом, а вместо него добавим два обработчика прерываний от каналов DMA
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 |
//---------------------------------------------------------- void DMA1_Channel4_IRQHandler(void) { if(READ_BIT(DMA1->ISR, DMA_ISR_TCIF4) == (DMA_ISR_TCIF4)) { WRITE_REG(DMA1->IFCR, DMA_IFCR_CTCIF4); fl_tx = 1; } else if(READ_BIT(DMA1->ISR, DMA_ISR_TEIF4) == (DMA_ISR_TEIF4)) { //Disable DMA channels CLEAR_BIT(DMA1_Channel4->CCR, DMA_CCR4_EN); CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_EN); } } //---------------------------------------------------------- void DMA1_Channel5_IRQHandler(void) { if(READ_BIT(DMA1->ISR, DMA_ISR_TCIF5) == (DMA_ISR_TCIF5)) { WRITE_REG(DMA1->IFCR, DMA_IFCR_CTCIF5); fl_rx = 1; } else if(READ_BIT(DMA1->ISR, DMA_ISR_TEIF5) == (DMA_ISR_TEIF5)) { //Disable DMA channels CLEAR_BIT(DMA1_Channel4->CCR, DMA_CCR4_EN); CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_EN); } } //---------------------------------------------------------- |
Здесь всё так же, как и в уроке 164.
Если мы попали в обработчик канала 4, значит это окончание передачи данных через DMA приёмного пакета. В данном случае, если это именно окончание, то мы сбрасываем флаг прерывания, а устанавливаем пользовательский по приёму, а если ошибка, то отключаем оба канала. В случае с каналом 5 всё то же самое, только флаг устанавливаем передачи.
В функции передачи пакета по шине USART в ПК удалим весь код из тела, а вместо него добавим вот такой
1 2 3 4 5 6 7 8 9 10 |
void USART_TX (uint8_t* dt, uint16_t sz) { //Disable DMA channel 4 CLEAR_BIT(DMA1_Channel4->CCR, DMA_CCR4_EN); //Set Number of data to transfer MODIFY_REG(DMA1_Channel4->CNDTR, DMA_CNDTR4_NDT, sz); //Enable DMA channel 4 SET_BIT(DMA1_Channel4->CCR, DMA_CCR4_EN); while (!fl_tx) {} fl_tx=0; |
Здесь мы сначала отключаем канал 4 DMA, затем устанавливаем размер пакета в соответствующем регистре DMA, затем включаем канал. Потом, дождавшись окончания передачи с помощью отслеживания установки пользовательского флага, сбрасываем его.
Ниже добавим функцию приёма пакета от ПК по шине USART
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//---------------------------------------------------------- void USART_RX (uint8_t* dt, uint16_t sz) { //Disable DMA channel 5 CLEAR_BIT(DMA1_Channel5->CCR, DMA_CCR5_EN); //Set Number of data to transfer MODIFY_REG(DMA1_Channel5->CNDTR, DMA_CNDTR5_NDT, sz); //Enable DMA channel 5 SET_BIT(DMA1_Channel5->CCR, DMA_CCR5_EN); while (!fl_rx) {} fl_rx=0; } //-------------------------------------------------------- |
Здесь всё происходит подобным образом, только используем мы уже канал 5 DMA и дожидаемся установки пользовательского флага приёма.
В бесконечный цикл функции main() добавим следующий код
1 2 3 4 5 6 7 8 9 10 |
while(1) { USART_RX(rx_str,256); USART_TX((uint8_t*)rx_str,strlen(tx_str)); Clear_7219(); for(i=0;i<=255;i++) { NumberR_7219(rx_str[(uint8_t)i]); delay_ms(10); } |
Здесь мы сначала принимаем пакет данных с массивом чисел из ПК, передаём нашу текстовую строку в ПК, далее мы гасим все разряды индикатора и в цикле с перерывом в 10 милисекунд выводи туда числа из принятого массива данных.
Соберём проект, прошьём контроллер, запустим терминальную программу, соединимся с портом и запустим тот же скрипт, который запускали в уроке 164.
Индикатор начнёт показ принятых чисел
А в терминальной программе начнут в ответ появляться наши строки, принятые из МК
Посмотрим также, как идёт передача данных по USART, в программе логического анализа
Всё передаётся без пропусков и непрерывно.
Итак, на данном занятии мы научились использовать периферию DMA для обеспечения передачи и приёма данных по USART, используя при этом возможности библиотеки CMSIS.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Индикатор светодиодный семиразрядный с драйвером MAX7219
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК (нажмите на картинку)
why didn't you use dma circular mode?
Здравствуйте, непонятно зачем в функциях USART_RX , USART_TX ссылки на буфер,если они там не используются?