Attiny25/45/85 FW Development Thread

Not sure if this will prove helpful, but here's a simplistic function to scale a duty cycle value according the battery level as provided by the BATTERY_VpT tables.

inline uint8_t pwm_reg(uint8_t pwm_val, uint8_t batt_lvl) {
 #define BATT_LOW 36 // the last battery level for 100% duty cycle, 3.6V is 36
 #define BATT_HIGH 42 // the high end of battery values, 4.2V is 42
 #define DUTY_CYCLE_ON_HIGH 70 // the duty cycle at the highest battery level, 70% is 70

uint8_t batt_val = 10*(batt_lvl>>5)+(batt_lvl&0b00011111);

if (batt_val <= BATT_LOW) { return pwm_val; }
else {
// scaler is a pwm multiplier 100% = 100, 90% = 90, etc
uint8_t scaler = 100 - (100 - DUTY_CYCLE_ON_HIGH) * (batt_val - BATT_LOW) / (BATT_HIGH - BATT_LOW);
return pwm_val * scaler / 100;
}
}

Then within set_output(), replace PWM_LVL = pwm1 with:

 uint8_t batt_lvl = battcheck();
 PWM_LVL = pwm_reg(pwm1,batt_lvl);

It will linearly scale the PWM value according to the battery level, starting at a given voltage level. It does not take different modes into consideration. It can be used for multiple channels if needed. It's a little heavy (bytes) but can be fit if needed by trimming stuff like BIKING_STROBE. I've got it baked into vanilla Bistro source (pulled from the repository this morning) here on pastebin. And lastly, I have not flashed/tested this.

Very nice!

How much room did it take up?

It’s around 240 bytes. It seems like multiplication and division take up quite a bit of space. Need to do something about that size :weary:

Wow, that is large.

Which reminds me, is it possible to add a simple define that can be commented out to disable this feature. It is possible this is something that would only be able to work on a Tiny85 unless it can be shrunk a lot.

Yeah, in the sample at pastebin I’ve got it surrounded in a #define PWM_REGULTAION

Ah, I missed that the first time glancing at the code, plus I know just enough about code to get in trouble which is why I try not to mess with it unless I have to.

Great job guys! I don’t know if I’ll be able to build these new drivers once they’re ready… :person_facepalming:

Hi TA, not sure where it said max modegroups is 8. Is that in a comment somewhere? It's defaulting to group 12 (array slot 11, wish we just named the groups from 0).

The new MAX_MODES define sets the maximumum number of modes IN a group. You can raise it. Actually I just tried it. It doesn't cost anything to raise it. It only costs a few bytes more in RAM not, flash. Yes, the voltage should read ~0.2V too high if you don't have a diode. Just shift all the values up two rows in the calibration (the second table) and extrapolate the missing two.

Yeah, it was in the comments, I am guessing it was left over from biscotti.

Good idea on the voltage shift, that is easy enough.

At gchart. I think really you want regulation like that to work on any defined power level less than 100%. It should automatically work out where the lowest batery level will be that will force it to 100%. Ideally that's just a wrap around check in the math (when output goes past 255, output=255) so doesn't need to be explictly determined, but with unsigned ints and compiler optimizations that's slightly tricky. Complicating all this that in bistro you first have to figure out if you're even in a FET mode, and you have to adjust all three PWMs together (well that's the easiest way), , it's kind of a shift from how the ramps are done now in bistro where the 7135s just go up to 100% when the fet is in use. I think it would be better for this to jst define those modes as equal PWM levels for all three. It just makes it trickier to guarantee that ramp definitions stay in order. (is 70% 7135s +70% fet more or less than 100% 7135's? Answer: it depends) There may be other ways, but I think it's tricky anyway.

Really though you might want ramp the pure 7135-only modes too, but that probably gets trickier to work out since you don't know what max voltage to target and depends on the setup and number of 7135s. Maybe have to rethink how to define modes entirely and define them by their minimum 100% voltage instead of by their duty factor at maximum.

Deleted comment needs more thought. Basically though, the last thing I said there. Might make sense to define modes by effective Vf... ie 100% duty factor voltage, which is more directly related to light output than PWM is.

The 7135’s should remain unaffected as they will maintain output anyways. Only the FET needs to be adjusted (although it needs the ability to control other channels as well for the new driver I am working on).

It could simple add X% to the duty for each .1v that voltage drops as well if that is simpler / takes up a lot less space. Not as adjustable but it is better then nothing.

"Side effect: can now have a couple of very long ( <8) groups without much memory cost at all."

That comment was meant to say >8 is now possible. But it's about the modes per group not number of groups. Not worded well.

Yes, but you don't know how much output they'll maintain, because you don't know how many there are. That's a problem with they're combined with the FET. What you know is that if you PWM everthing by 70%, you'll have about 70% as much light. The hard part though is matching up a 70% FET+70%7135 to a 100% 7135 at full battery. Which one is higher? So it doesn't really solve the problem of not knowing how much output you have in 7135s. It moves it to a different place though. It becomes a problem of mode ordering instead of a problem of regulation.

I haven't worked out all my thoughts on non-FET modes. But I think regulation there can also be beneficial.

Yes a 100% ALLFET mode will regulate as long as it can. But a 50% PWM all FET mode won't. Assuming no battery sag, just depletion, it will regulate until the same battery level as the 100% one would, because they're both pulling the same current during on cycles, and then it will start to drop. But it's only at 50% of its potential brightness when it starts dropping. You can still raise the PWM level up from there to maintain regulation longer. The more 7135s you have, the bigger both these issues get, the combined modes, and regulation of the pure 7135 modes.

At some point with either enough 7135's or low enough voltage, the 7135s are just acting exactly like a FET. They're 100% open. It's a bigger issue the more 7135s you use.

I am not worried about the 7135’s brightness and current. All that matters is that the FET maintain the added output as long as possible. Once it can not maintain that level of output the total output will start dropping regardless.

Better to have the 7135’s running at 100% duty and just mess with the FET by itself.

Plus in the new driver you can not control all the channels at the same time, they must be controlled separately.

Also this will mostly be used in triple builds and/or 219C’s that need the PWM turned down in order to keep the current in check. In those cases the FET plays a large role and you want the 7135’s to take as much of the load as possible to keep efficiency high.

Basically all we really need to worry about are the non-regulated FET modes, all the regulated modes are, well, regulated.

Also the worry about not knowing how many 7135’s there is not something I am worried about, you can get away with a surprising amount of variance. Plus you can always adjust the code before compiling and/or I will release multiple hex files like the TA drivers for different options.

I agree. Simply regulating the fet while allowing it to return to 100% duty when the voltage is at a level that can be better handled will allow the use of the fet drivers with newer low vf emitters without sacrificing output on high towards the middle and end of battery charge.

There are other considerations that could possibly creat more efficient drivers in the future but for now controlling the over powered fet channel would be greatly helpful.

Here's my attempt at regulation. I don't have time to test it on a light but I have tested the code on a PC and it appears to produce a decent ramp. It's 80 bytes on AVR as a function, probably a few less once inlined. Hopefully the comments are enough explanation but feel free to ask questions. Just don't expect too many answers before tomorrow.

The values I put in there for V_ZERO_AMP and V_FULL_PWM are placeholders, you'll need to figure out your own for a given driver and LED.

/* 
    This requires a linear voltage reading. So if the voltage divider is after 
    a diode, the diode drop must be added to the voltage readout (and voltage 
    constants below). 
V_ZERO_AMP is the voltage where the LED would produce 0A if the regulation 
curve was extended all the way to 0A. In practice, there will still be 
light there because the curve becomes very nonlinear at low currents. To 
get this value, project the part of the Vf/A curve where you want 
regulation to work all the way to 0A, in a straight line. Using one of 
djozz&#39;s awesome graphs makes this easy :)

V_FULL_PWM is the voltage below which there is no more regulation (ie. PWM 
is left untouched). 

The final PWM value is 
    in_PWM * (V_FULL_PWM - V_ZERO_AMP) / (Vcc - V_ZERO_AMP) 
when Vcc &gt; V_FULL_PWM, otherwise it is unchanged (no regulation). 

*/
#define V_ZERO_AMP 135
#define V_FULL_PWM 200

#include <stdint.h>

/*
This does a * b / c, with the intermediate result being 16-bit to avoid
overflow. The result is still 8-bit so it could overflow if b > c.
*/
static
uint8_t u8_mul_u8_div_u8( uint8_t a, uint8_t b, uint8_t c )
{
uint16_t tmp = 0;
uint16_t wa = a;
uint8_t i = 8;
while( 1 == 1 )
{
if( (b & 1u) != 0 )
tmp += wa;
if( --i == 0 )
break;
b >>= 1;
wa <<= 1;
}
uint16_t wd = (((uint16_t)c) << 8) >> 1;
uint8_t result = 0;
i = 8;
while( 1 == 1 )
{
if( tmp >= wd )
{
tmp -= wd;
result |= 1;
}
if( --i == 0 )
break;
wd >>= 1;
result <<= 1;
}
return result;
}

/* Takes vcc reading and pwm value and returns "regulated" pwm. */
static
uint8_t regulate_fet( uint8_t vcc, uint8_t pwm )
{
if( vcc > V_FULL_PWM )
{
pwm = u8_mul_u8_div_u8(
pwm, (V_FULL_PWM - V_ZERO_AMP), (uint8_t)(vcc - V_ZERO_AMP) );
}
return pwm;
}

I don't get the resistance.

In a 100% FET mode you can't regulate it period. We're talking about having regulated middle modes. We already have that for the all 7135 mode but it just depends how many 7135s you have on how long that can stay in regulation, and when it goes out, so do all the modes under it above one 7135. You MUST at least take into account pwming all channels even in fet modes or you just won't get this right at all, but if you don't take into acount in one way or another the 7135 output, you'll also end up with modegroups that are a confusing out-of-order mess that depends on battery level. The thing that kept them in order was leaving the 7135s on full while the FET is on, but you just can't do that now, well not without even more work to sort it out. And then you might as well just do things right:

This is what makes sense:

#define ALL7135_MAXVF // the Vf expected at the current level provided by all 7135s

#define ONE7135_MAXVF // the Vf expected at the current level provided by one 7135s

target_vf=vf_targets[mode_idx];

while (1){

actual_voltage=get_voltage();

if(target_vf>actual_voltage) {// Set a target we can actually reach. Correct for voltage sag next time around.

target_vf=actual_voltage; // better yet would be to lock out a mode once the target_vf of the next lower mode is>= voltage

}

if (target_vf>ALL7135_MAXVF){ // More than 7135's can handle, use everything.

PWM=light_output(target_vf)/light_output(actual_voltage);

PWM_ALL_CHANNELS(PWM);

} else if (target_vf > ONE7135_MXVF){// Using all 7135s

// compare desired light to what we'll actually get at the lesser of this voltage or the max 7135 voltage

// Note if target_vf is set equal to ALL7135_MAXVF we'll still have a no PWM mode.

PWM=light_output(target_vf)/light_output(actual_voltage);

PWM_ALL7135s(PWM);

} else { // Using one 7135

.. ok using voltage down here in the weeds probably doesn't make much sense.

.. just translate these to fixed single PWM levels.

}

// loop to keep up with voltage sag and drain.

}

light_output is the normalized lumens expected at a particular Vf. We rarely see this curve actually, but we have vf vs current curves and current vs light curves so you can combine them and work it out. I've never seen it.

The ratios are the thing to be approximated and can probably be done by

255-(actual_voltage-target_vf)*LED_CAL

Instead of by division.

It seems that multiplication by a constant isn't very expensive. The optimizer works out a a fixed algorithm for it.

I think it's multiplication or division of two variables that is expensive.

The hard part here is just knowing what the Vf corresponding the current of your ALL7135s is.

That depends on how many 7135s, and both that and LED_CAL will depend on the LED in use.

If configured right this will keep all the modes in order and all regulated as long as possible.

edit: removed a redundant comparison in second conditional.

Most drivers have a max of 8 regulators. Which is 2.8amps. In bistro there are four levels higher than this number. They add a bit of fet duty cycle for each. We are wanting to keep these leds under 5-6amps for a single and 10-12amps for a triple. Never will the fet be on when regulated under 2.8amps. So all regulation needs to take place above this number which is only the influence of the fet. If we were to say that we only want a max amps of 2.8, then the fet should be 0 and the regulators left alone. These new leds might stay regulated at 2.8amps all the way down to 3.3-3.5 battery voltage. So there is no situation where the regulators need to be regulated.

So say I want a nichia 219c to run at 5amps. The regulators will bring it to 2.8 then I would wat the fet to pick up the slack. The fet will be needed less when the battery is full and more when it’s empty. All modes under 2.8 will remain unchanged. All modes above 2.8 will be powered by 2.8+FET*%

When the battery is really low all regulators and the fet will be at 100% to create as little resistance as possible and give the led whatever the battery can give. At this point there will be no difference between the few highest modes but this is to be expected.

Right?