Ralim / IronOS

Open Source Soldering Iron firmware
https://ralim.github.io/IronOS/
GNU General Public License v3.0
7.14k stars 712 forks source link

Major issue in TS100 code - Chapter 1. ADC #1038

Closed sandmanRO closed 2 years ago

sandmanRO commented 3 years ago

Three weeks ago, while in the look for a portable iron I ended-up purchasing a Miniware TS100 unit. I was petrified by the crudeness of delivered factory firmware and I was looking for alternatives. I came across your firmware, but honestly I was not significantly impress as compared to factory firmware. Left without alternatives I had no choice but build my own. Nevertheless, I though I would share with you some of the issues I noticed in your code. No offense gents, but let me just say that your code works by pure accident. In fact, it's one tick away from failure. Where I should start? Oh well...there we go:

  1. Before bells and whistles let's focus first on what is matter the most, that data acquisition. a. The injected ADC1 channel is not triggered when you believe it does. In the way the TIM2 CTR1 was configured, the injected ADC1 (temperature reading) is triggered on period complete (as opposed to pulse complete) of TIM2 CTR1, that is, right when the heating is about to begin. Only the use of the smallest sampling time and the delay induced by software starting of TIM3 CTR1 (the actual power PMD control) from TIM2 CTR4 period complete interrupt saves the execution from total failure, that is reading the temperature while the tip heater is on. b. All the STM32 documentation specifically state not to simultaneously sample the same channels with both ADC1 and ADC2 when dual ADC mode is used as this could lead to conversion errors, and yet, the code does exactly just that, on both regular and injected channels (ADC2 duplicates the readings of ADC1, same channels at the same time). c. The input voltage readings are not synchronized with the PID. As the input voltage drops during heating window, you get a collection of random values between maximum voltage (voltage at idle) and minimum voltage (voltage during heating window). No wonder the voltage readings, even with the 16 sample DMA buffer averaging + extra post acquisition (8 samples deep) average filtering, jump all over the place. d. During the switch between 'fast' and 'slow' PWM, for some unknown reasons you took the approach of changing everything, tick rate, total tick count and the pulse count of TIM2 counters. This leads to variable PID cycle rates which in turn makes the full scale synchronization with all ADCs virtually impossible. e. The LIS2DH (the accelerometer with the latest revision of TS100) is not configured properly (if at all - the code is attempting to configure the interrupt registers using a wrong address with unknow side effects). No wonder your code ended up getting the accelerometer data in pulled mode. Otherwise getting the accelerometer data in pulled mode is not necessarily a bad thing as it allows to do some extra processing at your won pace. However, you activated the high pass filter which means you are eliminating exactly the static acceleration (Earth gravity) that is critical for orientation discrimination. No doubt, LIS2DH orientation data sucks. I agree. As the LIS2DH does not provide low pass filtering (only high pass filter), in my implementation I ended up using a simplified STD (standard deviation) filter, to determine if an orientation change was triggered by a temporary acceleration or by static acceleration (Earth gravitational field). Only the last one would be consider for an orientation change as I don't want the display to flicker every time I move the iron left or right. Moreover, LIS2DH generates orientation data only for the axis with maximum acceleration. So if the tip of the iron is oriented significantly upwards / downwards (accelerometer X axis) you will no longer get orientation for the axis of interest (accelerometer Y axis) in respect with left/right handed display orientation so I'm compensating this by using a software algorithm that considers the actual X, Y, Z accelerations as provided by the accelerometer. Now I'm keeping the iron only in automatic orientation mode by default as finally it's working consistently.

Well, these were the DAQ (data acquisition) problems. Here are my solutions. For start let's review the correct daq sequence:

Image2

As the injected ADC channels are triggered by the RISING edge of the TIM2 CTR1 counter (and NOT by the falling edge of the counter output), to get the timing right, it is critical then that TIM2 CTR1 is configured as ACTIVE LOW (sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;) Moreover, to avoid data conversion errors, I use the ADCs as follow: ADC1 regular, rank 1 through 4 for reading handle temperature, injected ADC1 ranks 1-4 for tip thermocouple, injected ADC2 ranks 1-4 for voltage input readings, both injected channels with a post acquisition (8 slots deep) average filter. Anything, regular and injected, use the same sampling time of ADC_SAMPLETIME_28CYCLES_5 (anything lower than this would not be enough to correctly measure a 'large' signal like when the input voltage has a value of 24V, even if internally divided by the R20 (75kOhm) and R21(10kOhm) resistive divider). Moreover, the samples are updated in ONE single place, that is within the PID loop. All other calls within app would call for filtered (average) result. This way we keep everything synchronized. You guys do that and you will be amazed how stable everything becomes (so stable that several times during development I though the STM32 just hanged on me...nope...only scary stable readings). For PID I'm using a simple, yet fully effective self-decaying integrator and nothing else (no proportional, no derivative term). When integrating I'm taking into account the PID cycle period as integration time, and as decaying factor I'm using the thermal inertia of the tip (thermal mass) that makes perfect physical sense. Despite its simplicity, it turned out to be so accurate that I even had to include a gain of 2 when integrating for compensating for the fact we are actually pumping current through the tip only half the time of TIP2 CTR4 pulse as the actual TIM3 CTR1 that drives the power has 50% duty cycle.

Regarding timers, in my implementation I keep the TIM2 period (total ticks) fixed at 199 ticks and change only the TIM2 frequency (TIM2 prescaler at 4000 and 8000 respectively) and the pulse width of TIM2 CTR4 as requested by the PID. The TIM2 Period / PID Cycle will switch only between fixed 5Hz and 10Hz rates so everything stays synchronized.

I would stop here. If I start talking about user interface, it would take me days.

Below it's a glimpse of my implementation. All sort of bells and whistles like OLED contrast, enhanced graphics, double click button events, single / dual language support + spot on data acquisition w/ augmented automatic display orientation. Despite my best efforts I could not manage to get more than two languages fit in at a time. In my implementation each language adds about 10k to the hex file. With one language the hex is about 113k, two languages about 123k, three languages in excess of 132k so it would no longer fit in.

Idle Screen (graphic mode) IMG_20210907_184503 Buttons pop-up with animation (they 'inflate' with animation from the small buttons) when sensing movement so the user would know which button does what IMG_20210907_184512 No tip display IMG_20210907_184942 Tip hot notification IMG_20210907_193149

sandmanRO commented 3 years ago

...and if I manage to do something that I fee would add value to your project most certainly I will post it up. That is a promise.