PIC16F887 [MikroC] - EUSART и прерывания [решено]

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Закрыто
Аватара пользователя
bschepan
Родился
Сообщения: 11
Зарегистрирован: Сб апр 11, 2009 18:09:45
Откуда: SPb
Контактная информация:

PIC16F887 [MikroC] - EUSART и прерывания [решено]

Сообщение bschepan »

Приветствую всех!

Исходные данные:
  • Микроконтроллер PIC16F887, скорость 16Мгц, слово конфигурации: _HS_OSC & _WDT_OFF & _LVP_OFF (то, что по умолчанию предлагается в MikroC в свойствах проекта)
  • EUSART настроен на скорость 9600bps, асинхронный режим, стоповый бит один, четности и контроля за потоком нет (в общем стандартнее некуда)
  • Компилятор MikroC 8.2.0.0 (также пробовался MikroC Pro 4.60)
  • Proteus 7.5 SP3 b7401

Понадобилось мне для вышеозначенного PIC'a написать обмен данными по последовательному порту, причем реализовать это через прерывания. Собственно, то, что получилось, в приложении. И все чудесным образом работает в Proteus'е, однако, в реальности получается полная охинея (скриншот охинеи также в приложении). Там видно, что вместо слова Ping принимается белиберда. Собственно, отправляется тоже не то, что нажимается на клавиатуре компа.

У меня просьба помочь мне разобраться, почему в реальности все работает не так, как надо.

И еще вопрос в том, как правильно обрабатывать ошибки переполнения буфера приемника (бит RCSTA.OERR) и отсутствия стопового бита (бит RCSTA.FERR)? В смысле, в какой момент времени надо проверять эти биты (прямо в прерывании или в основной программе)?

Вкратце про программу:
Сначала настраиваются порты и LCD, затем инициализируется TMR0 с периодом срабатывания 1мс (потом с помощью него раз в секунду мигает светодиод RB3, отсылается слово "Ping" в терминал и выводится на вторую строчку LCD принятый по USART символ). Потом идет настройка EUSART, я сделал это совсем "вручную" по битам, чтобы лучше понимать, что там происходит. Если делать настройку EUSART библиотечной командой Usart_Init(9600), то эффект будет такой же. Затем ждем нажатия кнопки RA4 и запускаем бесконечный цикл.
По пришествии какого нибудь символа программа сразу же (в прерывании) отправляет его назад, попутно выводя его на LCD. Это для начала, потом хочется сделать прием нормальных ASCIIZ строк.
В принципе, я вроде написал очень подробные комментарии в коде, так что там все должно быть понятно.

Да, попутно возник еще один вопрос: почему в Proteus'е не мигает светодиод при использовании такой инверсии бита в порте (в реальности все мигает):

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

PORTB ^= (1 << 3);

но если написать что-то вроде

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

value = ~value; // здесь value - это unsigned short value = 0;
PORTB.F3 = value;

то все заработает и в Proteus'e, и в реальности? Это такой глюк симулятора, или я чего-то не понимаю?
Вложения
pic16f887_usart_test_reallife.PNG
Охинея в реальной жизни
(35.94 КБ) 881 скачивание
pic16f887_usart_test_proteus.PNG
Работающая в Proteus программа
(63.65 КБ) 773 скачивания
usart_test1.rar
Исходник, проект для MikroC и файл симуляции для Proteus
(43.08 КБ) 319 скачиваний
Последний раз редактировалось bschepan Вс апр 17, 2011 16:40:42, всего редактировалось 1 раз.
Реклама
Аватара пользователя
urry
Сверлит текстолит когтями
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница
Контактная информация:

Re: PIC16F887 [MikroC] - проблема с EUSART и прерываниями

Сообщение urry »

странно, конечно, почему в протеусе работает. Вы переполняете буфер передачи.
Нужно ждать, пока освободится регистр выдачи и потом уже посылать следующий байт
#define wait_out() do{ }while(TRMT == 0);//пока полный сдвиговый регистр*
и обработка ошибок приема - да, в прерывании по приему байта
#define serial_error() OERR
#define nostop() FERR
#define serial_fix() {CREN = 0; CREN = 1;}
if (RCIF && RCIE)
{

if(serial_error()) serial_fix();
if (nostop()) RCREG;

if (nostop()) RCREG;- это фишка хайтека, впрочем, на других компиляторах может не заработать - при пропуске стопового бита прочитать RCREG в аккумулятор
Реклама
Аватара пользователя
bschepan
Родился
Сообщения: 11
Зарегистрирован: Сб апр 11, 2009 18:09:45
Откуда: SPb
Контактная информация:

Re: PIC16F887 [MikroC] - проблема с EUSART и прерываниями

Сообщение bschepan »

urry, спасибо большое за советы по поводу обработки ошибок, мне как раз про это и нужно было узнать.
Только вот про буфер передачи я не совсем понял. Я ведь записываю байт в регистр TXREG только по прерыванию TXIF, а оно срабатывает, когда TXREG пуст, то есть в этом месте я ничего не теряю. А из TXREG в TXR байт переносится только по окончании сдвига всех битов из TXR. Получается, что вперед очереди никакой байт не пролезет. Или я что-то не так понял про механизм работы передатчика?

2All, кстати, с проблемой "кракозябр" на реальном железе я разобрался, МК почему-то стартовал на частоте 20мгц, а не 16. А у меня все расчеты скорости были на 16. В общем, в железе все тоже заработало так же, как и в симуляторе. То есть без обработки регистра TSR.
Аватара пользователя
bschepan
Родился
Сообщения: 11
Зарегистрирован: Сб апр 11, 2009 18:09:45
Откуда: SPb
Контактная информация:

Re: PIC16F887 [MikroC] - проблема с EUSART и прерываниями

Сообщение bschepan »

Так, передачу строк я написал, получилось вот что:

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

if (PIR1.TXIF && PIE1.TXIE) {                    // Обработка прерывания от передатчика,
                                                 // если разрешена передача
    temp = StringOut[StringOutIndex];
    if (temp != '\0') {                          // Если это не терминальный символ, то:
       while (TRMT == 0);                        // Ждем освобождения регистра TSR
       TXREG = temp;                             // Начинаем передачу
       StringOutIndex++;                         // Переходим к следующему символу
   
    } else {                                     // А если это был конец строки, то передавать его не будем
      StringOutIndex = 0;                        // Перемотаем индекс строчки на начало
      PIE1.TXIE = DISABLE;                       // И отключаем прерывания передатчика (согласно пункту 12.1.1.3 даташита)
    }
 }

Оно даже работает. И даже как надо. :)

А вот с приемом пока не совсем понятно. Написал я пока вот такое:

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

if (PIR1.RCIF) {                                 // Обработка прерывания от приемника
    if (RCSTA.FERR) {                            // Если была ошибка кадра,
     RCSTA.SPEN = 0;                             // то выключаем и включаем последовательный порт
     RCSTA.SPEN = 1;                             // (это механизм принудительной очистки бита FERR из пункта 12.1.2.4 даташита)
    }
    temp = RCREG;                                // Читаем принятый байт в буфер
   
    if (RCSTA.OERR) {                            // Если была ошибка переполнения приемника, то
     RCSTA.CREN = 0;                             // то выключаем и включаем приемник.
     RCSTA.CREN = 1;
    }
 }

Если с битом OERR все понятно: его надо сбросить выключением и включением приемника, чтобы продолжался прием, то с FERR не очень понятно. В даташите написано, что его не надо сбрасывать и можно продолжать чтение из RCREG, но если его считать еще раз, то предыдущий прочтенный байт потеряется?
И можно ли как-нибудь принудительно-нарочно вызвать все эти ошибки при приеме, чтобы посмотреть, что при этом происходит?
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
urry
Сверлит текстолит когтями
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница
Контактная информация:

Re: PIC16F887 [MikroC] - проблема с EUSART и прерываниями

Сообщение urry »

ferr - пропуск стопового бита.для сброса ошибки просто читаете рсрег, данные не используете.
сымитировать переполнение легко - включите в прерывание задержку, и пока она там идет, пошлите ему 3 байта с терминала.
Реклама
Аватара пользователя
bschepan
Родился
Сообщения: 11
Зарегистрирован: Сб апр 11, 2009 18:09:45
Откуда: SPb
Контактная информация:

Re: PIC16F887 [MikroC] - проблема с EUSART и прерываниями

Сообщение bschepan »

urry, понятно, спасибо! Но в принципе можно FERR обрабатывать и так, как у меня?

В общем, прием строки в итоге получился таким:

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

 if (PIR1.RCIF && PIE1.RCIE) {                    // Обработка прерывания от приемника
    if (RCSTA.FERR) {                            // Если была ошибка кадра,
     RCSTA.SPEN = 0;                             // то выключаем и включаем последовательный порт
     RCSTA.SPEN = 1;                             // (это механизм принудительной очистки бита FERR из пункта 12.1.2.4 даташита)
    }
    temp = RCREG;                                // Читаем принятый байт в буфер
   
    if (RCSTA.OERR) {                            // Если была ошибка переполнения приемника, то
     RCSTA.CREN = 0;                             // то выключаем и включаем приемник.
     RCSTA.CREN = 1;
    }
   
   switch (temp) {
    case 0x08:                                   // Получен символ Backspace (ascii код = 8)
         if (StringInIndex > 0) {
            StringInIndex--;                     // Стираем последний полученный символ, если таковой имеется
         }
         break;
    case 0x0D:                                   // Получен символ возврата каретки (ascii код = 13)
    case 0x00:                                   // Получен символ окончания строки (ascii код = 0)
         StringIn[StringInIndex] = '\0';         // Ставим символ окончания строки
         StringInFlag = 1;                       // Прием строки закончен
         StringInIndex = 0;                      // Перематываем индекс строчки на начало
         PIE1.RCIE = DISABLE;
         break;
    default:
         StringIn[StringInIndex] = temp;         // Получены другие символы
         StringInIndex++;
         if (StringInIndex == MAX_STR_LNGTH) {   // Если принимать больше некуда,
            StringIn[StringInIndex] = '\0';      // то ставим терминальный символ
            StringInFlag = 1;                    // и флаг окончания приема строки
            StringInIndex = 0;                   // Перематываем индекс строчки на начало
            PIE1.RCIE = DISABLE;
         }
         break;
   }
 }

Я вот думаю, может вообще убрать флаг окончания приема и вместо него проверять PIE1.RCIE?

В приложении архив с проектом для MikroC Pro 4.60 и файл симуляции Proteus. Программа возвращает принятую строчку (окончание строкии - Enter либо символ с ascii кодом 0x00), попутно мигая светодиодом и отсылая строку Ping в терминал.

З.Ы. Я, кстати, удивлен, что не встречал подобных реализаций приема-передачи (через прерывания, без использования библиотечных функций MikroC). Неужели никто так не пишет?
Вложения
USART Test.zip
Проект для MikroC Pro 4.60 и файл симуляции Proteus
(53.08 КБ) 215 скачиваний
Реклама
Аватара пользователя
urry
Сверлит текстолит когтями
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница
Контактная информация:

Re: PIC16F887 [MikroC] - EUSART и прерывания [решено]

Сообщение urry »

ну это пока учебно-тренировочное все, тепличные условия.
нужно быть готовым к тому, что с ком-порта может придти все, что угодно. Я обычно ставлю байт маркера, дальше идет информация и контрольная сумма. Синхронизация - когда счетчик начала пакета устанавливается в 0 я беру по таймеру - если какое-то время ничего не приходило, таймер сбрасывает счетчик. Если пришел синхробайт - сбрасывается таймер.
Контрольная сумма бьет - информации верим.
Допустим, вы решили передать пакет по rs485, щелчок при переключении с режима приема на передачу уже взведет прерывание на другой стороне.
Есть готовые протоколы.
Аватара пользователя
bschepan
Родился
Сообщения: 11
Зарегистрирован: Сб апр 11, 2009 18:09:45
Откуда: SPb
Контактная информация:

Re: PIC16F887 [MikroC] - EUSART и прерывания [решено]

Сообщение bschepan »

urry, а Вы не могли бы поделиться каким-то каркасом кода? Я на словах не совсем понял про маркер и счетчик начала пакета. И описанием на какой-нибудь несложный протокол обмена?

Условия у меня кстати, хоть и учебные, но не совсем тепличные. Если вкратце (надо было, правда, с самого начала это написать), то это часть моего диплома. Диплом состоит в конструировании и программировании танкового робота, сейчас он выглядит вот так: RoboPica_v4 (фото 1.8мб) Это расширенный мною набор RoboPica. Связь с компом осуществляется Bluetooth модулем http://www.inexglobal.com/products.php?cat=wireless&cat=wireless&model=zxbluetooth (для него мне требовалась скорость 9600), так что помех при передаче может быть вагон + наводки от передатчика камеры, от двигателей.
Аватара пользователя
urry
Сверлит текстолит когтями
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница
Контактная информация:

Re: PIC16F887 [MikroC] - EUSART и прерывания [решено]

Сообщение urry »

Протокол - ну вот на этом же сайте от Ридико Леонида Ивановича
viewtopic.php?f=20&t=39776&hilit=%D0%BF%D1%80%D0%BE%D1%82%D0%BE%D0%BA%D0%BE%D0%BB
http://digit-el.com/files/open/wake/wake.html
Свой каркас кода на его фоне давать - это как играть на скрипке в присутствии Паганини... :)
Аватара пользователя
bschepan
Родился
Сообщения: 11
Зарегистрирован: Сб апр 11, 2009 18:09:45
Откуда: SPb
Контактная информация:

Re: PIC16F887 [MikroC] - EUSART и прерывания [решено]

Сообщение bschepan »

Почитал описание протокола Wake - суперская вещь, спасибо! Правда, времени на его полную реализацию у меня уже не хватит :(, придется взять его за базис и сделать что-то попроще...
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»