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

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

Доброго времени суток коллеги!

Наблюдаю странное поведение при отправке пакета через USART с использованием DMA.

У DMA Настроено прерывание на завершение отправки пакета, в теле этого прерывания происходит опускание ножки PA11.

По осциллографу наблюдаю отправку 10 байт а опускание ноги после отправки 8 байта.

При изменении длины пакета ситуация со срабатыванием прерывания и опусканием ноги на 2 байта раньше- сохраняется.

чип STM32F407, USART1 DMA2

Синий луч PA11

Желтый TX

Зеленый RX

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

void Init_USART1()
{
		// 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->ODR&=~(1<<11); // Приём
		
		// 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 = DMA_SxCR_CHSEL_2;		// Выбираем 4 канал
		DMA2_Stream7->CR |= DMA_SxCR_MINC;			// Инкремент адреса памяти
		DMA2_Stream7->CR |= DMA_SxCR_DIR_0;			// Направление - из памяти в перефереию
		DMA2_Stream7->CR |= 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 = 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;
}

void DMA2_Stream7_IRQHandler(void) {
   if (DMA2->HISR & DMA_HISR_TCIF7)
	 {
		DMA2->HIFCR = DMA_HIFCR_CTCIF7;	// сброс флага события TCIF
    GPIOA->ODR&=~(1<<11); 					// Приём
   }
};

// Принимает строку.
void USART1_put(uint8_t size, char *text)
{
	for(uint8_t i =0;i<size;i++)
		tx_buf[i]=text[i];
	GPIOA->ODR|=(1<<11); // Передача
  DMA2_Stream7->CR &= ~DMA_SxCR_EN;   // Отключаем поток DMA
  DMA2_Stream7->NDTR = size;
  USART1->SR = ~(USART_SR_TC);
  DMA2_Stream7->CR |= DMA_SxCR_EN;
};

void USART1_IRQHandler(void) 
{
	if((USART1->SR & USART_SR_IDLE)!= 0) 
		{	// Окончили прием ответа от МК
	}
}

443152790_2022-07-2121-53-03(1).thumb.jpg.73a80ccb192203ef8156bf2c343fb78e.jpg

Чтобы работало отправку делаю вот так

void Send_ModBUS_Preset_Single_Register(uint8_t SlaveID, uint16_t reg, uint16_t data)
{
	char msg[8];
	msg[0] = SlaveID;
	msg[1] = Preset_Single_Register;
	msg[2] = (uint8_t)(reg>>8);
	msg[3] = (uint8_t)(reg&0xFF);
	msg[4] = (uint8_t)(data>>8);
	msg[5] = (uint8_t)(data&0xFF);
	uint16_t crc = mmodbus_crc16((uint8_t*)msg,6);
	
	msg[6]=(uint8_t)(crc&0xFF);
	msg[7]=(uint8_t)(crc>>8)&0xFF;
	USART1_put(10, msg);
}

 

Изменено пользователем Reystlin

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


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

Все нормально: ведь USART тоже буферизуется, так что, окончание пересылки через DMA вообще не означает, что USART тоже все передал! Но это и не важно: все равно ведь уже можно запускать следующую транзакцию...

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


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

Ничего странного в этом поведении нет.  Дма запихивает последний байт в регистр уарта и с его точки зрения - "трансмишн комплит". Почему именно два байта: регистр DR - это на самом деле два регистра, буферный и сдвиговый.

image.png.c87a6bb3096c31cfae78c1938b7000f7.png

Изменено пользователем tgruzd

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


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

Зависит от того, какое событие вам нужно) Возможно, TC уарта image.thumb.png.31431ca7c16a13202916be91ed28cd82.png

Изменено пользователем tgruzd

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


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

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) 
{
	if((USART1->SR & USART_SR_TC)!= 0) 
		{	// Окончили отправку пакета
		USART1->CR1 &= ~USART_CR1_TCIE;	
		USART1->SR = ~(USART_SR_TC);
		GPIOA->ODR&=~(1<<11); 					// Приём
	} else if((USART1->SR & USART_SR_IDLE)!= 0) 
	{	// Окончили прием ответа от МК
			USART1->SR = ~(USART_SR_IDLE);
			GPIOA->ODR|=(1<<11); // Прием окончен
			recive_fn(rx_buf);
	}
}

Сделал вот так, получил что нужно, но кажется, что это костыль, возможно есть более элегантное решение

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


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

Элегантного решения вряд ли найдется, ибо события UART-модуля отвязаны от событий DMA.

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


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

  • Вообще-то ПОЛНОЕ завершение передачи идет с флагом TXE - Transmit Data Register Empty, который выставляется после флага TC и опустошения буфера передатчика

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


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

2 минуты назад, V_G сказал:

Вообще-то ПОЛНОЕ завершение передачи идет с флагом TXE - Transmit Data Register Empty, который выставляется после флага TC и опустошения буфера передатчика

Нет.

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


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

В 22.07.2022 в 16:38, Arlleex сказал:

Нет.

Пардон, действительно перепутал флаги TC и TXE. 

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


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

11 часов назад, Reystlin сказал:

Сделал вот так, получил что нужно, но кажется, что это костыль, возможно есть более элегантное решение

Проверку флагов в UART-обработчике еще бы правильно сделать, на всякий случай. Мало ли проект разрастется и флагов прибавится...

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


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

On 7/22/2022 at 12:06 PM, Arlleex said:

Проверку флагов в UART-обработчике еще бы правильно сделать, на всякий случай. Мало ли проект разрастется и флагов прибавится...

Как бы вы порекомендовали сделать правильнее?

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


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

Наряду с проверкой флага в SR проверять еще и факт разрешения самого прерывания

void USART1_IRQHandler(void) {
  const u32 sr = USART1->SR,
            cr = USART1->CR1;
  
  if(sr & USART_SR_TC &&
     cr & USART_CR1_TCIE) {
    ...
  }
  
  ...
}

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


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

13 часов назад, Reystlin сказал:

возможно есть более элегантное решение

Ну, можно сбрасывать TC и разрешать его прерывание перед началом отправки, тогда обработчик DMA_TCIF не понадобится (его действия можно перенести в обработчик TC). Но это если вы уверены, что ПДП натравливается на весь пакет целиком. У меня такое бывает редко, ибо практически всегда использую кольцевые буфера, а там пакет может начинаться в конце буфера, а заканчиваться в начале. 

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


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

Таким образом?

void Init_USART1()
{
		// 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->ODR&=~(1<<11); // Приём
		
		// 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 = DMA_SxCR_CHSEL_2;		// Выбираем 4 канал
		DMA2_Stream7->CR |= DMA_SxCR_MINC;			// Инкремент адреса памяти
		DMA2_Stream7->CR |= DMA_SxCR_DIR_0;			// Направление - из памяти в перефереию
		DMA2_Stream7->CR |= 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 = 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;
}

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 (*recive_fn)(uint8_t*);
// Принимает строку.
void USART1_put(uint8_t size, char *text, void (*recive)(uint8_t*))
{
	recive_fn = recive;
	for(uint8_t i =0;i<size;i++)
		tx_buf[i]=text[i];
	GPIOA->ODR|=(1<<11); // Передача
  DMA2_Stream7->CR &= ~DMA_SxCR_EN;   // Отключаем поток DMA
  DMA2_Stream7->NDTR = size;
  USART1->SR = ~(USART_SR_TC);
	USART1->CR1 |= USART_CR1_TCIE;		// Разрешение прерывания TCIE завершение передачи
  DMA2_Stream7->CR |= DMA_SxCR_EN;
};

void USART1_IRQHandler(void) 
{
	const uint32_t sr = USART1->SR, cr = USART1->CR1;
	if((sr & USART_SR_TC) && (cr & USART_CR1_TCIE)) 
		{	// Окончили отправку пакета
		USART1->CR1 &= ~USART_CR1_TCIE;	
		USART1->SR &= ~(USART_SR_TC);
		GPIOA->ODR&=~(1<<11); 					// Приём
	} else if((sr & USART_SR_IDLE) && (cr & USART_CR1_IDLEIE))
	{	// Окончили прием ответа от МК
			USART1->SR &= ~(USART_SR_IDLE);
			GPIOA->ODR|=(1<<11); // Прием окончен
			recive_fn(rx_buf);
	}
}

 

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


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

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

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

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

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

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

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

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

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

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