PIC Урок 4. Кнопка



Сегодня мы расширим свой кругозор по изучению работы портов микроконтроллера и изучим второе назначение порта — работу на вход. И для изучения работы на вход мы применим обычную тактовую кнопку.

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

Для этого мы в той же папке, где лежит папка с проектом BLINK01.X создадим ещё одну папку с именем таким, которое мы хотим присвоить новому проекту, в нашем случае будет BUTTON01.X.

И в эту папку из папки BLINK01.X мы перепишем папку nbproject и файлы Makefile и main.c.

Зайдём в папку nbproject в папке нового проекта и удалим там всё кроме файлов project.xml и configurations.xml.

Затем откроем файл project.xml и исправим в нём имя старого проекта на имя нового: в нашем случае BLINK01 на BUTTON01.

Всё!

Оказалось, что сгенерировать проект из старого легче чем в случае с AVR и STM.

А, ну заодно и файл проекта для протеуса тоже перепишем в новую папку, переименовав его тоже в свете требований имени нового проекта — в BUTTON01.X.pdsprj.

Запустим среду программирования MPLAB X IDE и просто откроем там наш новый проект из меню File -> Open Project либо с соответствующей кнопки тулбара.

Сделаем наш проект главным, чтобы из меню и тулбара всё управление шло именно к данному проекту, с помощью контекстного меню, вызвав его правой кнопкой мыши на имени проекта в дереве проектов по пункту Set as Main Project

 

 

Откроем файл main.c в новом проекте и попробуем проект собрать. Проект уже спокойно должен будет собраться, о чём будет свидетельствовать надпись в окне вывода информации, в котором также будет присутствовать и путь к файлу новой прошивки

 

 

Откроем теперь файл протеуса и исправим там в свойствах микроконтроллера путь к прошивке, так как он скорей всего остался к файлу старого проекта.

Запустим наш проект и проверим его работоспособность. Светодиоды должны будут поочерёдно зажигаться.

Теперь добавим кнопку, подключив её одним выводом к общему проводу, а другим к выводу RA2 контроллера

 

 

Также, чтобы в момент, когда кнопка не будет нажата, чтобы не получилось у нас неизвестное состояние у ножки порта, подтянем нашу ножку к выводу питания с помощью резистора, на 10 килоом, я думаю будет нормально

 

 

 

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

 

PORTA &= ~0x03;

TRISA |= 0x04; //Включим ножку RA2 на вход

 

Теперь над функцией main() давайте напишем функцию обнаружения нажатой кнопки, чтобы не загромождать функцию main

 

//--------------------------------------------

unsigned char CheckButton(void)

{

  unsigned char result=0;

  unsigned int butcount=0;

  while(!RA2)

  {

    if(butcount < 10000)

    {

      butcount++;

    }

    else

    {

      result = 1;

      break;

    }

  }

  return result;

}

//--------------------------------------------

 

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

Мы добавим две локальные беззнаковые целочисленные переменные, одну разрядностью в 8 бит, а другую в 16. Затем мы проверим наличие единицы в бите RA2, если такое имеет место, то попадём в тело цикла, в котором мы будем наращивать счётчик butcount в каждом прохождении по циклу при условии, что бит у нас будет оставаться в единице, то есть кнопка будет всё ещё нажата. Если мы в таком состоянии достигнем отметки в 10000, что будет соответствовать сколько-то милисекунд (подбирается на практике либо можно измерить с помощью инструмента отладки StopWatch, который мы испробовали на прошлом занятии), то мы уже не попадём в тело условия, что счётчик у нас меньше 10000, он же будет больше и мы тогда попадём в тело обратного (или противного) условия, где установим переменную результата в 1 и выйдем из цикла по команде break, а затем и из функции также с положительным результатом. А если мы так и не досчитаем до нашей цифры (которую вы можете для себя подобрать другую), то мы тогда и так выйдем из цикла while, и выйдем из функции, но уже с результатом 0, что будет свидетельствовать о том. что кнопка у нас не нажата. Таким образом мы не только отследим «нажатость» кнопки, но ещё и проведём борьбу с дребезгом, так как кнопки по качеству иногда отличаются.

 

 

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

Ну так вот он наш код бесконечного цикла

 

while(1)

{

  if(CheckButton())

  {

    PORTAbits.RA1 = 0;

    PORTBbits.RB0 = 1;

    __delay_ms(100);

    PORTBbits.RB0 = 0;

    PORTBbits.RB1 = 1;

    __delay_ms(100);

    PORTBbits.RB1 = 0;

    PORTBbits.RB2 = 1;

    __delay_ms(100);

    PORTBbits.RB2 = 0;

    PORTBbits.RB3 = 1;

    __delay_ms(100);

    PORTBbits.RB3 = 0;

    PORTBbits.RB4 = 1;

    __delay_ms(100);

    PORTBbits.RB4 = 0;

    PORTBbits.RB5 = 1;

    __delay_ms(100);

    PORTBbits.RB5 = 0;

    PORTBbits.RB6 = 1;

    __delay_ms(100);

    PORTBbits.RB6 = 0;

    PORTBbits.RB7 = 1;

    __delay_ms(100);

    PORTBbits.RB7 = 0;

    PORTAbits.RA0 = 1;

    __delay_ms(100);

    PORTAbits.RA0 = 0;

    PORTAbits.RA1 = 1;

    __delay_ms(100);

  }

}

 

Напомню, что то, что изменилось в коде, я выделяю. Невыделенное — это неизменённое и показываю я это с той целью, чтобы вы поняли, куда именно писать новый код.

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

 

    PORTAbits.RA1 = 1;

    __delay_ms(100);

    PORTAbits.RA1 = 0;

  }

}

return;

 

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

 

if(CheckButton())

{

  PORTAbits.RA1 = 0;

  PORTBbits.RB0 = 1;

 

Ну что ж. Соберём наш код, зайдём в протеус и попробуем запустить его.

Соответственно, мы увидим, что светодиоды наши больше не зажигаются.

А чтобы они зажглись нажмём кратковременно на кнопку (но не слишком кратковременно, чтобы выждать наш период борьбы с дребезгом).

Светодиоды начнут попеременно светиться

 

 

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

 

 

Подключим программатор, запустим программу PICkit 2, откроем нашу новую прошивку и зальём её в контроллер.

А вот и результат

 

 

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

Спасибо всем за внимание!

 

 

Предыдущий урок Программирование МК PIC Следующий урок

 

Исходный код

 

 

Приобрести программатор PICKit3 (неоригинальный) можно здесь PICKit3

Приобрести программатор PICKit3 (оригинальный) можно здесь PICKit3

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

PIC Кнопка

9 комментариев на “PIC Урок 4. Кнопка
  1. Пётр:

    Хорошие уроки.
    Автору — спасибо.
    Понравилось как Вы работаете с портом «А» — красиво.

  2. Валерий:

    Столкнулся с проблемой. При загрузке .hex файла в программатор PIC2, наблюдаю разрыв кодов на поле. Причем 0 ячейка занята, а далее вся программа уходит в конец поля. В середине — пустота? Чем это можно обьяснить?

  3. tamper:

    Отличные уроки.Ничего более доходчивого не встречал .
    Спасибо.

  4. Артур:

    Спасибо за ваш труд, очень доходчиво и понятно. К сожалению конфигурация данной кнопки не работает в PIC12F629. Перелопатил много текстов, просмотрел ролики на You Tube. Нашел такие вот кнопки:
    // PIC12F629 Configuration Bit Settings

    // 'C' source line config statements

    // CONFIG
    #pragma config FOSC = INTRCIO // Oscillator Selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
    #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
    #pragma config PWRTE = OFF // Power-Up Timer Enable bit (PWRT disabled)
    #pragma config MCLRE = OFF // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
    #pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD disabled)
    #pragma config CP = OFF // Code Protection bit (Program Memory code protection is disabled)
    #pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)

    // #pragma config statements should precede project file includes.
    // Use project enums instead of #define for ON and OFF.

    #include
    #define _XTAL_FREQ 4000000

    int x0,x1,y0,y1,z0,z1;
    void main() {
    GPIO = 0x00;
    CMCON = 0x07;
    TRISIO = 0b00111000;

    x0=1;x1=0;
    y0=1;y1=0;
    z0=1;z1=0;

    while (1)
    {
    if ((GP5==0)&&(x0==1))
    {
    x1=0;
    x0=1 ;
    GP0 = 0;
    __delay_ms(600);
    }

    if ((GP5==0)&&(x1==1))
    {
    x1=0;
    x0=1 ;
    GP0 = 0;
    __delay_ms(600);
    }

    if ((GP4==0)&&(y0==1))
    {
    y1=0;
    y0=1 ;
    GP1 = 0;
    __delay_ms(600);
    }

    if ((GP4==0)&&(y1==1))
    {
    y1=0;
    y0=1 ;
    GP1 = 0;
    __delay_ms(600);
    }

    if ((GP3==0)&&(z0==1))
    {
    z1=0;
    z0=1 ;
    GP2 = 0;
    __delay_ms(600);
    }

    if ((GP3==0)&&(x1==1))
    {
    z1=0;
    z0=1 ;
    GP2 = 0;
    __delay_ms(600);
    }
    }
    }

    Проект собирается в MPLAB X IDE v4.05, в Proteus создаю макет — не работает! У меня скоро голова лопнет!

    • Эдем:

      все дело в том, что в уроке все написано для процессора PIC16F84A. а вы пытаетесь эту программу скормить PIC12F629, у которого совсем другое назначение выводов .

  5. Артур:

    Опять спасибо за уроки, теперь кнопка заработала!
    Кнопка PIC12f629

    #pragma config FOSC = INTRCIO
    #pragma config WDTE = OFF
    #pragma config PWRTE = ON
    #pragma config MCLRE = OFF
    #pragma config BOREN = OFF
    #pragma config CP = OFF
    #pragma config CPD = OFF

    #include
    #define _XTAL_FREQ 4000000

    unsigned char CheckButton(void)
    {
    unsigned char result=0;
    unsigned int butcount=0;
    while(!GP3)
    {
    if(butcount < 2000)
    {
    butcount++;
    }
    else
    {
    result = 1;
    break;
    }
    }
    return result;
    }

    void main (void)
    {
    CMCON = 7;
    TRISIO = 0;
    GPIO = 0;
    GIE = 1;
    GPIE = 1;
    IOCB = 4;
    IOCB3 = 1;

    while(1)
    {
    if(CheckButton())
    {
    GP0 = 1;
    __delay_ms(200);
    GP0 = 0;
    GP1 = 1;
    __delay_ms(200);
    GP1 = 0;
    GP2 = 1;
    __delay_ms(200);
    GP2 = 0;
    GP4 = 1;
    __delay_ms(200);
    GP4 = 0;
    }
    }
    }

  6. Сергей:

    Не большая неточность. TRISA |= 0x04; //Включим ножку RA2 на вход. При программировании в версии 5.20 в TRISA=00001111. поэтому здесь надо убрать или. второй вариант оставить код как был на предыдущем уроке. с уважением Ваш ученик. Хоть и не официальный. Там кое где есть еще ряд неточностей, но для себя я уже исправил.

  7. Алексей:

    Скажите почему при нажатии кнопки в протеусе показывает замыкание (желтые квадратики насколько я понял это замыкание) и светодиоды не горят.Все сделал точно как в уроке.

  8. Андрей:

    Часов пять разбирался, почему на PIC16F690 кнопка не работает, огни запускаются сразу, т.е. на RA2 всегда ноль независимо от настройки регистров TRISА и PORTA. Чуть когнитивный диссонанс не заработал глядя на текст программы. ПРАВИЛЬНОЙ ПРОГРАММЫ!
    Пришлось делать ужасное — читать даташит, а там… есть хитрый регистр ANSEL, который назначает в каком режиме считывается вход, в цифровом или аналоговом. Если он настроен на аналоговое считывание, то независимо от подтяжек, на ножке считывается ноль.
    Хорошо что вы используете нетривиальные способы записи типа «PORTA &= ~0x03;», не скучно.
    Спасибо за уроки.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*