PIC Урок 3. Бегущие огни



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

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

Сначала давайте соберём схему с контроллером PIC16F84A и подключенными через токоограничивающие резисторы светодиодами в программе-стимуляторе — Proteus

 

image00

 

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

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

 

void main(void) {

  TRISB = 0x00;

  PORTB = 0x00;

  TRISA &= ~0x03;

  PORTA &= ~0x03;

  while(1)

 

Чуть не забыл, давайте настроим конфигурационные биты. Это биты, без которых нам не обойтись. С помощью них мы настраиваем некоторые свойства нашего контроллера, подобна фьюзам в AVR. Для того, чтобы более удобно их настраивать, есть специальный инструмент в среде программирования MPLAB X IDE.

Проследуем по пунктам меню Window -> PIC Memory Views -> Configuration Bits и внизу у нас появятся настроики этих самых битов конфигурации. Настроим там следующие занчения

 

image01

 

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

Сохраним наши конфигурационные биты в удобочитаемый код с помощью нажатию на кнопку ниже

 

image02

 

и получим код в окне вывода информации

 

image03

 

Скопируем его и добавим в наш файл main.c сразу после подключения библиотеки и заодно сразу объявим частоту нашего резонатора, иначе у нас не будут работать корректно задержки

 

#include <xc.h>

 

#define _XTAL_FREQ 4000000

 

#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)

#pragma config WDTE = OFF // Watchdog Timer (WDT disabled)

#pragma config PWRTE = ON // Power-up Timer Enable bit (Power-up Timer is enabled)

#pragma config CP = OFF // Code Protection bit (Code protection disabled)

 

Теперь давайте с помощью удобных инструкций (написанных с помощью макросов в стандартной библиотеке PIC) добавим код в бесконечный цикл

 

while(1)

{

  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);

}

 

То есть мы поочерёдно включаем ножки, отключая при этом предыдущие. Всё просто.

 

 

Соберём код и попробуем его поотлаживать.

Для этого установим брейкпоинт на какую-нибудь строку

 

image04

 

Запустим отладку соответствующей кнопкой на тулбаре

 

image05

 

Программа остановится на заданной точке.

Давайте теперь посмотрим какие-нибудь регистры. Запустим сначала инструмент для просмотра всех регистров посредством команды меню Window -> PIC Memory Views -> File Registers

 

image06

 

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

 

image07

 

У нас изменится только байт-счётчик, так как в бите RA1 у нас и так уже был ноль. Причём байт изменится в обоих банках ибо он банконезависимый

 

image08

 

Шагнём ещё один шаг

 

image09

 

И мы увидим что теперь байт по адресу 0x06 изменил своё значение

 

image10

 

Причём не обязательно искать в даташите, что это за адрес, достаточно поднести курсор мыши к значению и мы увидим всё в подсказке

 

image11

 

Только постоянно подносить курсор тоже не совсем удобно. Для этого есть другой инструмент, созданный именно для просмотра регистров специального назначения, который вызывается командой меню Window -> PIC Memory Views -> SFRs

 

image12

 

Шагнём ещё пару шагов и увидим изменение в регистре порта B

 

image13

 

Также существует ещё один интересный инструмент, позволяющий измерять время, за которое выполняется та или иная команда, а если поставить точку останова в другом месте и выполнить код сразу до неё, то и целого участка кода. Данный инструмент вызывается по команде меню Window -> Debugging -> Stopwatch. Вызовем его и шагнём одну команду

 

image14

 

Мы видим, что команда выполнилась за 1 микросекунду, то есть за 1 машинный цикл. Сбросим время кнопкой Clearstopwatch

 

image15

 

Время должно будет сброситься

 

image16

 

Теперь прошагаем ещё одну команду. У нас как раз задержка, вот и измерим её время

 

image17

 

 

Всё точно. 100 милисекунд. Остановим отладку, перейдём в Proteus. Настроим свойства контроллера, добавив там 16-битную величину наших конфигурационных битов, а также показав путь к нашей прошивке и задав частоту тактирования

 

image18

 

Сохраним настройки и попробуем запустить проект. Мы должны увидеть, как светодиоды «побегут» сверху вниз

 

image19

 

Конечно, в статической картинке я показать этого не смогу, поэтому смотрите видеоурок.

А теперь посмотрим нашу практическую схему, которую я собрал на макетной плате

 

image20

 

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

Программатор мы будем использовать PICkit2. У меня он клон. Настоящий фирменный у меня есть PICkit3, но так как для данной схемы достаточно и второго, да и не у всех есть третий, то будем использовать пока его.

А вот и программатор

 

image21

 

Подключается данный программатор к контроллеру по интерфейсу ICSP следующим образом

 

PICkit — MC

1 (VPP) — 4

2 (VDD) — 14

3 (VSS) — 5

4 (ICSPDAT) — 13

5 (ICSPCLK) — 12

6 (Aux) — в нашем случае не используется

 

Давайте так его и подключим (нажмите на картинку для увеличения изображения)

 

image23_0500

 

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

Скачаем программу, установим её и запустим, подключив перед этим программатор к схеме, а с другой стороны — к USB-разъёму компьютера. Если всё подключено правильно, то программа сама обнаружит и определит контроллер

 

image24

 

Покажем программе нашу прошивку, проследовав по пунктам меню File -> Import Hex и показав путь к файлу с прошивкой в файловом диалоге. Конфигурационные биты должны будут выставиться сами, как мы их и настраивали в среде программирования

 

image25

 

Включим питание, заранее выставив напряжение. Пойдёт и 5 вольт, но я всегда на всякий случай выбираю 4,8. Включение питание производится установкой галки в чекбокс On

 

image26

 

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

 

image27

 

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

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

 

 

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

 

Исходный код

 

 

Программа для прошивки PICkit2

 

 

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

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

 

 

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

 

PIC Бегущие огни

26 комментариев на “PIC Урок 3. Бегущие огни
  1. Сергей:

    судя по программе…. можно резисторы убрать и заменить одним в цепь светодиодов на землю

  2. Сергей:

    и схема… так себе…. обычно светодиоды нулём зажигают, чтобы контроллер не перегружать…
    поэтому- катодами в контроллер …. аноды в кучу и один резистор на +5
    ну и программу переделать 🙂

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

      • Сергей:

        это вы серьёзно? 🙂
        да хоть сотню светодиодов попробуйте зажечь…. один резистор в 250 ом — ни какому току не даст протечь 🙂
        просто светодиоды гореть не будут…
        я предложил упрощение схемы по вашей программе…..
        кстати- 250 ом… это 20ма на ножку…. если все одновременно «загорятся»- контроллер выдержит?

        • imperror:

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

          • Это общепринятая схема. У каждой ножки порта есть предельный ток, который она может выдержать. Можете проверить. Зажечь сразу все светодиоды, а затем один. Будет яркость 1 и та же.

          • Кстати в официальном мануале по FreeRTOS «Mastering the FreeRTOS™
            Real Time Kernel» есть раздел 12.3 Stack Overflow. Может поможет.

          • Сергей:

            для данной программы- нормальный вариант…. один гасится- другой зажигается 🙂

        • Игорь:

          При 250 Ом ток будет (5В-2В)/250Ом = 12мА.

  3. Сергей:

    добавлю…. по даташиту…. порт В имеет максимальную нагрузку 100-150ма если все сразу зажечь, то контроллеру может поплохеть….

    статья… для начинающих нормальная….
    не обижайтесь за критику- просто я разработкой больше 20 лет занимаюсь 🙂

  4. Сергей:

    программу поменять надо…
    сначала
    TRISA=0xFF;
    TRISB=0xFF;
    а по тексту:
    TRISBbits.TRISB4=0;
    PORTBbits.RB4=1;
    TRISBbits.TRISB4=1;

    и т.д.

    тогда точно одновременно не зажгутся

  5. Сергей:

    забыл про делей 🙂
    TRISBbits.TRISB4=0;
    PORTBbits.RB4=1;
    __delay_ms(100);
    TRISBbits.TRISB4=1;
    TRISBbits.TRISB5=0;
    PORTBbits.RB5=1;
    __delay_ms(100);
    TRISBbits.TRISB5=1;

  6. Сергей:

    кстати… неудобные посты… да и вообще все мои посты -можете удалить

  7. Вы не объяснили назначение регистров,какой же это урок для начинающих?
    TRIS – регистр выбора направления данных в каналах порта ввода/вывода, если соответствующий бит регистра «0», то линия вывода работает на выход, если «1» то на вход. По умалчиванию порт настроен на вход.
    PORT – регистр порта , считывает логическую информацию с выводов порта.

    • Во первых, я нигде не заявлял, что уроки эти для начинающих.
      1 предъява не принимается.
      Назначение данного регистра очевидна и мы в этом убедились практически.

  8. Елена:

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

    Так как мне будет для работы необходим другой контроллер — PIC16F73, то и эту программу я переписала для него (пока для части ног портов B и C потому что в дальнейшей работе будут задействованы именно они) — заодно для проверки работоспособности имеющихся у меня контроллеров вообще (так как более сложный проект сразу не получился).
    Резисторы в вашей схеме чуть увеличила — поставила 270 Ом. Плюс ещё добавила светодиод с сопротивлением 330 Ом непосредственно на питание (между + и — на входе).
    И столкнулась с неожиданными для меня явлениями.
    1) сразу после прошивки программы в контроллер (программатор у меня PICkit3, но не фирменный. Но MPLab его «видит»),контроллер НЕ перезагружается и выполнение программы НЕ начинается. Хотя питание на схему программатор подаёт и после прошивки. Пробовала на нескольких экземплярах контроллеров. Может быть нужно специально подать контроллеру какой-то сигнал?
    2) Если после прошивки питание от программатора отключить и подать иное внешнее питание, то выполнение программы начинается, но бесконечный цикл выполняется не бесконечно, а несколько раз (от 1-2-3 — чаще всего до нескольких десятков изредка). Т.е. огоньки пробегают несколько раз и больше не бегут — гаснут, хотя питание по прежнему подано. От чего именно зависит кол-во повторений так и не поняла. Думала, что от конкретного экземпляра контроллера, но многократные повторения с разными экземплярами контроллеров показали, что не только от этого.
    3) После того как огоньки погасли, снятие и повторная подача напряжения далеко не всегда приводит к тому, что они снова побегут. Чаще всего они НЕ бегут. Что приводит к мысли, что контроллер как-то испортился. Но через некоторое время (проведённое контроллером при выключенном питании) часто снова начинают бежать.

    Что же это за проблемы? И как их решить?? Особенно
    волнуют 2 и 3.
    Всё имеющиеся у меня контроллеры «дохлые»?
    Что-то не так с питанием? Источник даёт недостаточный ток питания? Пробовала USB порт компьютера и блок питания зарядного устройства (который вроде может выдавать до 1А).
    Может велико напряжение питания? Оба доступных мне внешних источника, судя по мультиметру, выдают напряжение 6В. (А по инструкции к контроллеру максимальное напряжение питания — 5.5). Программатор при установке в MPLab 5В выдаёт тоже 6В по мультиметру. Поэтому при прошивке ставлю в настройках 4.5В, тогда на выходе на контроллер намеревается 5.5В.

    Посоветуйте пожалуйста мне что-нибудь…

    • Александр:

      Елена, не превышайте напряжения питания выше 5 В. Если внешний кварц — проверте генерацию, не срывается ли? Сложно давать советы более детальные не видя схему и текст программы.

  9. Владимир:

    Подскажите зачем с портом А при инициализации делать такие финты:
    TRISA &= ~0x03;
    PORTA &= ~0x03;
    Я так понимаю что это:
    1. сначала инвертирует (~0x03) и мы получаем: 1111 1100.
    2. TRISA после любого ресета имеет 00011111 (так как порт имеет 5ть выводов)
    3. Далее мы делаем поразрядное И и записываем в TRISA — таким образом насильно записываем в два младших 00, и получаем 0x1C в регистре. Это можно сделать и сразу: TRISA = 0xFC (TRISA = 0x1C).
    PORTB — тоже. Но там после включении питания состояние неопределенно и логически было бы его обнулить.
    После других ресетов состояние портов не меняется и после &= в PORTA мы не меняем 2, 3, 4 бит но насильно сбиваем в 0 младшие, которые используем. В данной схеме это не нужно, но на будущее это хороший трюк. Но зачем это делать с TRISA, и почему мы не делаем єто с PORTB?
    Если мое предположение с сохранением предыдущего состояния верно,то: зачем делать инвертирование?

  10. Дмитрий:

    Компилятор ругается: Unable to resolve identifier __delay_ms
    помогает следующая строка:
    #define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))

  11. Алекс:

    Владимир, можно и по другому, зато нам показали как можно применять » & «. В будущем пригодиться.

  12. Олег:

    Подскажите программу как подключить 13 светодиодов и задействовать все выводы портов А и В. У, меня, диод с порта А4 не загорается.

  13. Фаизнур:

    PORTAbits.RA0 = 1;
    PORTAbits.RA1 = 0;
    PORTAbits.RA2 = 0;
    PORTCbits.RC0 = 0;
    PORTCbits.RC1 = 0;
    В протеусе эти команды вместе почему то не работают. Мне нужно каждый регистр менять по отдельности не затрагивая другие порты. Если командой porta = 0b00000001 я могу выставить первый выход в 1 то как мне остальные не трогать?

  14. mole85:

    make[2]: *** [build/default/production/main.p1] Error 1
    make[1]: *** [.build-conf] Error 2
    make: *** [.build-impl] Error 2
    main.c:10:1: error: expected '}'
    ^
    Здравствуйте. Решил научиться писать программы самостоятельно. MPLAMPLAB X IDE v6.00 После копирования программы с сайта в окно MPLAMPLAB X IDE v6.00 и сборки появляются такие строки. Как исправить?

  15. Сергей:

    У мня тоже самое ругался я в место __delay_ms, написал delay(), только в скобках надо указывать другое значение а то прерывания быстрые , не знаю по чему ругается компилятор __delay_ms(); красным не подсвечивает

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

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

*