ThinkClock Battery Labs (Assignment Answer sheet)
Q.1)
The program will:
1. Blink the onboard LED (GP25) 10 times in the first minute, 20 times in the second, and
30 times in the third.
2. Allow you to press a button (connected to GP15) to toggle the blinking pattern to 30
times (first minute), 20 times (second minute), and 10 times (third minute) for subsequent
3-minute cycles.
C Program for Raspberry Pi Pico:
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "pico/time.h" // Required for to_ms_since_boot, get_absolute_time
// --- Pin Definitions ---
// PICO_DEFAULT_LED_PIN is usually GP25, connected to the onboard LED.
// If not defined by the build system, we default it to 25.
#ifndef PICO_DEFAULT_LED_PIN
#define PICO_DEFAULT_LED_PIN 25
#endif
const uint LED_PIN = PICO_DEFAULT_LED_PIN; // Onboard LED
const uint BUTTON_PIN = 15; // GPIO15 for the button
// --- Global State ---
volatile bool g_reverse_pattern = false; // Flag to determine the blinking pattern
volatile bool g_button_pressed_event = false; // Flag set by ISR when button is pressed
volatile uint32_t g_last_button_press_time = 0; // For debouncing
1
ThinkClock Battery Labs (Assignment Answer sheet)
// --- Constants ---
const uint32_t DEBOUNCE_DELAY_MS = 50; // 50ms for button debounce
// --- Function Declarations ---
void blink_led_n_times_in_one_minute(int num_blinks);
void button_isr(uint gpio, uint32_t events);
// --- Main Function ---
int main() {
// stdio_init_all(); // Optional: Initialize standard I/O for debugging printf
// Initialize LED Pin
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
gpio_put(LED_PIN, 0); // Ensure LED is off initially
// Initialize Button Pin
gpio_init(BUTTON_PIN);
gpio_set_dir(BUTTON_PIN, GPIO_IN);
gpio_pull_up(BUTTON_PIN); // Enable internal pull-up resistor (button connects to GND)
// Setup Interrupt for Button
// Trigger on falling edge (button press connects pin to GND due to pull-up)
gpio_set_irq_enabled_with_callback(BUTTON_PIN, GPIO_IRQ_EDGE_FALL, true,
&button_isr);
int blink_counts[3]; // Array to hold blink counts for the 3 minutes
2
ThinkClock Battery Labs (Assignment Answer sheet)
while (true) {
// Check if a button press event occurred and process it
if (g_button_pressed_event) {
g_reverse_pattern = !g_reverse_pattern; // Toggle the pattern
g_button_pressed_event = false; // Reset the event flag
}
// Set blink counts based on the current pattern mode
if (!g_reverse_pattern) {
blink_counts[0] = 10; // First minute: 10 blinks
blink_counts[1] = 20; // Second minute: 20 blinks
blink_counts[2] = 30; // Third minute: 30 blinks
} else {
blink_counts[0] = 30; // First minute (reversed): 30 blinks
blink_counts[1] = 20; // Second minute (reversed): 20 blinks
blink_counts[2] = 10; // Third minute (reversed): 10 blinks
}
// Execute the 3-minute blinking cycle
for (int i = 0; i < 3; i++) {
blink_led_n_times_in_one_minute(blink_counts[i]);
}
}
return 0; // Should never be reached
}
3
ThinkClock Battery Labs (Assignment Answer sheet)
// --- Function Definitions ---
/**
* @brief Blinks the LED a specified number of times over a period of one minute.
* Each blink (ON + OFF cycle) is timed so that 'num_blinks' occur in 60 seconds.
* @param num_blinks The number of times the LED should blink within one minute.
* If 0 or negative, the LED remains off for one minute.
*/
void blink_led_n_times_in_one_minute(int num_blinks) {
if (num_blinks <= 0) {
gpio_put(LED_PIN, 0); // Keep LED off
sleep_ms(60000); // Wait for 60 seconds
return;
}
// Calculate duration for LED ON state and OFF state.
// Total time for one blink cycle (ON + OFF) = 60,000 ms / num_blinks
// Duration for ON or OFF state (half cycle) = (60,000 ms / num_blinks) / 2
uint32_t half_cycle_duration_ms = (60 * 1000) / (num_blinks * 2);
for (int i = 0; i < num_blinks; i++) {
gpio_put(LED_PIN, 1); // LED ON
sleep_ms(half_cycle_duration_ms);
gpio_put(LED_PIN, 0); // LED OFF
sleep_ms(half_cycle_duration_ms);
}
4
ThinkClock Battery Labs (Assignment Answer sheet)
/**
* @brief Interrupt Service Routine (ISR) for button presses.
* Handles debouncing and sets a global flag when a valid button press (falling edge) occurs.
* @param gpio The GPIO pin number that triggered the interrupt.
* @param events The type of event(s) that occurred.
*/
void button_isr(uint gpio, uint32_t events) {
if (gpio == BUTTON_PIN) { // Check if the interrupt is from our button
uint32_t current_time = to_ms_since_boot(get_absolute_time());
// Check for falling edge and debounce
if ((events & GPIO_IRQ_EDGE_FALL) && (current_time - g_last_button_press_time >
DEBOUNCE_DELAY_MS)) {
g_last_button_press_time = current_time; // Update last press time
g_button_pressed_event = true; // Set the global event flag
}
}
}
Code Explaination :
Pin Definitions:
LED_PIN: Set to PICO_DEFAULT_LED_PIN (typically GP25, the onboard LED).
BUTTON_PIN: Set to 15 (GP15). You'll connect your button to this pin.
Global Variables:
g_reverse_pattern: A boolean that's false for the normal pattern (10, 20, 30 blinks) and
true for the reversed pattern (30, 20, 10 blinks).
g_button_pressed_event: A flag set by the interrupt service routine (ISR) when the button
is pressed. This allows the main loop to handle the button press safely.
5
ThinkClock Battery Labs (Assignment Answer sheet)
g_last_button_press_time: Used for debouncing the button to prevent multiple triggers
from a single press.
main() function:
Initializes the LED pin as an output and the button pin as an input with an internal pull-up
resistor. This means the button should connect GP15 to Ground (GND) when pressed.
Sets up an interrupt (button_isr) to detect when the button is pressed (a falling edge on
GP15).
The main while(true) loop continuously checks g_button_pressed_event. If true, it
toggles g_reverse_pattern.
Based on g_reverse_pattern, it sets the number of blinks for each of the three minutes.
It then calls blink_led_n_times_in_one_minute() for each minute in the 3-minute cycle.
blink_led_n_times_in_one_minute(int num_blinks) function:
Takes the desired number of blinks for a one-minute period.
Calculates how long the LED should be ON and OFF for each blink to fit all blinks
within 60 seconds. For example, for 10 blinks in a minute, each full blink cycle (ON +
OFF) takes 6 seconds (3 seconds ON, 3 seconds OFF).
If num_blinks is 0 or less, it simply keeps the LED off and waits for 60 seconds.
button_isr(uint gpio, uint32_t events) function:
This function is called automatically when the button state changes (specifically, on a
falling edge, i.e., when pressed).
It includes a debounce mechanism: it only registers a press if enough time
(DEBOUNCE_DELAY_MS) has passed since the last registered press.
If a valid press is detected, it sets g_button_pressed_event to true.
Wokwi Simulator Project:
Link to Wokwi Project: https://wokwi.com/projects/399760980234448897
Q.2
First Circuit: Shift a 0V to 3.3V PWM signal down by 1.65V, resulting in a signal that swings
from -1.65V to +1.65V. The peak-to-peak amplitude remains 3.3V.
Second Circuit: Shift the -1.65V to +1.65V signal back up by 1.65V, restoring it to the original
0V to 3.3V range.
Circuit 1: Shifting 0V to 3.3V -> -1.65V to +1.65V
6
ThinkClock Battery Labs (Assignment Answer sheet)
This can be achieved using an op-amp in a difference amplifier configuration.
The goal is Vout=Vpwm−Vref
Explanation:
U1 is an op-amp.
Resistors R1,R2,R3,R4 are all of equal value
The input PWM signal (VPWM) is applied to the non-inverting path (via R1 and R2
forming a voltage divider, with R2 to ground).
The reference voltage VREF (+1.65V) is applied to the inverting path (via R3 and R4
forming the feedback network).
The standard difference amplifier formula, when all four resistors are equal (R1=R2=R3=R4=R),
is:
VOUT1=VPWM−VREF
let's verify:
When VPWM=0V: VOUT1=0V−1.65V=−1.65V
When VPWM=3.3V: VOUT1=3.3V−1.65V=+1.65V
This circuit achieves the desired shift.
Circuit 2: Shifting -1.65V to +1.65V -> 0V to 3.3V
This can be achieved using an op-amp in a non-inverting summing amplifier configuration.
The goal is Vout=VIN_shifted+VREF,
where ,VIN_shifted is the output from Circuit 1.
Explanation:
U2 is an op-amp.
VIN_shifted (the output of Circuit 1) is one input.
VREF (+1.65V from the buffered divider) is the second input.
Resistors R5 and R6 (e.g., 10k$\Omega$ each) connect these inputs to the non-inverting
terminal (V+) of U2. The voltage at V+ will be the average: V+=(VIN_shifted+VREF)/2.
Resistors R7 and R8 (e.g., 10k$\Omega$ each) configure U2 as a non-inverting amplifier
with a gain of (1+R8/R7). If R7=R8, the gain is 2.
7
ThinkClock Battery Labs (Assignment Answer sheet)
The output voltage is: VOUT2=V+×(1+R8/R7) VOUT2=2VIN_shifted+VREF×2 VOUT2
=VIN_shifted+VREF
let's verify:
When VIN_shifted=−1.65V: VOUT2=−1.65V+1.65V=0V
When VIN_shifted=+1.65V: VOUT2=+1.65V+1.65V=+3.3V
This circuit successfully shifts the signal back to the original 0V to 3.3V range.
Notes on Op-Amp Power Supplies for Circuit 2:
The non-inverting input of U2 (V+) ranges from (−1.65V+1.65V)/2=0V to
(+1.65V+1.65V)/2=+1.65V.
The output VOUT2 ranges from 0V to 3.3V.
A carefully chosen single-supply op-amp (e.g., rail-to-rail input/output type) might
operate Circuit 2 with a single +5V supply and Ground, as its input common-mode range
and output swing requirements could be met. However, using dual supplies (like ±5V) as
for Circuit 1 ensures wider compatibility with general-purpose op-amps.
Q3)
Both diagrams illustrate methods to assemble a 3S3P battery pack (3 cells in series to achieve a
higher voltage, and 3 such sets in parallel to achieve higher capacity), but they differ in how the
cells are grouped. the pros and cons of each:
Topology-1: Series Strings in Parallel (3S3P) :
Pros:
Graceful degradation on open cell.
Simpler fault isolation at string level (with fusing)
Potentially simpler initial wiring for series connections: Assembling individual series
strings might be straightforward.
Cons:
Risk with a shorted cell
Cell imbalance within strings
Circulating currents between strings
Weakest string limits performance
Complex BMS requirements for cell-level data
8
ThinkClock Battery Labs (Assignment Answer sheet)
Topology-2: Parallel Groups in Series (3S3P)
Pros:
Self-balancing within parallel modules
Robustness to cell variation within a module: A slightly weaker cell
Simpler BMS voltage monitoring at module level: The BMS primarily needs to monitor
the voltage of each series-connected parallel module (e.g., 3 module voltages for this
3S(3P) pack). This results in fewer voltage tap points compared to monitoring every cell
in Topology-1.
Effect of an open cell in a module
Cons:
Catastrophic failure from a single shorted cell within a module
Requires cell-level fusing or high-quality matched cells
Capacity defined by the weakest module: The total capacity of the entire battery pack is
limited by the module with the lowest capacity (e.g., if one module becomes a 2P
equivalent due to an open cell, while others are 3P).
Difficult to identify individual failing cells within a module
Thermal concerns: A failing cell in a parallel group can generate significant heat, which
can easily conduct to adjacent cells within the same tightly packed module, potentially
leading to cascading thermal issues within that module.