wokwi / avr8js

Arduino (8-bit AVR) simulator, written in JavaScript and runs in the browser / Node.js
https://blog.wokwi.com/avr8js-simulate-arduino-in-javascript/
MIT License
481 stars 78 forks source link

PWM disturbances when running code #94

Closed JonasForssell closed 3 years ago

JonasForssell commented 3 years ago

Dear Sir,

Thank you for a fantastic effort! I am using your Avr8js in combination with the Falstad simulator to try out PWM control.

This code successfully initiates mode15 and generates an approx 30kHz PWM on pin 9 However any code I run i main() disturbs the PWM signal out from AVR8JS making it irregular. I tried the same code on an Arduino Uno and the signal was rock solid despite whatever code I had running in the background

Something to look into I hope. /Jonas

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
void initialize() {​​​​​​​​​​
  // add external voltage (Javascript) with name "pin n" to access output pins
  pinMode(9, OUTPUT);
  pinMode(6, INPUT);
 
  // Clear interrupts
  cli();
  // Reset TCCR1B from Arduino meddeling
  TCCR1B = 0;
  // Set fast PWM Mode with Top value as a frequency controller
  TCCR1A |= (1 << WGM10) | (1 << WGM11);
  TCCR1B |= (1 << WGM12) | (1 << WGM13);
  // Set toggle on compare match
  TCCR1A |= (1 << COM1A0);
  // Set TOP value which will reflect period length between 0 to 65536
  OCR1A = 120;
  // Set prescaler to 1 and start PWM
  TCCR1B |= (1 << CS10);
 
}​​​​​​​​​​
 
int main() {​​​​​​​​​​​​​​​​​
 
initialize();
 
while (1) {​​​​​​​​​​​​​​​​​
  int v_out;
  
v_out = digitalRead(6);
delay (1);
 
}​​​​​​​​​​​​​​​​​
 
}
urish commented 3 years ago

Hi Jonas,

Are you using the latest version of AVR8js? How do you verify the PWM frequency?

In general, if you want to use delay() in your code, you should enable interrupts cli(), and also, shouldn't override main(), rather put your code inside setup() and loop(). The delay() function is part of the Arduino core and requires Timer 0 to be initialized in a specific way. When you define your own main(), the Arduino initialization code does not run and thus Timer 0 is not initialized and delay() won't work correctly.

JonasForssell commented 3 years ago

Hej Urish, Thanks for the quick reply. I am not sure which version is used. The simulator is here: http://www.falstad.com/circuit/avr8js/

I tried the exact same program on an Arduino Uno now without issues. The signal from pin 9 was rock solid. I used the Arduino editor to compile and upload the sketch.

I tried an adjusted code in falstad AVR8js without success. The signal is still irregular.

Thanks /Jonas

Here is the model (use import as text) $ 65 0.000001 0.575460267600573 50 5 50 5e-11 418 304 80 256 80 0 1 40 5 0 0 0.5 pin\s8 418 304 112 256 112 0 1 40 5 0 0 0.5 pin\s9 418 304 144 256 144 0 1 40 5 0 0 0.5 pin\s10 418 304 176 256 176 0 1 40 5 0 0 0.5 pin\s11 418 304 208 256 208 0 1 40 5 0 0 0.5 pin\s12 162 304 80 352 80 2 default-led 1 0 0 0.01 r 352 80 400 80 0 1000 g 400 80 416 80 0 0 g 400 112 416 112 0 0 r 352 112 400 112 0 1000 162 304 112 352 112 2 default-led 1 0 0 0.01 162 304 144 352 144 2 default-led 1 0 0 0.01 r 352 144 400 144 0 1000 g 400 144 416 144 0 0 g 400 176 416 176 0 0 r 352 176 400 176 0 1000 162 304 176 352 176 2 default-led 1 0 0 0.01 162 304 208 352 208 2 default-led 1 0 0 0.01 r 352 208 400 208 0 1000 g 400 208 416 208 0 0 o 10 1 0 4099 5 0.0125 0 2 10 3

And here is the adjusted code (copy and paste into the AVR8JS window)

include <avr/io.h>

include <avr/interrupt.h>

include <util/delay.h>

unsigned int top = 100;

void setup() { // add external voltage (Javascript) with name "pin n" to access output pins pinMode(9, OUTPUT); pinMode(6, INPUT);

// Clear interrupts cli(); // Reset TCCR1B from Arduino meddeling TCCR1B = 0; // Set fast PWM Mode with Top value as a frequency controller TCCR1A |= (1 << WGM10) | (1 << WGM11); TCCR1B |= (1 << WGM12) | (1 << WGM13); // Set toggle on compare match TCCR1A |= (1 << COM1A0); // Set TOP value which will reflect period length between 0 to 65536 OCR1A = 200; // Set prescaler to 1 and start PWM TCCR1B |= (1 << CS10); // Enable global interrupts cli(); }

void loop() {

while (1) { int v_out;

v_out = digitalRead(6);

delay (1);

}

}

urish commented 3 years ago

Hi Jonas,

I'm trying to isolate the problem and created a small project that should help reproducing the issue with just AVR8js:

https://stackblitz.com/edit/avr8js-pwm-scope?file=index.ts

I'm observing the frequency value jumping back-and-forth between 39801Hz and 19900Hz, is that also the case when you are running this in the falstad environment?

urish commented 3 years ago

Also, my bad, enabling interrupts is done with sei(), not cli()

JonasForssell commented 3 years ago

Yes that corresponds quite well with what I see in falstad. sometimes a pulse or gap is twice the length.

urish commented 3 years ago

This code successfully initiates mode15 and generates an approx 30kHz PWM on pin 9 However any code I run i main() disturbs the PWM signal out from AVR8JS making it irregular.

Do you also have a variation of the code that generates the PWM wave correctly in the emulator? Or did I understand you incorrectly?

JonasForssell commented 3 years ago

I have not managed to generate a stable pulse in avr8js other than when the while loop is completely empty.

urish commented 3 years ago

Ok, looking at the physical Uno output v.s. the emulated one, I can definitely see there's a problem.

The real one shows a steady PWM wave (captured with Saleae Logic 8):

image

While the emulated one seems to sometimes miss a transition and stay high/low for two periods (screen shot from PulseView):

image

and zooming-in on a good period:

image

v.s. a bad one:

image

So this is definitely a bug. I'm hoping to dig into it sometime next week!

urish commented 3 years ago

For reference: I uploaded my simulator logic analyzer setup here: https://wokwi.com/arduino/projects/300601028188832265

You can view the logic analyzer output by starting the simulation, stopping it after a second, and then it will automatically download a "signal.vcd" file to your computer. Import this file into pulseview to view the signal.

JonasForssell commented 3 years ago

Apologies for asking but have you made any progress urish?

urish commented 3 years ago

Unfortunately not yet. But I plan to do so this weekend.

By the way, what project are you working on?

JonasForssell commented 3 years ago

I am trying to model a resonant DC/DC converter in Falstad/avr8js. I want to try to make it digitally controlled by varying the frequency. The combination of your software and Falstad is very powerful and allows me to try out the concept before building anything in hardware. I am highly impressed by what you have written.

urish commented 3 years ago

That sounds really interesting! I'd love to see this in action :)

Regarding the issue - I've written a test case that reproduces it. Expect a solution soon!

  it('should set the OCF1A flag when OCR1A == 120 and the timer overflowed past 120 in WGM mode 15 (issue #94)', () => {
    const cpu = new CPU(new Uint16Array(0x1000));
    new AVRTimer(cpu, timer1Config);
    cpu.writeData(TCNT1, 118);
    cpu.writeData(OCR1A, 120);
    cpu.writeData(OCR1B, 4); // To avoid getting the OCF1B flag set
    cpu.writeData(TCCR1A, WGM10 | WGM11); // WGM: Fast PWM
    cpu.writeData(TCCR1B, WGM12 | WGM13 | CS10); // Set prescaler to 1
    cpu.cycles = 1;
    cpu.tick();
    cpu.cycles = 5;
    cpu.tick();
    expect(cpu.readData(TCNT1)).toEqual(1);
    expect(cpu.data[TIFR1] & (OCF1A | OCF1B)).toEqual(OCF1A);
    expect(cpu.pc).toEqual(0);
    expect(cpu.cycles).toEqual(5);
  });
urish commented 3 years ago

Fix released as avr8js 0.15.2. Now just need to wait for falstad to update :)

Meanwhile, I confirmed the fix on wokwi.com, seems to do the trick :)