Страница 1 из 2

ADM485 время задержки

Добавлено: Пт авг 07, 2015 08:12:58
krik_nk
Всем привет. Пишу программку, которая передает сообщения с ATMEGA32 на RS485. Имеется кварц с частотой 14745600. Использую модуль http://www.mikroe.com/add-on-boards/communication/rs485/ фирмы микроэлектроника для передачи данных. В данном модуле используется ADM485. Так вот у меня вопрос. При отправке байтов через USART я пишу байты в UDR, при этом конечно же должен быть включен режим передачи. Так вот если просто передать байты в UDR, то данные не пересылаются или пересылаются не полностью. Для того, чтобы передать байт я выдерживаю задержку Delay_ms(32), которая была подобрана методом "тыка". Меня интересует вопрос, как вычислить или где посмотреть время данной задержки? В течении кода, выставлено несколько задержек, но такое ощущение, что это все не совсем правильно. Ведь если потом поменять кварц, либо скорость передачи данных, не уверен что все дальше будет правильно работать. Подскажите как сделать правильно! Опыта с данными модулями совсем нет :?

Вот части моего кода:

Функция инициализации:

Код: Выделить всё

void USART_Init(void){
  // Baudrate calculation
  DATA_BAUDRATE = DATA_BAUDRATE*16;
  DATA_UBRR = (_DATA_CLOCK/DATA_BAUDRATE)-1;
  /*Set baud rate */
  UBRRH = Hi(DATA_UBRR);
  UBRRL = Lo(DATA_UBRR);

  DATA_TX_INDEX = 0;
  DDRD &=~(1<<0); // set PORTD.0 as input for ADM485 (RX)
  DDRD |= (1<<1); // set PORTD.1 as output for ADM485 (TX)
  DDRD |= (1<<3); // set PORTD.3 as output for ADM485 (transmit-recieve mode)

  // Normal BaudRate (if U2X == 1 then x2 BaudRate)
  UCSRA &=~ (1 << U2X);
  // RXCIE interrupt, RX enable
  UCSRB |= (1 << RXCIE)|(1 << RXEN)|(1 << TXEN);
  // URSEL -> 8 bit - one word; (1 stop bit)
  UCSRC |= (1 << URSEL)|(1 << UCSZ1)|(1 << UCSZ0);
}


Функция по которой принимается байт:

Код: Выделить всё

void interrupt_USART_RXC_vect() org IVT_ADDR_USART__RXC {
  DATA_RX = UDR;
}


Функция по которой отправляется массив байт:

Код: Выделить всё

void interrupt_USART_UDRE_vect() org IVT_ADDR_USART__UDRE {
  if(DATA_TX_INDEX < (_DATA_TX_COUNT)){
    UDR = DATA_TX[DATA_TX_INDEX];
    Delay_ms(40);
    // next element in array
    DATA_TX_INDEX = DATA_TX_INDEX + 1;
  }
  else{
    DATA_TX_INDEX = 0;
    // Exit vector
    UCSRB &=~ (1 << UDRIE);
    // Recive mode ON
    UCSRB |= (1 << RXEN);
    // Low level control pin
    PORTD &=~ (1 << 5);
    // delay of changing mode (to recive messages)
    Delay_ms(32);
  }
}


Собственно использование в главном цикле:

Код: Выделить всё

    // ---------- RS485 ----------
    if(DATA_RX == _DATA_REQUEST){
      DATA_RX = 0;
      // Recive mode OFF
      UCSRB &=~ (1 << RXEN);
      // High level control pin
      PORTD |= (1 << 5);
      // delay of changing mode (to trancive messages)
      Delay_ms(32);
      // Calculation CRC-16
      DATA_TX_CRC = CRC16(&DATA_TX,8);
      DATA_TX[8] = Hi(DATA_TX_CRC);
      DATA_TX[9] = Lo(DATA_TX_CRC);
      // entrance vector
      UCSRB |= (1 << UDRIE);
    }

    // ----------
    asm nop;

Re: ADM485 время задержки

Добавлено: Пт авг 07, 2015 08:52:23
akl
А вообще без задержки работать пробовали?
СпойлерИзображение
ADM485_RE_DE.GIF

Можно перевести ADM485 в режим мониторинга линии (RE<--- GND, DE<--- +5V) и посмотреть что там творится.

Re: ADM485 время задержки

Добавлено: Пт авг 07, 2015 09:09:00
Jack_A
Задержки в цикле не нужны, тем более такие громадные . Каждый последующий байт закидывается в UDR после того, как проверкой бита UDRE убедимся в том, что буфер пуст. Передав последний байт, дожидаемся установки бита TxC, свидетельствующего об окончании передачи, и можем снимать бит режима передачи в МАХ485 .

Re: ADM485 время задержки

Добавлено: Пт авг 07, 2015 11:43:54
krik_nk
akl, Если убрать все задержки, то вообще ничего не пересылает.

Jack_A, А переход в вектор прерывания void interrupt_USART_UDRE_vect() org IVT_ADDR_USART__UDRE разве не свидетельствует о том, что буфер пуст?
Убрал все задержки, вставил после вызова прерывания после последнего отправленного байта.

Код: Выделить всё

while (!(UCSRA & 1<<TXC0));  
asm nop;


Однако отправляется заместо 10 байт всего лишь 6 :(
Чтобы отправить все 10 байт, опять же методом тыка, получилось только, если добавить задержку в 10 мс... Почему 10 мс, то ??? Или нужно что-то по другому сделать?

Код: Выделить всё

void interrupt_USART_UDRE_vect() org IVT_ADDR_USART__UDRE {
  if(DATA_TX_INDEX < (_DATA_TX_COUNT)){
    UDR = DATA_TX[DATA_TX_INDEX];
    //Delay_ms(40);
    // next element in array
    DATA_TX_INDEX = DATA_TX_INDEX + 1;
    while (!(UCSRA & 1<<TXC0));
     asm nop;
    Delay_ms(10);
  }
  else{
    while (!(UCSRA & 1<<TXC0));
     asm nop;
    DATA_TX_INDEX = 0;
    // Exit vector
    UCSRB &=~ (1 << UDRIE);
    // Recive mode ON
    UCSRB |= (1 << RXEN);
    // Low level control pin
    PORTD &=~ (1 << 3);
    // delay of changing mode (to recive messages)
    //Delay_ms(32);
  }
}

Re: ADM485 время задержки

Добавлено: Пт авг 07, 2015 14:57:28
Jack_A
Я для AVRок Си не использовал, поэтому мне трудно судить и в Сишном коде разбираться лениво. У меня проекты все были на асме, и с обменом по 485-му не было вопросов типа какой код взбредет на ум сгенерить компилятору.
Это не очередной виток Си vs Asm! - просто что было, то и описываю.

Re: ADM485 время задержки

Добавлено: Пт авг 07, 2015 15:19:50
krik_nk
Да я не противник ASM. Просто тут нужно именно на СИ сделать. Принципе в теории разобрался, но вот на практике оказались эти самые задержки. Понятно, что они нужны какие-то, но когда в даташите написаны наносекунды, задаешься вопросом, а правильно ли я все делаю?

Re: ADM485 время задержки

Добавлено: Пт авг 07, 2015 16:12:00
Jack_A
Есть возможность взять осцилл и посмотреть ? А откуда известно, что не все байты уходят - по реакции приемника ? А если в нем косяк ?

Re: ADM485 время задержки

Добавлено: Пт авг 07, 2015 21:05:41
YS
А переход в вектор прерывания void interrupt_USART_UDRE_vect() org IVT_ADDR_USART__UDRE разве не свидетельствует о том, что буфер пуст?


То, что буфер UDR пуст, не означает, что передача закончена!

События UDRE и TXC - разные. UDRE означает только то, что данные из UDR были переданы в скрытый сдвиговый регистр и начали передаваться, а в UDR можно записать еще байт.

Даташит, страница 147:

A data transmission is initiated by loading the transmit buffer with the data to be transmitted. The
CPU can load the transmit buffer by writing to the UDR I/O location. The buffered data in the
transmit buffer will be moved to the Shift Register when the Shift Register is ready to send a new
frame. The Shift Register is loaded with new data if it is in idle state (no ongoing transmission) or
immediately after the last stop bit of the previous frame is transmitted. When the Shift Register is
loaded with new data, it will transfer one complete frame at the rate given by the Baud Register,
U2X bit or by XCK depending on mode of operation.


TXC означает, что данные реально передались. По очевидным причинам переключать режим трансивера можно только после окончания реальной передачи данных. Потому вместо задержки вам надо вставить ожидание TXC. Например,

Код: Выделить всё

while (!(UCSRA & _BV(TXC)))
{
}

Re: ADM485 время задержки

Добавлено: Пт авг 07, 2015 21:10:52
krik_nk
Думал тоже про приемник, но приемник у меня такой же микроконтроллер, с похожим модулем. Данные вывожу на LCD. Раз в 5 секунд опрашиваю и вывожу ответ. Пробовал использовать много вариантов, крутил вертел код, в итоге на задержки эти вышел. Нашел кстати информацию http://radiokot.ru/forum/viewtopic.php?f=20&t=32791&view=print. Там тоже проблема с задержкой была:
Было:
Код:

Код: Выделить всё

      USART_SendChar('O'); //отвечаем компу "Ok "
      USART_SendChar('k');
      LED_PORT=LED_PORT & ~(1<<send);
      LED_PORT=LED_PORT & ~(1<<LED2);
      LED_PORT=LED_PORT & ~(1<<LED1);
      __delay_cycles(10000000);


А надо:
Код:

Код: Выделить всё

      USART_SendChar('O'); //отвечаем компу "Ok "
      USART_SendChar('k');
      __delay_cycles(10000000);
      LED_PORT=LED_PORT & ~(1<<send);
      LED_PORT=LED_PORT & ~(1<<LED2);
      LED_PORT=LED_PORT & ~(1<<LED1);


Во втором косяке передатчик ADM'a отключался раньше, чем отправлялись данные запиханные в буфер USART'a.

Re: ADM485 время задержки

Добавлено: Сб авг 08, 2015 04:46:35
akl
YS писал(а):...TXC означает, что данные реально передались. По очевидным причинам переключать режим трансивера можно только после окончания реальной передачи данных. Потому вместо задержки вам надо вставить ожидание TXC.
Добавлю к сказанному, что флаг TxC аппаратно очищается только в соответствующем обработчике прерывания. Т.к. это прерывание не разрешено, то флаг TxC должен чиститься программно записью 1 в UCSRA. Например для ATmega64 по каналу USART0.

Код: Выделить всё

   LDI   R17,1<<TxEN0
   OUT   UCSR0B,R17

   SBI   PORTE,2            ; RS485 на передачу в линию

   LDI   YH,HIGH(RAM_FRAME_BEG)
   LDI   YL,LOW(RAM_FRAME_BEG)
   LDI   ZH,HIGH(RAM_FRAME_END)
   LDI   ZL,LOW(RAM_FRAME_END)
GO_TxD_RS:
   RCALL   TXD
   CP   YL,ZL
   CPC   YH,ZH
   BRLO   GO_TxD_RS

   СBI   PORTE,2            ; RS485 на приём с линии

   RJMP   GO
;*************************************************
TXD:
   LD      R0,Y+
   OUT      UDR0,R0
WAIT_TXC:
   SBIS   UCSR0A,TxC0
   RJMP   WAIT_TXC

   SBI      UCSR0A,TxC0   ; очистить установленный бит TxC0

   RET

Re: ADM485 время задержки

Добавлено: Сб авг 08, 2015 08:12:34
Jack_A
Ну раз есть приемник, задача диагностики облегчактся : первые байты передаются правильно, дальше молчание, или через байт, или вообще битый пакет приходит? Скорости и остальные параметры передачи совпадает на прд. и прм. ? И насчет бита TxC очень правильно YS обратил внимание.

Re: ADM485 время задержки

Добавлено: Сб авг 08, 2015 08:42:23
YS
О, кстати, в даташите на стр. 149 как раз про RS-485 пишут. :)

The Transmit Complete (TXC) Flag bit is set one when the entire frame in the transmit Shift
Register has been shifted out and there are no new data currently present in the transmit buffer.
The TXC Flag bit is automatically cleared when a transmit complete interrupt is executed, or it
can be cleared by writing a one to its bit location. The TXC Flag is useful in half-duplex
communication interfaces (like the RS485 standard), where a transmitting application must enter
receive mode and free the communication bus immediately after completing the transmission.


Т.к. это прерывание не разрешено, то флаг TxC должен чиститься программно записью 1 в UCSRA.


Уточню - чиститься непосредственно перед тем, как положить байт в UDR.

Re: ADM485 время задержки

Добавлено: Сб авг 08, 2015 21:46:40
krik_nk
В понедельник на работе попробую еще, отпишусь.

Re: ADM485 время задержки

Добавлено: Пн авг 10, 2015 08:28:42
krik_nk
Все таки решил воспользоваться вектором прерывания по отправке. Реально избавился от многих задержек. Но одну так победить и не получается. Задержка включения режима отправки ADM485 delay_ms(2). Без данной задержки приходят все байты неправильно.

В главном цикле проверяю пришел ли нужный запрос, если да, то отправляю пакет байтов:

Код: Выделить всё

    
if(DATA_RX == _DATA_REQUEST){
      DATA_RX = 0;
      // Recive mode OFF
      UCSRB &=~ (1 << RXEN);
      // High level control pin
      PORTD |= (1 << 3);
      delay_ms(2); // <<-------- DELAY
      USART_SendPacket();
    }


Отправка первого байта:

Код: Выделить всё

void USART_SendPacket(void)
{
  // Wait for empty transmit buffer
  while (!( UCSRA & (1<<UDRE)));
  // Put data into buffer, sends the data
  UDR = DATA_TX[0];
}


Отправка последующих байт, с проверкой окончания данных:

Код: Выделить всё

void interrupt_USART_TXC_vect() org IVT_ADDR_USART__TXC {
  if(DATA_TX_INDEX < (_DATA_TX_COUNT)){
    UDR = DATA_TX[DATA_TX_INDEX];
    // next element in array
    DATA_TX_INDEX = DATA_TX_INDEX + 1;
  }
  else{
    DATA_TX_INDEX = 1;
    // Recive mode ON
    UCSRB |= (1 << RXEN);
    // Low level control pin
    PORTD &=~ (1 << 3);
  }
}


Прием запроса, тоже вектор прерывания:

Код: Выделить всё

void interrupt_USART_RXC_vect() org IVT_ADDR_USART__RXC {
  DATA_RX = UDR;
}

Re: ADM485 время задержки

Добавлено: Пн авг 10, 2015 08:37:59
YS
while (!( UCSRA & (1<<UDRE)));


:facepalm:

Я же советовал заменить все ожидания UDRE на ожидание TXC. :idea:

Re: ADM485 время задержки

Добавлено: Пн авг 10, 2015 09:44:47
krik_nk
Пробовал, не помогает...

Re: ADM485 время задержки

Добавлено: Пн авг 10, 2015 12:46:29
krik_nk
Провел опыты и заметил, что в зависимости от скорости USART'а меняется и время необходимой задержки включения порта. К примеру:
100 baud = 16 ms,
150 baud = 4 ms,
200 baud = 3 ms,
400 baud = 2 ms,
1200 baud = 1 ms,
4800 baud = 0 ms,
9600 baud = 0 ms

С учетом того, что мне данные скорости нужны, как-то не очень получается.

Re: ADM485 время задержки

Добавлено: Пн авг 10, 2015 13:00:16
YS
Посмотрел код.

UCSRB &=~ (1 << RXEN);


Зачем вы дергаете сам UART? В этом нет необходимости. В даташит я не вчитывался, но, возможно, время тратится на инициализацию внутреннего конечного автомата, что-то там такое вроде было.

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

Re: ADM485 время задержки

Добавлено: Пн авг 10, 2015 13:47:06
krik_nk
Спасибо за совет. Это убрал. И правда дергать думаю не за чем. А вот на счет этой задержки автомата, то в даташите вообще все характеристики в нс указанны, непонятно откуда мс вылезают.

Re: ADM485 время задержки

Добавлено: Пн авг 10, 2015 17:36:37
YS
Я имел в виду конечный автомат UART'а. В ADM никакого конечного автомата нет (ну, почти), это обычная логика. :)

Гм. А физически никакого конденсатора, например, на этой линии не висит?

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