В частности, PIC16f18326

Мы живем в прекрасном подарке, где любой любитель может получить любую легкодоступную демонстрационную доску по однозначной цене и после разумных задержек. Когда я впервые начал возиться с Arduino и Raspberry Pi, я был в восторге от ощущения расширения возможностей, которое последовало за возможностью воплощать свои идеи в реальном мире оборудования.

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

Сегодня я собираюсь сделать краткое руководство о том, как программировать чистый PIC16, в частности PIC16f18326. Никаких демонстрационных плат и USB-кабеля, только микроконтроллер, макетная плата и специальный программатор.

Почему ПОС?

Какой замечательный вопрос. Почему стоит выбрать для работы PIC и, самое главное, Microchip? На этом этапе вы ожидаете, что я начну перечислять преимущества архитектуры, удобный набор программных инструментов, качества их семейств микроконтроллеров; единственная проблема в том, что я солгу.

Microchip, без сомнения, продает худшее семейство продуктов, с которыми мне когда-либо приходилось работать. Их документация загромождена и устарела, их отношение к сообществу не изменилось за 30 лет, а их программный пакет - не что иное, как непрезентабельность.

Однажды я, вероятно, найду время, чтобы должным образом оправдать свою тираду; Сегодня давайте сосредоточимся на том, почему я решил написать руководство по 8-битным PIC, несмотря на их явные недостатки. Основных причин три:

  1. У меня большой опыт: я работаю с PIC каждый день (поэтому я так сильно их ненавижу), и я научился использовать их с нуля, поэтому я уверен в своих знаниях.
  2. Я хотел сделать учебник для очень чистого металла, то есть мы будем работать только с микроконтроллером; ни демонстрационной платы, ни комплектов для разработки. Для этого само устройство должно быть в управляемом формате, который будет Dual In-Line Package (короче, PDIP). Я знаю только два семейства микроконтроллеров этого корабля в PDIP: PIC и Atmel. Atmel в целом лучше, но несколько лет назад его поглотила Microchip, и я держу пари, что еще через несколько лет они уберут все хорошее, что у него было, так что ...
  3. Острая нехватка обучающих программ. Серьезно, большинству материалов, которые вы найдете в Google, 10–20 лет. Начало программирования PIC было для меня настоящей болью, и я хочу сделать это немного проще.

Что тебе понадобится

Для использования этого руководства вам потребуются следующие компоненты:

  • PIC16f18326 в формате PDIP.
  • Программист Microchip SNAP.
  • Ужасный набор инструментов для разработки Microchip.
  • Макет.
  • Светодиод
  • Два резистора: 47 Ом и 10 кОм
  • Керамический конденсатор 10 мкФ
  • Некоторые перемычки

PIC16f18326

Простой 8-битный дешевый микроконтроллер. Их покупка в магазине Microchip стоит около 1 евро каждая, но они также бесплатно раздают их вместе со своей программой для образцов. Обязательно получите упаковку PDIP.

Сама по себе модель ничего особенного, просто случайно у меня одна валялась; Таким образом, аналогичное устройство тоже подойдет: мы просто мигаем светодиодом, поэтому процедура должна быть более или менее такой же. Здесь важно условие: продукты на основе микрочипов не очень хорошо соответствуют стандартам, и их регистры часто сильно различаются по названию и форме. Если у вас уже есть другой MCU, попробуйте; в противном случае получите ту же модель, что и у меня.

Программист

Одна из основных причин этого руководства - новый программатор MPLAB SNAP. Примерно год назад самым дешевым вариантом программирования PIC-устройства был PicKit3, который продавался по очень высокой цене ~ 40 евро - это далеко не типичный бюджет для начинающего любителя. Протокол SNAP, наконец, заполняет эту дыру, обходясь всего в 13 евро, сохраняя при этом большую часть функциональности. Это все еще дорого по сравнению с недорогими вариантами от конкурентов (я думаю о программаторах ST-Link за 2 евро), но, тем не менее, это улучшение. Вы можете купить один в официальном магазине Microchip.

Являясь недорогой альтернативой, он представляет собой голую печатную плату. Если у вас есть 3D-принтер, вы можете сделать его самостоятельно, возможно, используя тот, который я смоделировал на Thingiverse.

Электроника

Макетная плата и провода необходимы для основных подключений, и без светодиода вы ничего не увидите; Что касается резистора и конденсатора, подключенных к выводам программирования, последним можно пренебречь, в то время как первый важен.

Тем не менее, если что-то пойдет не так, первое, что вам нужно сделать, это проверить правильность и целостность вашей схемы.

MPLAB и XC8

Есть только один способ запрограммировать контроллеры PIC: дымящаяся куча мусора, которая является IDE Microchip. Он старый, захламленный, глючный, медленный и некрасивый. Несмотря на это, на самом деле его довольно просто установить, и мне никогда не требовалось более 10 минут, чтобы начать работу на новой машине, будь то Linux или Windows. Вы можете скачать последнюю версию с их сайта.

Также необходимо отдельно скачать их компилятор для 8-битных устройств: XC8. Здесь нет ничего сумасшедшего, это просто обязательный шаг. На момент написания последняя версия 2.05.

Существует также новая версия на основе браузера, идентичная старой IDE (если что-то не работает, зачем ее менять, не так ли?), Но, хоть убей, я не смог запустить слой интерфейса Браузер-USB Java. Попробуй, в случае успеха можно приступить к программированию еще быстрее.

С этого момента я предполагаю, что у вас установлены IDE и компилятор.

Схема и подключения

К сожалению, мы не можем просто подключить наш MCU и ожидать, что он заработает; нам нужно немного повозиться с соединениями и проводами. MPLAB SNAP имеет заголовок с 8 контактами, из которых по какой-то причине используются только 5 (те, которые подключены на схеме). Белый провод на изображении соответствует стороне заголовка, отмеченной стрелкой на печатной плате.

  1. Вывод MCLR (белый провод) - линия сброса; он используется программистом для сброса устройства перед программированием.
  2. Vdd (красный провод) - это линия питания, в нашем сценарии 3,3 В.
  3. Vss (черный провод) - линия заземления.
  4. PGD ​​(синий провод) - это линия передачи данных, по которой фактически отправляется программа.
  5. PGC (желтый провод) - это линия часов.

Из-за необъяснимо неудачного выбора конструкции SNAP не позволяет запитать ваше устройство, поэтому нам необходимо предоставить внешний источник. Достаточно 3V, в виде двух батареек AA.

В цепи остались только светодиод + ограничительный резистор (47 Ом), который не является строго необходимым, но мы будем использовать его для проверки активности, подтягивающий резистор 10 кОм на линии MCLR, который должен и керамический конденсатор (10 мкФ) на источнике питания, чтобы стабилизировать его (вы можете попробовать, даже если у вас его нет, но могут возникнуть некоторые проблемы).

Более подробную информацию о соединениях, необходимых для его программирования, вы можете найти в разделе 2.0 даташита.

Создание нового проекта

Как только все настроено и готово, откройте MPLABX и создайте новый пустой проект для PIC16f18326 (или любой другой вашей модели PIC). Нажмите «Файл»> «Новый проект» и заполните формы.

В пустом проекте не должно быть ничего, поэтому мы можем начать с создания main.c. Вы можете сделать это либо из представления проекта (щелкните левой кнопкой мыши на Исходные файлы ›New› main.c), либо создав новый файл и добавив его в проект (щелкните левой кнопкой мыши на Исходные файлы ›Добавить существующий элемент). К сожалению, одной main функции недостаточно для запуска чего-либо (иногда может быть, но давайте перестраховываемся); вам сначала нужно настроить ваше устройство.

MPLABX управляет всем, создавая автоматические make-файлы, поэтому вам не нужно беспокоиться о сценариях компоновщика и конфигурации памяти. По крайней мере, это облегчает начало работы.

Зарегистрируйте конфигурацию

Существуют определенные системные регистры, которые записываются программой во флэш-память (часть памяти, предназначенная только для чтения, где хранится микропрограммное обеспечение), поэтому их конфигурация является статической и не может быть изменена во время выполнения. В отличие от почти всех остальных, Microchip не использовал стандартный сценарий компоновщика для изменения определенных разделов программной памяти; вместо этого они используют #pragma директивы.

#pragma - это специальная директива препроцессора C, используемая для включения и выключения пользовательских функций в компиляторе, а компилятор XC8 определяет функцию config, которая позволяет изменять значения статических регистров.

Необходимо настроить 20–30 системных регистров. Поиск их имен и функций в таблице данных был бы огромной проблемой, но, к счастью, мы можем сгенерировать шаблонный код через приличный интерфейс, интегрированный в MPLABX.

Перейдите в Окно ›Представления целевой памяти› Биты конфигурации; внизу IDE должен появиться новый виджет со списком регистров. Справа вы можете убедиться, что FWDTEN выключен. Это включает сторожевой таймер, специальное устройство, которое сбрасывает наш MCU, если не обнаружено никакой активности. Не волнуйся об этом сейчас. Кроме того, мы должны позаботиться о внутреннем генераторе.

Конфигурация осциллятора

Осциллятор - это сердце нашего микроконтроллера. Это устройство, которое создает устойчивый тактовый сигнал, используемый для выполнения инструкций; в архитектуре PIC для выполнения каждой инструкции обычно требуется 4 тактовых цикла. Это означает, что, например, при тактовой частоте 32 МГц (32 миллиона тактовых импульсов в секунду) ваш MCU будет работать со скоростью 8 миллионов инструкций в секунду, причем каждая инструкция будет занимать 125 наносекунд.

Все PIC, которые я использовал до сих пор, могут использовать преимущества модуля внутреннего генератора, и наш не исключение. Однако, чтобы понять, как его правильно настроить, нам нужно углубиться в техническое описание, разделы 5.2 и 7.0.

В моем устройстве есть несколько вариантов генераторов, управляемых регистром RSTOSC. По умолчанию этот регистр содержит значение внешнего генератора; нам нужно изменить его и использовать более доступный внутренний. В окне конфигурации регистров выберите HFINT1, внутренний генератор работает на частоте 1 МГц.

После этого просто нажмите «Сгенерировать исходный код для вывода»: будет напечатан список #pragma директив, чтобы вы могли скопировать их и вставить поверх своего main.c файла.

Примечание. Я попытался оставить значения по умолчанию для битов конфигурации; иногда это работает, иногда нет. Согласно таблице данных, генератор по умолчанию должен быть внешним, который я не подключал: тем не менее, моя установка все еще работала, работая на частоте 32 МГц (так что, возможно, с использованием внутреннего). Вы не можете доверять документации Microchip, поэтому обязательно инициализируйте все значения.

Код

Наконец, мы можем начать что-то делать. Заполните функцию main какими-то бессмысленными инструкциями, чтобы мы могли убедиться, что все работает.

// Here you should have all the #pragma directives from the 
// register configuration
// ...
int main() {
    int x = 0;
    while(1) {
        x++;
        x--;
        x++;
    } 
    return 0;
}

Очевидно, это ничего не делает, но этого достаточно, чтобы протестировать отладчик. Добавьте точку останова, щелкнув номер строки инструкции x--. Подключите программатор MPLAB SNAP к вашему ПК, включите схему и нажмите кнопку «Отладка».

Теперь IDE должна творить чудеса, скомпилировать и прошить программу. Возможно, вам потребуется выбрать инструмент SNAP во всплывающем окне. Как только все будет сделано, вы должны увидеть, что программа достигла точки останова; затем вы можете попробовать отладчик, продвигаясь по циклу шаг за шагом.

При программировании PIC для отладки прошивка будет работать, только если подключен программатор. Убедившись, что все работает, вы должны использовать выделенную на изображении кнопку Программирование, чтобы она постоянно мигала.

Если что-то пошло не так, перейдите в раздел устранения неполадок в нижней части статьи.

Да будет свет

Отладчик работает. Прохладный. Попробуем использовать светодиод.

В моей схеме свет подключен к контакту в нижнем левом углу ПОС. Предусмотрен ограничительный резистор на 47 Ом, чтобы он не перегорел, но если вам повезет (или вы не против сломать светодиод), вы можете подключить его напрямую.

Порты GPIO названы в честь букв алфавита на PIC. Вы можете прочитать все об этом в разделе 12.0 таблицы данных, но я бы не советовал вам это делать, потому что это в основном электрический жаргон.

Достаточно сказать, что при работе с PIC GPIO существует 3 класса регистров: TRIS, PORT и LATCH.

  • Регистры TRIS выбирают направление GPIO (вход или выход).
  • Регистры PORT возвращают уровень, воспринимаемый на выводе, если он настроен как вход.
  • Регистры LATCH (сокращенно LAT) используются для установки уровня для выходного контакта.

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

Вы можете включить заголовок <xc.h>, чтобы воспользоваться набором определений регистров. В своей схеме я использую вывод RC3, поэтому, чтобы выбрать его как выход, я должен написать 0 в определении TRISCbits.TRISC3. Вместо этого 1 соответствует входному контакту. Затем я могу управлять линейным уровнем, записывая в регистр LATCbits.LATC3:

// Register configuration
// ...
#include <xc.h>
int main() {
    TRISCbits.TRISC3 = 0; // set C3 as output
    LATCbits.LATC3 = 1;
    while(1)
        ;
    return 0;
}

В этом примере ваш светодиод должен загореться!

Задерживать

Включение светодиода - это хорошо, но пока нет признаков заметной активности. Мигание - это начало, но как узнать, сколько времени прошло?

Итак, мы настроили наш осциллятор на работу с частотой 1 МГц, и каждая инструкция занимает 4 такта, поэтому мы можем использовать цикл занятости для ожидания произвольного количества времени; например, счет до 10000 сумм примерно до 1 секунды.

Однако есть встроенная функция для ожидания более или менее точного периода типа: __delay_ms(). Он включен в xc.h, но для его использования необходимо сначала определить специальный макрос _XTAL_FREQ, сокращение от «частота кристалла».

Внутри xc.h определение частоты кристалла используется для построения точного цикла ожидания занятости на основе указанной тактовой частоты.

#define __delay_ms(x) 
                 _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
// _delay is a native PIC function

Если мы #define _XTAL_FREQ 1000000 перед включением xc.h, мы сможем использовать __delay_ms(1000), чтобы подождать 1 секунду. Обратите внимание, что _XTAL_FREQ должен содержать фактическую частоту вашего генератора (1 МГц для нас), иначе время будет отключено.

Следующая программа будет мигать светодиодом один раз в секунду:

// Register configuration
// ...
#define _XTAL_FREQ 1000000
#include <xc.h>
int main() {
    TRISCbits.TRISC3 = 0; // Set C3 as output
    LATCbits.LATC3 = 1;
    while(1) {
        __delay_ms(1000);
        LATCbits.LATC3 = ~LATCbits.LATC3;
    }
    return 0;
}

Регистр LATCH также может быть прочитан для проверки уровня строки, поэтому мы используем его в сочетании с оператором ~ для реверсирования или переключения текущего вывода.

Прерывания

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

Каждая PIC может иметь несколько таймеров; пока мы просто собираемся использовать первый пригодный для использования (будет TMR2), описанный в разделе 28.0 таблицы данных.

Таймер - это не что иное, как регистр, который увеличивается каждый раз, когда выполняется условие (например, такт часов), и который может запускать прерывание при опрокидывании (достигает своего максимального значения и возвращается к 0) или при достижении определенного числа.

Таймеры - это своего рода сложное периферийное устройство, хотя на самом деле им быть не должно. Вот где все устройства расходятся по номеру регистра и расположению: некоторые позволяют вам выбирать источник тактовой частоты, у них могут быть предделитель, постделитель, компараторы, включения, разные режимы ... Я собираюсь объяснить, как использовать этот, но это знание нелегко перенести. Чтобы вызвать прерывание, вызванное TMR2, мы должны использовать не менее 8 регистров.

К счастью, источником тактовой частоты для TMR2 является системный генератор, деленный на 4, то есть 250 кГц, потому что мы работаем на частоте 1 МГц. Это означает, что счетчик таймера (который является 8-битным, поэтому он может достигать 255 перед переключением) увеличивается на единицу каждые 4 микросекунды и сбрасывается каждые 255 * 4 = 510 микросекунд; это довольно быстро, не очень удобный отрезок времени.

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

Максимальные настройки, которые мы можем иметь для TMR2, - это деление 1:16 для постскейлера и 1:64 для предделителя; вместе они снижают тактовую частоту 250 кГц до 244 Гц, что чуть ниже максимального значения для 8-битного счетчика, 255. Затем счетчик таймера будет переключаться немного чаще, чем один раз в секунду.

Затем мы можем установить регистр компаратора на 244–1 (отсчет начинается с 0), чтобы прерывание срабатывало ровно один раз в секунду. Это регистры, которые мы будем использовать:

  • T2OUTPS: регистр постделителя
  • T2CKPS: регистр предварительного делителя.
  • PR2: регистр компаратора.
  • TMR2ON: включить регистр для TMR2.
  • TMR2IE: регистр разрешения прерывания TMR2.
  • PEIE: включить регистр для периферийных прерываний.
  • GIE: включить регистр для всех прерываний.
  • TMR2IF: регистр флагов для прерывания TMR2.

Регистры предварительного делителя и постделителя имеют определенные коды для предустановленных значений (находятся в таблице данных); PR2 содержит счетчик, которого мы хотим достичь; различные регистры разрешения должны быть установлены в 1, чтобы периферийное устройство было активным, а регистр флага устанавливается всякий раз, когда прерывание ожидает обработки, и должен быть очищен после того, как оно было обработано, в противном случае новые прерывания не будут запущены.

В итоге вот что у нас должно получиться:

#define _XTAL_FREQ 1000000
#include <xc.h>
void __interrupt () my_isr(void)
{
    if (PIR1bits.TMR2IF) {
        LATCbits.LATC3 = ~LATCbits.LATC3;
        PIR1bits.TMR2IF = 0;
    }
}
int main() {
    TRISCbits.TRISC3 = 0;
    LATCbits.LATC3 = 1;
    
    T2CONbits.T2OUTPS = 0b1111; // Timer2 postscaler 1:16
    T2CONbits.T2CKPS = 0b11;    // Timer2 prescaler 1:64
    PR2 = 244-1;                // Set comparator
    
    T2CONbits.TMR2ON = 1; // Timer2 enable
    PIE1bits.TMR2IE = 1;  // Timer2 interrupt enable
    PIR1bits.TMR2IF = 0;  // Clear timer2 interrupt flag
    
    INTCONbits.PEIE = 1;  // Peripheral interrupt enable
    INTCONbits.GIE = 1;   // Global interrupt enable
    
    
    while(1) {
        __delay_ms(1000);
    }
    
    return 0;
}

8-битные устройства PIC имеют одну процедуру прерывания для каждого периферийного устройства, определенную директивой __interrupt(). Это приятно, упрощает настройку для разработчика. Наша функция main сейчас ничего не делает, но светодиод все еще мигает благодаря повторяющемуся прерыванию. Регистр компаратора нужно установить только один раз, и он будет продолжать срабатывать в нужное время.

Обратите внимание, что результирующая частота на самом деле составляет не 244 Гц, а 244,140625 Гц. Регистр компаратора и счетчика представляют собой цифровые целые числа, поэтому мы не можем фиксировать десятичные разряды; в результате время будет меньше на ~ 0,06%. TMR2 не может быть более точным, чем это, но мы могли бы запустить его несколько раз в секунду и увеличивать переменную, чтобы знать, когда мы закончим. В этом примере я сделал все просто.

Исправление проблем

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

Кроме того, сообщения об ошибках довольно общие и бесполезны. Если, например, вы не включите макетную плату перед программированием, вы получите следующую ошибку:

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

Для каждой другой проблемы с подключением IDE возвращает то же вводящее в заблуждение сообщение об ошибке:

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

Если ваша проблема в процессе компиляции, должны применяться обычные правила программирования на C; XC8 очень плохо обрабатывает сообщения об ошибках (какой сюрприз, а?), Поэтому, если вы в затруднительном положении, просто скачайте мой код и посмотрите, работает ли он.

Вывод

Это был простой пример того, как начать работу с устройством PIC16. Надеюсь, я облегчил вам жизнь с его помощью! С точки зрения оборудования они действительно хороши и доступны. Программный пакет должен быть полностью отменен или открыт сообществу для его замены, но Microchip, похоже, все еще застряла в 80-х годах со своим менталитетом, и я боюсь, что перейду на ARM задолго до того, как они осознают свою ошибку.