Продолжаем нашу работу с шиной USART контроллера STM32F4.
В данном уроке мы поработаем с возможностью использования периферии DMA при передаче данных по интерфейсу USART.
С передачей данных по USART контроллера STM32F4 с применением DMA в мы уже знакомы из урока 15, правда использовалась там библиотека HAL. А при работе с контроллером STM32F1 мы уже очень глубоко изучили данный процесс и не только с применением LL, но и CMSIS, а, так как мы уже убедились в идентичности USART и DMA наших контроллеров, то нам будет несложно написать проект и данного урока, и, следовательно, и достигнуть его цели.
Работать мы будем в нашем уроке с той же отладочной платой — STM32F429I-Discovery.
Проект мы в качестве основы возьмём из прошлого урока с именем LL_USART и назовём его LL_USART_DMA.
Откроем наш проект в Cube MX и задействуем DMA-потоки для приёма и передачи данных в USART
В настройках SPI5 немного поднимем приоритет DMA
Сгенерируем код и откроем наш проект в Cube IDE, где перейдём в файл main.c и у глобального буфера приёма изменим тип, а также объявим заодно и буфер передачи
uint8_t rx_str[30], tx_str[30];
А глобальный пользовательский флаг переименуем, сделав его именно для отслеживания события приёма и объявим аналогичный флаг для отслеживания события передачи
uint8_t fl_rx=0, fl_tx=0;
В функции main() мы можем уменьшить размер символьного массива
char str1[10];
Настроим каналы DMA, показав им также адреса наших буферов, аналогично как мы делали и для контроллера STM32F1 в подобном уроке и так же, как мы делали и для SPI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
TFT9341_SetFont(&Font24); LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_2); LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_7); LL_DMA_ClearFlag_TC2(DMA2); LL_DMA_ClearFlag_TC7(DMA2); LL_DMA_ClearFlag_TE2(DMA2); LL_DMA_ClearFlag_TE7(DMA2); LL_USART_EnableDMAReq_TX(USART1); LL_USART_EnableDMAReq_RX(USART1); LL_DMA_EnableIT_TC(DMA2, LL_DMA_STREAM_2); LL_DMA_EnableIT_TE(DMA2, LL_DMA_STREAM_2); LL_DMA_EnableIT_TC(DMA2, LL_DMA_STREAM_7); LL_DMA_EnableIT_TE(DMA2, LL_DMA_STREAM_7); LL_DMA_ClearFlag_TC2(DMA2); LL_DMA_ClearFlag_TE2(DMA2); LL_DMA_ClearFlag_TC7(DMA2); LL_DMA_ClearFlag_TC7(DMA2); LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_2, LL_USART_DMA_GetRegAddr(USART1), (uint32_t)&rx_str, LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_STREAM_2)); LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_7, (uint32_t)&tx_str, LL_USART_DMA_GetRegAddr(USART1), LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_STREAM_7)); |
Из бесконечного цикла пока удалим весь пользовательский код, чтобы нам не показывало ошибку.
У пользовательского обработчика DMA от SPI немного изменим имя, так как у нас DMA2, а не DMA1, а то как-то нечестно
void DMA2_Stream4_TransferComplete(void)
Пользовательский обработчик событий от USART с именем USART1_RX_Callback удалим вместе с телом, мы теперь будем обрабатывать события от DMA, а не от интерфейса USART. Вместо него мы добавим обработчики для каналов DMA
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//----------------------------------------------- void DMA2_Stream2_TransferComplete(void) { LL_DMA_ClearFlag_TC2(DMA2); fl_rx = 1; } //----------------------------------------------- void DMA2_Stream7_TransferComplete(void) { LL_DMA_ClearFlag_TC7(DMA2); fl_tx = 1; } //----------------------------------------------- |
В файле stm32f4xx_it.c изменим также имя прототипа обработчика от DMA, а прототип обработчика событий от USART удалим
void DMA2_Stream4_TransferComplete(void);
void USART1_RX_Callback(void);
Соответственно, добавим также прототипы на обработчики событий от каналов DMA, задействованных для USART
1 2 3 |
void DMA2_Stream4_TransferComplete(void); void DMA2_Stream2_TransferComplete(void); void DMA2_Stream7_TransferComplete(void); |
В штатном обработчике событий от USART USART1_IRQHandler мы удалим вызов нашего пользовательского обработчика, которого, соответственно, сейчас уже нет
USART1_RX_Callback();
А вместо этой строки мы просто считаем регистр данных
1 2 3 |
if(LL_USART_IsActiveFlag_RXNE(USART1) && LL_USART_IsEnabledIT_RXNE(USART1)) { (void) USART1->DR; |
В обработчике DMA SPI с именем DMA2_Stream4_IRQHandler мы также изменим имя нашего пользовательского обработчика
DMA2_Stream4_TransferComplete();
В теле обработчика DMA2_Stream2_IRQHandler обработаем события, отследив флаги, вызвав при этом наш пользовательский обработчик либо сбросив флаг ошибки
1 2 3 4 5 6 7 8 9 |
/* USER CODE BEGIN DMA2_Stream2_IRQn 1 */ if(LL_DMA_IsActiveFlag_TC2(DMA2) == 1) { DMA2_Stream2_TransferComplete(); } else if(LL_DMA_IsActiveFlag_TE2(DMA2) == 1) { LL_DMA_ClearFlag_TE2(DMA2); } |
Аналогично поступим и с функцией-обработчиком DMA2_Stream7_IRQHandler
1 2 3 4 5 6 7 8 9 |
/* USER CODE BEGIN DMA2_Stream7_IRQn 1 */ if(LL_DMA_IsActiveFlag_TC7(DMA2) == 1) { DMA2_Stream7_TransferComplete(); } else if(LL_DMA_IsActiveFlag_TE7(DMA2) == 1) { LL_DMA_ClearFlag_TE7(DMA2); } |
Вернёмся в файл main.c и немного изменим код тела функции USART_TX, в котором для начала просто удалим весь код и затем занесём значение размера передаваемых данных в соответствующий регистр потока DMA
1 2 3 4 5 |
void USART_TX (uint8_t* dt, uint16_t sz) { LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_7); LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_7, sz); LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_7); |
Затем дождёмся установки пользовательского флага и сбросим его
1 2 3 |
LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_7); while (!fl_tx) {} fl_tx=0; |
Ниже добавим также функцию для приёма из USART с абсолютно аналогичным кодом, только для другого потока DMA
1 2 3 4 5 6 7 8 9 10 |
//------------------------------------------------------- void USART_RX (uint8_t* dt, uint16_t sz) { LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_2); LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_2, sz); LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_2); while (!fl_rx) {} fl_rx=0; } //------------------------------------------------------- |
Теперь займёмся бесконечным циклом функции main(), в котором сначала попытаемся принять строку символов с заранее известным количеством байтов
1 2 |
/* USER CODE BEGIN 3 */ USART_RX(rx_str, 14); |
Я часто в комментариях к моим урокам вижу вопросы, а как принять строку с неизвестным количеством байтов с использованием DMA по SPI, I2C и UART. Ответ очевиден. Нужно для начала источнику таких посылок посылать какой-то маркер, в котором будет информация о количестве информации, который мы должны будем принять стандартным способом без использования DMA, а затем уже с использованием DMA принять весь основной поток байтов.
Из пришедшей строки заберём подстроку с цифрой
1 2 |
USART_RX(rx_str, 14); strncpy(str1, (char*)rx_str + 8, 4); |
Вычтем из числа 1024 число, преобразованное из нашей подстроки, и, преобразовав его обратно в строку, а также приклеив спереди другую строку, отправим всё это обратно в ПК
1 2 3 4 |
strncpy(str1, (char*)rx_str + 8, 4); i = 1024 - atoi(str1); sprintf((char*)tx_str, "String %04d\r\n", i); USART_TX(tx_str,strlen((char*)tx_str)); |
Запустим терминальную программу Terminal v1.9b, настроим на порт платы, также настроим скорость, собрав перед этим код в Cube IDE и прошив контроллер, затем соединимся с портом, запустим соответствующий скрипт, который будет передавать нужные строки в порт. Напомню код скрипта, хотя мы данный скрипт уже использовали в других уроках, но вдруг кто не видел данных уроков (хотя я не думаю, что уроки смотрятся не сначала, а избирательно, тогда просто смысл теряется)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//****************************** // Terminal Script // Created: 07.03.2018 19:11:54 //****************************** program test; var i: integer; str1: string; begin str1:='String '; while (true) do begin for i:=1 to 1023 do begin if (i<1000) and (i>=100) then comsendstr(str1+'0'+inttostr (i)+#13+#10+#0); if (i<100) and (i>=10) then comsendstr(str1+'00'+inttostr (i)+#13+#10+#0); if (i<10) then comsendstr(str1+'000'+inttostr(i)+#13+#10+#0); if (i>=1000) and (i>=10) then comsendstr(str1+inttostr (i)+#13+#10+#0); Delay(100); end; end; end. |
Если мы всё сделали правильно, то мы должны в окне терминальной программы увидеть строки ответных пакетов от платы
Можно было бы загнаться и отобразить принятые строки от ПК на дисплее платы, также можно посмотреть наши пакеты в программе логического анализа, но мы этим заниматься не будем, так как это несложно, и думаю, кому это понадобится, без труда это проделает.
Итак, на данном уроке нам удалось настроить DMA для приёма и передачи байтов через шину USART контроллера STM32F4.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату можно приобрести здесь STM32F429I-DISCO
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Добавить комментарий