Добрый день!
Не стал создавать тему ради одного вопроса, поэтому немного расширил.
Конкретно сейчас есть такая проблема - есть буфер для приема данных (массив на 100 байт) с UART по DMA в кольцевом режиме. Данные в буфер пишутся, но мне надо знать в какой элемент массива был записан последний принятый байт, а найти это не могу.
Не подскажете как решить проблему?
В свободное время у меня проводится поллинг - если индекс последнего проверенного элемента массива меньше чем индекс последнего принятого байта, то эти байты анализируются. А дальше уже при обнаружении символа конца строки происходить определение полученной команды.
Добавлено after 1 minute 14 seconds:
На прерываниях все работало как надо, но там индекс инкрементировался в прерывании. Теперь хочу все на ДМА перевести
isx, можно проверять флаг половины передачи (DMA_ISR_HTIFx) и, пока происходит передача второй половинки буфера, заполнять первую. Ну, а как только получили TCIFx, заполняем вторую половинку, а после окончания заполнения - запускаем опять передачу.
А по приему есть смысл DMA без анализа принятых данных запускать лишь если у вас протокол подразумевает постоянную длину пакета. Да и то, нужно по таймауту все равно проверять... Другое дело - если вы обрабатываете прерывание (как в F0) по принятому символу. Тогда можно, скажем, 64 байта на буфер DMA выделить (если строки больше 63 символов не передаете), а на прием '\n' вызывать прерывание, которое остановит прием по DMA, отправит принятую строку в буфер, выставит флаг готовности и заново запустит прием.
Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
[uquote="Reflector",url="/forum/viewtopic.php?p=3972772#p3972772"]Проверять CNDTR на 0, а на более продвинутой версии DMA, как у F4, проверять CR_EN, там DMA сам выключается после передачи.[/uquote]
Провел тесты. Поставил подряд две отправки через HAL_UART_Transmit_DMA.
Если поставить while (huart->hdmatx->Instance->CNDTR != 0){;}, (huart->hdmatx->State != HAL_DMA_STATE_READY) или (huart->TxXferCount != 0) перед HAL_UART_Transmit_DMA, то второй пакет не передается. Помогло только использование while (huart->gState != HAL_UART_STATE_READY){;}.
Это верно или я что-то не так делаю?
Добавлено after 2 minutes 26 seconds:
[uquote="Eddy_Em",url="/forum/viewtopic.php?p=3972782#p3972782"]isx, можно проверять флаг половины передачи (DMA_ISR_HTIFx) и, пока происходит передача второй половинки буфера, заполнять первую. Ну, а как только получили TCIFx, заполняем вторую половинку, а после окончания заполнения - запускаем опять передачу.
А по приему есть смысл DMA без анализа принятых данных запускать лишь если у вас протокол подразумевает постоянную длину пакета. Да и то, нужно по таймауту все равно проверять... Другое дело - если вы обрабатываете прерывание (как в F0) по принятому символу. Тогда можно, скажем, 64 байта на буфер DMA выделить (если строки больше 63 символов не передаете), а на прием '\n' вызывать прерывание, которое остановит прием по DMA, отправит принятую строку в буфер, выставит флаг готовности и заново запустит прием.[/uquote]
Строки произвольной длины. А если анализировать каждый байт в прерывании, то какой тогда смысл в ДМА?
Если строки переменной длины и не имеют маркера окончания, по которому можно прерывание запустить, DMA на прием не нужно.
Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
isx писал(а):Еще такой вопрос.
Как узнать, что все данные по ДМА были переданы и можно отправлять следующую партию в HAL_UART_Transmit_DMA?
Попробуйте
f4: while (READ_BIT(huartPtr->Instance->SR),USART_SR_TC)==0){asm("nop");}
f7: while (READ_BIT(huartPtr->Instance->ISR),USART_ISR_TC)==0){asm("nop");}
Если байты принимаются с паузами - можете ловить прерывание USARTx_IDLE
Как только наступает событие (с момента приёма байта прошло 9 пустых тайм-слотов) - опрашиваете DMA, считываете DMAx_ChannelY->CNDTR, записываете в VOLATILE переменную.
В основном потоке - сравниваете переменную "ДМА_ДОПИСАЛ_ДО_СЮДА", с другой переменной "Я_ДОЧИТАЛ_ДО_СЮДА". Если различаются - считаете сколько байт было принято, обновляете вторую переменную.
uint8_t RX_BUF[128];
volatile uint8_t RX_DONE;
volatile uint16_t LastPos;
void USART1_IRQHandler(void) {
if (USART1->SR & USART_SR_IDLE) {
(void)USART1->DR; // Очистка флага IDLE. Мне лично кажется такой подход странным.
RX_DONE = 1; // Флаг, чтобы не сравнивать переменные
LastPos = sizeof(RX_BUF) - DMA2_Stream5->NDTR; // Вычисляем позицию в буфере. NDTR считает ВНИЗ.
};
};