Перейти к содержанию
    

Передача через USART с DMA

32 минуты назад, jcxz сказал:

Оптимизатору оно до лампочки: он и так прекрасно видит, что нигде после они не используются на запись.

За последние лет 10 я видел миллион мнений на этот счет, с примерами и контрпримерами. Пусть даже компиляторы умные, но код пишет человек, и он в силу своей человечности может случайно что-то присвоить такой переменной, например, при копипасте куска кода. А потом ловить баги на ровном месте.

Цитата

...в первую очередь выкинуть cr из условий в ISR. Ибо не нужен там.

А Вы уверены, что завтра ТС не потребуется добавить в ISR обработку, например, ошибок принимаемых символов (да или просто сам прием символов по RXNE)  и Ваш совет не сломает ему код? Я, например, вижу, что ТС как бы и не в курсе о потенциальнх багах с обработкой флагов UART-а, поэтому и посоветовал сразу безопасное решение. Хотя вот конкретно сейчас да, проверять cr не нужно, ибо проблемы могут быть при обработке TXE.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 24.07.2022 в 12:32, Arlleex сказал:

добавить в ISR обработку, например, ошибок принимаемых символов (да или просто сам прием символов по RXNE)  и Ваш совет не сломает ему код?

Каким образом обработка ошибок UART связана с чтением CR? В STM32 для этого вроде достаточно SR и DR.

А насчёт приёма символов в ISR: так он и тут зачем-то читает DR в ISR. И это при том, что UART у него при этом находится в режиме работы с DMA! :wacko2:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 часов назад, jcxz сказал:

Каким образом обработка ошибок UART связана с чтением CR? В STM32 для этого вроде достаточно SR и DR.

С этими флагами я погорячился, признаю. Но, тем не менее, это точно касается флага TXE, который нельзя сбросить кроме как записью в DR. Соответственно, недостаточно лишь его проверки в SR ISR. Иначе при возникновении другого события, например, RXNE или ошибок приема, и при пустом регистре передатчика if(sr & USART_SR_TXE) запустит ветку на исполнение, что может быть совершенно не правильным для драйвера.

Цитата

А насчёт приёма символов в ISR: так он и тут зачем-то читает DR в ISR. И это при том, что UART у него при этом находится в режиме работы с DMA! 

Да, казалось бы, есть ошибка. Но нет. К сожалению, этот момент довольно хреново описан в RM, т.к. сброс флагов IDLE и всех ошибок (FE, ORE и т.д.) осуществляется строго определенной последовательностью: чтение SR, затем чтение DR. Я по-моему даже, когда разбирался с DMA + UART в F4, проверял этот момент, и действительно там флаги не чистились при других последовательностях. К тому же, когда ISR срабатывает по IDLE, DMA уже все вычитал в память и новых пересылок из UART-периферии не осуществляет (если, правда, прием снова не начался).

Глянул в свой драйвер (правда, там я обрабатываю только ошибки) - тоже читаю DR после SR

void cUARTDMARx::svcOnTmrEvt() {
  if(uartCtrl.hw->CR1 & USART_CR1_RE) {
    const u32 bufNum = this->bufNum,
              ndtr   = dmaCtrl.hw.str->NDTR;
    if(uartCtrl.hw->SR & (USART_SR_PE | USART_SR_FE |
                          USART_SR_NE | USART_SR_ORE))
      uartCtrl.hw->DR, isTrash = true;
    if(!isOvf && !isTrash) {
      const u32 wpos = this->wpos,
                i    = bufNum * FIFO_RXBUF_LEN,
                j    = i + FIFO_RXBUF_LEN - ndtr;
      if((s32)(j - wpos) > 0 || wpos - i > FIFO_RXBUF_LEN)
        this->wpos = j == FIFO_RXBUF_SIZE ? 0 : j, ledOn();
    }
  }
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Почитал я повнимательнее, ради спортивного интереса, раздел USART в STM32F405. Вон чо пишут в разделе коопераций с DMA:biggrin:

Цитата

When the number of data transfers programmed in the DMA Controller is reached, the DMA controller generates an interrupt on the DMA channel interrupt vector.

The DMAR bit should be cleared by software in the USART_CR3 register during the interrupt subroutine.

Видимо, обратное тоже здесь применимо: когда выполняется обработчик ISR USART-модуля, DMAR (или DMAT) должен быть очищен.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

как тогда правильно устанавливать и сбрасывать биты в регистрах без чтения их значения?

с GPIO понятно, там BRR есть. а остальные как?

к примеру вот тут DMA2_Stream5->CR &= ~(DMA_SxCR_EN);

чтобы правильное значение в регистре получить нужно считать его значение и умножить на инвертированную маску необходимого бита для опустошения.... какие ещё есть способы?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 26.07.2022 в 23:23, Reystlin сказал:

чтобы правильное значение в регистре получить нужно считать его значение

Зачем?

Всё элементарно: DMA2_Stream5->CR = 0;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При этом сбросятся все установленные настройки в нем...

и при каждом включении нужно будет все биты настроек выставлять, либо какую-то инлайновую функцию включения/отключения делать

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 26.07.2022 в 23:34, Reystlin сказал:

При этом сбросятся все установленные настройки в нем...

А зачем они при выключенном stream-е?

В 26.07.2022 в 23:34, Reystlin сказал:

и при каждом включении нужно будет все биты настроек выставлять

И что?

Будет DMA2_Stream5->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_PFCTRL | DMA_SxCR_EN; - это всего 2 ассемблерные команды.

Ваш паровоз:

		DMA2_Stream5->CR = DMA_SxCR_CHSEL_2;		// Выбираем 4 канал
		DMA2_Stream5->CR |= DMA_SxCR_MINC;			// Инкремент адреса памяти
		DMA2_Stream5->CR |= DMA_SxCR_PFCTRL;		// DMA - Ведомый
		//DMA2_Stream5->CR |= DMA_SxCR_TCIE;	
		DMA2_Stream5->CR |= DMA_SxCR_EN;

это - как минимум 11 команд.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При выключенном, конечно, не нужны. но его же нужно будет включить.

этот "паравоз" я привел вот к такому виду:

DMA2_Stream5->CR = 0
        | DMA_SxCR_CHSEL_2                                // Выбираем 4 канал
        | DMA_SxCR_MINC                                    // Инкремент адреса памяти
        | DMA_SxCR_PFCTRL                                // DMA - Ведомый

При каждом включении необходимо будет вставить этот кусок кода, затрудняя возможность внесения изменений в инициализацию, через пол года можно забыть в каких местах в коде включение работы DMA происходит, а если этот код другому человеку поддеживать придется?

стараюсь всегда всю конфигурацию вынести в одно место а не размазывать по коду без крайней необходимости

С точки зрения эффективности со стороны работы процессора да - ваша рекомендация хороша, не спорю.

с точки зрения поддержки кода не совсем...

кроме инлайн функции не вижу другого выхода из ситуации, чтобы оптимизировать код со стороны работы процессора и не потерять его гибкость изменения и поддержки.

В любом случае, спасибо за вашу помощь и советы

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Reystlin сказал:

При каждом включении необходимо будет вставить этот кусок кода

Занесите это значение в константу, потом присваивайте ее значение.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 7/27/2022 at 1:23 AM, Сергей Борщ said:

Занесите это значение в константу, потом присваивайте ее значение.

Хороший вариант, спасибо, об константе не подумал)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 7/27/2022 at 1:23 AM, Reystlin said:

с GPIO понятно, там BRR есть. а остальные как?

Внимательно читать RM. Много периферии, у которой флаги сбрасываются при чтении. Есть периферия, тот же UART в разных МК, у которых есть регистры на запись для сброса бит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Продолжаю изыскания по организации общения через ModBUS поверх RS485

на текущий момент вот такой код:


#include "USART1.h"
#include <stdio.h>
#include <string.h>

uint8_t rx_buf[256];
uint8_t	tx_buf[256];	

#define TIM7_En() TIM7->CR1 = TIM_CR1_OPM | TIM_CR1_CEN;	// Таймер задержки отправки следующего пакета
#define DMA2_Stream5_En() DMA2_Stream5->CR = DMA_SxCR_EN | DMA_SxCR_CHSEL_2	| DMA_SxCR_MINC	| DMA_SxCR_PFCTRL // DMA на прием
#define DMA2_Stream7_En() DMA2_Stream7->CR = DMA_SxCR_EN | DMA_SxCR_CHSEL_2	| DMA_SxCR_MINC	| DMA_SxCR_DIR_0 | DMA_SxCR_TCIE // DMA на отправку

void Init_USART1()
{
	// USART1
		RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
		// APB2 = 84MHz
        uint32_t baud_rate = 38400;
        uint16_t uartdiv = 168000000 / baud_rate;
        USART1->BRR = (( ( ( uartdiv / 16 ) << USART_BRR_DIV_Mantissa_Pos ) |
    				( ( uartdiv % 16 ) << USART_BRR_DIV_Fraction_Pos ) ))-2;
		
		USART1->CR1 = 0
		| USART_CR1_OVER8			// Размер кадра 8 бит
		| USART_CR1_UE				// Включаем USART1
		//| USART_CR1_M				// Формат кадра Start bit, 8 bits, n Stop bits
		//| USART_CR1_PCE			// Parity mode control disable
		//| USART_CR1_PS			// Parity mode Even
		//| USART_CR1_PEIE		    // Разрешение прерывания PEIE ошибка четности
		//| USART_CR1_TXEIE		    // Разрешение прерывания TXEIE опустошение регистра передатчика
		//| USART_CR1_TCIE		    // Разрешение прерывания TCIE завершение передачи
		//| USART_CR1_RXNEIE	    // Разрешение прерывания RXNEIE завершение приема
		| USART_CR1_IDLEIE	        // Разрешение прерывания IDLEIE обнаружение сигнала IDLE на линии
		| USART_CR1_TE				// Разрешение передачи
		| USART_CR1_RE				// Разрешение приема
		//| USART_CR1_RWU			// Reciver wakeup
		//| USART_CR1_SBK			// Send break
		;
		
		USART1->CR2 = 0
		//	USART_CR2_LINEN		// Включение режима LIN
		|	USART_CR2_STOP_1	// 2 Стоп бита
		//| USART_CR2_STOP_0
		//| USART_CR2_CLKEN		// Включить вывод CK
		//| USART_CR2_CPOL		// Определяет уровень сигнала на линии CK в отсутствии симпульсов
		//| USART_CR2_CPHA		// Определяет по которому фронту импульсов на линии CK происходи захват
		//| USART_CR2_LBCL		// Нужно ли тактировать через линию CK последний передаваемый бит
		//| USART_CR2_LBDIE		// Разрешение прерывания по обнаружению сигнала break
		//| USART_CR2_LBDL		// Длительность ожидаемого сигнала break
		;
		
		USART1->CR3 = 0
		//	USART_CR3_CTSIE			// Включение прерывания по линии CTS
		//USART_CR3_CTSE			// Включение контроля линии CTS(Данные начнут передаваться когда CTS = 0)
		//|	USART_CR3_RTSE			// Включение контроля линии RTS(Dscnfdkztncz КЕЫ = 0 когда приемник пуст и готов)
		|	USART_CR3_DMAT		    // Включить режим DMA для передатчика
		| USART_CR3_DMAR		    // Включить режим DMA для приемника
		//| USART_CR3_SCEN			// Включить режим Smartcard
		//| USART_CR3_NACK			// Поылать ли NACK в режиме Smartcard
		//| USART_CR3_HDSEL			// Однопроводный полудуплексный режим
		//| USART_CR3_IRLP			// Включение режима low-power для IrDA
		//| USART_CR3_IREN			// Включение режиме IrDA
		//| USART_CR3_EIE			// Включение прерывания по ошибкам передачи
		;
		
	// DMA2 - Stream7 - Transmit
		RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
		
		DMA2_Stream7->PAR  = (uint32_t)&(USART1->DR);	// Адрес назначения
		DMA2_Stream7->M0AR = (uint32_t)(tx_buf); 		// Адрес источника
		DMA2_Stream7->FCR = 0;							// Настройка FIFO буфера
		DMA2_Stream7->CR = 0
        | DMA_SxCR_CHSEL_2		                        // Выбираем 4 канал
		| DMA_SxCR_MINC		                            // Инкремент адреса памяти
		| DMA_SxCR_DIR_0		                        // Направление - из памяти в перефереию
		| DMA_SxCR_TCIE 		                        // Прерывание по окончанию пересылки пакета
		;
        
		NVIC_EnableIRQ(USART1_IRQn);
		NVIC_SetPriority(USART1_IRQn, 0x04);
		NVIC_SetPriority(DMA2_Stream7_IRQn, 0x04);
		NVIC_EnableIRQ(DMA2_Stream7_IRQn);
		
	// DMA2 - Stream 5 - Recive
		DMA2_Stream5->PAR  = (uint32_t)&(USART1->DR);	// Адрес источника 
		DMA2_Stream5->M0AR = (uint32_t)(rx_buf); 		// Адрес назначения
		DMA2_Stream5->FCR = 0;							// Настройка FIFO буфера
		DMA2_Stream5->NDTR = 255;
		/*DMA2_Stream5->CR = 0
        | DMA_SxCR_CHSEL_2		                        // Выбираем 4 канал
		| DMA_SxCR_MINC			                        // Инкремент адреса памяти
		| DMA_SxCR_PFCTRL		                        // DMA - Ведомый
        ;*/
	//TIM7 - Таймер для Timeout и задержек
		RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
        
		//TIM7->CR1 = TIM_CR1_OPM;	// Однократный режим		
		TIM7->PSC=84-1;		
		TIM7->ARR=500-1;
        TIM7->EGR |= TIM_EGR_UG;
        TIM7->SR=0;
        TIM7->DIER = TIM_DIER_UIE;	// Разрешение прерывания
		NVIC_EnableIRQ(TIM7_IRQn);
		
	// GPIO
		RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
		GPIO_InitTypeDef GPIO_InitStruct_ModBUS = {0};
        GPIO_InitStruct_ModBUS.Speed = GPIO_SPEED_FAST;
        GPIO_InitStruct_ModBUS.Alternate = GPIO_AF7_USART1;
        // PA9 - DI - TX
        GPIO_InitStruct_ModBUS.Pin = GPIO_PIN_9;
        GPIO_InitStruct_ModBUS.Mode = GPIO_MODE_AF_PP;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_ModBUS);
        // PA10 - RO - RX
        GPIO_InitStruct_ModBUS.Pin = GPIO_PIN_10;
        GPIO_InitStruct_ModBUS.Mode = GPIO_MODE_AF_OD;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_ModBUS);
            // PA11 - DE/RE - CTS
        GPIO_InitStruct_ModBUS.Pin = GPIO_PIN_11;
        GPIO_InitStruct_ModBUS.Mode = GPIO_MODE_OUTPUT_PP;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_ModBUS);
		
		GPIOA->BSRR = GPIO_BSRR_BS11; // Передача
}

void (*recive_fn)(uint8_t*);
volatile enum USART_RS485_Status status = USART_IDLE;
// Принимает строку.
enum USART_RS485_Status USART1_put(uint8_t size, char *text,void (*recive)(uint8_t*))
{
	if(status == USART_IDLE)
    {
        status = USART_SEND;
        recive_fn = recive;
        memcpy(tx_buf, text, size);
        GPIOA->BSRR = GPIO_BSRR_BS11; // Передача
        DMA2_Stream7->NDTR = size;
        DMA2_Stream7_En();
    }
	return status;
};

void DMA2_Stream7_IRQHandler(void) 
{
   if (DMA2->HISR & DMA_HISR_TCIF7)
    {
		DMA2->HIFCR = DMA_HIFCR_CTCIF7;	// сброс флага события TCIF
		USART1->SR = ~(USART_SR_TC);
        USART1->CR1 |= USART_CR1_TCIE;		// Разрешение прерывания TCIE завершение передачи
   }
};

void USART1_IRQHandler(void) 
{
	const uint32_t sr = USART1->SR, cr = USART1->CR1;
	if((sr & USART_SR_TC) && (cr & USART_CR1_TCIE)) 
    {	// Окончили отправку
		USART1->SR = ~(USART_SR_TC);
		DMA2_Stream7->CR = 0;			
		DMA2_Stream5_En();
        GPIOA->BSRR = GPIO_BSRR_BR11;   // Прием
        status = USART_READ;
        // Настройка таймера на Timeout
        TIM7->ARR = 5000-1; // Задержка отправки второго пакета после приема       
		TIM7_En();  // Включаем счетчик для задержки отправки второго пакета
	}
    else if((sr & USART_SR_IDLE) && (cr & USART_CR1_IDLEIE))
	{	// Окончили прием ответа
        (void)USART1->DR;
		USART1->SR = ~(USART_SR_IDLE);
		DMA2_Stream5->CR = 0;
        GPIOA->BSRR = GPIO_BSRR_BR11; // Прием окончен
        status = USART_WAIT;
        TIM7->ARR = 500-1; // Задержка отправки второго пакета после приема       
		TIM7_En();  // Включаем счетчик для задержки отправки второго пакета
        recive_fn(rx_buf);
	}
}

void TIM7_IRQHandler(void)
{
    TIM7->SR = ~(TIM_SR_UIF);
    if(status == USART_READ)
    {
        // Timeout
        status = USART_IDLE;
    }
    else
    {
        // Окончили получение и задержку
        status = USART_IDLE;
    }
}

Slave устройство не подключено.

Желтый TX

Зеленёый RX

Синий PA11

https://disk.yandex.ru/i/vkatL_l1n5cT2A

не могу понять в чем дело почему на PA11 меняется сигнал во время отправки одного целого пакета

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вряд ли кто-то кроме Вас скажет, почему у Вас дергается нога PA11. Из приведенных исходников этого не видно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нашел ошибку. вот здесь

GPIOA->BSRR = GPIO_BSRR_BR11; // Прием окончен

надо было GPIOA->BSRR = GPIO_BSRR_BS11; // Прием окончен

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...