Почнемо створення своєї програми підтримки для РКІ з простої задачі – вивести один символ на індикатор Я використовую спочатку програму ISIS для перевірки результату, потім використовую дисплей робота

Згадаймо, як вмикається дисплей:

Рис 505 Включення РКІ робота ROBOPICA

Для написання коду програми і її налагодження використовуємо MPLABX, створивши новий проект, який я назву lcd_hold, слідуючи всім тим інструкціям, з якими ми визначилися в попередньому розділі Пізніше я приведу ту програму, яку використовую в якості основи для роботи, а зараз почну з тих функцій, що створять основу файлу підтримки: lcd_dat (unsigned char x), lcd_com (unsigned char x), lcd_init (void) Як було написано вище, нам будуть потрібні команди передачі даних (символів і команд), знадобиться ініціалізація дисплея – це і визначає вибір функцій

void lcd_com (unsigned char lcd)

{

unsigned char temp

temp = (lcd & ~ (1 < < RS)) | (1 << E); / / При RS = 0 записується команда

PORTD = temp / / Виводимо в порт D старшу тетраду команди, сигнали RS, E

 asm / / Невелика затримка nop

nop nop nop nop

  endasm

PORTD = temp & ~ (1 < < E); / / Запис команди

temp = ((lcd * 16) & ~ (1 < < RS)) | (1 << E); / / При RS = 0 виводиться команда

PORTD = temp / / Виводимо в порт D молодшу тетраду команди, сигнали RS, E

 asm / / Невелика затримка nop

nop nop nop nop

  endasm

PORTD = temp & ~ (1 < < E); / / Запис команди

pause (10 * TIME) / / Пауза для виконання команди

}

Нагадаю, не я автор цієї програми І перейдемо до виправлень З явних проблем, які виникнуть, відсутність функції pause () Але вона є в тексті програми, додамо її:

void pause (unsigned int a)

{

unsigned int i

for (i=ai>0i–)

}

Крім того, не визначені RS, E і TIME, визначення яких автор додає в програму:

# Define RS 2 / / RS = RD2 – сигнал управління РКІ

# Define E 3 / / E = RD3 – сигнал управління РКІ

# Define TIME 50 / / Константа тимчасової затримки для РКІ 20 МГц

І тепер функція виведення символу:

void lcd_dat (unsigned char lcd)

{

unsigned char temp

temp = (lcd | (1 < < RS)) | (1 << E); / / При RS = 1 виводяться дані

PORTD = temp / / Виводимо в порт D старшу тетраду даних, сигнали RS, E

 asm / / Невелика затримка nop

nop nop nop nop

  endasm

PORTD = temp & ~ (1 < < E); / / Сигнал запису даних

temp = ((lcd * 16) | (1 < < RS)) | (1 << E); / / При RS = 1 виводяться дані

PORTD = temp / / Виводимо в порт D молодшу тетраду даних, сигнали RS, E

 asm / / Невелика затримка nop

nop nop nop nop

  endasm

PORTD = temp & ~ (1 < < E); / / Сигнал запису даних pause (10 * TIME); / / Пауза для виведення даних

}

Останнє, що нам потрібно, це функція ініціалізації РКІ:

void lcd_init (void)

{

lcd_com (0x28) / / 4-дротовий інтерфейс, 5×8 розмір символу pause (100 * TIME)

lcd_com (0x0С) / / Показати зображення, курсор не переміщується показувати pause (100 * TIME)

lcd_com (0x01) / / Очистити DDRAM і встановити курсор на 0x00 pause (100 * TIME)

}

Часи я привів до тактовій частоті 20 МГц, в оригіналі використана частота 4 МГц Перша перевірочна програма (нижче без функцій) виглядає наступним чином:

#include <pic16f887h>

typedef unsigned int word

word at 0x2007 CONFIG1 = 0x2FF2

void main(void) {

TRISD = 0x03 PORTD $= 0x03

pause(5000) lcd_init() lcd_dat(A) while(1)

}

Я не привожу програму повністю з причини того, що на екрані дисплея в ISIS нічого не відображається Аналогічний результат і при перевірці програми в «залізі» Начебто все правильно, але ..

Перевіривши і перевіривши різні варіанти, що викликають сумніви, я, зрештою,

«Підгледів» те, як зроблена ініціалізація в програмі Flowcode До речі, вийшла пята версія програми, обмеження демоверсії якої, в першу чергу, стосується розміру памяті, обмеженого до 2 Кбайт

Зараз функція ініціалізації виглядає так:

void lcd_init (void)

{

lcd_com(0x33) pause(100*TIME) lcd_com(0x33) pause(100*TIME) lcd_com(0x32) pause(100*TIME)

lcd_com (0x28) / / 4-дротовий інтерфейс, 5×8 розмір символу pause (100 * TIME)

lcd_com (0x0С) / / Показати зображення, курсор не переміщується показувати pause (100 * TIME)

lcd_com(0x06) pause (100*TIME)

lcd_com(0x0C) pause(100*TIME)

}

І тепер при перевірці в програмі ISIS я бачу відображення символу:

Рис 506 Перевірка програми після виправлення функцій

Перш, ніж зібрати всі функції в файл lcdh, що я маю намір зробити, хотілося б розібратися, які команди виконують додані рядки:

lcd_com(0x33) lcd_com(0x32) lcd_com(0x06)

Перша команда в двійковому вигляді: 110011, – як я розумію, відноситься до формату даних (Function Set в таблиці інструкцій), як і наступна Я, орієнтуючись на чотири лінії даних, написав би цю команду інакше, але, спробуйте – зміна цих рядків призводить до того, що програма не працює

Можливо, пізніше, можливо, ви самі розберетеся, у чому справа Я не виключаю, що контролер РКІ інший На платі використовується бескорпусной контролер І, якщо працює, то нехай працює

Створимо файл lcdh:

#define RS 2

#define E 3

#define TIME 50

void pause (unsigned int a)

{

unsigned int i

for (i=ai>0i–)

}

void lcd_com (unsigned char lcd)

{

unsigned char temp

temp=(lcd&amp~(1<<RS))|(1<<E) PORTD = temp

_asm

nop

nop nop nop nop

_endasm

PORTD = temp&amp~(1<<E)

temp = ((lcd*16)&amp~(1<<RS))|(1<<E) PORTD = temp

_asm

nop nop nop nop nop nop

_endasm

PORTD = temp&amp~(1<<E) pause (10*TIME)

}

void lcd_dat (unsigned char lcd)

{

unsigned char temp

temp = (lcd|(1<<RS))|(1<<E) PORTD = temp

  asm

nop nop nop nop nop

  endasm

PORTD = temp&amp~(1<<E)

temp = ((lcd*16)|(1<<RS))|(1<<E) PORTD = temp

  asm

nop

  endasm

nop nop nop nop

PORTD = temp&amp~(1<<E) pause(10*TIME)

}

void lcd_init (void)

{

lcd_com(0x33)

pause(100*TIME) lcd_com(0x32) pause(100*TIME) lcd_com(0x28) pause(100*TIME) lcd_com(0x0C) pause(100*TIME) lcd_com(0x06) pause (100*TIME) lcd_com(0x0C) pause(100*TIME)

}

Створений файл додамо в папку програми, саму програму оформимо так:

#include <pic16f887h>

#include &quotlcdh&quot

typedef unsigned int word

word at 0x2007 CONFIG1 = 0x2FF2

void main(void) { TRISD = 0x03 PORTD &amp= 0x03

pause(5000) lcd_init() lcd_dat(I)

lcd_dat(\’)

lcd_dat(m)

lcd_dat( )

lcd_dat(R)

lcd_dat(O)

lcd_dat(B)

lcd_dat(O)

lcd_dat(P)

lcd_dat(I)

lcd_dat(C)

lcd_dat(A) while(1)

}

Звичайно, виводити повідомлення за допомогою символів, це не саме правильне Слід використовувати рядок тексту Але зараз важливо перевірити, чи правильно працює програма

Перевіримо це за допомогою ISIS (Proteus):

Рис 507 Перевірка програми виведення повідомлення на РКІ І завершимо перевірку, завантаживши програму в ROBOPICA

Рис 508 Завантажена програма в ROBOPICA

У програмі є рядки, які мені хотілося б пояснити Пояснити, використовуючи не посилання на підручник, а програму MPLABX з компілятором SDCC Звичайно, потрібно обовязково читати підручник, суперечці немає, але, читаючи підручник, корисно виконати операції в середовищі розробки Отже:

temp = ((lcd*16)&amp~(1<<RS))|(1<<E)

Розберемо цю рядок «по цеглинці» Напишемо таку програму:

#include <pic16f887h>

typedef unsigned int word

word at 0x2007 CONFIG1 = 0x2FF2

#define RS 2

unsigned char temp = 0

void main()

{

temp = 1<<RS

}

Створимо в MPLABX новий проект, новий вихідний файл для SDCC, скомпілюємо програму і запустимо налагодження На інструментальній панелі натиснемо паузу (панель налагодження), а потім скидання (кнопка Reset) На інструментальній панелі У вікні редактора відображається текст програми на асемблері Для спостереження за змінними використовуйте вікно спостереження:Window-

>Debugging->Watches Щоб додати спостереження за змінної temp, досить клацнути у вікні редактора правою клавішею мишки і вибрати пункт New Watch…

Рис 509 Вікно введення змінної для спостереження

Змінну я ввожу в тому вигляді, в якому вона представлена ​​на асемблері, і вона виділена кольором, якщо є в програмі Після того, як натиснута кнопка ОК, Змінна зявляється у вікні спостереження Натиснувши на інструментальній панелі (панель налагодження) кнопку скидання після запуску налагодження, можна почати покрокове проходження програми Спочатку змінна у вікні спостереження дорівнює нулю:

Рис 5010 Початкове значення змінної у вікні спостереження

Після покрокового проходження програми змінна змінить своє значення:

Рис 5011 Нове значення змінної

Запишемо це значення в двійковому вигляді: 00000100

Тепер змінимо програму, записавши єдину її рядок так: temp = 1 < < E;

Відкомпілюємо програму заново (Run->Build Project), Запустимо, зупинимо відладчик, скинемо програму і пройдемо її по кроках, щоб отримати нове значення змінної у вікні спостереження:

Рис 5012 Ще одне значення змінної після зміни програми І це значення теж запишемо в двійковому вигляді: 00001000

Звернемо увагу на визначення, яке задає E:

#define E 3

Тобто, у програмі E – Це число 3

Операція < < здійснює побітовий зрушення вліво. Зсувається число, що стоїть ліворуч від знака на кількість позицій, яке записано справа. У першому випадку можна було б записати так 1 << 2. Що означало б, число 0000001 зсувається на два розряди вліво: 00000100. У другому випадку, записуючи його 1 << 3, ми отримуємо зрушення на три розряди: 00001000.

Тепер подивимося, як впливає на результат значок, який доданий до першого зрушенню вліво:

~(1<<RS)

Внісши зміни до програми, я використовую компіляцію командою Run->Clean and Build Project І далі запускаючи налагодження, перервавши її і зробивши скидання кнопкою інструментальної панелі Reset, Я покроково виконую програму:

Рис 5013 Результат операції інверсії

Запишемо результат зсуву та отримане значення в двійковому вигляді: 00000100 і 11111011 Операція інверсії або заперечення змінює одиниці на нулі і навпаки

Наступне, що я хочу показати, це результат множення: lcd * 16 Додаючи в програму змінну lcd = 0x0F, (двійкове число 00001111) я помножу її на 16 (десяткове):

Рис 5014 Результат множення числа на 16

У двійковому вигляді це число виглядає наступним чином: 11110000 Множення двійкового числа на 2 схоже на множення десяткового числа на 10, тобто, додаємо нуль справа Кількість цих нулів визначається кількістю десяток в десяткового системі числення і кількістю двійок в двійковій

Коли ми виводимо значення на рідкокристалічний індикатор, використовуючи чотирьох-бітову шину даних, ми виводимо старшу тетраду байта Щоб підготувати молодшу тетраду, ми і множимо число на 16

Як і в арифметиці, в будь-якій мові програмування порядок виконання операцій строго визначений, а вплинути на це можна за допомогою виділення операцій дужками Розглянемо, як виглядає результат операції:

temp = ((lcd*16)&amp~(1<<RS))

Всі операції, крім операції &, ми вже розібрали Ця операція – побітовое логічне І Значення lcd те ж, що і в попередньому прикладі

Рис 5015 Результат виконання логічного І

Перше число в дужках – 11110000, друге – 00000100 Друге число після операції НЕ – 11111011 І результат виходить таким, як якби ми «побитово помножили» одне число на інше

Нарешті, виконаємо всю послідовність операцій З них залишилася операція |, з якою ми ще не знайомі Це операція побітового АБО двох операндів Операція, яка нагадує арифметичне «побітовое складання »

Рис 5016 Результат виконання всіх досліджених операцій

Ми розібрали один рядок програми Дуже корисно, використовуючи запропонований мною механізм дослідження, або орієнтуючись на підручники, розібрати всі операції мови Сі, щоб ясно розуміти, що ви отримаєте в результаті операції

Програмі доводиться маніпулювати з тетрадами байта даних і управляти сигналами RS і E, Перший перемикає шину даних з прийому команд на прийом символів, другий дозволяє передачу цих даних в регістри контролера РКІ

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

Використання середовища розробки MPLABX, як ви бачите, завдяки вбудованому механізму налагодження не тільки допомагає налагоджувати написаний вами текст програми, але і допомагає глибше вивчити мову Сі

Ціною відмови від обмеження на розмір програми і стає необхідність глибше вивчати мову програмування Але це відкриває для вас і нові горизонти, нові можливості

Модуль аналого-цифрового перетворювача може бути вбудований в мікроконтролер, а може бути відсутнім Модуль АЦП може бути багатоканальним

Дуже зручно застосовувати АЦП мікроконтролера в парі з датчиками, які плавно змінюють своє значення Наприклад, якщо в якості датчика температури використовувати терморезистор, то напруга, мінливий при зміні температури, можна зчитувати програмно

У MicroC і для цих цілей є бібліотечний файл, обслуговуючий роботу з АЦП (ADC) Як і в попередніх розділах, для компілятора SDCC нам буде потрібно створити такий допоміжний файл, що має всі необхідні функції

Джерело: Гололобов ВН, – Самовчитель гри на паяльнику (Про електроніці для школярів і не тільки), – Москва 2012