Traumflug / Teacup_Firmware

Firmware for RepRap and other 3D printers
http://forums.reprap.org/read.php?147
GNU General Public License v2.0
312 stars 199 forks source link

Heater max PWM #253

Closed Wurstnase closed 7 years ago

Wurstnase commented 7 years ago

A new feature is born: https://github.com/Traumflug/Teacup_Firmware/commit/c1f10fe5c9300a19d6c2d https://github.com/Traumflug/Teacup_Firmware/commit/952990b351205d

This time with configtool and everything needed. Just one thing I'm not so happy with it. This feature is only needed when one heater max. value is set below 100. I've insert a new #define only to enable this, but it is maybe possible to check, if one or more heater has a value below 100%.

Any better ideas to check the value in DEFINE_HEATER with a preprocessor trick?

phord commented 7 years ago

:+1: Thanks for this. I have 12V heaters I wanted to run with 20V, but I had other parts of my build to focus on at the time.

phord commented 7 years ago

I pushed some new commits to heater_max_pwm designed to help ease the use of DEFINE_HEATER when the number of parameters to this macro changes. The changes can also stand alone on experimental with some tweaks, but this branch inspired me to write this feature, and it will benefit from it first.

Wurstnase commented 7 years ago

Thanks! Your code looks as promising as ever.

Wurstnase commented 7 years ago

Some rebase and an introduce of softPWM.

config Program Data
config.regtest-gen7-avr.h 26 6
config.regtest-ramps.h 28 8
config.regtest-nanoheart.h 26 6
config.regtest-teensy2.h 132 8
config.regtest-gen3.h 16 0
config.h.Profiling 76 6
config.regtest-display.h 26 6
config.regtest-acceleration-reprap.h 28 8
config.regtest-acceleration-temporal.h 28 8
config.regtest-gen7-arm.h 12 0
config.regtest-no-endstops.h 54 6
config.regtest-no-lookahead.h 26 6

As you can see, this will cost a little space. But not that much. On config.regtest-teensy2.h softPWM is active. I currently have a nice idea for my STM where I want to use the PWMs for the stepper. So it could be necessary to have softPWM.

Wurstnase commented 7 years ago

Oops... Other than described in some comments, the softPWM routine runs currently with 0,4Hz not 100Hz. However, I will put softPWM in the clock_tick(). So this will run at ~2Hz. This should be sufficient for most applications.

triffid commented 7 years ago

If you're implementing soft PWM, consider sigma delta modulation. It gives more rapid heater response vs clock rate, doesn't care when you update the power figure and is vastly friendlier to both zero crossing AC SSRs and driving MOSFET gates directly from gpio.

See https://github.com/Smoothieware/Smoothieware/blob/edge/src/libs/Pwm.cpp#L55 for an implementation I made for smoothie which I believe is licence compatible.

On 13 Mar 2017 19:51, "Nico Tonnhofer" notifications@github.com wrote:

Oops... Other than described in some comments, the softPWM routine runs currently with 0,4Hz not 100Hz. However, I will put softPWM in the clock_tick(). So this will run at ~2Hz. This should be sufficient for most applications.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Traumflug/Teacup_Firmware/issues/253#issuecomment-286086326, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKBGhQl0CiDlh_96LfFsnMizmQ9nEk2ks5rlS3MgaJpZM4LDu4m .

Wurstnase commented 7 years ago

Thanks @triffid. Looks like breseham for PWM. I will give this a try!

triffid commented 7 years ago

Yeah I suppose it's conceptually quite similar, hadn't made that connection :)

I suppose it also resembles fractional division, working out if the next bit needs to be high or low to bring the accumulated result closer to the desired value.

Fwiw, I believe class D audio amplifiers use sigma delta, as do numerous other DACs for precisely the same reasons that make it excellent for this usage case! And successive approximation ADCs use this technique but in reverse :D

On 13 Mar 2017 21:27, "Nico Tonnhofer" notifications@github.com wrote:

Thanks @triffid https://github.com/triffid. Looks like breseham for PWM. I will give this a try!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Traumflug/Teacup_Firmware/issues/253#issuecomment-286106249, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKBGvXVMgHoYNeMaajtsCGET4xabWfGks5rlUREgaJpZM4LDu4m .

Blue-Marlin commented 7 years ago

That many state changes will increase the switching losses in the power transistors. For relays this is completely unusable.

Wurstnase commented 7 years ago

This depends on the frequency. This algorithm will go into a loop of 10ms. There are at max only 100 changes per second. This is much less than normal PWM.

Wurstnase commented 7 years ago

Really really nice, @triffid!

I made a simple test with printfs. While changing the pwm each tick() the output is very smooth. With this, 10ms cycle time is really enough.

++++++-+++++-+++++-+++-++++-+++-+++-+++--++++
-+++--++++--+++-++--+++--+++--+++--++--+++--+
+--++--++--++--+--++--++---++---++---++---+--
+--+--+---++----+---+---+---+----+---+-----+-
----+------+--------+--------------------+---
---------------------+--------+------+-----
Wurstnase commented 7 years ago

I found a bit simpler code: https://www.mikrocontroller.net/topic/293454#3128867

sd_accu += pwm - (sd_dir * PWM_MAX);
if (sd_accu > 0) {
  sd_dir = 1;
} else {
  sd_dir = 0;
}
WRITE(pin, sd_dir);

@triffid, I'm a bit unsure about your sanity-check. The sd_accumulator should never be >256, but you check that it should be < 512. https://github.com/Smoothieware/Smoothieware/blob/edge/src/libs/Pwm.cpp#L112

triffid commented 7 years ago

Because if heater power is slightly less than half when it steps from 0 to just below half, then heater power is set to near max, the sum will go over 255 - and if you cap it at 255 it'll do something mathematically weird to the resulting bitstream. that's why the negative cap is at -MAX rather than just at zero. It probably doesn't matter in the slightest for heaters I suppose..

I wrote that a while ago, perhaps the guide I based it on wasn't the best shrug

On 14 March 2017 at 15:01, Nico Tonnhofer notifications@github.com wrote:

I found a bit simpler code:

sd_accu += pwm - (sd_dir * PWM_MAX); if (sd_accu > 0) { sd_dir = 1; } else { sd_dir = 0; } WRITE(pin, sd_dir);

@triffid https://github.com/triffid, I'm a bit unsure about your sanity-check. The sd_accumulator should never be >256, but you check that it should be < 512. https://github.com/Smoothieware/Smoothieware/blob/edge/src/libs/Pwm.cpp# L112

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Traumflug/Teacup_Firmware/issues/253#issuecomment-286338528, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKBGuiI-cb6VQUSuPz0vfInze-CK6D6ks5rljs3gaJpZM4LDu4m .

Wurstnase commented 7 years ago

I compared both again. The code is doing exactly the same. You've only one comparison less.

Wurstnase commented 7 years ago

While playing with excel...

When the input of pwm is 8bit (0 to 255) and PWM_MAX = 255 * (100 / max_pwm_in_percent), you will get smooth reduced power for your heater 👍

Wurstnase commented 7 years ago

I don't know if it is a good idea to use the pointer from the hardware-pwm pins. Any cons? https://github.com/Traumflug/Teacup_Firmware/blob/heater_max_and_sigma_delta/heater-avr.c#L20-L27

Minimal example:

#define U8_HEATER_PWM uint8_t *
U8_HEATER_PWM heater_pwm;
#define pwm 1
heater_pwm = (pwm >= 2) ? (XYZ_TIMER) : (U8_HEATER_PWM)1;
...
if (heater_pwm == (U8_HEATER_PWM)2) {}
...
phord commented 7 years ago

It's certainly ugly, but not much uglier than the original code.

I'm not quite sure how it will be used, though. Is there real value in this complicated overloading?

Wurstnase commented 7 years ago

I just compared the Arm and AVR. On Arm we use a union for PWM and normal pins. I will change this on AVR where we save any time PWM and standard port.

For avr we have then also the PWM-type flag and I don't need to overload the pointer.

Wurstnase commented 7 years ago

Removing the loop/switch in heater-avr-init and save the pre-compiled masked pin instead of doing this at run-time will save alot space. https://github.com/Traumflug/Teacup_Firmware/commit/640c425be8

config Program Data
config.regtest-gen7-avr.h -122 0
config.regtest-ramps.h -254 0
config.regtest-nanoheart.h -108 0
config.regtest-teensy2.h -178 0
config.regtest-gen3.h 0 0
config.h.Profiling -120 0
config.regtest-display.h -122 0
config.regtest-acceleration-reprap.h -254 0
config.regtest-acceleration-temporal.h -254 0
config.regtest-gen7-arm.h 0 0
config.regtest-no-endstops.h -130 0
config.regtest-no-lookahead.h -122 0
Wurstnase commented 7 years ago

While reworking the max_heater-stuff and introducing the new software pwm this could/will break old config-files.

Currently hardware pwm is on by DEFINE_HEATER -> pwm = 1. New hardware pwm is on by DEFINE_HEATER -> pwm >= 2.

Any good idea to inform the user, that he should update/change it's config-file?

Wurstnase commented 7 years ago

I'm going around this for now. So Teacup has a new option.

Normally everything is close the same before. pwm is set to 0, no pwm is used at all. pwm is set to 1, hardware pwm is used when available. If not, software pwm is used.

A second option you can force software pwm. In that case no pwm: 0 software pwm: 1 hardware pwm >= 2

phord commented 6 years ago

Apparently I'm reading this wrong. It seems to do the right thing in spite of my interpretation. :man_shrugging:

~I think this code always selects SOFTWARE_PWM now if DEFINE_HEATER has pwm=1. But your last note on this issue sounds like you are saying it should auto-select Hardware/Software depending on if it's available. Can you clarify?~

// When pwm >= 2 it's hardware pwm, if the pin has hardware pwm.
// When pwm == 1 it's software pwm.
// pwm == 0 is no pwm at all.
// Use this macro only in DEFINE_HEATER_ACTUAL-macros.
#define PWM_TYPE(pwm, pin) \
  (((pwm) >= HARDWARE_PWM_START) ? \
    ((pin ## _PWM) ? \
      HARDWARE_PWM : \
      SOFTWARE_PWM ) : \
    pwm)