Карма: 16
Рейтинг сообщений: 205
Зарегистрирован: Вс дек 02, 2012 16:58:33 Сообщений: 936 Откуда: от туда
Рейтинг сообщения:0
Уважаемые коты. Прикрутил я на STM32F051R8 (короче, на STM32F0Discovery) терминал. Всё работает под FreeRTOS760. Работают очереди, передаются данные эхом от терминала и от задачки-дёргалки. Работает буфер приёма - передачи, передаются в очередях ссылки на буферы и их длина. Всё нормально. Двигаюсь дальше. Делаю передачу USART1 через DMA, чтобы не отнимать у процессора время. Вот инициализация канала DMA:
Код:
void vInit_dma (void) { RCC->AHBENR |= RCC_AHBENR_DMA1EN; // включить тактирование DMA1 SYSCFG->CFGR1 |= SYSCFG_CFGR1_USART1TX_DMA_RMP; // переключить запрос USART1 TXE на канал 4 DMA1_Channel4->CPAR = (__IO uint32_t)&(USART1->TDR); // адрес перифериии DMA1_Channel4->CMAR = (__IO uint32_t)g.USART1_outbuf; // адрес памяти DMA1_Channel4->CNDTR = DMA_CNDTR_NDT & g.usart1_outlen; // длина пересылки
DMA1_Channel4->CCR &= ~DMA_CCR_MEM2MEM; // не из памяти в память DMA1_Channel4->CCR &= ~DMA_CCR_PL; // приоритет низкий DMA1_Channel4->CCR |= DMA_CCR_PL_1; // приоритет высокий DMA1_Channel4->CCR &= ~DMA_CCR_MSIZE; // размер данных 8 бит DMA1_Channel4->CCR &= ~DMA_CCR_PSIZE; // размер периферии 8 бит DMA1_Channel4->CCR |= DMA_CCR_MINC; // память инкрементировать DMA1_Channel4->CCR &= ~DMA_CCR_PINC; // периферию не инкрементировать DMA1_Channel4->CCR &= ~DMA_CCR_CIRC; // циркулярный режим выключен DMA1_Channel4->CCR |= DMA_CCR_DIR; // направление от памяти в периферию }
Использую 4 канал ДМА, для этого делаю ремап в SYSCFG. Инициализация USART1 (только настройки, пины не выкладываю)
И теперь пытаюсь передать буфер в USART1 через DMA1_Channel4:
Код:
void vUSART1_Send_DMA (uint32_t source, uint16_t len ) { // использование ДМА для передачи строки в USART USART1->CR1 &= ~USART_CR1_UE; //Выключаем USART1 DMA1_Channel4->CMAR = (__IO uint32_t)source; // адрес памяти буфера DMA1_Channel4->CNDTR = DMA_CNDTR_NDT & len; // длина пересылки DMA1->IFCR = DMA_IFCR_CGIF4; // сбросить флаги прерываний ДМА USART1->CR3 |= USART_CR3_DMAT; // разрешить USART1 передачу через ДМА USART1->CR1 |= USART_CR1_UE; //Включаем USART1 DMA1_Channel4->CCR |= DMA_CCR_EN; // разрешить DMA4 }
Запускаю всё это в IAR. Регистры USART и DMA соответствуют программе, адреса в DMA правильные, а передача не происходит. В инете есть примеры с использованием через функции SPL, и для 104 контроллера. Вроде бы всё повторил, но где-то недосмотрел. Если переключаю программу на вывод по ожиданию в цикле без ДМА, то всё отлично работает. Где недосмотрел, подскажите.
В нециклическом режиме. Сначала конфигурируем DMA и включаем , только потом включаем периферию. По отработке DMA, сначала отключаем DMA только потом периферию.
Карма: 16
Рейтинг сообщений: 205
Зарегистрирован: Вс дек 02, 2012 16:58:33 Сообщений: 936 Откуда: от туда
Рейтинг сообщения:0
Заодно подскажите ответ на другой вопрос. Пересылка данных в USART с использованием DMA. В первый раз DMA свободен, все флаги прерывания равны нулю. Мы указываем ему переслать пачку данных в USART, а процессор тем временем занимается своим делом. Но вот понадобилось передать ещё пачку данных. Определить, закончилась ли предыдущая передача возможно по установленному флагу TCIFx в регистре DMA_ISR. Но в первый раз у нас регистр был равен нулю, а теперь надо ждать единичку. Мне что, нужно переменную состояния вставлять для корректной работы? Или этот вопрос можно решить попроще?
GARMIN ключевое слово нециклический режим - именно тот что у тебя. Для того что бы переписать количество транзакций , а оно после отработки DMA становиться равным нулю . Нужна последовательность описанная мной . Ты же это не собираешься использовать единственный раз, как товарищ-учитель по ссылке. У него еще все впереди ...
GARMIN писал(а):
Мне что, нужно переменную состояния вставлять для корректной работы?
Тебе что нужно ? Посылать буфер через какой то равный промежуток времени или запускать передачу "вручную" ?
Последний раз редактировалось dosikus Вт дек 17, 2013 22:51:29, всего редактировалось 1 раз.
В нециклическом режиме. Сначала конфигурируем DMA и включаем , только потом включаем периферию. По отработке DMA, сначала отключаем DMA только потом периферию.
Да что бы не было недомолвок. Надеюсь понимаешь что под включаем выключаем DMA здесь имеется ввиду канал
GARMIN писал(а):
Запускать передачу вручную по событию. Приём нужен постоянно - это дуплекс.
Ну дык , событие событию рознь. Что за событие то ? Это Event'ы подразумеваешь или что то иное ?
Вообщем опишу реальную ситуацию . Через равные промежутки у меня выплевывает через DMA массив-буфер . Реализовано так: Канал DMA в нециклическом режиме. Вся конфигурация DMA и SPI ( для USART таже хрень) кроме разрешения канала DMA и SPI делается до main. Прерывание от таймера настроено на нужный нам промежуток, который кстати намного больше чем общая длительность всех транзакций. В прерывании заносим количество транзакций DMA и разрешаем канал DMA , Затем SPI . В прерывании от окончания передачи DMA (флаг TCIFx) естесно сбрасываем флаг , затем отключаем сначала канал DMA потом SPI. Да , для SPI там еще хитрость есть.
Последний раз редактировалось dosikus Вт дек 17, 2013 23:27:47, всего редактировалось 3 раз(а).
void StartDMAChannel4(unsigned int LengthBufer) { DMA1_Channel4->CCR &= ~DMA_CCR4_EN; //запретить работу канала DMA1_Channel4->CNDTR = LengthBufer; //загрузить количество данных для обмена DMA1->IFCR = DMA_IFCR_CTCIF4; //сбросить флаг окончания обмена DMA1_Channel4->CCR |= DMA_CCR4_EN; //разрешить работу канала }
Прерывание по окончанию передачи DMA разрешено, но оно не выполняется, так как нет окончания передачи. Надо где-то покопать. Пока, чтобы не останавливаться, сделаю передачу USART на прерываниях самого USART. Это лучше, чем просто ждать окончания передачи.
Начало передачи DMA : Работаю только с DMA, USART включён постоянно, его не трогаю.
Код:
inline void vUSART1_Send_DMA (uint32_t source, uint16_t len ) { xSemaphoreTake (g.xDMA4Semaphore, portMAX_DELAY); // ожидать отдания семафора DMA1_Channel4->CMAR = (__IO uint32_t)source; // адрес памяти буфера DMA1_Channel4->CNDTR = len; //загрузить количество данных для обмена DMA1_Channel4->CCR |= DMA_CCR_EN; //разрешить работу канала }
команду xSemaphoreTake можно заменить циклом ожидания флага готовности DMA Разрешено прерывание окончания передачи DMA, где разрешается новый цикл передачи:
Код:
void DMA1_Channel4_5_IRQHandler (void) { portBASE_TYPE xWoken = pdFALSE; if (DMA1->ISR & DMA_ISR_GIF4) // прерывание по окончанию вывода в USART1 { DMA1_Channel4->CCR &= ~DMA_CCR_EN; // запретить DMA4 DMA1->IFCR = DMA_IFCR_CGIF4; // сбросить все флаги прерывания ДМА4 xSemaphoreGiveFromISR (g.xDMA4Semaphore, &xWoken); // отдаём флаг готовности DMA4 portYIELD_FROM_ISR (xWoken); } }
здесь команду xSemaphoreGiveFromISR можно заменить на команду установки флага готовности DMA
Работает всё чётко. Дальше прикручу LCD дисплей 16х1, который давно этого ждёт.
Сейчас этот форум просматривают: Zikon и гости: 18
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения