Yuri Panchul (panchul) wrote,
Yuri Panchul
panchul

Categories:

Как начать работать с Microchip PIC32. Часть первая

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

Спустя 35 лет я встретил одну из этих однокласниц, и узнал, что она стала директором школы. Я обсудил с ней идею об использовании микроконтроллеров и FPGA для обучения школьников, и она высказала мне мысль о пользе пошаговых инструкций (школьники не привыкли копать тысячестраничные документации). Поэтому я решил написать текст как начать использовать студенческую плату с микроконтроллером Microchip PIC32, иллюстрируя текст своими фотографиями и картинками, выдранными из документации.

Как начать работать с Microchip PIC32?

1. Предсказуемые вопросы и ответы

Заранее отвечаю:

Вопрос: А почему бы школьникам и младшим студентам не использовать классический Ардуино?

Ответ: Микроконтроллер AVR в классическом Ардуино 8-битный, а Microchip PIC32 - 32-битный. PIC32 в несколько раз быстрее, чем 8-битный AVR, у PIC32 гораздо больше памяти, он позволяет писать более интересные программы. Кроме этого, опыт с основанным на ядре MIPS M4K микроконтроллером PIC32 может быть использован при работе с старшими ядрами MIPS, которые используются в планшетах и сетевых устройствах.

Вопрос: А почему бы школьникам и младшим студентам не использовать Ардуино-подобную систему разработки MPIDE вместо MPLAB X, используемой в инструкции?

Ответ: MPIDE - небольшая элегантная система для школьников, хоббистов, людей которые не любят читать документацию, а также инженеров, которым нужно смастерить что-то небольшое быстренько. MPLAB X - максимально гибкая профессиональная система, которая поддерживает весь спектр возможностей, предоставляемых PIC32. С моей точки зрения, разница в уровне сложности для начала работы с MPIDE и MPLAB X недостаточно велика, чтобы сначала учить MPIDE, а потом - MPLAB X. Если в конечном итоге человек собирается получить профессиональные навыки, лучше сразу начинать с MPLAB X. Если же целью является скажем научить программированию микроконтроллеров гуманитариев, то лучше использовать MPIDE и на нем оставаться.

Вопрос: А зачем вы работаете с устройствами ввода вывода прямо через регистры? Вот, я нашел в интернете библиотеку которая поддерживает SPI/UART/I2C и т.д.

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

Вопрос: А почему вы используете плату Cerebot MX3cK с устройствами ввода-вывода Digilent Pmod, а не chipKit Uno32 c chipKit Basic I/O Shield, которую вы сами же рекомендуете для использования в школах и вузах?

Ответ: Они программируются очень похоже, только пины и устройства ввода-вывода другие. Пусть примеры для Uno32 сделают сами преподаватели для собственной практики, а их студенты не будут имет возможность у меня списывать. Uno32 лучше как универсальная платформа, чем Cerebot MX3cK, так как она совместима по пинам с Ардуино. Хотя для профессионального программиста встроенных систем ценность Ардуино-совместимости не очень велика, но для школьников, кружковцев и гуманитариев это предоставляет возможность использовать MPIDE и Arduino Shields. Иными словами, Uno32 подходит и для обучения профессионалов, и для обучения непрофессионалов, а Cerebot MX3cK для непрофессионалов менее удобен.

Вопрос: А как насчет ARM?
Ответ: Если вам хочется поста про ARM, то напишите его сами.

Вопрос: А как насчет Intel?

Ответ:Intel x86 плохо подходят для встроенных приложений из-за плохой метрики производительность / милливатт и много другого. Intel 8051 устарел и кроме этого плохо привязывается к курсу компьютерной архитектуры, в отличие от конвейерного MIPS M4K / Microchip PIC32. Кстати, один российский профессор сказал мне, что российское Министерство Образования рекомендует учить студентов программированию микроконтроллеров используя советский аналог микроконтроллера Intel 8048 (предшественника 8051) 8-)

Вопрос: Это что, и есть FPGA / ПЛИС?

Ответ: Не, это не имеет никакого отношения к FPGA / ПЛИС-ам. Этот пост про программирование, встроенные софтвер. Использование FPGA / ПЛИС - это разработка хардвера, кроме случая, когда процессор имплементируется на FPGA. Примерчик калькулятора на FPGA у меня будет в отдельном посте. FPGA и микроконтроллеры это столь же разные материи, как конструирование автомобиля (хардвер) и его вождение (софтвер).


2. Общая информация

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

_MG_9525


Документация к набору:

Информация про плату - http://digilentinc.com/Products/Detail.cfm?NavPath=2,396,984&Prod=CEREBOT-MX3CK
Документация к плате - http://digilentinc.com/Data/Products/CEREBOT-MX3CK/Cerebot_MX3cK_rm.pdf
Схематика к плате - http://digilentinc.com/Data/Products/CEREBOT-MX3CK/Cerebot%20MX3cK_sch.pdf

Информация про дисплейчик - http://digilentinc.com/Products/Detail.cfm?NavPath=2,401,473&Prod=PMOD-CLS
Документация к дисплейчику - http://digilentinc.com/Data/Products/PMOD-CLS/PmodCLS_rm_RevD-E.pdf
Схематика к дисплейчику - http://digilentinc.com/Data/Products/PMOD-CLS/PmodCLS_sch.pdf

Информация про дисплейчик - http://digilentinc.com/Products/Detail.cfm?NavPath=2,401,940&Prod=PMODKYPD
Документация к клавиатурке - http://digilentinc.com/Data/Products/PMODKYPD/PmodKYPD_rm.pdf
Схематика к клавиатурке - http://digilentinc.com/Data/Products/PMODKYPD/PmodKYPD_sch.pdf

Документация к микроконтроллеру - http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en532434
Основной документ - PIC32 Family Reference Manual

Документация к системе разработки MPLAB X и система разработки как таковая - http://www.microchip.com/pagehandler/en-us/family/mplabx/


Код примерчика:

Код примерчика на Google code: http://code.google.com/p/pic32-examples/source/browse/trunk/#trunk%2Fshowroom%2Fcalculator
Код примерчика на моем персональном сайте: http://panchul.com/education/2013_02_03_calculator/sources/

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

Поэтому код использует короткие идентификаторы, буфера фиксированного размера, никакой C++ - ности, никакой реентерабильности, никаких штучек для портабильности на троичные машины и прочие извращения. Я НЕ пытаюсь показать, как структурировать гибкий код для большого многослойного индустриального проекта. Все максимально тупо, если торчащий из микроконтроллера провод называется G9, то и в коде он называется G9. Если вам это не нравится - вы можете написать свой пример и внести вашу версию в pic32-examples.


Итого, вот конструкция в собранном видею Можно так:


_MG_9475-2


А можно - так:


_MG_9552-4


3. Работа с дисплеем с помощью протокола SPI - Serial Peripheral Interface

Дисплейчег имеет три способа к нему подсоединиться - UART, SPI и I2C. Я выбрал SPI - Serial Peripheral Interface. Подсоединение через SPI делается через пины слева сверху:

_MG_9488

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

_MG_9490-2

Сверху штырьки для SPI выглядят вот так:

_MG_9499

А с другой стороны платы дисплея - вот так:


_MG_9504-2


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

_MG_9479-2

Также обратите внимание на табличку соединения сигналов из документации на плату:



Что все это физически означает? SPI - это просто способ передавать биты из пункта A в пункт B, одновременно с передачей других битов из пункта B в пункт A. Биты передаются по проводам SI и SO, причем момент передачи определяется изменением состояния синхросигнала CK. Другие провода на фотке означают V - питание +3.3 вольта, G - ground 0 вольт, SS - slave select, о котором мы поговорим чуть позже.

При этом устройство, которое генерирует синхросигнал, называется master ("хозяин"), а устройство, которое его принимает, назыается slave ("рабыня"). Вы не поверите, но американские правозащитники, соратницы российской феминистки Наталии Радуловой, совершенно всерьез хотят запретить использование терминов master и slave инженерами. В Лос-Анжелесе правозащитники даже смогли это осуществить на уровне городского муниципалитета, несмотря на то, что с 1976 года в США было выдано 19708 патентов, в тексте которых были термины master и slave:



http://www.historyoftechnology.org/eTC/v48no2/eglash.html

In November 2003, after receiving a discrimination complaint from a county employee, the Los Angeles County Office of Affirmative Action Compliance sent a memo to all its equipment vendors asking that they stop using the words “master” and “slave” in reference to computer hardware and other equipment.

... U.S. patents since 1976 for “master” and “slave” returned 19,708 items

http://www.cnn.com/2003/TECH/ptech/11/26/master.term.reut/

"We would request that each manufacturer, supplier and contractor review, identify and remove/change any identification or labeling of equipment components that could be interpreted as discriminatory or offensive in nature," Sandoval said in the memo, which was distributed last week and made available to Reuters.


Да, так насчет SPI. Вот так выглядит процесс передачи данных на уровне хардвера:



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


http://code.google.com/p/pic32-examples/source/browse/trunk/showroom/calculator/spi.c

char spi_put_get_char (char c)
{
    SPI2BUF = c;                   // send data to slave
    while (SPI2STATbits.SPIBUSY);  // wait until SPI transmission complete
    return SPI2BUF;
}


char spi_get_char (void)
{
    return spi_put_get_char (0);
}


void spi_put_char (char c)
{
    (void) spi_put_get_char (c);
}



Инициализируется SPI в самой простой конфигурации вот так:


http://code.google.com/p/pic32-examples/source/browse/trunk/showroom/calculator/spi.c

void spi_init (uint baud)
{
    char dummy;

    SPI2CONbits.ON      = 0;        // disable SPI to reset any previous state
    dummy               = SPI2BUF;  // clear receive buffer
    SPI2BRG             = PBCLK_FREQUENCY / 16 / baud - 1;
    SPI2CONbits.MSTEN   = 1;        // enable master mode
    SPI2CONbits.CKE     = 1;        // set clock-to-data timing
    SPI2CONbits.ON      = 1;        // turn SPI on
    . . . . . . . . . . . . 



Да, так насчет сигнала Slave Select. В PIC32 этот сигнал является вводом и используется только в специальном режиме, когда микроконтроллер является не master, а slave. В обычном режиме этот пин можно использовать как вывод из цифрового порта общего назначения, который называется G. А в дисплейчике с помощью специального джампера можно подключить данный пин к сигналу сброса (reset) дисплейчика. Что мы и сделаем. Если дисплею не делать reset определенное время, то он не будет нормально работать. Итого:

Джампер для сброса (reset):


_MG_9541


Почему это работает (обратите внимание на "Programing Jumper":



Ниже код для сброса, который я поместил в функцию инициализации SPI. Длительность ожидания после сброса почему-то не указана в документации к дисплею, поэтому длительность пришлось устанавливать экспериментально. Обращаю внимание, что я жду (трачу время) используя цикл с nop-ами (пустыми операциями), а не при помощи аппаратного таймера, которых есть в M4K и PIC32 несколько. Таймер нам пригодится для прерываний во время работы с клавиатурой - лучше его не занимать для избежания конфликта.


http://code.google.com/p/pic32-examples/source/browse/trunk/showroom/calculator/spi.c

void spi_init (uint baud)
{
    . . . . . . . . . . . . 

    // Signal G9 is on the same pin as SPI2SS (Slave Select) signal.
    // Since slave select is not used in this configuration
    // the pin is used as a reset signal for the external SPI slave.

    TRISGbits.TRISG9    = 0;
    PORTGbits.RG9       = 0;
    delay_for_1000_nops_x (1000);
    PORTGbits.RG9       = 1;
    delay_for_1000_nops_x (1000);
}



4. Следущий уровень работы с дисплеем - escape-последовательности

Данный дисплей сам имеет внутри собственный микроконтроллер (не PIC32, а 8-битный AVR), который интерпретирует поступающие снаружи байты как текст и команды (escape-последовательности). В данных командах вы разберетесь сами по документации и моему коду, который использует escape-последовательности только для позиционирования курсора и приведен ниже. Обращаю внимание, что я установил скорость передачи информации в 100000 байтов в секунду. Сергей Вакуленко говорил, что SPI может выдержать миллионы, но с данным дисплейчиком скорости больше 100000 не получается - наверное его внутренний микроконтроллер не может обрабатывать быстро.

Да, я знаю, что "нормальный программист изолировал бы "spi_put_str ("\033[0;0H"); // set cursor position to row 0 column 0" - в отдельную функцию". Как я уже сказал - цель данного кода чтобы его было меньше, и следовательно, чтобы его можно было бы быстрее прочитать без взгляда скользящего по нескольким функциям. Нет, я не буду заворачивать эту функциональность в C++ класс, хотя это хорошая идея для создания абстрактной иерархии классов, работающих с разными дисплеями. Но не в этом примере.


http://code.google.com/p/pic32-examples/source/browse/trunk/showroom/calculator/display.c

static char buf [16];
static int col;

void display_init (void)
{
    spi_init (100000);  // baud rate

    memset (buf, ' ', sizeof (buf));
    col = 0;
}

static void display_scroll ()
{
    int i;

    spi_put_str ("\033[0;0H");  // set cursor position to row 0 column 0

    for (i = 0; i < sizeof (buf); i++)
        spi_put_char (buf [i]);

    spi_put_str ("\033[1;0H");  // set cursor position to row 1 column 0

    for (i = 0; i < sizeof (buf); i++)
        spi_put_char (' ');

    spi_put_str ("\033[1;0H");  // set cursor position to row 1 column 0

    memset (buf, ' ', sizeof (buf));
    col = 0;
}

void display_char (char c)
{
    if (c == '\n')
    {
        display_scroll ();
        return;
    }

    spi_put_char (c);
    buf [col ++] = c;

    if (col == sizeof (buf))
        display_scroll ();
}

void display_str (char *s)
{
    while (*s != '\0')
        display_char (*s++);
}



5. Работа с клавиатурой - опрос, буфер и прерывания

Теперь перейдем к работе с клавиатурой. Данное устройство не использует никаких протоколов типа USB или PS/2. Оно использует восемь проводов ввода-вывода, провод питания, кнопочки и резисторы. Программист работает с этими проводами непосредственно, как с битами в регистре E. Концептуально опрос состояния клавиатуры выглядит очень просто. Четыре провода вывода соответствуют четырем вертикальным колонкам - col1, col2, col3, col4, а четыре провода ввода соответствуют четырем горизонтальным рядам - row1, row2, row3, row4. Если подать на col1 ноль, а на col2, col3, col4 - единицы, то на вводах row1-4 появятся состояния нажатости клавиш в колонке 1. Действительно (см. схему), если клавиша "1" нажата, то на row1 появится 0, который пришел от col1, а если не нажата - то прийдет единичка от VCC через pull-up резистор R1 (если вы очень чистый программист и никогда не сталкивались с pull-up резисторами, то я расскажу об этом в отдельном посте). Короче, вот схема:




А вот код, который опрашивает состояние клавиатуры, проверяя что нажато и сравнивая это с состоянием во время предыдущей проверки. PORTE - это регистр, непосредственно подключенный к проводам, идущим к клавиатурке.


http://code.google.com/p/pic32-examples/source/browse/trunk/showroom/calculator/keypad.c

static void keypad_poll ()
{
    int row, col;
    int in;

    for (col = 0; col < n_cols; col ++)
    {
        PORTE = ~ (8 >> ((col + 1) & 3));
        in = PORTE >> 4;

        for (row = 0; row < n_rows; row ++)
        {
            bool on = ! (in & 8);
            in <<= 1;

            if (on && ! matrix [row][col])
                keypad_put (translation [row][col]);

            matrix [row][col] = on;
        }
    }
}



Клавиатура вставляется в 12-дырочный разъем JA или напрямую, или через кабель - так, что и клавиатура и плата "смотрят" в одну сторону. Разъем находится около кнопки сброса POC32 (reset):

_MG_9508

_MG_9507

Также вот схематика пинов из документации по клавиатурке:



И табличка из документации на микроконтроллерную плату:



Опрос клавиатуры удобно посадить на прерывание по таймеру - например опрашивать 50 раз в секунду. Это предотвратит дребезг (bouncing), когда при нажатии на клавишу контакт дергается вверх-внизы, вызывая у программы галлюцинации, что на кнопку нажали много раз. Кроме этого, работа на таймере высвобождает микроконтроллер от постоянного опроса ввода-вывода - вместо этого микроконтроллер может заниматься какими-нибудь полезными вычислениями. Вот как устанавливается прерывание по таймеру:


http://code.google.com/p/pic32-examples/source/browse/trunk/showroom/calculator/keypad.c

//
//  The Timer 1 interrupt is Vector 4, using enable bit IECO<4>
//  and flag bit IFSO<4>, priority IPC1<4:2>, subpriority IPC1<1:0>

void __attribute__ ((interrupt (IPL7))) __attribute__ ((vector (4))) keypad_timer (void)
{
    keypad_poll ();
    IFS0bits.T1IF = 0;
}

void keypad_init (bool use_interrupts)
{
    . . . . . . . . . . . .

    T1CONbits.ON     = 0;      // turn timer off
    TMR1             = 0;      // reset timer to 0

    T1CONbits.TCKPS  = 3;      // 1:256 prescale
    PR1              = PBCLK_FREQUENCY / 256 / 50;  // 1/50th of a second

    INTCONbits.MVEC  = 1;      // enable multi-vector mode
    IPC1bits.T1IP    = 7;      // interrupt priority
    IPC1bits.T1IS    = 3;      // interrupt subpriority
    IFS0bits.T1IF    = 0;      // clear the Timer 1 interrupt flag
    IEC0bits.T1IE    = 1;      // enable the Timer 1 interrupt

    asm volatile ("ei");       // enable interrupts

    T1CONbits.ON     = 1;      // turn timer on
}



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

6. Как вставить программатор / отладочный модуль

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

chipKIT PGM Programmer/Debugger - это новое устройство, и в его первой партии, которая разошлась осенью, был брак - из-за двух неправильных резисторов устройство работало как программатор, но не работало как отладчик. Сейчас Digilent бракованные программаторы позаменяло. Также можно использовать старые надежные отладчики PICkit3:

http://www.digilentinc.com/Products/Detail.cfm?NavPath=2,739,974&Prod=PG164130



Но я все-таки использую chipKIT PGM Programmer/Debugger, исправленную партию без брака. Вот как он выглядит:


_MG_9515


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

_MG_9513

Вот как все выглядит в вставленном состоянии:

_MG_9479-2

7. Калькулятор в работе

А теперь моя программка-калькулятор в работе. Она принимает целые числа, четыре операции арифметики: A это +, B это -, С это *, D это /; далее клавиша E - это скобка (левая или правая, в зависимости от предудущего ввода) и клавиша F - это "равно". Парсер / эвалюатор выражений использует простую рекурсию сверху-вниз. Вообще, я предпочитаю более надежный для общего случая bottom-up operator precedence parser, или вообще обобщенный LR(k) парсер, но всевозможные парсеры и эвалюаторы выражений пусть пишут студенты 1-2 курса в качестве упражнений.

Код калькулятора:

http://code.google.com/p/pic32-examples/source/browse/trunk/showroom/calculator/calculator.c

Головная программа:


http://code.google.com/p/pic32-examples/source/browse/trunk/showroom/calculator/main.c

void main (void)
{
    int i;

    running_fast ();
    display_init ();
    keypad_init  (true);  // use_interrupts

    display_str ("Calculator");

    for (;;)
        display_str (calculator (keypad_get ()));
}



Калькулятор в работе:

Заставка:

_MG_9481

Без скобок:

_MG_9484

Со скобками:

_MG_9485

Синтаксическая ошибка:

_MG_9487

Переполнение:

_MG_9486

А если нажать на вот эту кнопочку, то у микроконтроллера произойдет сброс (reset), но так как программа во flash памяти останется, то она просто запустится сначала, без загрузки ее с вашего большого компьютера:

_MG_9531


Продолжение - http://panchul.livejournal.com/245939.html


Какой пост-продолжение написать после сегодняшних?

Пример имплементации калькулятора в чистом хардвере на Xilinx FPGA
6(20.7%)
Пример имплементации калькулятора в чистом хардвере на Altera FPGA
3(10.3%)
Организация и защита памяти в ядре MIPS M4K и микроконтроллере Microchip PIC32
1(3.4%)
Все о прерываниях в ядре MIPS M4K и микроконтроллере Microchip PIC32
5(17.2%)
Все о таймерах в ядре MIPS M4K и микроконтроллере Microchip PIC32
0(0.0%)
Все о цифровых портах в Microchip PIC32, а также pull-up резисторах, кнопочках и лампочках
0(0.0%)
Все о prefetch cache в Microchip PIC32 и о связанным с ним ускорении работы программы
1(3.4%)
Все о осцилляторах / генераторах тактовой частоты в Microchip PIC32
0(0.0%)
Подробно об интерфейсе SPI
0(0.0%)
Подробно об интерфейсе UART с исторической перспективой
2(6.9%)
Подробно об интерфейсе I2C
3(10.3%)
Я жажду поста с какой-нибудь русской развиртуализацией на дикой природе
1(3.4%)
Я жажду поста про презентацию, которую Панчул будет делать перед американскими любителями камелий в Санта-Крузе
0(0.0%)
Следущий пост должен быть посвящен Дню Рождения Феминистки Натальи Радуловой, которой 10 февраля исполняется 38 лет
5(17.2%)
Из-за бугра плюете?
2(6.9%)
Tags: important, mips
Subscribe

Recent Posts from This Journal

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 25 comments

Recent Posts from This Journal