Неожиданное поведение энкодера при увеличении оборотов в минуту

Я использую двигатель постоянного тока с энкодером и пытаюсь вычислить, сколько импульсов записывается в каждом секундном интервале. (обратите внимание, что источник питания 12 В для двигателя)

Схема и код:

int encpin=3;
volatile long npc=0; //new pulse count
volatile long opc=0;  //original pulse count
volatile long pulsecount;
unsigned long int newtime;
unsigned long int prevtime=0;
unsigned long int time;
void setup()
{
  pinMode(encpin, INPUT_PULLUP);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(encpin),function,RISING);
}
void function(){
  npc++;
}
void loop()
{
 newtime=millis();
 time=newtime-prevtime;
 pulsecount=npc-opc;
  if(time==1000){
    Serial.println(pulsecount);
    prevtime+=1000;
    opc=npc;
  }
}

Кажется довольно интуитивным, что увеличение оборотов должно увеличить количество импульсов, записанных за одну секунду. Однако результаты довольно неустойчивы:

 RPM  PULSECOUNT
  26    2365
  32    2367
  38    2400
  44    2375
  52    2400
 116    2412
 142    2384
 195    2416
 280    2406
 350    3008
 416    1580
 520    1975
 624    2370
 730    2334
2737    2188

Увеличение оборотов в минуту иногда увеличивает количество импульсов, но иногда уменьшает его....в чем может быть причина такого поведения?

, 👍1

Обсуждение

Вы все еще проверяете "время==x"? Серьезно??!, @Sim Son


2 ответа


1

Я думаю что проблема заключается в симуляции tinkercad: Энкодер присоединен к двигателю, и двигатель всегда вращается с одной и той же скоростью. Выбор другой частоты вращения компонента изменяет передаточное число и частоту вращения вала, но не двигателя. Это означает, что число, выводимое через 1 с, всегда должно быть одним и тем же. Почему это не совсем то же самое, могут сказать только программисты моделирования. Возможно, это связано со скоростью моделирования, потому что там я получаю совершенно другие цифры.

Чтобы точно сказать, что происходит, необходима документация компонентов моделирования и самой симуляции.

,

какие цифры вы получаете с вашей стороны? И что еще более важно, следуют ли они ожидаемой тенденции: то есть растут ли вместе с оборотами?, @satan 29

они находятся в диапазоне ~1600 и изменяются случайным образом в зависимости от настройки оборотов двигателя, но они также не согласованы во время 1 запуска, @Kerbolosh


1

Одним из важных принципов, которые следует усвоить при работе с прерываниями, является принцип атомарных операций. При работе с любыми данными, которые не могут быть обработаны одной инструкцией по сборке, крайне важно защитить их от изменения на полпути при любом чтении.

В вашей ситуации вы работаете с 32-битными значениями. На 8-битном микроконтроллере для манипулирования ими требуется много-много команд, и в любой момент во время этой манипуляции может сработать прерывание и изменить значение, испортив ваши показания.

В программировании существует понятие, называемое критическими секциями. Это "заблокированные" биты кода, где никакие прерывания не допускаются, пока вы делаете все необходимое с вашими общими переменными. По понятным причинам вы хотите, чтобы эти разделы были как можно более краткими.

Кроме того, вам не нужно вычислять количество импульсов, сравнивая его с предыдущим количеством - просто начинайте с 0 каждую секунду. А что касается секунды, то лучше всего проверить, прошла ли хотя бы секунда. Всегда есть шанс, что вы увидите 1001 мс вместо 1000 мс. Слабый шанс, но лучше перестраховаться.

Вот пример использования вашего кода в качестве базы:

void loop() {

    if (millis() - prevtime >= 1000) { // you should check for more time passing than you expect
        prevtime += 1000; // Don't assume you always had 1 second, force it instead
   
        // This block is the "critical section". Keep it as short as possible
        // so just grab the data and reset the count.
        noInterrupts(); // Stop any interrupts happening
        uint32_t currentCount = npc; // Grab this second's count
        npc = 0; // And reset the count to zero
        interrupts(); // Release the block on interrupts

        // And output the results.
        Serial.println(currentCount);
    }

}
,