Počítání pomocí optické závory.
Optická závora se skládá z infračervené LED diody a fototranzistoru. Pokud dopadá světlo na fototranzistor, tak je tranzistor otevřený a logická úroveň na něm je nula. Je-li světlo přerušeno, fototranzistor je zavřen a pomocí pull-up rezistoru (interní v Pico) je logická úroveň na něm jedna.
Optická závora se používá třeba k počítání otáček motoru nebo k zjištění toho, zda určitá součást dosáhla dané polohy. My ji budeme používat k počítání otáček motoru.

Schéma zapojení

Optická závora má tři vodiče, jeden vstupní k IR diodě, druhý výstupní z fototranzistoru a třetí je společná zem. Rezistor R1 (470 Ohm) omezuje proud tekoucí fotodiodou na přibližně 5 mA.
Test zapojení vodičů k optické závoře můžeme udělat snadno pomocí multimetru a měření diod. Pokud na dvou vodičích naměříme úbytek napětí okolo 1 V (IR dioda je v propustném směru), potom červený drát z multimetru vede k anodě fotodiody a černý drát je společná zem. Třetí drát je výstup z fototranzistoru.

Program
Program je velmi jednoduchý. Hlídáme událost náběžná hrana (zastínění fototranzistoru) na vstupním GPIO3 pinu, což znamená že závora byla přerušena. Pomocí pinu GPIO2 můžeme optickou závoru zapínat nebo vypínat. Pokud chceme, aby byla závora zapnutá neustále, přemístíme vodič z GPIO2 pinu na 3.3V (pin 36) a můžeme odstranit kód nastavení výstupního pinu.
tzavora.c
/* Počitadlo s optickou závorou
* tzavora.c
* (c) Jirka Chráska 2025, <jirka@lixis.cz>
*/
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/structs/iobank0.h"
#include <stdio.h>
#define OUT_PIN 2
#define IN_PIN 3
uint32_t pocitadlo = 0;
// přehled událostí na pinu
uint32_t gpio_get_events(uint gpio)
{
int32_t mask = 0xF << 4 * ( gpio % 8 );
return (iobank0_hw->intr[gpio / 8] & mask) >> 4 * ( gpio % 8 );
}
// vymazání událostí na pinu
void gpio_clear_events(uint gpio, uint32_t events)
{
gpio_acknowledge_irq(gpio, events);
}
int main()
{
stdio_init_all();
sleep_ms(1500);
printf("Pocitadlo = %d\n", pocitadlo);
// nastaveni vstupního pinu (fototranzistor)
gpio_set_function(IN_PIN, GPIO_FUNC_SIO);
gpio_set_dir(IN_PIN,false);
gpio_pull_up(IN_PIN); // pull-up rezistor musí být nastaven
// nastavení výstupního pinu - zapnutí závory
gpio_set_function(OUT_PIN, GPIO_FUNC_SIO);
gpio_set_dir(OUT_PIN, true);
gpio_put(OUT_PIN,true); // spouštíme počítadlo
gpio_clear_events(IN_PIN, GPIO_IRQ_EDGE_RISE);
while (true) {
uint32_t p = pocitadlo;
if( (gpio_get_events(IN_PIN) & GPIO_IRQ_EDGE_RISE) ) {
pocitadlo++;
gpio_clear_events(IN_PIN, GPIO_IRQ_EDGE_RISE);
}
if( pocitadlo > p ) {
printf("Pocitadlo = %d\n",pocitadlo);
p = pocitadlo;
}
}
}
src/CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(tzavora C CXX ASM)
pico_sdk_init()
# pridani zdrojovych kodu
add_executable(tzavora
tzavora.c
)
# enable usb output, disable uart output
pico_enable_stdio_usb(tzavora 1)
pico_enable_stdio_uart(tzavora 0)
# create map/bin/hex/uf2 file etc.
pico_add_extra_outputs(tzavora)
target_link_libraries(tzavora pico_stdlib hardware_gpio)
Abychom mohli počítat otáčky motoru, bude potřeba přesunout hlídání náběžné hrany na vstupním pinu na druhé jádro procesoru, abchom mohli na první jádro procesoru osvobodit od nekonečné smyčky a místo toho na něm mohli provozovat řídící program, což bude v další verzi programu.
Nezávislé počítadlo
Problém prvního programu je v tom, že počítadlo neustále testuje náběžnou hranu a Pico tak nemá moc času na jiné věci.
Necháme druhé jádro (core1) procerosu RP2040 aby se zabývalo testováním náběžné hrany a zapisovalo impulsy do globální proměnné pocitadlo1.
Na prvním jádru (core0) potom může spokojeně běžet náš program a číst si hodnotu počitadla, kdy sám potřebuje.
Globální proměnnou volatile uint32_t pocitadlo1 musíme zabezpečit mutexem (výhradním přístupem) pocitadlo1_mutex.
To znamená, že core0 nemůže číst hodnotu pocitadlo1 v ten samý okamžik, když do proměnné pocitadlo1 zapisuje core1.
Kdybychom to neudělali a stalo by se že ve stejný okamžik bude core0 číst a core1 zapisovat, tak core0 může přečíst nesmysly.
Slovo volatile v definici proměnné pocitadlo1 říká kompilátoru tolik, že proměnná se může měnit kdykoliv a tudíž ji program musí vždy přečíst znovu a nespoléhat na nějakou zapamatovanou hodnotu v rámci optimalizace.
auto_init_mutex(pocitadlo1_mutex);
Chce-li core1 zapisovat do proměnné pocitadlo1, musí nejprve získat přístup k proměné voláním funkce mutex_try_enter().
Získá-li mutex, zvětší hodnotu proměnné pocitadlo1 o jedničku a potom mutex uvolní voláním funkce mutex_exit().
if( mutex_try_enter(&pocitadlo1_mutex,&owner) ) {
pocitadlo1++;
mutex_exit(&pocitadlo1_mutex);
}
Obdobně postupuje core0. Chce-li číst hodnotu proměnné pocitadlo1, musí nejprve získat přístup voláním funkce mutex_try_enter().
Když získá mutex, přečte si hodnotu a mutex potom uvolní voláním funkce mutex_exit().
| Mutexy fungují pěkně, pokud po získání mutexu ho zase rychle uvolníme. Pak se nám nestane, že by obě jádra čekala na mutex a program se takzvaně "kousnul". |
if(mutex_try_enter(&pocitadlo1_mutex, &owner) ) {
p = pocitadlo1;
mutex_exit( &pocitadlo1_mutex );
} else {
printf("Zamčený mutex\n");
}
Funkci, která má běžet na druhém jádru spouštíme takto:
multicore_launch_core1(pocitadlo);
Celý program src2/zavora_multicore.c
/* Počitadlo s optickou závorou na core1 a s mutexy
* zavora_multicore.c
* (c) Jirka Chráska 2025, <jirka@lixis.cz>
*/
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/structs/iobank0.h"
#include <stdio.h>
#include "pico/multicore.h"
#define OUT_PIN 2
#define IN_PIN 3
#define LED_PIN 25
volatile uint32_t pocitadlo1 = 0;
auto_init_mutex(pocitadlo1_mutex);
// přehled událostí na pinu
uint32_t gpio_get_events(uint gpio)
{
int32_t mask = 0xF << 4 * ( gpio % 8 );
return (iobank0_hw->intr[gpio / 8] & mask) >> 4 * ( gpio % 8 );
}
// vymazání událostí na pinu
void gpio_clear_events(uint gpio, uint32_t events)
{
gpio_acknowledge_irq(gpio, events);
}
// funkce pro pocitadlo pobezi na core1
void pocitadlo()
{
uint32_t owner;
uint32_t p = 0;
gpio_clear_events(IN_PIN, GPIO_IRQ_EDGE_RISE);
while (true) {
if( (gpio_get_events(IN_PIN) & GPIO_IRQ_EDGE_RISE) ) {
p++;
gpio_clear_events(IN_PIN, GPIO_IRQ_EDGE_RISE);
if( mutex_try_enter(&pocitadlo1_mutex,&owner) ) {
pocitadlo1 = p;
mutex_exit(&pocitadlo1_mutex);
}
}
}
}
int main()
{
bool led_stav = true;
uint32_t p;
uint32_t owner;
stdio_init_all();
sleep_ms(1500);
printf("Pocitadlo = %d\n", pocitadlo);
// nastaveni vstupního pinu (fototranzistor)
gpio_set_function(IN_PIN, GPIO_FUNC_SIO);
gpio_set_dir(IN_PIN,false);
gpio_pull_up(IN_PIN); // pull-up rezistor musí být nastaven
// nastavení výstupního pinu - zapnutí závory
gpio_set_function(OUT_PIN, GPIO_FUNC_SIO);
gpio_set_dir(OUT_PIN, true);
gpio_put(OUT_PIN,true);
// nastaveni LED
gpio_set_function(LED_PIN, GPIO_FUNC_SIO);
gpio_set_dir(LED_PIN, true);
gpio_put(LED_PIN,led_stav);
// spouštíme počítadlo na druhém jádru (core1)
multicore_launch_core1(pocitadlo);
// tady si můžeme dělat co chceme
// po jedné sekundě vypisujeme stav počítadla a blikáme LEDkou
while( true ) {
// pokud chceme cist pocitadlo1, musime ziskat mutex
if(mutex_try_enter(&pocitadlo1_mutex, &owner) ) {
p = pocitadlo1;
mutex_exit( &pocitadlo1_mutex );
} else {
printf("Zamčený mutex\n");
}
printf("Stav pocitadla je: %d\n",p);
gpio_put(LED_PIN, led_stav);
led_stav = led_stav ? false : true;
sleep_ms(1000);
}
return 0;
}
src2/CMakeLists.txt pro multicore verzi
cmake_minimum_required(VERSION 3.12)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(tzavora2 C CXX ASM)
pico_sdk_init()
# pridani zdrojovych kodu
add_executable(tzavora2
zavora_multicore.c
)
# enable usb output, disable uart output
pico_enable_stdio_usb(tzavora2 1)
pico_enable_stdio_uart(tzavora2 0)
# create map/bin/hex/uf2 file etc.
pico_add_extra_outputs(tzavora2)
target_link_libraries(tzavora2 pico_stdlib hardware_gpio pico_multicore)
Použití kódu pro odrazové čidlo
Kód lze beze změn pro odrazové čidlo na obrázcích.


Potenciometrem nastavíme citlivost čidla.
| čidlo | RPi Pico |
|---|---|
VCC |
3.3V |
GND |
0V zem |
D0 |
GPIO2 |
A0 |
GPIO3 nebo nic |
Zdroje a odkazy
pico_e-paper — rychloměr na kolo.