PIC16F1503. Тачка на прокачку - 3. Харчування

Раніше було про звук і світ.

Нарешті відступати більше нікуди і настав час зібрати машинку в щось ціле і майже не ділене. Залишився один малесенький питання: як ця машинка буде включатися і вимикатися? І як бути з традицією продовжувати наступного тему попереднього посту?



Що б не порушувати традицію, продовжу тему попереднього поста: миготіння светодиодиков. Що з ними не так?


Не так з ними багато, починаючи від кривої по часу затримки і закінчуючи страшним обробником переривання від таймера.

Для початку треба зробити нормальну функцію затримки. Ставимо TMR1 на період 1мс і додаємо в код

volatile uint16_t millis=0;
void DelayMS(uint16_t m)
{
millis=0;
while(millis<m);
}
...
void TMR1_ISR(void)
{
millis++;
...

Таким нехитрим чином ми отримаємо не залежить від частоти/наявності іншого коду функцію затримки (головне, що б таймер цокав) до 65 секунд. Для наших цілей досить з великим запасом.

Тепер дивимося на код запалювання світлодіодів. Якось негарно у перериванні робити затримки (так ще циклом), плюс купа порівнянь, які по-хорошому треба винести куди-небудь подалі. Немає, для домашнього проекту цілком собі, але для промислового — нікуди не годиться.

Давайте знову змінимо алгоритм. В чому суть? Розгляну на прикладі порту C, який цілком відданий під світлодіоди.

Молодші 3 біта у нас відповідають за кольору. Старші 3 — за перші 3 світлодіода. Подумки включимо перший світлодіод червоний, другий на зелений, а третій на «помаранчевий» (або червоний+зелений). Що буде в порту?

Перший крок, «червоний» канал
0b00101001
Другий крок, «зелений» канал
0b00110010
Третій крок, «синій» канал
0b00000100

Як бачимо, нам в принципі достатньо раз за разом відправляти в порт по байту, що б отримати той же функціонал. А формування цих байт віддамо функції setLed — вона викликається набагато рідше, ніж код в перериванні і може дозволити собі всякі розрахунки, цикли та інші надмірності.

Добре, що ми знаємо, що можемо пхати в порт З всяке — у нього 2 старших біта нікуди не підключені (якщо судити по даташіту і кодом). Але що робити з портом А, у якого нам треба міняти тільки один біт, не чіпаючи інших? Для цього можна скористатися чудовою функцією «бітове І» і «бітове АБО».

Що б було зрозуміліше, розпишу на прикладі:

0b00001010 Читаємо з порту поточне значення. Потрібний нам біт я виділив — нам треба змінити його значення. Решта випадково розставив.

Робимо «бітове АБО» (десь повинна бути 1, що б у результаті вийшла теж 1) із заздалегідь підготовленою функцією setLed маскою. Для включення біта вона повинна бути 0b00100000. Скажімо, нам треба включити біт

0b00001010 | 0b0010000 = 0b00101010

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

А тепер той самий біт скинемо з допомогою «бітового І» (і там і там має бути 1, що б на виході теж 1)

0b00101010 & 0b11011111 = 0b00001010

Начебто все логічно. Переписуємо код.

Загалом, на функцію setLed покладається завдання готувати 6 байта того, що ми будемо віддавати в порт. Міняємо led.c і led.h

// basic colors
#define RED 1 
#define GREEN 2
#define BLUE 3
#define OFF 0

extern volatile uint8_t lc[3];
extern volatile uint8_t la[3];

void setLed(uint8_t n,uint8_t c)
{
uint8_t b;

if(n==0) b=0b11110111;
if(n==1) b=0b11101111;
if(n==2) b=0b11011111;
if(c>0)
{
c--;
if(n==3)
{
la[0]=1;
la[1]=1;
la[2]=1;
la[c]=0;
}
else
{
lc[c]=lc[c]&b;
}
}
else
{ // turn off channel N
b=b^0xff;
if(n==3)
{
la[0]=1;
la[1]=1;
la[2]=1;
}
else
{
lc[0]=lc[0]|b;
lc[1]=lc[1]|b;
lc[2]=lc[2]|b;
}
}

І в обробника переривання прописуємо

LATC=lc[current_led];
_LED4_ = la[current_led];
current_led++;
if(current_led==3) current_led=0;


Погодьтеся, набагато коротшим від попереднього варіанту (правда, роботу з бітами я залишив для порту С, а порт А виявилося простіше «вручну» смикати). Разів коротше, отже, виконується швидше. А раз виконується швидше, значить загальна продуктивність контролера теж вище. Суцільний бонус з усіх боків. Правда, для отримання «білого» тепер доведеться три рази викликати setLed, але це дрібниці.

Тепер залишилося вирішити найголовніше питання: як її включати і як вимикати?

В принципі, у мене є варіант, запропонований початковими творцями машинки — кнопка на двері. Але перш ніж займатися прикладним творчістю, треба прикинути «а чи варто?

Пишемо наступний код

setLed(3, RED);
setLed(2, BLUE);
PWM3_LoadDutyValue(49*2);
TMR2_LoadPeriodRegister(49);
uint8_t c,q;
for(q=0;q<1;q++) {
setLed(0, RED);
setLed(1, OFF);
DelayMS(100);
for (c = 0; c < 10; c++) {
setLed(0, OFF);
DelayMS(50);
setLed(0, RED);
DelayMS(50);
}

setLed(1, BLUE);
setLed(0, OFF);
DelayMS(100);
for (c = 0; c < 10; c++) {
setLed(1, OFF);
DelayMS(50);
setLed(1, BLUE);
DelayMS(50);
}
}
PWM3_LoadDutyValue(0);
TMR2_LoadPeriodRegister(0);
setLed(0, OFF);
setLed(1, OFF);
setLed(2, OFF);
setLed(3, OFF);
DelayMS(10);
SLEEP();

Зверніть увагу на останню інструкцію — SLEEP() заганяє мікроконтролер у сплячку, коли все вимкнено і витягти з цього стану може тільки яке-небудь переривання. Переривань нам поки не завезли, тому просто включаю-вимикаю вручну при вимірюванні.

Підключаю мультиметр для вимірювання споживання струму. Мій видав коли светодиодики горять і динамік ниє, то споживання струму 2.97-3мА. Якщо не пілікати динаміком, то 2,85-2,9. А коли все закінчується, то 0,01 мА і менше (просто у мого мультиметра 0,01 мА нижня межа вимірювань). Різниця між тактовою частотою 1МГц і 4МГц теж мінімальна — буквально 0,2 мА.

Погодьтеся, шикарні цифри для 12 миготливих світлодіодів? Якщо б ми їх підключали «як зазвичай», то отримали б струм в 30-40мА.

Так як у мене буде 2 батарейки ААА (середня ємність у них 1000мАч), то легко можна прикинути, скільки проживе машинка.

Якщо весь час буде пищати і блимати — 1000мАч/3мА/24ч=13 днів.
А якщо весь час спати — 1000/0,1/24= 416 днів.

Зрозуміло, в реальності цифри будуть менше, так як ні одна батарейка не дозволить себе розрядити «в нуль». Але і так рік у сплячці для машинки дуже непоганий. Загалом, необхідності в окремому вимикачі немає і тому я вирішую скористатися вже приготовленої заготівлею для кнопки. Підключаю кнопку між RA4 і «мінусом», в Code Configurator включаємо цю ніжку на читання і не забуваємо включити підтягуючий резистор і генерацію переривань для цієї ніжки.

Чомусь Code Configurator забули додати код для дозволу переривань (хоча ставляться галочки) і з-за цього мені доводиться відкривати даташит на процесор і шукати, які біти за що відповідають (взагалі-то це дуже корисне заняття). Включаємо дозвіл переривань від периферії і дозволяємо підключеного до порту А4 їх генерувати.

INTCONbits.IOCIE =1;
IOCANbits.IOCAN4 =1;


І знову перевіряємо. В результаті у нас по натисненню кнопки все прокидається, пару раз блимає і засинає.

Красива мигалка вже є (код вище), а з частотами сирен мені допомогли в коментарях. Сирена швидкої допомоги: частотно-модульований сигнал трикутної форми, з мінливою у часі частотою генерації від 850 Гц до 1550 Гц. зі швидкістю 0.2 герц. Сирена міліції від 650 Гц. до 1350 Гц. І не питайте мене, як швидкість можна міряти в герцах :) Але звук виходить дуже схожим.

Тепер залишилося зібрати все напрацьоване (звук, мигалки, сон) в один шматок коду. В найкращих традиціях навчалок, я залишу це зробити вам.

Майже готове можна взяти як звичайно, у мене multik.org/pic/policecar.rar

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.