Jump to content

    

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 вышеописанных метода решения это "костыли" чистой воды, прошу помочь проблему более правильно.

Edited by Neo_Matrix

Share this post


Link to post
Share on other sites

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

Цитата

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.

 

Share this post


Link to post
Share on other sites
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 и т.д.) и сбрасывать их? Т.к. в некоторых случаях прием может "затыкаться" если эти флаги установлены.

Share this post


Link to post
Share on other sites
25 минут назад, ivan24190 сказал:

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

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

Edited by Neo_Matrix

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

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

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

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

Share this post


Link to post
Share on other sites
У меня так тоже было.
помогла специальная инициализация:

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);

Share this post


Link to post
Share on other sites

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

В 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 сказал:

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

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

 

 

Edited by Neo_Matrix

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites
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;
      // и делай с ним что хошь 
    }
  }

Share this post


Link to post
Share on other sites

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;
	}
}

 

Edited by Neo_Matrix

Share this post


Link to post
Share on other sites

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

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

 

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

 

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

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

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

pUSART->DR;

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now