I have been working on a few different versions of a bicycle light control circuit which reads in various buttons and sets various outputs accordingly such as headlight, taillight, blinkers, etc.
My current design is based around an ATtiny24 (Datasheet) using the internal oscillator (8MHz), programmed in C using AVR-GCC. I would like to keep external components such as shift registers to a minimum. Resistors are OK since they are cheap and small, and I am using MOSFETs (NTD4960N: Datasheet) to drive the LED banks.
Essentially, what I am looking to do is drive the MOSFETs with a 200Hz PWM signal to control the LED brightness, but limit the duty cycle to 90%. With that in mind, I would only need a pin to be an output for a maximum of 90% of the time. In the remaining 10% of the time, I want to switch the pin to an input to read the state of a button.
I tried a lot of various configurations to allow the MOSFET to stay OFF when it is supposed to be OFF, regardless of input from the button, and this is the best I could come up with:
The supply voltage (VCC) is 5V, so when the button is pressed, about 0.9V is seen at the transistor gate. This is not enough for the transistor to start conducting, but it can be read by the MCU ADC which has been enabled using the internal 1.1V reference.
So the code drives the MOSFET with a 200Hz PWM signal. The duty cycle will vary with the desired LED brightness, but will not be greater than 90%. During the last 10% of the PWM cycle, the I/O pins are set as inputs (internal pullups disabled, digital input buffers disabled) and the ADC is used to read the state of the pin. The 10k resistor keeps the MOSFET off, and if a button is pressed, 0.9V is read by the ADC. This value is still not high enough to turn the MOSFET on, so the LEDs stay off, regardless of the button press.
Since the ADC is turned off and on, I discard the first reading and keep the second. This is done in plenty of time before the pin needs to be reset as an output to start the next PWM cycle. Since I am reading multiple buttons, I only read one each duty cycle, then update the ADC multiplxer to read the next button at the end of the next duty cycle - hence, each button is read once every [(# of buttons) * (200Hz)^-1]. The PWM and button reads are handled entirely by interrupts while MAIN determines what to do with the state of any particular button.
I have built and tested this circuit, and it seems to work perfectly, but I wanted to present the description here with the following questions:
- First, is this a bad idea? If so, then why?
- Is there an obviously better way to do this besides GPIO expanders/shift registers?
I can post some (or all) of the code if you want. I just thought the description might be enough, and I didn't want to make this any longer than it already was.
I should have said this the first time, but the MCU supply voltage (VCC) is 5V, the battery voltage (V_bat) is 12V (could be more like 14 on a fully charged battery), and the total LED current (as in the amount going through the FET) will be close to 2A. The LED bank will consist of numerous parallel strings of 3 series LEDs (white, V_LED = 3.3V) and a series resistor in each string. The pulsed current is around 50mA per string. With 40 or so strings equaling about 2A total.
I have tested and verified my ADC version of the circuit as well as the "digital" version presented by DrFriedParts below with a small current (50mA total), but have not yet tested either circuit with the larger current load.
This is the original project that I am redesigning. When I first built this bike light controller, I was new to microconctollers, so it was more of a learning experience than anything else.