A Design Challenge

ElectricSpidey

Joined Dec 2, 2017
3,335
Here is a breakdown of my "design philosophy" in order of priority. (roughly)

Practicality
Utility...It has to serve a purpose.
Function...It has to do the job.
Reliability
Choosing the right components for the job.
Use as many parts out of stock as possible.
Simplicity
Modularity...Ease of construction.
Elegance/Aesthetics
Cost

I don't really have a preference as far as Digital vs. Analog vs. Micro vs. Discrete...etc. but for the examples given I would choose to use 2 coin batteries instead of one I would use a low power 555 and one of the 12 volt 5mm diffused blue LEDs I have in stock, and not worry about the current.

So, the BOM would be as follows.

2 Coin Batteries
1 Battery holder
1 Proto board
1 low power 555
2 resistors
2 caps
1 12 volt LED (they are plenty bright @ 5 volts)
1 Pot (optional)
1 enclosure (optional)
Feet (optional)
Switch (optional)
And some wire.

I would not have a board produced for a one off project.
I have not used dead bug construction for at least 5 decades.

After building I would go and have a cup of coffee and try to remember why I built this thing. :)
 
Last edited:

wraujr

Joined Jun 28, 2022
259
PIC16LF15313 with one (1) 0.1uf cap across power supply pins
Two LEDs (supplied), each connected to an I/O thru current limit resistor (PIC switches LED anode to GND)
One pot (supplied) , wiper connected connected to PIC I/O (ADC input), BUT GND side of pot connected to another PIC I/O allowing for the pot to only be "turned on" for short periods for sampling purposes. This avoids a constant 10K load across battery.
One push button switch connected to PIC I/O with other side switch to GND (PIC will enable internal pullup.
Push button switch allows cycling thru the three modes plus power-down sleep.
One (1) battery supplied.

PIC pin count (VCC, VSS, LED1, LED2, POT, POT-GND, PB-SW) 7 out of 8 used.
One circuit implements all three functions and operates down to 1.8V.
The actual schematic and parts list is left for the reader as an exercise.

Note: solutions is intended for typical LEDs with Vf = 1.7V and low current. If you want fancy colors or a flashlight, look elsewhere.

1 - PIC 8-pin DIP
1 - 0.1u X7R Capacitor
2 - Resistor for LED (2.2V-1.7V/5ma = 100 ohm)
1 - push-button switch (should be supplied)
2 - LED (supplied)
1 - 10K pot (supplied)
1 - CR2032 battery supplied

Since you should have supplied the push button switch, I claim an additional 4 parts.

Design Enhancement #1: Using 8th pin tied to VCC I will monitor battery voltage. This in combination with PWM of the LEDs will allow the design to provide constant LED brightness from battery new to battery expired at 1.8V.
 
Last edited:

nsaspook

Joined Aug 27, 2009
16,326
A blink led controller design. Reads (via MODBUS) the current charging mode from a solar charge controller and blinks the LED a number of times corresponding to the mode. It also send a analog voltage in defined steps for the charging mode.
https://forum.allaboutcircuits.com/...c-controlled-battery-array.32879/post-1489470
1691854265764.png1691854317193.png
1691855383124.png
Software snips from the actual blinking routine.
C:
/*
* File:   ihc_vector.h
* Author: root
*
* Created on October 22, 2017, 4:25 PM
*/

#ifndef IHC_VECTOR_H
#define    IHC_VECTOR_H

#ifdef    __cplusplus
extern "C" {
#endif
#include <xc.h>
#include <stdlib.h>
#include <stdio.h>
#include "ibsmon.h"

    extern volatile struct V_data V;
    extern volatile uint8_t cc_stream_file, cc_stream_file_prev, cc_buffer[MAX_DATA];
    extern volatile uint16_t timer0_off, link_count;

    void clear_2hz(void);
    void clear_500hz(void);
    uint32_t get_2hz(uint8_t);
    uint32_t get_500hz(uint8_t);

    void set_led_blink(uint8_t);
#ifdef    __cplusplus
}
#endif

#endif    /* IHC_VECTOR_H */

#include "ihc_vector.h"

static void led_blink(void);

void __interrupt() tm_handler(void) // timer/serial functions are handled here
{
    static uint8_t c_error = 0;
    uint16_t tmp;

    if (PIR1bits.RCIF) { // is data from RS485 port
        cc_stream_file = RCREG;
        if (RCSTAbits.OERR || RCSTAbits.FERR) {
            cc_stream_file = 0x00; // nulls for data on errors
            RCSTAbits.CREN = FALSE; // clear overrun
            RCSTAbits.CREN = TRUE; // re-enable
            if (c_error++>MAX_C_ERROR) {
                c_error = 0;
            }
        } else {
            /*
             * process received charge controller data stream
             */
            cc_buffer[V.recv_count] = cc_stream_file;
            if (++V.recv_count >= MAX_DATA)
                V.recv_count = 0; // reset buffer position
        }
    }

    if (PIR1bits.TMR1IF) { //      Timer1 int handler
        PIR1bits.TMR1IF = FALSE; //      clear int flag
        tmp = SAMPLEFREQ >> 8;
        TMR1H = tmp;
        tmp = SAMPLEFREQ & 0xFF;
        TMR1L = tmp;
        V.clock_500hz++;
    }

    if (INTCONbits.TMR0IF) { //      check timer0 irq time timer
        INTCONbits.TMR0IF = FALSE; //      clear interrupt flag
        tmp = TIMERFAST >> 8;
        TMR0H = tmp;
        tmp = TIMERFAST & 0xFF;
        TMR0L = tmp;
        V.clock_2hz++;
        V.clock_blinks++;
        led_blink();
    }

    if (PIR1bits.TMR2IF) { //      check timer0 irq time timer
        PIR1bits.TMR2IF = FALSE; //      clear interrupt flag
    }

    if (PIR1bits.CCP1IF) { //      check timer0 irq time timer
        PIR1bits.CCP1IF = FALSE; //      clear interrupt flag
    }

}

void clear_2hz(void)
{
    INTCONbits.GIEH = 0;
    V.clock_2hz = 0;
    INTCONbits.GIEH = 1;
}

void clear_500hz(void)
{
    INTCONbits.GIEH = 0;
    V.clock_500hz = 0;
    INTCONbits.GIEH = 1;
}

uint32_t get_2hz(uint8_t mode)
{
    static uint32_t tmp = 0;

    if (mode)
    {
        return tmp;
    }

    INTCONbits.GIEH = 0;
    tmp = V.clock_2hz;
    INTCONbits.GIEH = 1;
    return tmp;
}

uint32_t get_500hz(uint8_t mode)
{
    static uint32_t tmp = 0;

    if (mode) {
        return tmp;
    }

    INTCONbits.GIEH = 0;
    tmp = V.clock_500hz;
    INTCONbits.GIEH = 1;
    return tmp;
}

/*
* link condition status server via blinking led
* runs in timer 0 ISR @ 2Hz
*/
static void led_blink(void)
{
    // range checks
    if (V.num_blinks == 255) {
        LED1 = ON;
        V.clock_blinks = 0;
        V.blink_lock = 0;
        return;
    }
    if (!V.num_blinks || V.num_blinks > MAX_BLINKS) {
        LED1 = OFF;
        V.clock_blinks = 0;
        V.blink_lock = 0;
        return;
    }

    // time spacing and blink counter
    if (V.clock_blinks > BLINK_SPACE) {
        if ((BLINK_SPACE + (V.num_blinks << 1)) <= V.clock_blinks) {
            V.clock_blinks = 0;
            LED1 = OFF;
            V.blink_lock = 0;
        } else {
            LED1 = ~LED1;
        }
    }
}

/*
* set the number of blinks variable from mainline code
*/
void set_led_blink(uint8_t blinks)
{
    if (V.blink_lock)
        return;

    if (blinks > MAX_BLINKS && (blinks != 255))
        blinks = 0;

    INTCONbits.GIEH = 0;
    V.blink_lock = 1;
    V.num_blinks = blinks;
    INTCONbits.GIEH = 1;
}

5 blinks, charge controller in battery 'FLOAT' mode.

https://raw.githubusercontent.com/nsaspook/mbmc_k42/i400hz_modbus/pat/ibsmon.c
C:
            case G_MODE: // check for current operating mode
            default:
                req_length = sizeof(re20a_mode);
                if ((V.recv_count >= req_length) && (cc_buffer[0] == 0x01) && (cc_buffer[1] == 0x03)) {
                    uint8_t temp;
                    static uint8_t volts = CC_OFFLINE;

                    c_crc = crc16(cc_buffer, req_length - 2);
                    c_crc_rec = (uint16_t) ((uint16_t) cc_buffer[req_length - 2] << (uint16_t) 8) | ((uint16_t) cc_buffer[req_length - 1] & 0x00ff);

                    if (c_crc == c_crc_rec) {
                        if ((temp = cc_buffer[4])) {
                            set_led_blink(temp);
                            switch (temp) {
                            case 1:
                                volts = CC_ACT;
                                break;
                            case 2:
                                volts = CC_MPPT;
                                break;
                            case 3:
                                volts = CC_EQUAL;
                                break;
                            case 4:
                                volts = CC_BOOST;
                                break;
                            case 5:
                                volts = CC_FLOAT;
                                break;
                            case 6:
                                volts = CC_LIMIT;
                                break;
                            default:
                                volts = CC_ACT;
                                break;
                            }
                        } else {
                            set_led_blink(BON);
                            volts = CC_DEACT;
                        }
                    } else {
                        crc_error++;
                        set_led_blink(BOFF);
                    }
                    V.pwm_volts = volts;
                    SetDCPWM1(V.pwm_volts);
                    cstate = CLEAR;
                } else {
                    if (get_500hz(FALSE) > RDELAY) {
                        set_led_blink(BOFF);
                        cstate = CLEAR;
                        V.pwm_volts = CC_OFFLINE;
                        SetDCPWM1(V.pwm_volts);
                        mcmd = G_MODE;
                    }
                }
            }
        }
        break;
 

MrAl

Joined Jun 17, 2014
13,707
Thank you so much for not using an MCU.
Hi,

Hand wound coils are not something I like to do anymore unless really necessary. However, that's the difference between how one person and another person would approach this design problem, and that's what the thread is all about.
 
Top