Творчі пориви в напрямку
створення електронних пристроїв змушують зосереджувати увагу на
мікроконтролерах та програмуванні. Для
більш якісного засвоєння матеріалу вирішив писати такий собі конспект. Це не
буде інструкцією, закликом до чого небудь чи хизування. Має бути просто
конспект зі своїми спостереженнями і висновками, які мій мозок вивів на основі
опрацьованої інформації з інтернету, або іншими словами результат самонавчання.
Не факт, правда, що отримана інформація була вірною чи висновки зроблені з неї є
вірними.
Загалом для роботи з мікроконтролерами
потрібно знати 3 предмета:
-
англійська мова, благо є гугл;
-
програмування, є повно уроків в
інтеренеті;
-
радіоелектроніка, гадаю свої 20 років
дружби з паяльником можна врахувати в орієнтовне знайомство з предметом.
До списку не входить багато чого
з таких речей як: користування комп’ютером, читання, писання, використання, налаштовування програмних та апаратних засобів розробки.
Для роботи з сімейством STM8 обраний
набір STVD +Cosmic C compiler.
Програми робочі, безкоштовні, без обмежень в кількості коду. ST Visual develop https://www.st.com/en/development-tools/stvd-stm8.html - фірмове IDE
(інтегроване середовище розробки) від STM, але в ньому
можна працювати лиш з Асемблером, тому в якості зовнішнього модуля
підключається Cosmic C compiler, щоб
мати змогу писати-компілювати програми на мові Сі. Cosmic надає
безкоштовну ліцензію на використання свого програмного забезпечення для
мікроконтролерів STM8 після
реєстрації на їх сайті http://www.cosmicsoftware.com/, також там вказано, що всі продукти Cosmic включають один
рік технічної підтримки та оновлень, як буде після року користування то
побачимо...
В якості піддослідних чіпів
обрались STM8S103F3P6 на
платі та STM8S001, STM8L001 в
якості дрібних і дешевих восьмилапок.
В мікроконтролері є така штука як
регістри і якщо ми хочемо чогось від контролера то потрібно спілкуватись з
регістрами: записувати або читати дані,
які знаходяться в комірках(розрядах) регістрів. Коли регістр 8-ми бітний в
ньому є 8 комірок пам’яті котрі можуть записувати 1 біт інформації тобто 0 або
1. Відповідно для запису – читання бітів регістра використовуються логічні операції
з бітами. Бітові операції в мові програмування Сі потрібно знати, як 2+2 з
першого класу школи. Особисто мені не завадить нагадування, що є що і куди саме
його впихнути потрібно, тому алгебра логіки та бітові операції в Сі:
https://folkprog.net/pobitovie-operatsii-v-c/
https://ph0en1x.net/81-howto-work-with-ports-register-bits-in-microcontroller.html
https://pikabu.ru/story/nemnogo_o_portakh_i_bitovyikh_operatsiyakh_4316272
http://www.atmega8.ru/wiki/view/doc.20.html
https://diodov.net/logicheskie-operatsii-programmirovanie-mikrokontrollerov-avr/
https://diodov.net/pobitovye-operatsii-programmirovanie-mikrokontrollerov-avr-na-c/
https://habr.com/ru/post/406889/
Символ |
назва |
дія (приклад) |
& |
Побітове логічне «І» («AND», множення) |
010 & 101
результат 000 100 & 101 результат 100 Побітова операція логічного «І»
відбувається з кожним розрядом (кожною цифрою) чисел відповідно таблиці істинності Х У & 0 0 0 0 1 0 1 0 0 1 1 1 |
&& |
Логічне «І», “AND” |
Використовується для перевірки умови істинності чи
хиби If( 101 && 010 ) – вираз дасть значення
істини і код оператора умови if буде виконано.
x && y – буде
істинним допоки один із операндів не стане 0, при чому кажуть якщо перший з
операндів стане 0 то другий і наступні опранди до уваги не беруться і
перевірятись не будуть в зв’язку з відсутнісю необхідності відповідно таблиці
істинності логічного «І», тобто весь вираз прийме стане хибним. |
& |
отримання
адреси |
y = &x –така операція призначає адресу комірки
пам’яті (номер квартири) де зберігається значення х (гроші) змінній Y,
тобто Y тепер знає
номер квартири де лежать гроші і при зверненні до у залюбки каже його(номер). |
* |
звернення за адресою |
z=*y –використовується
для доступу до грошей з квартири номер якої знає у. Враховуючи попередній
приклад вийде, що зет приймає значення ікс
через звернення до ігрека z == x. |
| |
побітове логічне
«АБО», “OR” (додавання) |
101 | 010 буде 111 згідно таблиці істинності х у | 0 0 0 0 1 1 1 0 1 1 1 1 |
|| |
логічне «АБО», “OR” |
001 || 000 дасть результаті значення істини, якщо хоча б один з
операндів не 0 то вираз буде істиною згідно таблиці істинності для логічного
«АБО». Використовується у if(001||000)… |
! |
логічне заперечення «НІ», “NOT” (інверсія) |
!0 дасть в результаті 1 !1 дасть в результаті 0 If(1!=0 ) – вираз істинний і читається, як -один
не дорівнює нулю. Операція використовується в порівняннях як і
&&, ||. |
~ |
побітове логічне заперечення «НІ», “NOT” |
~
010 буде дорівнювати 101. Дана операція
- унарна, така, що проводиться
тільки над одним числом. |
^ |
побітове виключне «АБО»,“XOR” |
х у ^ Одиниця
в результаті виходить тільки тоді, 0 0 0
коли значення бітів в тих само розрядах 1 0 1
чисел різне. Тобто 1 «АБО» 0
дасть 1 в 0 1 1
результаті, а 1«АБО» 1 та 0
«АБО» 0 дадуть 1 1 0
логічну хибу чи просто 0. |
<< |
логічний зсув вліво |
010<<1 дасть 100 010<<3 дасть 000 Операція зміщує вліво біти лівої частини (числа 010) на кількість
позицій вказану справа виразу. 0b00000000 |
(1<<5) – тут же одиниця встановлюється на 5-ту позицію 0b00000000 | 0b00100000
== 0b00100000 0b00000000 |
(1<<0) == 0b00000001 Біти рахуються з 0 !!!! Зсув числа вліво рівноцінно множенню на 2, на
скільки позицій зсунули, на стільки двійок і множити: 2 == 0b00000010 0b00000010 << 2 == 0b00001000 == 2 *2 *2 == 8 0b00000010 << 5 == 0b01000000
== 2*2*2*2*2*2 == 64 |
>> |
логічний зсув вправо |
010>>1 001 010>>2 000 Аналогічно зсуву вліво тільки в іншу сторону і при
зсуві вправо відбувається ділення на 2 64 == 0b01000000 0b01000000
>> 4 == 0b00000100
== 64/2/2/2/2 == 4 |
|
|
|
Оператори порівняння >, <, >=, <=, ==, != разом з логічними операторами !, &&, || в якості результату завжди дають 0 або
1, істину або хибу, чисел не утворюють!!!
Бітові оператори &, |, ~, ^,
<<, >> в результаті використання можуть утворювати числа відповідно
до своєї дії, тобто числа відмінні від просто логічної істини чи логічної хиби.
Це звісно не вся інформація по
операціям в Сі, тому читаємо, наприклад :
https://narodstream.ru/programmirovanie_n_c/
«ВВЕДЕНИЕ В ЯЗЫК СИ» (c) Курсков С.Ю., составление, 2006-2012
Б.В. Керниган, Д.М. Ричи. «Язык С».
Оскільки Stm8 – мікроконтролери 8-ми бітні, то й регістри мають 8
бітів, або 8 комірок, є звісно виключення коли 2 8-ми бітні регістри
поєднуються в один 16-ти бітний, як то лічильники в таймерах, але контролер від
цього 16-ти бітним не стає.
Регістри,
їх біти описані в даташИтаманУала референс мануалі, а те які саме з них
реалізовані в конкретному контролері вказано вже в даташиті на контролер.
Наприклад регістр DDR (data direction register-регістр напрямку даних) має 8 бітів кожен з яких відповідає лінії порта вводу-виводу даних. Портом можна, також, називати лапку (вивід, контакт) контролера, але краще не треба. Н випадку різних корпусів, різних серій, не всі порти можуть мати всі фізичні виводи, тобто порт B (PB) є весь в наявності, а виводи має тільки PB4 та PB5. В документах від СТМ є рекомендації переключати віртуальні виводи, які не використовуються(не мають фізичного виводу, лапки) в режим виводу даних (output), конфігурації ПУШ-ПУЛЛ, та подати низький рівень сигналу на них. Цитата з даташити на STM8S001J3:
«The PA2, PB0, PB1, PB2, PB3, PB6, PB7, PC1, PC2, PC7, PD0, PD2, PD4, PD7, PE5 and PF4 GPIOs should be configured after device reset in output push-pull mode with output
low-state to reduce the device’s consumption and to improve its EMC immunity. The GPIOs
mentioned above are not connected to pins, and they are in input-floating mode
after a
device reset»
За
допомогою регістра ДДР можна сконфігурувати лапку порта на роботу входом чи
виходом. Тут же на допомогу ДДР приходять регістри CR1 та CR2. При роботі
входом лапка приймає високоімпедансний стан плаваючий, або притягнутий до плюса
живлення, коли ж призначити її виходом то додатково потрібно вказати чи вихід
буде відкритим колектором чи буде пуш пул.
Регістри
можна представити, як дисковий кодовий замок в якому першим диском ми вибираємо
регістр, іншими 8 –ма дисками(або 16-ма, чи 32-ма) вибираємо код, тобто, функціонал який нам
потрібен.
Писати
в регістр можна так
PB_DDR = 0b11111111
//всі лінії порта Д встановлено виходом (всі 8 лапок)
PB_DDR =0xFF // результат від такого запису
буде ідентичним попередньому.
Наскільки я зрозумів простим
присвоюванням (опрація «=») можна конфігурувати порт на початку програми, в
процесі ж роботи програми, коли потрібно записати якісь біти в регістр порта
використовуються логічні операції та логічний зсув бітів
PB_DDR |=
(1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)
Так як нумерація бітів
починається з нульового біту то відповідно для нього використовується зсув
вліво на нульове положення. Результат
такої операції буде ідентичний попереднім двом.
Хоча на перший погляд і не зрозуміло, чому б
не писати через присвоєння завжди: PB_DDR= 0b00000001. Кажуть, що у
випадку логічних операцій з конкретними бітами інші біти регістра не змінюються
тому користуються таким способом. А ще, я собі думаю, що в процесі виконання
програми не можна знати точно де яке значення знаходиться 0 чи 1 тому й
використовується зсув разом з логічними операціями для отримання, так би
мовити, гарантованого результату незалежно від вмісту регістра.
Отож бітова магія!
Записуємо
одиничку:
PB_DDR = 0b00000000; //
початкове значення порта, після скидання контролера всі виводи (лапки)
// порта
налаштовані, як входи.
PB_DDR = 0b00100000; // лапка PB5 налаштована на вихід.
PB_DDR = 0x20; // те саме.
PB_DDR = PB_DDR| (1<<5); //знову те саме тільки за допомогою логічного
«АБО» та логічного зсуву
//одинички вліво на позицію з номером
5 (012345)
PB_DDR |= (1<<5); // те саме, але скороченим
записом.
Записуємо
нулик (стираємо одиничку обнуляємо ):
PB_DDR = 0b11111111; // весь
порт налаштовано виходом.
PB_DDR
& = ~ (1<<7); // скидаємо
в 0 сьомий біт (вивід PB7 стає
входом).
PB_DDR
== 0b01111111; // результат
попередньої дії.
Логіка тут наступна: (1<<7) – робимо зсув одиниці вліво на
сьому позицію. Стоп, нам же треба НУЛЬ,
добре чіпляємо хвостика логічного заперечення
~ (1<<7) і отримуємо НУЛЬ,
але в нас на 7-му місці ОДИНИЦЯ і якщо один
додати нуль отримаємо ОДИНИЦЮ, а це означає необхідність виконати дію
множення & = і в результаті отримаємо бажаний НУЛЬ в бажаномі місці.
Всі дії описано відповідно до пріоритету
виконання операцій в Сі, спочатку круглі дужки, потім інверсія, потім складений
оператор множення з присвоєнням.
Зміна
стану біта на протилежний:
PB_DDR=0b00000000;
//початковий стан.
PB_DDR^=
(1<<5); //
змінюємо 5-й біт порта на протилежний.
PB_DDR==0b00100000; // результат операції.
PB_DDR^=
(1<<5); // повторно
змінюємо.
PB_DDR==0b00000000;
// результат повторної зміни.
За пріоритетом спершу виконуються дужки, потім
побітове «АБО» разом з присвоюванням. Коли змінюється нуль на одиницю то має
бути типу такої дії 0^1 що в результаті дасть 1, яка в свою чергу має стати на
5-ту позицію (5-й розряд). У
випадку наявної одиниці 1^1 буде 0 який
і запишеться в 5-й розряд. Все чинно-ладно по таблиці істинності побітового виключного
«АБО».
Дані дії треба прийняти, як є. Незважаючи на
деяку нелогічність, яка може ЗДАТИСЬ з пріоритетами, діями та результатами.
Для зміни декількох, або всіх бітів, необхідно
перечислити в правій частині операції всі зсуви потрібних бітів через побітове «АБО»
PB_DDR |=
(1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)
PB_DDR &=
(1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)
PB_DDR ^=
(1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)
Потрібно не тільки писати, змінювати, а ще ж читати біти в розрядах портів.
В STM8 є свої регістри які потрібно прочитати, щоб отримати
інформацію про стан входів порта вводу-виводу даних. Читати потрібно регістри ODR (output data
register, Px_ODR) та
IDR (input data register, Px_IDR). З назви видно, що регістр
ODR потрібен коли пін працює, як вихід, а IDR
коли пін є входом. При цьому в ODR можна писати і читати з нього дані (зручна штука коли,
наприклад, програма щось виконала, але результат невідомий наперед, то його
можна просто прочитати з регістра порта і зробити щось чи не робити, як то в генераторі
випадкових чисел щось випало і треба зробити інтерпретацію вихідних даних, дати
відповідь), а регістр IDR тільки читати, що в свою чергу дуже навіть логічно )).
Як пишеться тут: https://diodov.net/pobitovye-operatsii-programmirovanie-mikrokontrollerov-avr-na-c/
https://obanracer.ru/chtenie-portov-avr-v-si-izuchenie-sistemy-komand-mikrokontrollera-avr.html
Перевірка,
читання бітів в регістрі проводиться з
операторами if-else, while.
Перевірка
на 0:
if (0== (PB_ODR&( 1<<5))) //порівнюємо 0 із п’ятим розрядом порта B, який в свою чергу
// перевіряється на 0
зсувом 1 на 5-ту позицію та побітовим «І» з
//значенням, яке знаходиться в регістрі PB_ODR. Відповідно коли
//там 1 то й результатом лог«І» буде одиниця, а коли одиницю
//прирівняєм з 0 то отримаємо логічну брехню (хибу).
if (~ (PB_ODR&( 1<<5))) //інша форма
запису того самого виразу.
{
Some code // якийсь код
}
else{
Other code //
інший якийсь код
}
Особисте спостереження на основі користування ARDUINO при використанні if дуже доречним буде додавання else, для того, щоб програма випадково не потрапила в
«незрозумілий стан» і подальший код гарненько виконувався.
Перевірка на 1:
if (0!= (PB_ODR&( 1<<5)))
{
Some code // якийсь код
}
else{
Other code //
інший якийсь код
}
if (PB_ODR&( 1<<5)) //
інша форма
запису того самого виразу.
Все те саме окрім порівняння 0!=, яке читається
-нуль не дорівнює. Коли нуль не дорівнює одиниці значення істинне і виконується
код «Some code», коли ж в регістрі в п’ятому розряді знаходиться 0 то
операція & в (PB_ODR&( 1<<5)) дасть
0, а 0!=0 це вже хибне ствердження, а тому буде виконуватись код «Other code».
Очікування
0:
while (PB_IDR & (1<<0)) //як видно операція
порівняння не змінилась,
//
цикл while буде виконувати Some code до
тих пір, допоки на PB0
//буде
одиниця, як тільки порт отримає 0, цикл завершиться, якщо
//програма має ще
код після while то продовжиться його
виконання.
{
Some code
}
Очікування
1:
while (!(PB_IDR & (1<<0))) //while буде крутитись, а відповідно виконувати Some code допоки на PB0 буде 0.
{
Some code
}
while (~(PB_IDR & (1<<0))) //те саме тільки для порівняння використовується операція побітового заперечення, тобто
//поки PB0 буде 0 до тих пір вираз буде істинним, а
відповідно цикл буде виконувати Some code.
{
Some code
}
Надалі не
завадить література:
«Основы микропроцессорной техники: Микроконтроллеры STM8» С.Н.
Торгаев. Томск 2014
«Начало работы с микроконтроллерами STM8» Матюшов Н.В.
RM0016, Reference manual STM8S Series and
STM8AF Series 8-bit microcontrollers разом з даташитами на
конкретний чіп.
Achima Abelard, ваш коментар надзвичайно важливий для нас. Не соромтесь видаляти рекламну контактну інформацію разом із машинним перекладом та й загалом цілим коментарем, який ніяким боком не відноситься до теми написаного. Щиро вдячний MegaSkilledTechnician!
ВідповістиВидалити