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

STM32F407 зависание UART4 периферийного модуля

Столкнулся со странным поведение периферии UART4(на других не замечено), а точнее подвисанием. Итак имеется 2 устройства одно из них мое второе третьей стороны, оба устройства подключены по RS232. С моей стороны стоит процессор STM32F407 за ним MAX3232(питание от 3.3вольта) с сапрессорами с другой стороны находится такая же MAX3232 с сапрессорами далее уходит на проц(не смотрел какой). Так вот недалекий обслуживающий персонал частенько вынимает коннектор RS232 "на горячую", поскольку мое устройство питается от блока питания стороннего устройства земли(GND) у них в этот момент не разрываются, но конструкция разъема такова, что при попадании на металлический корпус коннектора RX, TX могут на него замкнуть(от этого защищают резисторы и сапрессоры).

Теперь суть проблемы: В момент отключения кабеля посылка данных приходящих ко мне может быть принята не полностью, на что мой софт отвечает NACK(0x15), по непонятной мне причине в этот момент может выставится флаг LBD, который используется если настроить порт как LIN, но я не использую этот самый LIN и он не активен и прерываний от него нет, именно в такие моменты периферия подвисает. Если потом вернуть разъем на место, то прерывания RXNE, TXE более не срабатывают, а софт моей задачи вылетает по таймауту.

В начале попробовал сбрасывать сам флаг LBD - флаг нормально сбрасывается но передача не возобновляется. Нашел 2 способа решить проблему костылем:

1) Сбросить ЮАРТ периферию и заново перенастроить.

2) При появлении флага LBD в регистре DR содержится(в случае зависания всегда) значение 0x0015(Мой ответ NACK). Если произвести запись в регистр любых данных все начинает снова работать, даже не нужно сбрасывать LBD. LBD - продолжаю сбрасывать для того что бы хоть как то отследить повторение зависания.

Еще раз повторюсь функционал LIN Не используется мной, а отслеживание флага LBD сделано для работы костыля. Так же повторюсь, что прерывания после такой ситуации именно перестают срабатывать, а не ПО подвисает в прерывании.

 

Использую следующую инициализацию порта:

	LL_USART_DisableRTSHWFlowCtrl(UART4);
	LL_USART_DisableCTSHWFlowCtrl(UART4);
	LL_USART_DisableLIN(UART4);
	LL_USART_DisableSCLKOutput(UART4);
	LL_USART_DisableSmartcard(UART4);
	LL_USART_DisableIrda(UART4);
	LL_USART_DisableHalfDuplex(UART4);
	LL_USART_SetTransferDirection(UART4, LL_USART_DIRECTION_TX_RX);
	LL_USART_SetParity(UART4, LL_USART_PARITY_NONE);
	LL_USART_SetDataWidth(UART4, LL_USART_DATAWIDTH_8B);
	LL_USART_SetStopBitsLength(UART4, LL_USART_STOPBITS_1);
	LL_USART_SetOverSampling(UART4, LL_USART_OVERSAMPLING_16);
	LL_USART_SetBaudRate(UART4, RCC_GetPCLK1ClockFreq(RCC_GetHCLKClockFreq(RCC_GetSystemClockFreq())), LL_USART_OVERSAMPLING_16, baudrate);
	LL_USART_Enable(UART4);
	LL_USART_EnableIT_RXNE(UART4);

 

Может кто то сталкивался с такой ситуацией? Так как 2 вышеописанных метода решения это "костыли" чистой воды, прошу помочь проблему более правильно.

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

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


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

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

Цитата

2.7.7 Break request can prevent the Transmission Complete flag (TC) from being set Description

After the end of transmission of a data (D1), the Transmission Complete (TC) flag will not be set if the following conditions are met:

CTS hardware flow control is enabled.

D1 is being transmitted.

A break transfer is requested before the end of D1 transfer.

nCTS is de-asserted before the end of D1 data transfer.

Workaround

If the application needs to detect the end of a data transfer, the break request should be issued after checking that the TC flag is set.

 

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


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

3 hours ago, Neo_Matrix said:

Столкнулся со странным поведение периферии UART4(на других не замечено), а точнее подвисанием. Итак имеется 2 устройства одно из них мое второе третьей стороны, оба устройства подключены по RS232. С моей стороны стоит процессор STM32F407 за ним MAX3232(питание от 3.3вольта) с сапрессорами с другой стороны находится такая же MAX3232 с сапрессорами далее уходит на проц(не смотрел какой). Так вот недалекий обслуживающий персонал частенько вынимает коннектор RS232 "на горячую", поскольку мое устройство питается от блока питания стороннего устройства земли(GND) у них в этот момент не разрываются, но конструкция разъема такова, что при попадании на металлический корпус коннектора RX, TX могут на него замкнуть(от этого защищают резисторы и сапрессоры).

Теперь суть проблемы: В момент отключения кабеля посылка данных приходящих ко мне может быть принята не полностью, на что мой софт отвечает NACK(0x15), по непонятной мне причине в этот момент может выставится флаг LBD, который используется если настроить порт как LIN, но я не использую этот самый LIN и он не активен и прерываний от него нет, именно в такие моменты периферия подвисает. Если потом вернуть разъем на место, то прерывания RXNE, TXE более не срабатывают, а софт моей задачи вылетает по таймауту.

В начале попробовал сбрасывать сам флаг LBD - флаг нормально сбрасывается но передача не возобновляется. Нашел 2 способа решить проблему костылем:

1) Сбросить ЮАРТ периферию и заново перенастроить.

2) При появлении флага LBD в регистре DR содержится(в случае зависания всегда) значение 0x0015(Мой ответ NACK). Если произвести запись в регистр любых данных все начинает снова работать, даже не нужно сбрасывать LBD. LBD - продолжаю сбрасывать для того что бы хоть как то отследить повторение зависания.

Еще раз повторюсь функционал LIN Не используется мной, а отслеживание флага LBD сделано для работы костыля. Так же повторюсь, что прерывания после такой ситуации именно перестают срабатывать, а не ПО подвисает в прерывании.

 

Использую следующую инициализацию порта:


	LL_USART_DisableRTSHWFlowCtrl(UART4);
	LL_USART_DisableCTSHWFlowCtrl(UART4);
	LL_USART_DisableLIN(UART4);
	LL_USART_DisableSCLKOutput(UART4);
	LL_USART_DisableSmartcard(UART4);
	LL_USART_DisableIrda(UART4);
	LL_USART_DisableHalfDuplex(UART4);
	LL_USART_SetTransferDirection(UART4, LL_USART_DIRECTION_TX_RX);
	LL_USART_SetParity(UART4, LL_USART_PARITY_NONE);
	LL_USART_SetDataWidth(UART4, LL_USART_DATAWIDTH_8B);
	LL_USART_SetStopBitsLength(UART4, LL_USART_STOPBITS_1);
	LL_USART_SetOverSampling(UART4, LL_USART_OVERSAMPLING_16);
	LL_USART_SetBaudRate(UART4, RCC_GetPCLK1ClockFreq(RCC_GetHCLKClockFreq(RCC_GetSystemClockFreq())), LL_USART_OVERSAMPLING_16, baudrate);
	LL_USART_Enable(UART4);
	LL_USART_EnableIT_RXNE(UART4);

 

Может кто то сталкивался с такой ситуацией? Так как 2 вышеописанных метода решения это "костыли" чистой воды, прошу помочь проблему более правильно.

 

А может стоит проверять флаги ошибок приема (FE, NF и т.д.) и сбрасывать их? Т.к. в некоторых случаях прием может "затыкаться" если эти флаги установлены.

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


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

25 минут назад, ivan24190 сказал:

А может стоит проверять флаги ошибок приема (FE, NF и т.д.) и сбрасывать их? Т.к. в некоторых случаях прием может "затыкаться" если эти флаги установлены.

ORE - проверяю, на остальные прерывания отключены. В дебагере четко видно, что ни один из флагов(FE и т.д.) в момент зависания приема - не выставлен, кроме того, как объяснить продолжение нормальной работы после записи в регистр DR(чтение этого регистра проблему не убирает!)

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

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


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

Добавил проверку ошибок UART по принципу проверили флаг -> напечатали принтом ошибку -> сбросили флаг. Несколько раз при горячем отключении ошибки проскакивали, но UART от их обработки виснуть не перестал.

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


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

Ну во первых флаг LBD - это не обязательно LIN. Например я его использую как флаг начала пакета.

Во вторых странно, что у вас не выставляется флаг FE. Хотя он не на что и не влияет. 

В третьих флаг OVR заслуживает отдельного внимания, поскольку ( как я понял в некоторых камнях ) он блокирует дальнейший приём. В мэнуале как то странно написано, что сбросить его можно чтением приемника ??? и/или ??? чтением регистра SR (вроде). Поэтому при приёме байта сначала вычитываем RX и SR в каие нибудь переменные, а потом уже занимаемся всем остальным. Правда во время обработки приема байта, флаг OVR опять может установиться ( от разного там дребезга в контактах ). Ситуация редкая, но я уже с этим сталкивался (приёмник просто намертво виснет, хотя казалось бы, что ранее флаг OVR был сброшен).

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

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


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

У меня так тоже было.
помогла специальная инициализация:

GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);


//UART4_TX   PC.10 PC.11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &GPIO_InitStructure); 


    GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_UART4);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_UART4);

//Usart1 NVIC 

NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;      
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         
NVIC_Init(&NVIC_InitStructure); 

USART_InitStructure.USART_BaudRate = Baud;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; 

USART_Init(UART4, &USART_InitStructure); 
USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);
USART_Cmd(UART4, ENABLE);

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


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

Уезжал на праздники, потому долго не отвечал.

В 28.04.2019 в 11:19, vlad_new сказал:

Ну во первых флаг LBD - это не обязательно LIN. Например я его использую как флаг начала пакета. 

Во вторых странно, что у вас не выставляется флаг FE. Хотя он не на что и не влияет. 

В третьих флаг OVR заслуживает отдельного внимания, поскольку ( как я понял в некоторых камнях ) он блокирует дальнейший приём. В мэнуале как то странно написано, что сбросить его можно чтением приемника ??? и/или ??? чтением регистра SR (вроде). Поэтому при приёме байта сначала вычитываем RX и SR в каие нибудь переменные, а потом уже занимаемся всем остальным. Правда во время обработки приема байта, флаг OVR опять может установиться ( от разного там дребезга в контактах ). Ситуация редкая, но я уже с этим сталкивался (приёмник просто намертво виснет, хотя казалось бы, что ранее флаг OVR был сброшен).

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

Ну, LBD у меня при нормальном приеме данных не выставляется(если кабель не дергать на горячую), или я не замечал. FE точно не выставляется, прикрепил скриншот к сообщению, там написано, что этот флаг работает только в мультибуферном режиме . А что за флаг OVR, не нашел такого?

Спойлер

image.thumb.png.9229d8ed2ecdef57723902b11551f979.png

В 28.04.2019 в 12:19, dsl2640free сказал:

У меня так тоже было. помогла специальная инициализация:

Не смог найти нечего специального, ткните носом плиз.

 

 

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

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


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

Код программы прерывания по приему можно увидеть ? Можно не весь - хотя-бы половину, но с самого начала. Для примера: распространенные примеры процедур приема по CUBE HALL - требует мусорника и вечного забытия, а не использования. (HALL STM (индус-софт) разработанное API не позволяет правильно обрабатывать ошибки, так как сброс некоторых ошибок происходит после прочтения регистра SR, а предоставленый халом API может  за 1 раз проверить только один указаный бит - в общем архичёсы были еще те). Его писали студенты индусы не читающие тех. мануалы - да и еще в аквариумных условиях в офисе, без помех. И этот код никак не фиксится - примеров с чудо обработкой данных - полно и там одна и та же ошибка. Благо дело аппаратные нюансы по сбросам ошибок на другой периферии более менее нормальные, в противном случае горе индусы бы и дальше слонов пасли.

Софтварный (не DMA) прием любого байта должен начинаться в 100 % случаев с двух действий - чтение SR а потом чтение DR, никак не иначе.

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


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

47 минут назад, Картошка сказал:

Софтварный (не DMA) прием любого байта должен начинаться в 100 % случаев с двух действий - чтение SR а потом чтение DR, никак не иначе.

+1, с таким подходом ни разу в жизни не ловил подвисание UART.

  uint32_t sr = USART1->SR;

  if(sr & USART_SR_RXNE)
  {   
    if(sr & (USART_SR_PE | USART_SR_FE | USART_SR_NE | USART_SR_ORE))
    { //Была ошибка в приеме байта      
      USART1->DR;
    }
    else
    {    
      uint8_t RCV_data = USART1->DR;
      // и делай с ним что хошь 
    }
  }

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


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

DMA не использую, так как скорость 9600, максимальная посылка байтов 100 от силы, в основном по 5-10 байт, периодичность посылок 100мс а то и более. Как видите в ДМА нет резона.

Вот обработчик прерывания, правда не последней версии. Но глючит в любом из них.(Немного отредактировал пост)

void uart_irq_handler(uint32_t uart_number) {
	uart_data_t *uart_data;
	volatile uint32_t tmp_reg;
	uint32_t cr3_reg;
	uint32_t cr1_reg;
	uint32_t sr_reg;
	uint8_t tmp_error = CLEAR;
	union {
		uint8_t data_8[2];
		uint16_t data_16;
	} tmp_data = {0};
	union {
		uint8_t *pdata_8;
		uint16_t *pdata_16;
		void *data_void;
	} tmp_pdata;

	uart_data = get_uart_port_data(uart_number);
	cr3_reg = read_reg(uart_data->uart_typedef, CR3);
	cr1_reg = read_reg(uart_data->uart_typedef, CR1);
	sr_reg = read_reg(uart_data->uart_typedef, SR);
  	if((read_bit(cr1_reg, LL_USART_CR1_PEIE) != (CLEAR)) && \
		 (read_bit(sr_reg, LL_USART_SR_PE) != (CLEAR))) {
		tmp_error |= UART_ERROR_PE;
	}
	if(read_bit(cr3_reg, LL_USART_CR3_EIE) != (CLEAR)) {
		if(read_bit(sr_reg, USART_SR_ORE) != (CLEAR)) {
			tmp_error |= UART_ERROR_ORE;
		}
		if(read_bit(sr_reg, LL_USART_SR_FE) != (CLEAR)) {
			tmp_error |= UART_ERROR_FE;
		}
		if(read_bit(sr_reg, LL_USART_SR_NE) != (CLEAR)) {
			tmp_error |= UART_ERROR_NE;
		}
	}
	if((read_bit(cr1_reg, USART_CR1_RXNEIE) != (CLEAR))) {
		if(read_bit(sr_reg, USART_SR_ORE) != (CLEAR)) {
			tmp_error |= UART_ERROR_ORE;
		}
		if(read_bit(sr_reg, USART_SR_RXNE) != (CLEAR)) {
			if(read_bit(cr1_reg, USART_CR1_M) != (CLEAR)) {
				tmp_data.data_16 = (uint16_t)(read_bit_reg(uart_data->uart_typedef, DR, USART_DR_DR));
				tmp_pdata.data_void = (void *)&tmp_data.data_16;
			}
			else {
				tmp_data.data_8[1] = (uint8_t)(read_bit_reg(uart_data->uart_typedef, DR, USART_DR_DR));
				tmp_pdata.data_void = (void *)&tmp_data.data_8;
			}
			drv_rx_from_isr(uart_data->driver_data, tmp_pdata.data_void);
		}
	}
	if(tmp_error != CLEAR) {
		tmp_reg = read_reg(uart_data->uart_typedef, SR);
		(void)tmp_reg;
		tmp_reg = read_reg(uart_data->uart_typedef, DR);
		(void)tmp_reg;
		//tmp_data.data_8[0] |= tmp_error;
		drv_err_from_isr(uart_data->driver_data, tmp_error);
	}
	if((read_bit(cr1_reg, USART_CR1_TXEIE) != (CLEAR)) && \
		 (read_bit(sr_reg, USART_SR_TXE) != (CLEAR))) {
		tmp_pdata.data_void = uart_data->uart_tx_data; 
		if(read_bit(cr1_reg, USART_CR1_M) != (CLEAR)) {
			write_reg(uart_data->uart_typedef, DR, (*tmp_pdata.pdata_16 & 0x01FFU));
			if(--uart_data->uart_tx_data_len == 0U) {
				clear_bit_reg(uart_data->uart_typedef, CR1, USART_CR1_TXEIE);
				set_bit_reg(uart_data->uart_typedef, CR1, USART_CR1_TCIE);
			}
			else {
				tmp_pdata.pdata_16++;
				uart_data->uart_tx_data = tmp_pdata.data_void;
			}
		}
		else {
			write_reg(uart_data->uart_typedef, DR, (*tmp_pdata.pdata_8 & 0xFFU));
			if(--uart_data->uart_tx_data_len == 0U) {
				clear_bit_reg(uart_data->uart_typedef, CR1, USART_CR1_TXEIE);
				set_bit_reg(uart_data->uart_typedef, CR1, USART_CR1_TCIE);
			}
			else {
				tmp_pdata.pdata_8++;
				uart_data->uart_tx_data = tmp_pdata.data_void;
			}
		}
		//return;
	}
	if((read_bit(cr1_reg, LL_USART_CR1_TCIE) != (CLEAR)) && \
		 (read_bit(sr_reg, LL_USART_SR_TC) != (CLEAR))) {
		clear_bit_reg(uart_data->uart_typedef, SR, USART_SR_TC);
		clear_bit_reg(uart_data->uart_typedef, CR1, USART_CR1_TCIE);
		drv_tx_from_isr(uart_data->driver_data);
		//return;
	}
}

 

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

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


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

Меня тоже бы взглючило от такого...

Как писал Картошка, первым делом надо считать SR и по его содержимому уже решать что делать. Читать DR только если взведён RXNE, а использовать как валидный только при отсутствии битов ошибки в SR. Лучше SR и DR один раз считать во временные локальные переменные, они будут в регистрах процессора и оптимизатор с ними справится.

 

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


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

Ну и где же регистр SR читается не первым? Имеете ввиду CR1, CR3. Как же без их чтения узнать включено ли прерывание в данный момент по определенному событию? Кроме того DR там и читается только при условии, что RXNE установлен при включенном соответствующем прерывании, в других случаях он читается только для очистки флагов ошибок и об этом явным образом написано в даташите.

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


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

Когда я писал ответ там был совсем другой код. И всё равно, зачем обрабатывать ошибки, если не взведён RXNE?

 

А что вот это за конструкция, кстати? Дичь какая-то.

volatile uint32_t tmp_reg;
tmp_reg = read_reg(uart_data->uart_typedef, DR);
(void)tmp_reg;

Можно ассемблерный листинг этого куска посмотреть? Вообще, это пишется вот так.

pUSART->DR;

 

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


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

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

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

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

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

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

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

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

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

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