Обсуждаем контроллеры компании Atmel.
Ответить

Atmega328 rs485 на прерываниях

Ср июн 22, 2022 21:41:49

Всем привет. Пытаюсь реализовать прием и передачу по rs485 на прерываниях. Тоесть с компьютера отправить массив из 14 байт и получить его обратно. Все это работает но не долго. В какой то момент ножка направления передачи остается поднятой и все встает колом. Такое ощущение что прерывание по отправке не всегда срабатывает и не скидывает ножку направления.

Код:
ISR(USART_TX_vect) // прерывание по завершению отправки
{
    PORTD &= ~0b00000100; // Отпускаем ножку направления PD2 по прерыванию завершения отправки данных
    UCSR0B &= ~(1 << TXCIE0);
    UCSR0B |= (1 << RXCIE0);
}
//Прерывание по опустошению буффера УАПП
ISR(USART_UDRE_vect)
{   
    PORTD |= 0b00000100; // Поднимаем ножку направления PD2 по прерыванию начала отправки данных   
    if (bufferIndexOut >= INDEX_MAX) // Вывели весь буффер?
    {
        bufferIndexOut = 0;     
        UCSR0B &= ~(1 << UDRIE0); // Запрещаем прерывание по опустошению - передача закончена     
    }
    else
    {
        UDR0 = inString[bufferIndexOut];
        bufferIndexOut++; // Увеличиваем индекс
    }
}
ISR(USART_RX_vect) // прерыванию по окончанию приема модуля USART
{
    inchar = UDR0;
    if (inchar == 0x2A)
        bufferIndexIn = 0;
    if (bufferIndexIn < 14)
    {
        inString[bufferIndexIn] = inchar;
        bufferIndexIn++;
    }
    if (bufferIndexIn > 13) // Вывели весь буффер?
    {
        bufferIndexIn = 0;                       // Сбрасываем индекс массива
        UCSR0B |= (1 << UDRIE0) | (1 << TXCIE0); // Разрешаем прерывание по опустошению - начинаем передачу массива
    }
}

Re: Atmega328 rs485 на прерываниях

Чт июн 23, 2022 10:09:12

Попробуйте без запрещения прерывания, но с флагом состояния массива… то есть, если массив не опустошился полностью, но в это время пришёл новый байт, то сохранять его уже не в опустошаемый массив, а в дополнительный (резервный для таких случаев), с поднятием флага задействия резервного массива… это лишь моя гипотеза засады с запретом прерывания… возможно у вас и другие подводные камни имеются… :dont_know: Данного языка не знаю, потому мне не видно имеется ли ещё проверка опустошения буфера передачи перед новым его заполнением? :roll:

Re: Atmega328 rs485 на прерываниях

Чт июн 23, 2022 10:42:57

-
Последний раз редактировалось roman.com Чт июн 23, 2022 16:13:06, всего редактировалось 1 раз.

Re: Atmega328 rs485 на прерываниях

Чт июн 23, 2022 14:48:06

А на кой черт вы лапу задираете по опустошению буфера?

Re: Atmega328 rs485 на прерываниях

Чт июн 23, 2022 15:49:17

А на кой черт вы лапу задираете по опустошению буфера?

Я ее где уже только не задирал. И по опустошению и в прерывании приемника когда данные пришли и пора начинать передачу. Сейчас я вообще отключил опустошение и оставил только прерывание по окончанию передачи. Результат лучше.. теперь это может работать минут 10 но потом опять нога остается задранной. При этом в терминале весь пакет полностью принят и по идее на последнем символе нога должна погаснуть. но это видимо происходит не всегда почему то.

Добавлено after 1 minute 57 seconds:
Попробуйте без запрещения прерывания, но с флагом состояния массива… то есть, если массив не опустошился полностью, но в это время пришёл новый байт, то сохранять его уже не в опустошаемый массив, а в дополнительный (резервный для таких случаев), с поднятием флага задействия резервного массива… это лишь моя гипотеза засады с запретом прерывания… возможно у вас и другие подводные камни имеются… :dont_know: Данного языка не знаю, потому мне не видно имеется ли ещё проверка опустошения буфера передачи перед новым его заполнением? :roll:

По состоянию флага не очень хорошо. Программа может выполняться достаточно долго прежде чем проверит состояние флага. Это приведет к замиранию всей линии. Поэтому я и решил на прерываниях сделать.

Добавлено after 9 minutes 31 second:
Код:
ISR(USART_TX_vect) // прерывание по завершению отправки
{
   cli();
   
   if (bufferIndexOut <= 13)
   {

      UDR0 = inString[bufferIndexOut];
      bufferIndexOut++; // Увеличиваем индекс
   }
   else if (bufferIndexOut > 13) // Вывели весь буффер?
   {
      
      PORTD &= ~0b00000100; // Опускаем ножку направления PD2 по прерыванию начала отправки данных
      bufferIndexOut = 0;
      
      UCSR0B &= ~(1 << TXCIE0);
      
      UCSR0B |= (1 << RXCIE0);
      
   }
   sei();
}
//Прерывание по опустошению буффера УАПП

ISR(USART_RX_vect) // прерыванию по окончанию приема модуля USART
{

   cli();
   
   inchar = UDR0;
   if (inchar == 0x2A)
      bufferIndexIn = 0;
   if (bufferIndexIn < 14)
   {
      receiveDog=0;
      
      inString[bufferIndexIn] = inchar;
      bufferIndexIn++;
   }

   
   if (bufferIndexIn > 13) // Вывели весь буффер?
   {
      
      bufferIndexIn = 0;   // Сбрасываем индекс массива
      bufferIndexOut = 0; // Сбрасываем индекс массива
      
      UCSR0B &= ~(1 << RXCIE0);
      UCSR0B |= (1 << TXCIE0);
      PORTD |= 0b00000100; // Поднимаем ножку направления PD2 по прерыванию начала отправки данных
      UDR0 = inString[bufferIndexOut];
      
      bufferIndexOut++;
      
   }
sei();
}


Сейчас пробую такой подход без UDRE
фактически результат такой же просто дольше работает

Re: Atmega328 rs485 на прерываниях

Чт июн 23, 2022 16:53:04

Алгоритм простой: собрались что-то передать - задрали ногу, заполнили буфер, если данные переданы и буфер пуст - сбросили ногу. Все.

Re: Atmega328 rs485 на прерываниях

Чт июн 23, 2022 17:42:00

Алгоритм простой: собрались что-то передать - задрали ногу, заполнили буфер, если данные переданы и буфер пуст - сбросили ногу. Все.

Делал и так но результат тот же. В приведенном следующем коде примерно так и происходит.

Re: Atmega328 rs485 на прерываниях

Чт июн 23, 2022 17:58:09

из строки
else if (bufferIndexOut > 13) { ... // Вывели весь буффер?

if (bufferIndexOut > 13)
лишнее. Нет 3-го варианта. Отсюда следует:

else { ...
достаточно. (не то чтобы оптимизатор не удалил..., и хотя бы предупредил)

(А ничего не меняет логику этих исправлений :) ).

Re: Atmega328 rs485 на прерываниях

Чт июн 23, 2022 18:05:51

По состоянию флага не очень хорошо. Программа может выполняться достаточно долго прежде чем проверит состояние флага. Это приведет к замиранию всей линии. Поэтому я и решил на прерываниях сделать.

Вы меня не поняли… :dont_know: я не предлагал проверять флаги в теле основной программы и отказаться от прерываний… флаги проверяются при возникновении соответствующих прерываний… главный посыл был не запрещать прерывания когда очищаете массив… может ваша программа очищает массив как раз тогда когда передаются данные (об этом вы же не писали)... :roll: как вы пишите: - нога не отпускается по завершению отправки данных – это значит нет прерывания по завершению передачи байта… Возникает вопрос, каким образом передаётся байт, аппаратными средствами или программными? Если аппаратными, то что-то напутано в подпрограмме прерывания… или что-то мешает (запрещает) наступить этому прерыванию. Какие действия выполняет при этом МК (в момент передачи данных)? У вас прерывания в этот период не запрещаются?

Re: Atmega328 rs485 на прерываниях

Чт июн 23, 2022 20:33:40

По состоянию флага не очень хорошо. Программа может выполняться достаточно долго прежде чем проверит состояние флага. Это приведет к замиранию всей линии. Поэтому я и решил на прерываниях сделать.

Вы меня не поняли… :dont_know: я не предлагал проверять флаги в теле основной программы и отказаться от прерываний… флаги проверяются при возникновении соответствующих прерываний… главный посыл был не запрещать прерывания когда очищаете массив… может ваша программа очищает массив как раз тогда когда передаются данные (об этом вы же не писали)... :roll: как вы пишите: - нога не отпускается по завершению отправки данных – это значит нет прерывания по завершению передачи байта… Возникает вопрос, каким образом передаётся байт, аппаратными средствами или программными? Если аппаратными, то что-то напутано в подпрограмме прерывания… или что-то мешает (запрещает) наступить этому прерыванию. Какие действия выполняет при этом МК (в момент передачи данных)? У вас прерывания в этот период не запрещаются?

А программы то самой и нет я ее удалил. Там работает пустой цикл while(1)
То что я здесь выложил то и есть. Я уже битую неделю бьюсь над приемом и передачей и безрезультатно. Прежде чем возвращать программу на место надо понять что же не так с прерываниями. В каком месте я допускаю ошибку.

Добавлено after 4 minutes 16 seconds:
Изображение
На это можно смотреть целый день пока не всрянет ножка направления.

Добавлено after 45 minutes 6 seconds:
из строки
else if (bufferIndexOut > 13) { ... // Вывели весь буффер?

if (bufferIndexOut > 13)
лишнее. Нет 3-го варианта. Отсюда следует:

else { ...
достаточно. (не то чтобы оптимизатор не удалил..., и хотя бы предупредил)

(А ничего не меняет логику этих исправлений :) ).

Я конечно же займусь упрощением всей этой петрушки.

Re: Atmega328 rs485 на прерываниях

Пт июн 24, 2022 01:36:49

А программы то самой и нет я ее удалил. Там работает пустой цикл while(1)
То что я здесь выложил то и есть.

Давай сделаем так… вот прошивка для меги328Р, тактовая 16 МГц. Настроен UART 9600, 8 Data, 1 stop bit, Disabled. МК в дежурном режиме находится как приёмник (на выводе РD2 низкий уровень). МК принимает не более 14 байт (меньше можно, но только в этом случае в завершении передачи должен быть послан CR ($0D), чтобы МК понимал, что уже конец передачи). Приняв пакет данных МК переводит вывод PD2 в высокий уровень и возвращает данные обратно в той же последовательности. Вот проверь как будет работать. Только ПК должен повторно передавать данные тогда, когда примет весь пакет от МК (не раньше).
UART_Demo.hex
(1.07 KiB) Скачиваний: 69

Re: Atmega328 rs485 на прерываниях

Пт июн 24, 2022 10:37:43

алгоритм, имхо, должен быть такой:
1. драйвер 485 всегда находится в режиме приема, прерывание по приёму разрешено и заполняет буфер и т.п.
2. когда надо отправить буфер, прерывание по приему запрещается, драйвер 485 переключается в режим передачи, в UDR записывается первый байт, разрешаются прерывания по опустошению буфера (в такой последовательности)
3. в обработчике прерывания по опустошению передачи очередной байт записывается в UDR, а если весь буфер передан, то разрешается прерывание по окончанию передачи
4. в обработчике по окончанию передачи драйвер переключается в режим приема, прерывания по передаче запрещаются, прерывание по приему разрешается.

Re: Atmega328 rs485 на прерываниях

Пт июн 24, 2022 11:46:33

А программы то самой и нет я ее удалил. Там работает пустой цикл while(1)
То что я здесь выложил то и есть.

Давай сделаем так… вот прошивка для меги328Р, тактовая 16 МГц. Настроен UART 9600, 8 Data, 1 stop bit, Disabled. МК в дежурном режиме находится как приёмник (на выводе РD2 низкий уровень). МК принимает не более 14 байт (меньше можно, но только в этом случае в завершении передачи должен быть послан CR ($0D), чтобы МК понимал, что уже конец передачи). Приняв пакет данных МК переводит вывод PD2 в высокий уровень и возвращает данные обратно в той же последовательности. Вот проверь как будет работать. Только ПК должен повторно передавать данные тогда, когда примет весь пакет от МК (не раньше).
UART_Demo.hex

Ваша прошивка работает. Поставил на час и никаких проблем.
Изображение

Добавлено after 24 minutes 9 seconds:
алгоритм, имхо, должен быть такой:
1. драйвер 485 всегда находится в режиме приема, прерывание по приёму разрешено и заполняет буфер и т.п.
2. когда надо отправить буфер, прерывание по приему запрещается, драйвер 485 переключается в режим передачи, в UDR записывается первый байт, разрешаются прерывания по опустошению буфера (в такой последовательности)
3. в обработчике прерывания по опустошению передачи очередной байт записывается в UDR, а если весь буфер передан, то разрешается прерывание по окончанию передачи
4. в обработчике по окончанию передачи драйвер переключается в режим приема, прерывания по передаче запрещаются, прерывание по приему разрешается.

Казалось бы все просто и понятно. Но видимо что то я упускаю.

Re: Atmega328 rs485 на прерываниях

Пт июн 24, 2022 15:42:46

2. когда надо отправить буфер, прерывание по приему запрещается,

Это лишнее… всего на всего необходимо вывод Rx (PD0) подтянуть внутренним резистором к положительной шине питания, чтобы при переключении микры RS-485 на передачу, данный вывод не остался висеть в воздухе и тогда запрещать прерывание по приёму нет необходимости… а так как RS-485 полудуплексный интерфейс, принять что либо по факту в режиме передачи невозможно.
1.png
(29.56 KiB) Скачиваний: 49

а если весь буфер передан, то разрешается прерывание по окончанию передачи

В моей второй версии данное прерывание разрешено всегда… нет повода его запрещать, так как по прерыванию опустошения регистра данных в него записывается следующий байт… и когда все байты будут переданы наступит прерывание по завершению передачи…

Добавлено after 2 minutes 36 seconds:
Казалось бы все просто и понятно. Но видимо что то я упускаю.

У Вас не так сделано как описал ARV. Вот я точно так же как и Вы сделал только два прерывания и они работают (по завершения принятого байта и по завершении передачи байта)… только такой подход для передачи пакетов медленнее работает чем как предложил ARV отслеживания опустошения регистра данных UDR0. Так как когда регистр данных (UDR0) копирует своё значение в сдвиговый регистр, то считается что он уже освободился и в него можно записывать следующий байт для передачи, в то время как регистр сдвига передаёт своё содержимое в шину… В нашем же варианте есть некая задержка ожидание полного завершения передачи байта, и лишь затем вновь записывается следующий байт для передачи…
Если быстродействие не важно, то можно и так, а если важно, то лучше отслеживать опустошения регистра данных.

Вот немного изменённый вариант с отслеживанием регистра данных (UDR0)…
UART_Demo_V2.hex
(1.18 KiB) Скачиваний: 69

Передача пакета должна происходить быстрее по сравнению с предыдущей версией. Попробуй… если всё будет работать нормуль, то попробую описать славами как нужно сделать…

Re: Atmega328 rs485 на прерываниях

Сб июн 25, 2022 16:03:39

Вот немного изменённый вариант с отслеживанием регистра данных (UDR0)…
Вложение:
UART_Demo_V2.hex [1.18 KiB]
Скачиваний: 2

Передача пакета должна происходить быстрее по сравнению с предыдущей версией. Попробуй… если всё будет работать нормуль, то попробую описать славами как нужно сделать…

Значит вот что произошло. В цикле while(1) я закоментировал весь выполнявшийся код для дальнейших мучений с uart. Но оказалось что последнюю строчку я не закоментировал случайно и этого не заметил out(outs); . И вот что скрывается под этой функцией:
Код:
void out(uint16_t x)
{
   PORTD |= 0xC0;                                 // OE1,2 disable
   DDRB = 0b00001111;                              // portB out
   DDRC = 0b00011111;                              // portC out
   PORTB = (PORTB & 0xF0) | (uint8_t)(x & 0x000F);         // 1nd 4bit write
   PORTC = (PORTC & 0xF0) | (uint8_t)((x & 0x00F0) >> 4);   // 2nd 4bit write
   PORTD |= 0x10;                                 // CLK1 OUT ENABLE
   PORTD &= 0xEF;                                 // CLK1 OUT DISABLE
   PORTB = (PORTB & 0xF0) | (uint8_t)((x & 0x0F00) >> 8);   // 3nd 4bit write
   PORTC = (PORTC & 0xF0) | (uint8_t)((x & 0xF000) >> 12); // 4nd 4bit write
   PORTD |= 0x20;                                 // CLK2 OUT ENABLE
   PORTD &= 0xDF;                                 // CLK2 OUT DISABLE
   DDRB = 0b00000000;                              // portB IN
   DDRC = 0b00010000;                              // portC IN
}


Значит я закоментировал эту функцию и все вроде бы заработало почти как надо. Кривенько но заработало. Ножка направления перестала сходить с ума. Самое интересное то что раньше с этим проблем не возникало видимо это связано с тем что uart работал без прерываний.
Сама эта функция имеет важное значение и без нее никак не обойтись. Такое ощущение эти строчки которые обращаются к PORTD как то на это влияют. Но при этом ножка направления задиралась через разные промежутки времени.

Кстати на осцилограммах видно как у меня ножка приема повисает в воздухе.

Re: Atmega328 rs485 на прерываниях

Сб июн 25, 2022 16:38:46

Кстати на осцилограммах видно как у меня ножка приема повисает в воздухе.

Это может спровоцировать прерывание по приёму… есть два решения, чтобы этого избежать… первый как подсказывал ARV, запрещать прерывание по приёму… второе, это подтянуть вывод к плюсу питания через резистор…

Re: Atmega328 rs485 на прерываниях

Пн июн 27, 2022 16:19:59


Вот немного изменённый вариант с отслеживанием регистра данных (UDR0)…
UART_Demo_V2.hex

Передача пакета должна происходить быстрее по сравнению с предыдущей версией. Попробуй… если всё будет работать нормуль, то попробую описать славами как нужно сделать…

Этот вариант работает так.. После каждого 14 пакета он выдает ответ. Не после CR символов а 14 пакета.

Добавлено after 53 minutes 40 seconds:
Значит вот что получается. Когда функция не подключена все вроде работает. Как только подключаем функцию нога начинает дрыгаться.
Изображение

И вот по поводу висящей ножки приема. Выставил PORTD |= 0b00000001; и вход перестал повисать в воздухе. Внутренняя подтяжка сработала в этом случае.

С одной стороны причина дрыгfния ножки найдена. Но что именно в этой функции заставляет ее менять направление что то пока не могу понять.

Re: Atmega328 rs485 на прерываниях

Пн июн 27, 2022 17:04:39

Что-то вы делаете не так… :dont_know:
Проверил… посылал 20 раз подряд с ПК 14 байт и сразу получал от МК ответ те же 14 байт… затем, послал 5 раз подряд 3 байта с обозначением о конце посылки $0D и так же сразу получал ответ с теми же тремя байтами и с обозначением конца посылки в виде байта $0D.
Вот скрин в подтверждение работы программы… обратите внимание на время отправления с ПК и получения обратного ответа с МК (с лево – байты отправляемые с ПК, с право – байты отправляемые ответом с МК ).
1.png
(54.89 KiB) Скачиваний: 47


В моей прошивке вывод PD0 подтянут внутренним резистором к плюсу питания…

Добавлено after 34 minutes 11 seconds:
Этот вариант работает так.. После каждого 14 пакета он выдает ответ. Не после CR символов а 14 пакета.

Видимо Ваша посылка с ПК идёт не пакетом из 14 байт, а по одному байту 14 раз… вот МК когда получает 14_й байт и только тогда формирует обратный пакет из этих принятых 14 байт. А вот если бы Вы после любого байта отправили байт $0D (байт окончания пакета), то МК бы сформировал бы ответ из тех байт которые были получены до байта $0D, ну и его же поместил в отправляемый пакет дабы сообщить об окончании своего пакета.

Re: Atmega328 rs485 на прерываниях

Пн июн 27, 2022 17:15:02

Что-то вы делаете не так… :dont_know:

После перезагрузки компьютера заработало.

Re: Atmega328 rs485 на прерываниях

Пн июн 27, 2022 17:21:01

По алгоритму есть вопросы? Или уже сами поняли как это работает?
Ответить