Микроконтроллеры PIC. Архитектура и программирование. Часть 10. Последовательный интерфейс микроконтроллеров PIC24F » Программирование устройств на PIC микроконтроллерах


Логин:
Пароль:
О сайте:

Pic.Rkniga.ru - Сайт как для начинающих, так и для опытных радиолюбителей, разрабатывающих свои устройства на популярных PIC микроконтроллерах.
Здесь можно обмениваться сообщениями на форуме, а также добавлять на сайт статьи и схемы своих устройств.

Меню сайта
Главная Форум по PIC микроконтроллерам Форум Статьи по PIC микроконтроллерам Статьи Справочная информаци по PIC микроконтроллерам Справочник Литература по PIC микроконтроллерам Литература Схемотехника Схемотехника устройств на PIC микроконтроллерах Микроконтроллеры Программаторы Все по программированию PIC микроконтроллеров Программы, Софт Программы Ссылки
Опрос

На каком языке программирования вы пишите программы?


Ассемблер
Си
Бейсик
Паскаль
Другой


Последние материалы
  • Тестовая плата для отладки программ на микроконтроллере PIC18F4550
  • Кнопка On/OFF на PIC12F629.
  • Часы с синхронизацией от китайского будильника
  • ШИМ регулятор на PIC16F628A.
  • Счетчики прямого и обратного счета на PIC16F628A.
  • Таймер отключения питания для мультиметра и не только.
  • Измеритель напряжения и тока
  • Маршрутный компьютер для электровелосипеда
  • Простой двухканальный термометр на PIC16F690 и датчиках DS18B20
  • Электронная "Незабудка" для забывчивых
  • Популярные материалы
    Случайная книга
    Программирование устройств на PIC микроконтроллерах » Справочник » Микроконтроллеры PIC. Архитектура и программирование. Часть 10. Последовательный интерфейс микроконтроллеров PIC24F
    Микроконтроллеры PIC. Архитектура и программирование. Часть 10. Последовательный интерфейс микроконтроллеров PIC24F
    Автор публикации: alex Просмотров: 8357 Добавлен: 27-09-2012, 14:08 Комментарии: 0

    Содержание
    1. Обзор 16-битных PIC-микроконтроллеров
    2. Архитектура микроконтроллеров PIC24F
    3. Система команд и основы программирования микроконтроллеров PIC24F
       3.1 Программная модель микроконтроллеров PIC24F
       3.2 Режимы адресации и система команд
    4. Программирование портов ввода-вывода
       4.1 Аппаратно-программная архитектура портов ввода/вывода
       4.2 Программирование портов ввода/вывода
       4.3 Модуль регистрации событий
    5. Программирование прерываний
    6. Программирование таймеров
       6.1 Практическое использование 16-битных таймеров
       6.2 Работа таймеров в 32-битном режиме
    7. Интерфейс SPI микроконтроллеров PIC24F
       7.1 Аппаратно-программная реализация SPI в микроконтроллерах PIC24F
       7.2 Практическое программирование обмена данными по SPI
    8. Интерфейс I2C микроконтроллеров PIC24F
       8.1 Принципы функционирования интерфейса I2C
       8.2 Модуль интерфейса I2C микроконтроллеров PIC24F
       8.3 Практическое использование интерфейса I2C
    9.Программирование интерфейса PMP
       9.1 Режимы работы PMP
       9.2 Практические примеры программирования интерфейса PMP
    10. Последовательный интерфейс микроконтроллеров PIC24F
       10.1 Аппаратно$программная архитектура UART
       10.2 Практическое использование последовательного порта
    11. Обработка аналоговых сигналов в микроконтроллере PIC24F
       11.1 Программная модель интегрированного АЦП
       11.2 Практическое использование модуля АЦП
       11.3 Использование внешнего АЦП
    12. Генерация аналоговых и цифровых сигналов
       12.1 Модуль генерации цифровых сигналов
       12.2 Аналоговые компараторы в микроконтроллерах PIC24F

         Асинхронный последовательный интерфейс является одним из первых интерфейсов, разработанных для обмена данными между устройствами. Однако, несмотря на свой почтенный возраст, этот интерфейс в настоящее время продолжает очень широко использоваться, причем его не смогли вытеснить ни USB, ни другие, разработанные в последнее время, стандарты обмена данными. Причиной популярности асинхронного последовательного интерфейса является его надежность, простота реализации и дешевизна. В промышленных сетях обмена данными и управления этот интерфейс занимает доминирующее положение благодаря относительно новым быстродействующим стандартам, таким, например, как RS-485.
         Асинхронный обмен данными лежит в основе нескольких популярных стандартов, наиболее распространенными из которых являются:
    • RS-232 — на него часто ссылаются как на «последовательный порт» обмена данными. Данный протокол применяется в персональных компьютерах, модемах и другом коммуникационном оборудовании;
    • RS-485 — протокол асинхронного обмена, позволяющий взаимодействовать одновременно с несколькими устройствами. Данный протокол используется в промышленных сетях обмена данными;
    • шина LINBUS — используется в недорогих мобильных системах с автонастройкой несущей;
    • беспроводные ИК-протоколы обмена данными; обычно работают с частотой модуляции 38…40 кГц.
         Микроконтроллеры PIC24F поддерживают все указанные типы протоколов.
         Функциональные узлы модуля асинхронного обмена данными микроконтроллеров PIC24F можно представить в виде блок-схемы (Рис. 10.1).
         Каждый микроконтроллер семейства PIC24F может иметь один или более асинхронных портов, поэтому в обозначениях на рисунке присутствует литера «x». В каждом конкретном случае программисту следует обращаться к технической документации на данное устройство.

    Рис. 10.1. Упрощенная схема модуля асинхронного обмена данными


    10.1. Аппаратно$программная архитектура UART

         Для управления параметрами обмена данными, а также контроля передачи/приема данных по асинхронному интерфейсу используется группа программно доступных регистров, которые мы вкратце и рассмотрим.
         Назначение всех битов мы обсуждать не будем, а рассмотрим только те, которые наиболее часто используются при настройках. Как правило, при программировании асинхронного обмена данными разработчик использует только некоторые биты регистров, оставляя остальные установки по умолчанию, — этого вполне достаточно для выполнения несложных процедур обмена данными. Для управления и контроля асинхронным обменом данными служит группа регистров, функции которых перечислены далее:
    • UxMODE — регистр установки режимов работы. Он позволяет установить режим работы асинхронного порта «x». При анализе примеров и разработке приложений мы будем использовать бит 15 (обозначается как UARTEN) для разрешения работы порта, биты 8:9 (UEN) для управления выводами портов, бит 3 (BRGH) для установки повышенной скорости обмена, биты 1:2 (PDSEL) для выбора формата данных и четности и бит 0 (STSEL) для выбора количества стоповых битов;
    • UxSTA — регистр управления/состояния асинхронного порта «х». Нас будет интересовать бит 10 (UTXEN) разрешения передачи, бит 9 (UTXBF) состояния буфера передачи, бит 0 (URXDA) состояния буфера приема;
    • UxRXREG — регистр-приемник данных. Здесь бит 8 используется только при обмене 9-битными данными, а биты 0:7 будут содержать 8 бит данных. Данные доступны только для чтения;
    • UxTXREG — регистр передачи данных. Здесь бит 8 используется только при обмене 9-битными данными, а биты 0:7 будут содержать 8 бит данных. Данные доступны только для записи;
    • UxBRG — регистр установки скорости обмена. Биты 15:0 содержат значение коэффициента деления частоты.
         Прежде чем выполнять обмен данными по асинхронному интерфейсу, следует установить скорость обмена данными. Модуль асинхронного порта включает генератор синхронизации обмена, который задает скорость обмена данными. Для этого в регистр установки скорости обмена следует записать 16-битное значение, которое и будет определять требуемую скорость. Это число зависит от значения бита BRGH регистра режима UxMODE. Если BRGH = 0, то значение, записываемое в регистр UxBRG, вычисляется из выражения:
         Скорость обмена = FCY/(16 x (UxBRG + 1)), откуда UxBRG = FCY/16 x Скорость обмена – 1
         Здесь FCY — значение тактовой частоты, деленное на 2. Например, если процессор работает на частоте 8 МГц, то FCY будет равна 4 МГц. Максимальная скорость обмена в этом случае (BRGH = 0) достигает FCY/16 при UxBRG = 0, а минимальная скорость обмена будет равна FCY/16 x 65536.
         Если бит BRGH = 1, то выражение для вычисления значения регистра UxBRG будет иметь вид:
         Скорость обмена = FCY/(4 x (UxBRG + 1)), откуда UxBRG = FCY/4 x Скорость обмена – 1
         Максимальная скорость обмена в этом случае (BRGH = 1) достигает FCY/4 при UxBRG = 0, а минимальная скорость обмена будет равна FCY/4 x 65536.
         При работе модуля в режиме IrDA на выводе BCLKx микроконтроллера будет генерироваться сигнал с частотой, в 16 раз превышающей заданное значение скорости обмена. В данной книге режим IrDA рассматриваться не будет.

    10.2. Практическое использование последовательного порта

         Рассмотрим на практике программирование последовательного порта микроконтроллеров PIC24F. Как и ранее, будем работать с устройством PIC24FJ128GA010.
         В первом примере будем передавать введенный с последовательного порта символ обратно в последовательный порт. Предположим, что обмен данными происходит через порт UART2 микроконтроллера в 8-битном формате, с одним стоповым битом на скорости 9600 бод. Аппаратное управление потоком использовать не будем для упрощения программирования.
         С помощью мастера проектов среды MPLAB IDE создадим новый проект и включим в него, как мы это делали в предыдущих примерах, файл p24fj128ga010.gld.
         Теперь приступим к разработке программы на языке Си, после чего включим этот файл в проект.
         Как обычно, в начале программы укажем заголовочный файл:
    #include <p24fj128ga010.h>

         Затем определим источник тактовой частоты микроконтроллера, для чего добавим в исходный текст следующий макрос:
    _CONFIG2(FCKSM_CSDCMD & OSCIOFNC_ON & POSCMOD_HS & FNOSC_PRI)

         Теперь укажем значения некоторых констант, которые нам понадобятся при настройке параметров последовательного порта:

    #define SYSCLK 8000000
    #define BAUDRATE2 9600
    #define BAUDRATEREG2 SYSCLK/8/BAUDRATE2-1

         Здесь мы выбираем тактовую частоту, равную 8 МГц (SYSCLK), и скорость обмена 9600 бод (BAUDRATE2).. Константа BAUDRATEREG2 используется в регистре установки скорости обмена U2BRG и вычисляется по формуле, приведенной ранее в разделе 10.1 (примем, что бит BRGH регистра режима U2MODE (порт UART2) должен быть установлен в 1 (повышенная скорость обмена)).
         Кроме того, для удобства определим биты конфигурации портов ввода/вывода, которые будут использоваться для последовательного обмена данными:
    #define UART2_TX_TRIS TRISFbits.TRISF5
    #define UART2_RX_TRIS TRISFbits.TRISF4

         В качестве линии вывода (сигнал TX) в программе будет использоваться RF5, а в качестве линии ввода (сигнал RX) — RF4.
         После описания базовых констант нашей программы займемся реализацией алгоритма обмена данными по интерфейсу UART2. Прежде всего, нужно установить параметры обмена данными, записав соответствующие значения
         в регистры модуля. Этот процесс называется инициализацией порта, и мы реализуем его в отдельной функции, которую назовем UARTInit:

    void UART2Init()
    {
       UART2_TX_TRIS = 0;
       UART2_RX_TRIS = 1;
       U2BRG = BAUDRATEREG2;
       U2MODE = 0;
       U2MODEbits.BRGH = 1;
       U2MODEbits.UARTEN = 1;
       U2STA = 0;
       U2STAbits.UTXEN = 1;
       IFS1bits.U2RXIF = 0;
    }
    В этой функции с помощью операторов
    UART2_TX_TRIS = 0;
    UART2_RX_TRIS = 1;

         устанавливается направление обмена данными через выводы RF4 и RF5. Затем в регистр установки скорости обмена U2BRG записывается ранее определенная константа BAUDRATEREG2:
    U2BRG = BAUDRATEREG2;

         С помощью следующей группы операторов устанавливаются соответствующие биты регистра режима U2MODE:
    U2MODE = 0;
    U2MODEbits.BRGH = 1;
    U2MODEbits.UARTEN = 1;

         Здесь установка бита UARTEN разрешает работу модуля. Наконец, в регистре контроля и управления U2STA нужно установить бит разрешения передачи UTXEN:
    U2STA = 0;
    U2STAbits.UTXEN = 1;

         Практически все готово для обмена данными. Теперь возникает вопрос: а как система узнает, что в буфере приема имеется байт или что в буфер передачи помещен байт для отправки? В этой конкретной программе для анализа буфера данных приемника используется флаг прерывания U2RXIF, который устанавливается при поступлении символа в буфер. Этот флаг устанавливается независимо от того, разрешено прерывание последовательного порта или нет.
         В подпрограмме инициализации мы сбрасываем этот флаг:
    IFS1bits.U2RXIF = 0;

         Для обмена данными нам понадобятся еще две функции: для приема символа из последовательного порта и его отправки обратно в последовательный порт. Функцию приема символа назовем UART2GetChar(), а ее исходный текст приведен далее:

    char UART2GetChar()
    {
       char Temp;
       while(IFS1bits.U2RXIF == 0);
       Temp = U2RXREG;
       IFS1bits.U2RXIF = 0;
       return Temp;
    }

         Функция ожидает установки флага прерывания U2RXIF (оператор while), что будет свидетельствовать о помещении байта данных в биты 0:7 регистра приемника U2RXREG модуля. Затем содержимое регистра помещается в переменную Temp, которая и возвращается вызывающей программе. Флаг прерывания должен быть обязательно сброшен во избежание повторного вызова прерывания. Замечу, что в этой программе мы не используем механизм прерываний для обработки данных последовательного порта, но в ситуации, когда прерывание последовательного порта будет разрешено, это приведет к непроизводительной трате машинных циклов.
         Для передачи байта данных в последовательный порт используется функция UART2PutChar, исходный текст который представлен далее:
    void UART2PutChar(char Ch)
    {
       while(U2STAbits.UTXBF == 1);
       U2TXREG = Ch;
    }

         Функция имеет единственный параметр, представляющий собой символ(байт) данных, который передает ей основная программа. Функция ожидает освобождения буфера передачи, в качестве которого используется регистр U2TXREG. Если бит UTXBF регистра U2STA установлен, то это свидетельствует о наличии символа в буфере передачи, поэтому следует ожидать его освобождения (оператор while). Если буфер освободился, то можно записать в него байт данных для отправки:
    U2TXREG = Ch;

         Теперь все это можно собрать вместе в одну программу. Вот ее исходный текст:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define BAUDRATE2 9600
    #define BAUDRATEREG2 SYSCLK/8/BAUDRATE2-1
    #define UART2_TX_TRIS TRISFbits.TRISF5
    #define UART2_RX_TRIS TRISFbits.TRISF4
    void UART2Init()
    {
       UART2_TX_TRIS = 0;
       UART2_RX_TRIS = 1;
       U2BRG = BAUDRATEREG2;
       U2MODE = 0;
       U2MODEbits.BRGH = 1;
       U2MODEbits.UARTEN = 1;
       U2STA = 0;
       U2STAbits.UTXEN = 1;
       IFS1bits.U2RXIF = 0;
    }
    void UART2PutChar(char Ch)
    {
       while(U2STAbits.UTXBF == 1);
       U2TXREG = Ch;
    }
    char UART2GetChar()
    {
       char Temp;
       while(IFS1bits.U2RXIF == 0);
       Temp = U2RXREG;
       IFS1bits.U2RXIF = 0;
       return Temp;
    }
    void main()
    {
       char c1;
       UART2Init();
       while (1)
       {
         c1 = UART2GetChar();
         UART2PutChar(c1);
       }
    }

         Поскольку микроконтроллер PIC24FJ128GA010 имеет еще один последовательный порт, то, сделав небольшие изменения в исходном тексте программы, можно работать и с портом 1. Рассмотрим следующий пример, в котором мы увидим, как передать строку байтов в последовательный порт. Для этого, как и в предыдущем примере, создадим стандартный проект, но слегка модифицируем исходный текст программы, который будет выглядеть теперь так:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define BAUDRATE2 9600
    #define BAUDRATEREG2 SYSCLK/8/BAUDRATE2-1
    #define UART2_TX_TRIS TRISFbits.TRISF5
    #define UART2_RX_TRIS TRISFbits.TRISF4
    void UART2Init()
    {
       UART2_TX_TRIS = 0;
       UART2_RX_TRIS = 1;
       U2BRG = BAUDRATEREG2;
       U2MODE = 0;
       U2MODEbits.BRGH = 1;
       U2MODEbits.UARTEN = 1;
       U2STA = 0;
       U2STAbits.UTXEN = 1;
       IFS1bits.U2RXIF = 0;
    }
    void UART2PutChar(char Ch)
    {
       while(U2STAbits.UTXBF == 1);
       U2TXREG = Ch;
    }
    char UART2GetChar()
    {
       char Temp;
       while(IFS1bits.U2RXIF == 0);
       Temp = U2RXREG;
       IFS1bits.U2RXIF = 0;
       return Temp;
    }
    void main()
    {
       char c1;
       int i1;
       char s1[] = "ENTER THE CHARACTER:";
       char *ps1 = s1;
       char CRLF[] = {0xd, 0xa};
       char *pCRLF = CRLF;
       int len = strlen(s1);
       UART2Init();
       for(i1 = 0; i1 < len; i1++)
       UART2PutChar(*ps1++);
       UART2PutChar(*pCRLF++);
       UART2PutChar(*pCRLF);
       UART2PutChar('>');
       while (1)
       {
         c1 = UART2GetChar();
         c1 += 1;
         UART2PutChar(c1);
       }
    }

         Текст программы во многом нам уже знаком, поэтому рассмотрим только изменения, которые были внесены. Во-первых, в программе определяется строка символьных данных s1, которая будет выводиться в последовательный порт. Если эти данные принимает, например, терминальная программа, работающая на персональном компьютере, то пользователь увидит в окне приложения приглашение к вводу символов. Строка символов и приглашение выводятся следующими операторами:
    for(i1 = 0; i1 < len; i1++)
    UART2PutChar(*ps1++);
    UART2PutChar(*pCRLF++);
    UART2PutChar(*pCRLF);
    UART2PutChar('>');

         Каждый байт строки s1 выводится в последовательный порт при помощи функции UART2PutChar в цикле for, счетчиком для которого является длина строки s1, вычисленная при помощи функции strlen. После передачи очередного символа указатель ps1 инкрементируется, указывая на следующий символ.
         Строка CRLF представляет собой набор стандартных символов («возврат каретки» и «перевод строки»). После передачи приглашения из порта в переменную c1 читается символ, инкрементируется и передается обратно.
         До сих пор для приема-передачи байтов через последовательный порт мы использовали обычный метод опроса. Можно не загружать процессор, а использовать для работы с последовательным портом прерывания. В нашем следующем проекте мы будем использовать прерывание для приема байта из последовательного порта и пересылки его обратно. Функция-обработчик прерывания будет пересылать в последовательный порт код символа, увеличенный на 2, и переключать бит 0 порта RA0 в противоположное состояние. Это чисто демонстрационный пример, показывающий технику использования прерываний последовательного порта.
         С помощью мастера проектов создадим в среде MPLAB IDE новый проект и добавим в него файл сценария p24fj128ga010.gld. Создадим файл, содержимое которого показано далее:
    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define PORTA_0 PORTAbits.RA0
    #define SYSCLK 8000000
    #define BAUDRATE2 9600
    #define BAUDRATEREG2 SYSCLK/8/BAUDRATE2-1
    #define UART2_TX_TRIS TRISFbits.TRISF5
    #define UART2_RX_TRIS TRISFbits.TRISF4
    void UART2Init()
    {
       UART2_TX_TRIS = 0;
       UART2_RX_TRIS = 1;
       U2BRG = BAUDRATEREG2;
       U2MODE = 0;
       U2MODEbits.BRGH = 1;
       U2MODEbits.UARTEN = 1;
       U2STA = 0;
       U2STAbits.UTXEN = 1;
       IFS1bits.U2RXIF = 0;
       _U2RXIE = 1;
    }
    char cBuf;
    void __attribute__((__interrupt__)) _U2RXInterrupt(void)
    {
       IFS1bits.U2RXIF = 0;
       while(U2STAbits.UTXBF == 1);
       cBuf = U2RXREG;
       cBuf++;
       cBuf++;
       U2TXREG = cBuf;
       PORTA_0 =~PORTA_0;
    }
    void main()
    {
       TRISA = 0xff00;
       UART2Init();
       while (1)
       {
       }
    }

         В этой программе мы определили функцию-обработчик прерывания последовательного порта при приеме байта данных как
    void __attribute__((__interrupt__)) _U2RXInterrupt(void)
    {
       . . .
    }

         Обработчик прерывания вначале сбрасывает флаг прерывания приема последовательного порта 2:
    IFS1bits.U2RXIF = 0;

         После этого выполняется проверка буфера передатчика на готовность к передаче следующего байта:
    while(U2STAbits.UTXBF == 1);

         Конечно, в реальном приложении обработчик прерывания не должен ждать готовности передатчика, поэтому на практике такой код в обработчике прерывания лучше не применять. Если буфер передатчика пуст, то код обработчика читает байт в переменную cBuf и увеличивает код байта на 2:
    cBuf = U2RXREG;
    cBuf++;
    cBuf++;

         Затем байт cBuf записывается в буфер передатчика, а бит 0 порта RA0 инвертируется:
    U2TXREG = cBuf;
    PORTA_0 =~PORTA_0;

         Для того чтобы прерывание по приему байта порта 2 работало, необходимо его разрешить, для чего в уже знакомую нам функцию инициализации асинхронного последовательного порта 2 UART2Init добавляется оператор:
    _U2RXIE = 1;

         Перед установкой разрешения прерывания (не только этого, но и любого) флаг прерывания должен быть предварительно сброшен.
         Немного об остальной части программы. В начале программы для удобства работы мы определяем бит 0 порта RA0:
    #define PORTA_0 PORTAbits.RA0

         Кроме того, конфигурируем в основной программе выводы 0:7 порта RA0 как выходы:
    TRISA = 0xff00;

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

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define BAUDRATE2 9600
    #define BAUDRATEREG2 SYSCLK/8/BAUDRATE2-1
    #define UART2_TX_TRIS TRISFbits.TRISF5
    #define UART2_RX_TRIS TRISFbits.TRISF4
    char s1[] = "1-st TEST String is passed to the Serial Port!";
    char s2[] = "2-nd TEST String is passed to the Serial Port!";
    char CRLF[] = {0xd, 0xa, 0x0};
    static char *ps;
    void UART2Init()
    {
       UART2_TX_TRIS = 0;
       UART2_RX_TRIS = 1;
       U2BRG = BAUDRATEREG2;
       U2MODE = 0;
       U2MODEbits.BRGH = 1;
       U2MODEbits.UARTEN = 1;
       U2STA = 0;
       U2STAbits.UTXEN = 1;
    }
    void __attribute__ ((interrupt)) _U2TXInterrupt(void)
    {
       while(U2STAbits.UTXBF == 1);
       if (*ps != '\0')
       {
         U2TXREG = *(ps++);
       }
       else
       {
         _U2TXIF = 0;
       }
    }
    void main()
    {
       _U2TXIF = 0;
       _U2TXIP = 4;
       _U2TXIE = 1;
       UART2Init();
       ps = s1;
       _U2TXIF = 1;
       while(U2STAbits.TRMT != 1);
       ps = CRLF;
       _U2TXIF = 1;
       while(U2STAbits.TRMT != 1);
       ps = s2;
       _U2TXIF = 1;
       while (1)
       {
       }
    }

         В этой программе, как и в предыдущих, содержатся стандартные установки режима асинхронного обмена данными для тактовой частоты 8 МГц. В программе определены две строки s1 и s2, которые будут с использованием прерывания передаваться в последовательный порт 2. Строка CRLF содержит символы «возврат каретки» и «перевод строки». Перед вызовом прерывания в указатель ps помещается адрес одной из строк s1, s2 или CRLF.
         В нашей демонстрационной программе прерывание асинхронного порта вызывается установкой флага прерывания _U2TXIF в основной программе. В этом случае вызывается функция-обработчик прерывания _U2TXInterrupt, в начале которой выполняется проверка буфера передачи (оператор while). Если в буфер можно поместить следующий байт для передачи, то это делается при помощи оператора
    U2TXREG = *(ps++);
         При этом следует проверить значение передаваемого байта. Строки s1 и s2 являются строками с завершающим нулем (null-terminated string), поэтому проверяется равенство байта значению 0. Если байт строки не равен 0, то он помещается в регистр U2TXREG, при этом указатель ps инкрементируется, чтобы указывать на следующий байт. Флаг прерывания _U2TXIF не сбрасывается до тех пор, пока не будет передана вся строка — в этом случае после передачи очередного байта прерывание вызывается повторно. По достижении конца строки, т. е. байта со значением 0, флаг прерывания сбрасывается и передача заканчивается.
         При передаче данных из буфера в последовательный порт передача байта данных считается законченной только в том случае, если бит TRMT регистра U2STA устанавливается в 1. Это означает, что регистр сдвига передающего модуля микроконтроллера пуст, что в свою очередь свидетельствует об окончании передачи байта данных. В нашей программе передача следующей строки начинается по завершении передачи предыдущей. Интервал времени между помещением байта данных в буфер передачи U2TXREG и завершением его отправки незначительный, поэтому можно подождать окончания передачи, применив оператор while:
    while (U2STAbits.TRMT != 1);

         Обмен данными по последовательному порту можно с успехом использовать при построении иерархических систем управления и контроля. Например, можно передавать данные по интерфейсу RS-232 в другую систему на базе микроконтроллера или в персональный компьютер. Кроме того, можно использовать последовательный порт и в целях отладки и диагностики работающей программы, пересылая данные из различных точек программы в персональный компьютер. В заключение главы приведу еще один пример. В этом приложении система ожидает внешнего прерывания 3 (INT3) и при его возникновении передает сообщение в последовательный порт (Рис. 10.2).

    Рис. 10.2. Аппаратная часть проекта

         Передача сообщения осуществляется в самом обработчике прерывания. С помощью мастера проектов среды MPLAB IDE создадим каркас приложения и добавим в него файл со следующим исходным текстом:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define BAUDRATE2 9600
    #define BAUDRATEREG2 SYSCLK/8/BAUDRATE2-1
    #define UART2_TX_TRIS TRISFbits.TRISF5
    #define UART2_RX_TRIS TRISFbits.TRISF4
    #define INT3port PORTAbits.RA14
    char s1[] = "INTERRUPT 3 burnst!";
    char CRLF[] = {0xd, 0xa, 0x0};
    static char *ps;
    void UART2Init()
    {
       UART2_TX_TRIS = 0;
       UART2_RX_TRIS = 1;
       U2BRG = BAUDRATEREG2;
       U2MODE = 0;
       U2MODEbits.BRGH = 1;
       U2MODEbits.UARTEN = 1;
       U2STA = 0;
       U2STAbits.UTXEN = 1;
    }
    void UART2PutString(char* ps)
    {
       while (*ps != '\0')
       {
         while(U2STAbits.UTXBF == 1);
         U2TXREG = *(ps++);
       }
    }
    void __attribute__ ((interrupt)) _INT3Interrupt(void)
    {
       char *ps1 = s1;
       UART2PutString(ps1);
       ps1 = CRLF;
       UART2PutString(ps1);
       _INT3IF = 0;
    }
    void main()
    {
       TRISA = 0xff00;
       INTCON2bits.INT3EP = 1;
       _INT3IF = 0;
       _INT3IP = 4;
       _INT3IE = 1;
       UART2Init();
       while (1)
       {
       }
    }

         В обработчике прерывания _INT3Interrupt строка s1 передается в последовательный порт при помощи функции UART2PutString(ps1). В последней команде обработчика флаг прерывания сбрасывается с помощью оператора _INT3IF = 0;
         В программе используются те же установки параметров последовательного обмена, что и в предыдущих примерах. Инициализация асинхронного порта 2 осуществляется в основной программе с помощью функции UART2Init. Кроме того, необходимо установить, по какому фронту сигнала на входе INT3 будет генерироваться прерывание. Оператор INTCON2bits.INT3EP = 1;
         определяет, что прерывание должно генерироваться по спадающему фронту сигнала. Перед установкой разрешения внешнего прерывания 3 сбрасываем флаг прерывания _INT3IF = 0;
         и устанавливаем приоритет запроса на прерывание: _INT3IP = 4;
         Здесь в качестве уровня приоритета выбрано значение 4. Наконец, устанавливаем бит разрешения внешнего прерывания 3: _INT3IE = 1;
         При смене уровня сигнала на входе INT3 с ВЫСОКОГО на НИЗКИЙ будет инициироваться запрос на внешнее прерывание 3, после чего в последовательный порт будет передана строка сообщения s1.
         Компилятор MPLAB C для PIC24 содержит ряд специальных 16-битных библиотечных функций, упрощающих работу с периферийными устройствами микроконтроллера PIC24FJ128GA010. Во многих случаях это избавляет разработчика от необходимости написания собственного кода. Для всех библиотечных функций периферийных устройств имеются заголовочные файлы. Например, для функций, работающих с асинхронными портами обмена данных, имеется файл uart.h, для работы с ШИМ имеется файл pwm.h и т. д.
         Для работы с 16-битными функциями периферийных устройств в программу необходимо включить соответствующий заголовочный файл. В нашем случае, в начале программы должна присутствовать директива #include <uart.h>
         Кроме того, в проект нужно включить один из библиотечных файлов: libpPIC24Fxxx-elf.a или libpPIC24Fxxx-coff.a (это зависит от выбранного формата объектного файла). Эти файлы размещаются в каталоге \. . . \MPLABC30\lib\PIC24F.
         Модифицируем предыдущий пример так, чтобы в нем можно было использовать библиотечную функцию putsUART2, записывающую строку байтов, адрес которой передается функции, в последовательный порт 2.
         Вот исходный текст программы с учетом сделанных изменений (выделены жирным шрифтом):

    #include <p24fj128ga010.h>
    #include <uart.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define BAUDRATE2 9600
    #define BAUDRATEREG2 SYSCLK/8/BAUDRATE2-1
    #define UART2_TX_TRIS TRISFbits.TRISF5
    #define UART2_RX_TRIS TRISFbits.TRISF4
    #define INT3port PORTAbits.RA14
    char s1[] = "INTERRUPT 3 burnst (16-bit lib functions used)!";
    char CRLF[] = {0xd, 0xa, 0x0};
    void UART2Init()
    {
       UART2_TX_TRIS = 0;
       UART2_RX_TRIS = 1;
       U2BRG = BAUDRATEREG2;
       U2MODE = 0;
       U2MODEbits.BRGH = 1;
       U2MODEbits.UARTEN = 1;
       U2STA = 0;
       U2STAbits.UTXEN = 1;
    }
    void __attribute__ ((interrupt)) _INT3Interrupt(void)
    {
       putsUART2(s1);
       putsUART2(CRLF);
       _INT3IF = 0;
    }
    void main()
    {
       TRISA = 0xff00;
       INTCON2bits.INT3EP = 1;
       _INT3IF = 0;
       _INT3IP = 4;
       _INT3IE = 1;
       UART2Init();
       while (1)
       {
       }
    }

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

    Комментарии