Jump to content

    

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

Да, я выше отписал, что внес изменения. Просто сейчас нет с собой последнего кода. По поводу обработки ошибок посмотрите, на скриншот.

https://electronix.ru/forum/uploads/monthly_2019_05/image.png.9b928738676a71ba399fd46b3b49b619.png

Там есть 2 варианта прерываний на ошибки(EIE, RXNEIE при этом флаг ORE может вызывать прерывание в обоих случаях). Один из них это выключить прерывание по приему и включить прерывания на ошибки, тогда подвиснем в прерывании навечно(до выставления флага RXNE так точно). Потому прерывания ошибок обрабатываю отдельно. Но это не суть. В случае зависания в отладчике эти флаги не выставлены, т.е. не выставлен не один из всех возможных флагов прерываний кроме LBD

Share this post


Link to post
Share on other sites

До того, как выше посоветовали внести обработку ошибок, код был где-то таким:

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

	uart_data = get_uart_port_data(uart_number);
	cr1_reg = read_reg(uart_data->uart_typedef, CR1);
	sr_reg = read_reg(uart_data->uart_typedef, SR);
	if((read_bit(cr1_reg, USART_CR1_RXNEIE) != (CLEAR))) {
		if(read_bit(sr_reg, USART_SR_ORE) != (CLEAR)) {
			tmp_reg = read_reg(uart_data->uart_typedef, SR); // Clear error
			(void)tmp_reg;
			tmp_reg = read_reg(uart_data->uart_typedef, DR);
			(void)tmp_reg;
		}
		else {
			if(read_bit(sr_reg, USART_SR_RXNE) != (CLEAR)) { // Save data
				data = (uint8_t)(read_bit_reg(uart_data->uart_typedef, DR, USART_DR_DR));
				drv_rx_from_isr(uart_data->driver_data, tmp_pdata.data_void);
			}
		}
	}
  .........................................
}

 

Share this post


Link to post
Share on other sites

Попробуй вот так, больше чем уверен, что ничего виснуть не будет.

  uint32_t sr = USART1->SR;

  if((sr & USART_SR_RXNE) && (USART1->CR1 & USART_CR1_RXNEIE))
  {   
    uint32_t dr = USART1->SR;
    if(sr & (USART_SR_PE | USART_SR_FE | USART_SR_NE | USART_SR_ORE))
    { //Была ошибка в приеме байта      
      // в dr мусор
    }
    else
    {    
      // в dr нормальные данные       
    }
  }

 

Share this post


Link to post
Share on other sites

Попробую, но как-то не уверен, что поможет, не может же быть, что какой-то флаг установлен, а дебагер его не отображает. С флагом ОРЕ должна быть другая обработка, текущий байт ведь в порядке, потерян следующий.

Завтра сделаю скрины с дебагера, в случае зависания порта.

Edited by Neo_Matrix

Share this post


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

Попробуй вот так, больше чем уверен, что ничего виснуть не будет.

В даташите сказано, что последовательное чтение регистров SR - > DR очищает флаг прерывания. У вас между этими чтениями происходит чтение регистра CR1 в условии if. Это точно правильно?

Ст. 1009 допустим:

Цитата

This bit is set by hardware when a parity error occurs in receiver mode. It is cleared by a software sequence (a read from the status register followed by a read or write access to the USART_DR data register). The software must wait for the RXNE flag to be set before clearing the PE bit

 

Edited by Neo_Matrix

Share this post


Link to post
Share on other sites

Очистит, никуда он не денется. В любом случае, можно и вынести чтение наружу. Но я бы вообще не выключал прерывания по приёму никогда и отказался от этой проверки.

Share this post


Link to post
Share on other sites

У меня есть юарты работающие только на прием и только на передачу(точнее такой был раньше).

Share this post


Link to post
Share on other sites

Ну значит оставь проверку CR1, ни на что она не влияет. А вот работу с "портом по номеру" стоит переосмыслить. Ну это же правда дичь какая-то. Смотри насколько прощё и читабельнее всё.

void uart_irq_handler(USART_TypeDef * pUSART) 
{
  if(pUSART->SR & USART_SR_RXNE)
  {
    pUSART->DR;
  }  
}
//  void uart_irq_handler(USART_TypeDef * pUSART) 
//  {
//    if(pUSART->SR & USART_SR_RXNE)
        LDR      R1,[R0, #+0]
        LSLS     R2,R1,#+26
        IT       MI 
        LDRMI    R0,[R0, #+4]
//    {
//      pUSART->DR;
//    }  
//  }
        BX       LR               ;; return

 

Share this post


Link to post
Share on other sites

А как переписать универсально под все порты, не применяя номера портов или указатели на структуры? Я выбрал номер и создал массив структур, в которых хранится доп инфа, допустим указатели на буферы РТОСа, а номер передается в качестве индекса массива. Я не нашел других простых подходов. Лично для меня код хорошо читаем, при компиляции с включенной оптимизацией собирается в достаточно компактный вид, другого мне не нужно. Функции на подобие read_bit() это обычные макросы, в которых описано, что и у вас в условии if.

Share this post


Link to post
Share on other sites

Дело твоё, но когда я вижу вот это

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

то возникают большие сомнения в адекватности того что от меня спрятали за макросами и "массивами структур указателей".

А вообще, очень хорошо, даже просто замечательно, что это всё не у меня. Заставить виснуть UART не многим удаётся. 

Мой совет такой - напиши простой работающий на 100% во всех условиях код, а затем его "универсализируй". 

Share this post


Link to post
Share on other sites
9 часов назад, Neo_Matrix сказал:

не применяя номера портов или указатели на структуры?

Никак. Но предпочтительнее хранить указатель, чтобы избежать постоянного вычисления "номер => указатель".

Share this post


Link to post
Share on other sites
9 часов назад, Neo_Matrix сказал:

А как переписать универсально под все порты, не применяя номера портов или указатели на структуры? Я выбрал номер и создал массив структур, в которых хранится доп инфа, допустим указатели на буферы РТОСа, а номер передается в качестве индекса массива. Я не нашел других простых подходов.

Создать структуру, в которой будут храниться все эти указатели, в том числе и указатель на периферийные регистры UART. На каждый порт - своя такая структура. И везде передавать указатель на неё вместо номера порта.

У меня так:

struct UartHndVar {
  u16 volatile rposTx, wposTx; //позиции чтения/записи передачи
  u16 volatile rposRx, wposRx; //позиции чтения/записи приёма
  u8 volatile waitTx, waitRx;  //id ожидающей задачи ОС; если нет такой ==OS_PRIO_SELF
};

struct UartHnd {
  UartHndVar *var;
  HwRegsUSIC::T_CH volatile *io;
  char *bufTx, *bufRx;
  u16 sizTx, sizRx;
  u8 srFmrTx, srFmrRx;
  void Init() const;
  void IsrRx() const;
  void IsrTx() const;
  void IsrErr() const;
  void Timer() const;
  void Tx(int) const;
  void Tx(void const *, uint) const;
  int  Rx() const;
  int  Rx(u32, u32) const;
  int  NoEmptyTx() const;
  int  NoEmptyRx() const { int i = var->rposRx; return i - var->wposRx; }
};

где: io - указатель на регистры периферии данного UART; далее - все необходимые данные по данному порту для драйвера UART.

UartHnd - const и может храниться во флешь. UartHndVar - находится в ОЗУ, содержит текущие изменяющиеся параметры (позиции чтения/записи в FIFO и т.п.).

Share this post


Link to post
Share on other sites

 

В 15.05.2019 в 07:11, VladislavS сказал:

Дело твоё, но когда я вижу вот это


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

то возникают большие сомнения в адекватности того что от меня спрятали за макросами и "массивами структур указателей".

А вообще, очень хорошо, даже просто замечательно, что это всё не у меня. Заставить виснуть UART не многим удаётся.  

Мой совет такой - напиши простой работающий на 100% во всех условиях код, а затем его "универсализируй".  

Нечего необычного в этом нет, это стандартный набор макросов из CMSIS, просто маленькими буквами. Неужели не видели?

#define read_reg(__INSTANCE__, __REG__) READ_REG(__INSTANCE__->__REG__)

#define SET_BIT(REG, BIT)     ((REG) |= (BIT))
#define CLEAR_BIT(REG, BIT)   ((REG) &= ~(BIT))
#define READ_BIT(REG, BIT)    ((REG) & (BIT))
#define CLEAR_REG(REG)        ((REG) = (0x0))
#define WRITE_REG(REG, VAL)   ((REG) = (VAL))
#define READ_REG(REG)         ((REG))

А volatile перед tmp_reg нужен для того что бы оптимизатор не выкинул пустое чтение регистра, которое необходимо для очистки флагов, попробуйте без него и будете потом долго искать почему флаги не сбрасываются, так что меня терзают сомнения, что у вас код намного лучше моего. А вот эта конструкция (void)tmp_reg; необходима для того, что бы анализаторы кода не давали ложных варнингов, потому как переменная нигде не используется.

Удается ли добиться многим зависание порта незнаю, возможно просто этому не придают значения. У меня все хорошо работало на многих точках(300+) с закрытыми коннекторами на протяжении почти 2х лет. проблемы возникли только после установки на новое оборудование с коннекторами, которые могут дотронутся своим RX TX до металлического корпуса устройства тем самым посадив линии в ноль. Корпус устройства заземлен и соединен с минусом питания.

В 15.05.2019 в 08:34, Сергей Борщ сказал:

Никак. Но предпочтительнее хранить указатель, чтобы избежать постоянного вычисления "номер => указатель".

Здесь согласен на 100%, но тогда структура должна быть глобальной и видимой во всех файлах, которые ее используют. Так как она используется и в прерывании тоже. Мне наглядней использовать номер порта, как это сделано в стандартном PPP стеке LWIP. Это к сожалению уже личные предпочтения каждого и у всех фломастеры разные на вкус.

Edited by Neo_Matrix

Share this post


Link to post
Share on other sites
В 15.05.2019 в 08:47, jcxz сказал:

Создать структуру, в которой будут храниться все эти указатели, в том числе и указатель на периферийные регистры UART. На каждый порт - своя такая структура. И везде передавать указатель на неё вместо номера порта. 

У меня так: 

Как и в ответе выше, у всех подход свой и у всех он разный. Мне показалось, что ваша структура содержит слишком много данных, но для вас это в самый раз. Так что здесь каждому свое. Мне вспомнился подобный подход в портах под разные отладочные платы, которые пишет СТМ(не утверждаю что это плохо или хорошо).

Share this post


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

Как и в ответе выше, у всех подход свой и у всех он разный. Мне показалось, что ваша структура содержит слишком много данных, но для вас это в самый раз.

При чём тут количество данных в ней? Она содержит те данные, которые нужны для работы драйвера. Не больше и не меньше. Если для вашего драйвера нужны другие данные - естественно они будут другие и количество будет другое.

И тот мой пост был ответом на ваше сообщение, в котором вы писали, что не смогли найти способа как всё объединить в единое целое (и указатель на периферию UART и указатели на буфера FIFO и т п.) с единым указателем на это. Я продемонстрировал как это сделать. И далее во всех функциях пользоваться только этим указателем, без всяких "номеров порта" и т.п.

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