Skocz do zawartości

Problem z zakresem obrotu enkodera w Trinket 5V i generowanie PWM.


Pomocna odpowiedź

Napisano

Jako że Trinket jest mały i w miarę w rozsądnej cenie, chciałem wykonać sterowanie jasnością taśmy LED, czy PowerLEDa.
W tym celu zmontowałem układ jak na rysunku:

1010252069_Zrzutekranu2021-08-11o22_29_03.thumb.png.bcb90a51c45de9a3d6b285b7cab4fb4d.png
Natomiast pojawił się problem z programem. W założeniu obroty w lewo enkodera mają ściemniać, a w prawo rozjaśniać.  Do tego chciałem zaimplementować liniowość zmiany jasności LED, bo jak wiadomo zmiana widocznej jasności nie idzie w parze z wielkością wypełnienia PWM.
Jako że inni już przede mną temat rozkminiali, to na bazie ich osiągnięć próbuję napisać program.
Źródła to: 
Implementacja liniowej zmiany jasności LED<=> PWM  https://gist.github.com/netmaniac/8be83f2e66ae25e949f1
Tu trochę bardziej rozpisana: https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/

Oraz projekt na Trinketa - sterowanie głośnością muzyki w PC za pomocą enkodera i portu USB. 
https://learn.adafruit.com/trinket-usb-volume-knob

Z uwagi na to że nie interesuje mnie sterowanie przez port USB to zmodyfikowałem w/w kod z uwzględnieniem liniowości jasności LED:
https://pastebin.com/BHe8FKth

#include <avr/power.h>
#include <avr/pgmspace.h>

#define PIN_ENCODER_A 0
#define PIN_ENCODER_B 2
#define TRINKET_PINx  PINB
#define PWM_PIN 1
#define krok 9

#define CIELPWM(a) (pgm_read_word_near(CIEL8+a))
const uint8_t CIEL8[] PROGMEM ={ 0,1,2,3,4,5,7,9,12,15,18,22,27,32,38,44,
                                51,58,67,76,86,96,108,120,134,148,163,180,
                                197,216,235,255};

static uint8_t enc_prev_pos = 0;
static uint8_t enc_flags    = 0;
short int stan_pwm =0;

void stepCorrectedPWM(int b) {
  analogWrite(PWM_PIN,CIELPWM(b));
}

void setup()
{
 if(F_CPU == 16000000)
   clock_prescale_set(clock_div_1);
  // set pins as input with internal pull-up resistors enabled
  pinMode(PIN_ENCODER_A, INPUT);
  pinMode(PIN_ENCODER_B, INPUT);
  pinMode(PWM_PIN, OUTPUT);
  digitalWrite(PIN_ENCODER_A, HIGH);
  digitalWrite(PIN_ENCODER_B, HIGH);
  analogWrite(PWM_PIN,0);

  // get an initial reading on the encoder pins
  if (digitalRead(PIN_ENCODER_A) == LOW) {
    enc_prev_pos |= (1 << 0);
  }
  if (digitalRead(PIN_ENCODER_B) == LOW) {
    enc_prev_pos |= (1 << 1);
  }
}

void loop()
{
  int8_t enc_action = 0; // 1 or -1 if moved, sign is direction

  uint8_t enc_cur_pos = 0;
  // read in the encoder state first
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_A)) {
    enc_cur_pos |= (1 << 0);
  }
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_B)) {
    enc_cur_pos |= (1 << 1);
  }

  // if any rotation at all
  if (enc_cur_pos != enc_prev_pos)
  {
    if (enc_prev_pos == 0x00)
    {
      // this is the first edge
      if (enc_cur_pos == 0x01) {
        enc_flags |= (1 << 0);
      }
      else if (enc_cur_pos == 0x02) {
        enc_flags |= (1 << 1);
      }
    }

    if (enc_cur_pos == 0x03)
    {
      // this is when the encoder is in the middle of a "step"
      enc_flags |= (1 << 4);
    }
    else if (enc_cur_pos == 0x00)
    {
      // this is the final edge
      if (enc_prev_pos == 0x02) {
        enc_flags |= (1 << 2);
      }
      else if (enc_prev_pos == 0x01) {
        enc_flags |= (1 << 3);
      }

      // check the first and last edge
      // or maybe one edge is missing, if missing then require the middle state
      // this will reject bounces and false movements
      if (bit_is_set(enc_flags, 0) && (bit_is_set(enc_flags, 2) || bit_is_set(enc_flags, 4))) {
        enc_action = 1;
      }
      else if (bit_is_set(enc_flags, 2) && (bit_is_set(enc_flags, 0) || bit_is_set(enc_flags, 4))) {
        enc_action = 1;
      }
      else if (bit_is_set(enc_flags, 1) && (bit_is_set(enc_flags, 3) || bit_is_set(enc_flags, 4))) {
        enc_action = -1;
      }
      else if (bit_is_set(enc_flags, 3) && (bit_is_set(enc_flags, 1) || bit_is_set(enc_flags, 4))) {
        enc_action = -1;
      }

      enc_flags = 0; // reset for next time
    }
  }

  enc_prev_pos = enc_cur_pos;

  if (enc_action > 0) {
   // Jaśniej
    stan_pwm = stan_pwm + krok;
 
  }
  if (enc_action < 0) {
   //ciemniej
   stan_pwm = stan_pwm - krok;
 
  }
  if (stan_pwm >= 255)
           {
            stan_pwm=255;
           }
 if (stan_pwm <=1)
         {
            analogWrite(PWM_PIN,0);
         }
 else
         {        
          stepCorrectedPWM(abs(stan_pwm/8));
          }
}

I teraz problem. Kod działa w pewnym zakresie poprawnie. Tzn. gdy zwiększam jasność diody (enkoderem obrót w prawo) jasność wzrasta aż do maksymalnej i dalsze obracanie już jej nie zmienia, ale co ważne "odkręcanie" jasności (obrót w lewo) od razu reaguje zmianą wypełnienia PWM i spadkiem jasności. I ta część działa poprawnie.

Ściemniając diody dochodzimy do wygaszenia i... dalsze obracanie w lewo niby już nic nie robi, ale niestety zakres obrotu domyślam się że nie kończy się na wygaszeniu tylko dalej obracając niejako "nabija się licznik" kroków. Czyli np. wykonując po wygaśnięciu diody jeszcze np. 4 obroty enkodera w lewo, musimy wykonać tyle samo w prawo + troszkę by znów dioda zaczęła jaśnieć.

Moim założeniem jest to by przedział zmian jasności był 0-255 (tak jak w sygnale PWM), a nie jak obecnie również poniżej zera. Po prostu po wygaszeniu diody, niezależnie ile jeszcze obrotów w lewo wykonamy, jeden krok w prawo powinien już włączać diodę i zwiększać jej jasność. Problem niby trywialny, ale nie potrafię znaleźć gdzie tkwi błąd.
Jako mniej eleganckie rozwiązanie, zawsze zostaje reset układu (przycisk w enkoderze), no ale to ostateczność.

Coś doradzicie?

Po odjęciu / dodaniu kroku

if (enc_action > 0) {
   // Jaśniej
    stan_pwm = stan_pwm + krok;
 
  }
  if (enc_action < 0) {
   //ciemniej
   stan_pwm = stan_pwm - krok;
 
  }

Nie limitujesz licznika w lewo.

 if (stan_pwm >= 255)
           {
            stan_pwm=255;
           }

To ogranicza licznik w prawą stronę.

Ale w lewo już nie masz ograniczenia

 if (stan_pwm <=1)
         {
            analogWrite(PWM_PIN,0);
         }

Tutaj tylko resetujesz pin, ale nie ograniczasz limitu

Zmień to ostatnie na:

 if (stan_pwm <=1)
         {
            stan_pwm = 1;
            analogWrite(PWM_PIN,0);
         }

I powinno być dobrze 😉 

  • Lubię! 1
  • Pomogłeś! 1
10 minut temu, MC2Systems napisał:

Kurcze trywialny błąd.

Po kilku-kilkunastu w zawodzie nadal będziesz robić takie same błędy 😉 Taki urok programowania 😛 

Dołóż rezystory pomiędzy kondensatorami a stykami A i B tak około 100Ω zmniejszy to ilość generowanych zakłóceń oraz wydłuży żywotność styków enkodera

  • Lubię! 1

To może obrazek poglądowy, wycinek schematu z mojego projektu

enc.thumb.JPG.ed60aa8bcd12f144750606b89f3532f6.JPG

Chodzi o r4 i r5 są po to aby kondensatory nie były zwierane bezpośrednio do masy, taki szczególik na pewno nie zaszkodzi

(edytowany)

No i wszystko jasne. Każde dopracowanie projektu się przyda. Zważywszy, że potem płytka ma być u "majfrendów" zamówiona żeby było tak bardziej na poważnie 🙂
Dzięki

Edytowano przez MC2Systems

Trochę się grzeje ale radiator do mosfeta dorzucę. Natomiast rezystor. Hmm no na schemacie mam błąd. W rzeczywistości mam tam 1k. 10k w testach wychodził zbyt duży i mosfet się do końca nie otwierał.

Z tego co pamiętam na studiach był, z jakichś notatek. Tylko dostosowałem go do tego co obecnie można znaleźć na rynku. A co? Coś z nim nie tak? Wiesz dawno to było wiec może nie wszystko pamiętam :)

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...