Предлагаю продолжить тему про этот девайс и найденные программные и технические решения.
Кто реализовал подключение энкодера?
Не хочу повторять читать доку, что мешает на пин энкодер повесить прерывание и в нем читать второй пин энкодера?
В примерах есть реализация на PIO
Мы ищем более лёгкие пути, нашёл реализацию для С++ тут, чуток адаптировал под IDE:
/*
This code is to read and debounce a KY-040 rotary encoder (maybe others) using interrupts on the Raspbery Pi PICO.
This code should also work on other processors with a few changes to the hardware specific parts of the code.
Here is a crude represntation of the output of the encoder
CW rotation
______ ______
F1____| Phase A
_________ ______
F2_____| Phase B
Notice that Phase A falling edge (F1 in the diagram) occurs before Phase B (F2) for CW rotation
CCW Rotation
_________ ______
F2____| Phase A
______ ______
F1____| Phase B
Notice that Phase B falling (F1 in the diagram) edge occurs before Phase A (F2)for CCW rotation
This code works by watching for the first falling edge (F1) and setting cw_fall TRUE if Phase A is the first falling edge
and setting ccw_fall TRUE is phase B is the first falling edge. After one of these (cw_fall or ccw_fall) have been set
the code watches to see which phase is next to trigger the interrupt (F2). After the second (F2) interrupt, the code can detrmine the
direction that the encoder has been turned.
cw A leads B
ccw B leads A
this may change depending on your wiring and what encoder is used
The code does not handle the encoder push button switch.
This can be implemented by adding additional code to hangle the switch interrupt
if (gpio == ENC_SW)
{
//handle switch event here
}
*/
// https://www.reddit.com/r/raspberrypipico/comments/pacarb/sharing_some_c_code_to_read_a_rotary_encoder/
/**BEGIN CODE HERE****************************************************************************************************/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/gpio.h"
#include "stdlib.h"
/*Encoder GPIO*/
// GPIO 10 is Encoder phase A,
// GPIO 11 is Encoder phase B,
// GPIO 12 is the encoder push botton switch.
// change these as needed
#define ENC_A 10
#define ENC_B 11
#define ENC_SW 12
/* Encoder Callback*/
/*
"LEVEL_LOW", // 0x1
"LEVEL_HIGH", // 0x2
"EDGE_FALL", // 0x4
"EDGE_RISE" // 0x8
*/
void encoder_callback(uint gpio, uint32_t events)
{
uint32_t gpio_state = 0;
gpio_state = (gpio_get_all() >> 10) & 0b0111; // get all GPIO them mask out all but bits 10, 11, 12
// This will need to change to match which GPIO pins are being used.
static bool ccw_fall = 0; //bool used when falling edge is triggered
static bool cw_fall = 0;
uint8_t enc_value = 0;
enc_value = (gpio_state & 0x03);
if (gpio == ENC_A)
{
if ((!cw_fall) && (enc_value == 0b10)) // cw_fall is set to TRUE when phase A interrupt is triggered
cw_fall = 1;
if ((ccw_fall) && (enc_value == 0b00)) // if ccw_fall is already set to true from a previous B phase trigger, the ccw event will be triggered
{
cw_fall = 0;
ccw_fall = 0;
//do something here, for now it is just printing out CW or CCW
Serial.printf("CCW \r\n");
}
}
if (gpio == ENC_B)
{
if ((!ccw_fall) && (enc_value == 0b01)) //ccw leading edge is true
ccw_fall = 1;
if ((cw_fall) && (enc_value == 0b00)) //cw trigger
{
cw_fall = 0;
ccw_fall = 0;
//do something here, for now it is just printing out CW or CCW
Serial.printf("CW \r\n");
}
}
}
void setup() {
Serial.begin(115200);
pinMode(ENC_SW,INPUT_PULLUP);
pinMode(ENC_A,INPUT_PULLUP);
pinMode(ENC_B,INPUT_PULLUP);
/*
// GPIO Setup for Encoder
gpio_init(ENC_SW); //Initialise a GPIO for (enabled I/O and set func to GPIO_FUNC_SIO)
gpio_set_dir(ENC_SW, GPIO_IN);
gpio_disable_pulls(ENC_SW);
gpio_init(ENC_A);
gpio_set_dir(ENC_A, GPIO_IN);
gpio_disable_pulls(ENC_A);
gpio_init(ENC_B);
gpio_set_dir(ENC_B, GPIO_IN);
gpio_disable_pulls(ENC_B);
*/
gpio_set_irq_enabled_with_callback(ENC_SW, GPIO_IRQ_EDGE_FALL, true, &encoder_callback);
gpio_set_irq_enabled(ENC_A, GPIO_IRQ_EDGE_FALL, true);
gpio_set_irq_enabled(ENC_B, GPIO_IRQ_EDGE_FALL, true);
sleep_ms(500);
// stdio_init_all();
}
void loop() {
while (1)
{
// do something here forever
sleep_ms(1000);
}
}
работает?
@b707 да!
Кстати, взял используемую мной библиотеку и если немножко кода добавить для поддержки RP2040 в сам скетч, не меняя ничего в библиотеке, то тоже работает, выглядит код примерно так:
Скетч тут
// -----
// InterruptRotator.ino - Example for the RotaryEncoder library.
// This class is implemented for use with the Arduino environment.
//
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD 3-Clause License. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// -----
// 18.01.2014 created by Matthias Hertel
// 04.02.2021 conditions and settings added for ESP8266
// 03.07.2022 avoid ESP8266 compiler warnings.
// 03.07.2022 encoder instance not static.
// -----
// This example checks the state of the rotary encoder using interrupts and in the loop() function.
// The current position and direction is printed on output when changed.
// Hardware setup:
// Attach a rotary encoder with output pins to
// * 2 and 3 on Arduino UNO. (supported by attachInterrupt)
// * A2 and A3 can be used when directly using the ISR interrupts, see comments below.
// * D5 and D6 on ESP8266 board (e.g. NodeMCU).
// Swap the pins when direction is detected wrong.
// The common contact should be attached to ground.
//
// Hints for using attachinterrupt see https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
#include <Arduino.h>
#include <RotaryEncoder.h>
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY)
// Example for Arduino UNO with input signals on pin 2 and 3
#define PIN_IN1 A2
#define PIN_IN2 A3
#elif defined(ESP8266)
// Example for ESP8266 NodeMCU with input signals on pin D5 and D6
#define PIN_IN1 D5
#define PIN_IN2 D6
#elif ( defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || \
defined(ARDUINO_GENERIC_RP2040) )
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/gpio.h"
#include "stdlib.h"
#define PIN_IN1 10
#define PIN_IN2 11
#endif
// A pointer to the dynamic created rotary encoder instance.
// This will be done in setup()
RotaryEncoder *encoder = nullptr;
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY)
// This interrupt routine will be called on any change of one of the input signals
void checkPosition()
{
encoder->tick(); // just call tick() to check the state.
}
#elif defined(ESP8266)
/**
@brief The interrupt service routine will be called on any change of one of the input signals.
*/
IRAM_ATTR void checkPosition()
{
encoder->tick(); // just call tick() to check the state.
}
#elif ( defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || \
defined(ARDUINO_GENERIC_RP2040) )
void checkPosition(uint gpio, uint32_t events)
{
encoder->tick(); // just call tick() to check the state.
}
#endif
void setup()
{
Serial.begin(115200);
while (!Serial)
;
Serial.println("InterruptRotator example for the RotaryEncoder library.");
// setup the rotary encoder functionality
// use FOUR3 mode when PIN_IN1, PIN_IN2 signals are always HIGH in latch position.
encoder = new RotaryEncoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::FOUR3);
// use FOUR0 mode when PIN_IN1, PIN_IN2 signals are always LOW in latch position.
// encoder = new RotaryEncoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::FOUR0);
// use TWO03 mode when PIN_IN1, PIN_IN2 signals are both LOW or HIGH in latch position.
//encoder = new RotaryEncoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
// register interrupt routine
#if ( defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || \
defined(ARDUINO_GENERIC_RP2040) )
pinMode(PIN_IN1, INPUT_PULLUP);
pinMode(PIN_IN2, INPUT_PULLUP);
gpio_set_irq_enabled_with_callback(PIN_IN1, GPIO_IRQ_EDGE_FALL, true, &checkPosition);
gpio_set_irq_enabled(PIN_IN2, GPIO_IRQ_EDGE_FALL, true);
sleep_ms(500);
#else
attachInterrupt(digitalPinToInterrupt(PIN_IN1), checkPosition, CHANGE);
attachInterrupt(digitalPinToInterrupt(PIN_IN2), checkPosition, CHANGE);
#endif
} // setup()
// Read the current position of the encoder and print out when changed.
void loop()
{
static int pos = 0;
encoder->tick(); // just call tick() to check the state.
int newPos = encoder->getPosition();
if (pos != newPos) {
Serial.print("pos:");
Serial.print(newPos);
Serial.print(" dir:");
Serial.println((int)(encoder->getDirection()));
pos = newPos;
} // if
} // loop ()
// To use other pins with Arduino UNO you can also use the ISR directly.
// Here is some code for A2 and A3 using ATMega168 ff. specific registers.
// Setup flags to activate the ISR PCINT1.
// You may have to modify the next 2 lines if using other pins than A2 and A3
// PCICR |= (1 << PCIE1); // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
// PCMSK1 |= (1 << PCINT10) | (1 << PCINT11); // This enables the interrupt for pin 2 and 3 of Port C.
// The Interrupt Service Routine for Pin Change Interrupt 1
// This routine will only be called on any signal change on A2 and A3.
// ISR(PCINT1_vect) {
// encoder->tick(); // just call tick() to check the state.
// }
// The End
Что-то не разберусь как спрятать под спойлер
Этот скетч можно переделать в библиотеку, то, что для энкодера надо использовать три последовательных пина не сильно большое ограничение,надеюсь.
Скетч тут
//28.10.22
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/gpio.h"
#include "stdlib.h"
/*Encoder GPIO*/
// GPIO 10 is Encoder phase A,
// GPIO 11 is Encoder phase B,
// GPIO 12 is the encoder push botton switch.
// change these as needed
#define ENC_A 10
#define ENC_B 11
#define ENC_SW 12
/* Encoder Callback*/
/*
"LEVEL_LOW", // 0x1
"LEVEL_HIGH", // 0x2
"EDGE_FALL", // 0x4
"EDGE_RISE" // 0x8
*/
void encoder_callback(uint gpio, uint32_t events)
{
uint32_t gpio_state = 0;
gpio_state = (gpio_get_all() >> 10) & 0b0111; // get all GPIO them mask out all but bits 10, 11, 12
// This will need to change to match which GPIO pins are being used.
static bool ccw_fall = 0; //bool used when falling edge is triggered
static bool cw_fall = 0;
uint8_t enc_value = 0;
enc_value = (gpio_state & 0x03);
static uint32_t old_m = 0;
static bool st_button = false;
static int32_t count = 0;
if (gpio == ENC_SW)
{
if (millis() - old_m >= 180 ) {
old_m = millis();
Serial.printf("SW is Click \r\n");
} else {
old_m = millis();
}
}
if (gpio == ENC_A)
{
if ((!cw_fall) && (enc_value == 0b10)) // cw_fall is set to TRUE when phase A interrupt is triggered
cw_fall = 1;
if ((ccw_fall) && (enc_value == 0b00)) // if ccw_fall is already set to true from a previous B phase trigger, the ccw event will be triggered
{
cw_fall = 0;
ccw_fall = 0;
//do something here, for now it is just printing out CW or CCW
Serial.printf("CCW \r\n");
count--;
Serial.println(count);
}
}
if (gpio == ENC_B)
{
if ((!ccw_fall) && (enc_value == 0b01)) //ccw leading edge is true
ccw_fall = 1;
if ((cw_fall) && (enc_value == 0b00)) //cw trigger
{
cw_fall = 0;
ccw_fall = 0;
//do something here, for now it is just printing out CW or CCW
Serial.printf("CW \r\n");
count++;
Serial.println(count);
}
}
}
void setup() {
Serial.begin(115200);
pinMode(ENC_SW, INPUT_PULLUP);
pinMode(ENC_A, INPUT_PULLUP);
pinMode(ENC_B, INPUT_PULLUP);
gpio_set_irq_enabled_with_callback(ENC_SW, GPIO_IRQ_EDGE_FALL, true, &encoder_callback);
gpio_set_irq_enabled(ENC_A, GPIO_IRQ_EDGE_FALL, true);
gpio_set_irq_enabled(ENC_B, GPIO_IRQ_EDGE_FALL, true);
sleep_ms(500);
// stdio_init_all();
}
void loop() {
while (1)
{
// do something here forever
sleep_ms(1000);
}
}
Кто-то уже юзал модуль с WiFi?
Энкодер на любых портах, не на прерываниях…
Скетч тут
// -----
// SimplePollRotator.ino - Example for the RotaryEncoder library.
// This class is implemented for use with the Arduino environment.
//
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD 3-Clause License. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// -----
// 18.01.2014 created by Matthias Hertel
// 04.02.2021 conditions and settings added for ESP8266
// -----
// This example checks the state of the rotary encoder in the loop() function.
// The current position and direction is printed on output when changed.
// Hardware setup:
// Attach a rotary encoder with output pins to
// * A2 and A3 on Arduino UNO.
// * D5 and D6 on ESP8266 board (e.g. NodeMCU).
// Swap the pins when direction is detected wrong.
// The common contact should be attached to ground.
#include <Arduino.h>
// https://github.com/mathertel/RotaryEncoder
#include <RotaryEncoder.h>
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY)
// Example for Arduino UNO with input signals on pin 2 and 3
#define PIN_IN1 A2
#define PIN_IN2 A3
#elif defined(ESP8266)
// Example for ESP8266 NodeMCU with input signals on pin D5 and D6
#define PIN_IN1 D5
#define PIN_IN2 D6
#elif (defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || \
defined(ARDUINO_GENERIC_RP2040))
#define PIN_IN1 11
#define PIN_IN2 12
#endif
// Setup a RotaryEncoder with 4 steps per latch for the 2 signal input pins:
RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::FOUR3);
// Setup a RotaryEncoder with 2 steps per latch for the 2 signal input pins:
//RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
void setup()
{
Serial.begin(115200);
while (! Serial);
Serial.println("SimplePollRotator example for the RotaryEncoder library.");
} // setup()
// Read the current position of the encoder and print out when changed.
void loop()
{
static int pos = 0;
encoder.tick();
int newPos = encoder.getPosition();
if (pos != newPos) {
Serial.print("pos:");
Serial.print(newPos);
Serial.print(" dir:");
Serial.println((int)(encoder.getDirection()));
pos = newPos;
} // if
} // loop ()
// The End
Для дисплея LCD1602_I2C вполне рабочей оказалась эта библиотека:
Подключение стандартное:
// RP2040
GND - GND
VCC - VOUT
SDA - GPIO4
SCL - GPIO5
К вопросу как испортить RP2040
Я тоже одну седня окирпичил… прошил код с вызовом прерывания каждые пару тактов ядра… не определяется ни на одном компе теперь. Обидно, что 16Меговую, а не 4-ре или 2х
Какие-то они нежные очень.
Может еще разберусь как оживить. Пока отложил
В BOOT режим разве нельзя войти?
не выходит
двумя кнопками делаешь бут?
я когда окирпичивал разгоном, ставил питон )))
Попробуй полное снятие питания, зажимаешь кнопку бут, подаёшь питание, отпускаешь кнопку бут
Как это поможет? есть ссылка?
И - помогло?
Короче, оживил. Точнее, разобрался.
Оказывается я и не окирипичивал вовсе, просто кнопка BOOT сдохла (перестала замыкать контакты). Эта кнопка замыкает пин QSPI SS на GND, по этому сигналу МК выходит в режим накопителя.
Когда замкнул контакты помимо кнопки - вошла в бут режим, получилось перешить
Китайские кнопки последнее время совсем гавняные стали