Electronics

How to DC-sweep resistive voltage divider using PySpice

The following code simulates a resistive 10kΩ / 1kΩ voltage divider using PySpice using a DC sweep and can serve as a suitable starting point for simulating simple circuits. We sweep from 0V to 5V in steps of 10mV.

Also see our previous post How to simulate resistive voltage divider using PySpice for an alternate version using transient analysis:

import PySpice.Logging.Logging as Logging
logger = Logging.setup_logging()

from PySpice.Probe.Plot import plot
from PySpice.Spice.Netlist import Circuit
from PySpice.Unit import *

circuit = Circuit("MyCircuit")
# Create voltage source: 5V DC
source = circuit.VoltageSource('in', 'in', circuit.gnd, dc_value=5@u_V)
# Create resistor divider
r1 = circuit.R('R1', 'in', 'n1', 10@u_kΩ)
r2 = circuit.R('R2', 'n1', circuit.gnd, 1@u_kΩ)
# Simulate for 1 second with steps of 1 millisecond
simulator = circuit.simulator(temperature=25, nominal_temperature=25)
analysis = simulator.dc(Vin=slice(0, 5.0, 0.01))

You can access the array of output voltages of the divider (i.e. node n1) using analysis['n1']Keep in mind that if the first argument of circuit.VoltageSource is 'in', the argument to simulator.dc will be valled Vin,  not just  in! A Vwill automatically be  prepended!

 

This is the code we used to plot this:

import matplotlib.ticker as mtick
import matplotlib.pyplot as plt
from UliEngineering.EngineerIO import format_value

def format_volts(value, pos=None):
    return format_value(value, 'V')

plt.style.use("ggplot")
plt.xlabel("Input voltage")
plt.xlabel("Divider output voltage")
plt.gca().yaxis.set_major_formatter(mtick.FuncFormatter(format_volts))
plt.gca().xaxis.set_major_formatter(mtick.FuncFormatter(format_volts))
plt.gcf().set_size_inches(8,5)
plt.plot(analysis["in"], analysis["n1"], label="Input voltage")
plt.savefig("/ram/PySpice-Voltage-Divider-Sweep.svg")
Posted by Uli Köhler in Electronics, Python, SPICE

How to simulate resistive voltage divider using PySpice

The following code simulates a resistive 10kΩ / 1kΩ voltage divider using PySpice and can serve as a good starting point for simulating simple circuits.

This post shows you how to simulate the voltage divider using transient analysis. Also see an alternative variant of this post using DC sweep analysis instead: How to DC-sweep resistive voltage divider using PySpice

import PySpice.Logging.Logging as Logging
logger = Logging.setup_logging()

from PySpice.Probe.Plot import plot
from PySpice.Spice.Netlist import Circuit
from PySpice.Unit import *

circuit = Circuit("MyCircuit")
# Create voltage source: 5V DC
source = circuit.VoltageSource('V1', 'in', circuit.gnd, dc_value=5@u_V)
# Create resistor divider
r1 = circuit.R('R1', 'in', 'n1', 10@u_kΩ)
r2 = circuit.R('R2', 'n1', circuit.gnd, 1@u_kΩ)
# Simulate for 1 second with steps of 1 millisecond
simulator = circuit.simulator(temperature=25, nominal_temperature=25)
analysis = simulator.transient(step_time=1@u_ms, end_time=1@u_s)

You can access the array of output voltages of the divider (i.e. node n1) using analysis['n1']:

This is the code we used to plot this:

import matplotlib.ticker as mtick
import matplotlib.pyplot as plt
from UliEngineering.EngineerIO import format_value

def format_volts(value, pos=None):
    return format_value(value, 'V')

plt.style.use("ggplot")
plt.xlabel("Time [ms]")
plt.ylim([0.0, 5.5])
plt.gca().yaxis.set_major_formatter(mtick.FuncFormatter(format_volts))
plt.gcf().set_size_inches(8,5)
plt.plot(analysis["in"], label="Input voltage")
plt.plot(analysis["n1"], label="Voltage divider output")
plt.gca().legend()
plt.savefig("/ram/PySpice-Voltage-Divider.svg")

 

Posted by Uli Köhler in Electronics, Python, SPICE

How to create resistor in PySpice: Minimal example

This creates a resistor named R1 which has its + pin connected to node n1 and its - pin connected to GND. The value of the resistor is set to 1kΩ.

r1 = circuit.R('R1', 'n1', circuit.gnd, 1@u_kΩ)

Full example:

import PySpice.Logging.Logging as Logging
logger = Logging.setup_logging()

from PySpice.Probe.Plot import plot
from PySpice.Spice.Netlist import Circuit
from PySpice.Unit import *

circuit = Circuit("MyCircuit")
r1 = circuit.R('R1', 'n1', circuit.gnd, 1@u_kΩ)

 

Posted by Uli Köhler in Electronics, Python, SPICE

How to create constant voltage source in PySpice

This creates a voltage source named V1 which has its + pin connected to node n1 and its - pin connected to GND. The voltage source is set to 5V DC.

source = circuit.VoltageSource('V1', 'n1', circuit.gnd, dc_value=5@u_V)

Full example:

import PySpice.Logging.Logging as Logging
logger = Logging.setup_logging()

from PySpice.Probe.Plot import plot
from PySpice.Spice.Netlist import Circuit
from PySpice.Unit import *

circuit = Circuit("MyCircuit")
source = circuit.VoltageSource('V1', 'n1', circuit.gnd, dc_value=5@u_V)

 

Posted by Uli Köhler in Electronics, Python, SPICE

Simple OpAmp selectable gain amplifier: Select gain 1x or 10x via GPIO

Download KiCAD 6.x Selectable gain amplifier schematic SGA.kicad_sch

This circuit is a simple variant of a selectable gain amplifier.

If CTRL is pulled low (i.e. to GND), effectively the circuit will look like this:

Due to negligible input bias current for JFET opamps like the TL084 (i.e. most modern opamps below 100 MHz GBW), this circuit is basically just a Gain 1 buffer.

If CTRL is pulled high (i.e. pulled to 5V for example), the circuit effectively looks like this:


which has a gain of (\frac{10 k\Omega}{10 k\Omega + 90 k\Omega} = \frac{10 k\Omega}{100 k\Omega} = 10. Adjust these

One aspect to consider here is the RDSon of the MOSFET Q1. This is added to R1 and introduces a gain error to the system. The BSS123, for example, has a relatively high RDSon of up to 10 Ohms at VGS = 4.5V. For R1 = 10kOhm, the effective worst-case gain is:

\frac{10 k\Omega + 10 \Omega}{10 k\Omega + 10 \Omega + 90 k\Omega} = \frac{10 k\Omega}{100 k\Omega} = 10.001  or 0.01%. For most applications this does not matter since other aspects like the resistnance tolerance or resistance coefficient of temperature will affect the system error orders of magnitude This gain error will vary with temperature since the RDSon is temperature-sensitive and often the variation will be a larger issue than the gain error itself.

One way to mitigate is to choose large values for R1 and R2. Choosing R1=100kOhm and R2=900kOhm in this example will reduce both the gain error and the gain error temperature coefficient from the RDSon of Q1 by an order of magnitude. I recommend not to choose resistors larger than 1 MOhm without taking further consideration of opamp bias current, bias current temperatur drift etc, since those start to get relevant even for JFET input opamps when exceeding a couple of Megaohms.

Note that the RDSon of the MOSFET will be worse for VGS=3.3V will be worse and some MOSFETs will not switch on properly at this voltage. Choose a MOSFET that fits both your logic voltage and the RDSon i.e. gain error requirements. I often start with the BSS138 since I have many of those in stock, which tends to have less than 1 Ohm resistance in most practical conditions.

Another consideration for this circuit is that the MOSFET body diode will conduct if the voltage at the inverting input of the opamp will go beyond the Vf of the MOSFET body diode (typically you should use 0.7V here even if the specified body diode voltage is slightly higher, in order to have sufficient headroom). In effect, this typically means that you should not use this circuit without modification when using bipolar supplies and signals that go more negative than GND.

Posted by Uli Köhler in Analog, Electronics

How to check only a single schematic symbol against KLC (KiCAD library conventions)

In our previous post How to check KiCAD symbol library against KLC (KiCAD library conventions) we showed how to use check_symbol.py to check a symbol library against the KiCAD library guidelines.

You can also use it to check only a specific symbol in that library by name, which removes most of the clutter from the output. Use -c "CD4066B" to just check the component named CD4066B.

~/kicad-library-utils/klc-check/check_symbol.py MyLibrary.kicad_sym -vv -c "CD4066B"

This will print, for example:

Checking symbol 'Analog_Switch:CD4066B':
  Violating S3.1
    Origin is centered on the middle of the symbol
    Symbol unit 3 slightly off-center
     -   Center calculated @ (-25, -25)
    Symbol unit 4 slightly off-center
     -   Center calculated @ (-25, -25)
    Symbol unit 5 not centered on origin
     - Center calculated @ (-100, 0)
  Violating S4.1
    General pin requirements
    Pins not located on 100mil (=2.54mm) grid:
     - Pin C (6) @ (-400,50) 
     - Pin C2 (9) @ (350,-100) 
     - Pin D2 (10) @ (350,-100) 
     - Pin D (12) @ (-400,50) 
  Violating S5.2
    Footprint filters should match all appropriate footprints
    No footprint filters defined

 

Posted by Uli Köhler in Electronics, KiCAD

How to check KiCAD symbol library against KLC (KiCAD library conventions)

First, clone kicad-library-utils using

git clone https://gitlab.com/kicad/libraries/kicad-library-utils.git

Then, run the check script against your library using

~/kicad-library-utils/klc-check/check_symbol.py MyLibrary.kicad_sym -vv

You might need to adjust the path to kicad-library-utils accordingly.

This will provide colored output on the command line such as

Checking symbol 'Analog_Switch:FSA3157L6X':
Checking symbol 'Analog_Switch:NC7SB3157P6X':
  Violating S3.1
    Origin is centered on the middle of the symbol
    Symbol unit 1 not centered on origin
     - Center calculated @ (0, -112)
  Violating S3.6
    Pin name position offset
    Pin offset outside allowed range
     - Pin offset (5) should not be below 20mils

 

Posted by Uli Köhler in Electronics, KiCAD

How to connect CHIP_PU pin on ESP32Sx

The CHIP_PU (Chip PowerUp) pin on ESP32 family processors such as the ESP32S2 is equivalent to the EN (enable) or ~RST pin on other microcontrollers.

When CHIP_PU is pulled high (i.e. to 3.3V), the ESP32 is enabled and will run the firmware.

When CHIP_PU is pulled low (i.e. to GND), the ESP32 is disabled and shut down and will not run the firmware.

In other words, in most applications you want to connect a 10kOhm resistor from CHIP_PU to 3.3V and – if needed – a reset button from the CHIP_PU pin to GNDYou can not operate the ESP32S2 without a pullup resistor . This is clearly stated in the ESP32S2 datasheet, section 2.2:

Note: Do not leave the CHIP_PU pin floating.

On the ESP32S2-WROOM-I module, the CHIP_PU is directly connected to the module’s EN pin wut

Posted by Uli Köhler in ESP8266/ESP32

How much RAM does the ESP32-C3 have?

The ESP32-C3 has 400 kBytes of integrated SRAM (16 kbytes cache).

Source: ESP32-C3 datasheet

Posted by Uli Köhler in ESP8266/ESP32

Teensy 4.x timer interrupt example using TimerTree

void myTimerInterrupt() {
  // TODO Your code goes here
  // The following functions may be useful here:
  // Timer3.start(); // Start counting & clear counter
  // Timer3.stop(); // => stop counting but do not clear counter
  // Timer3.restart(); // => clear counter
  // Timer3.resume(); // => Start, without clearing counter
}

void setup()
{
  Timer3.initialize(20000);
  Timer3.attachInterrupt(myTimerInterrupt);
  Timer3.start();
}

 

Posted by Uli Köhler in Arduino, Electronics, Embedded, Teensy

How to Arduino attachInterrupt() with both RISING and FALLING edge

You can use attachInterupt() with CHANGE to trigger on both RISING and FALLING flanks.

#define INTERRUPT_PIN 13 // Choose any pin with interrupt functionality here.

void myInterrupt() {
  // TODO Your code goes here.
}

void setup() {
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), myInterrupt, CHANGE);
}

 

Posted by Uli Köhler in Arduino, Embedded

Teensy 4.1 Arduino external interrupt (pin interrupt) minimal example

In setup(), use

attachInterrupt(digitalPinToInterrupt(23), myInterrupt, RISING);

to configure the interrupt on Teensy Pin 23 on the RISING edge.

Add this function, which will be called during the interrupt:

void myInterrupt() {
  // Your code goes here
}

 

Posted by Uli Köhler in Electronics, Teensy

PyVISA Rigol DL3021 via LXI (TCP SCPI) example

First install using

sudo pip install pyvisa pyvisa-py

The following example will connec to a LXI-enabled (license required!!!) DL3021 at IP address 192.168.178.31

import pyvisa
import time

rm = pyvisa.ResourceManager()
inst = rm.open_resource("TCPIP0::192.168.178.112::INSTR")
# Query if instrument is present
# Prints e.g. "RIGOL TECHNOLOGIES,DL3021,DL3A204800938,00.01.05.00.01"
print(inst.query("*IDN?"))

# Set to constant resistance mode
inst.write(":SOURCE:FUNCTION RESISTANCE")
# Set to 3 Ohms
inst.write(":SOURCE:RESISTANCE:LEVEL:IMMEDIATE 3.0")
# Enable electronic load
inst.write(":SOURCE:INPUT:STATE On")
# Wait for value to stabilize
time.sleep(2)
# Measure!
print("Voltage: ", inst.query(":MEASURE:VOLTAGE?").strip())
print("Current: ", inst.query(":MEASURE:CURRENT?").strip())
print("Power: ", inst.query(":MEASURE:POWER?").strip())

This will print, for example:

RIGOL TECHNOLOGIES,DL3021,DL3A204800938,00.01.05.00.01

Voltage:  2.885810
Current:  0.961862
Power:  2.775752

with some power supply attached of course.

Posted by Uli Köhler in Electronics

How to fix st-flash ERROR common.c: stlink_flash_loader_run(0x8000000) failed! == -1

Problem:

While flashing an STM32 using st-flash using a command like

st-flash write build/firmware.bin 0x8000000

you see an error message like

2022-02-12T01:31:34 ERROR flash_loader.c: flash loader run error
2022-02-12T01:31:34 ERROR common.c: stlink_flash_loader_run(0x8000000) failed! == -1

See below for a full error log

Solution

Most likely your STM32 is locked (readout protection). Use OpenOCD to unlock it, see How to unlock STM32F0x using OpenOCD for an example. Adjust to your STM32 family as needed.

Full st-flash log

st-flash 1.6.1
2022-02-12T01:31:34 INFO common.c: F0xx small: 4 KiB SRAM, 16 KiB flash in at least 1 KiB pages.
file build/mom.bin md5 checksum: da211df7131de9de15f3b6c7a96176dd, stlink checksum: 0x000d0d03
2022-02-12T01:31:34 INFO common.c: Attempting to write 11108 (0x2b64) bytes to stm32 address: 134217728 (0x8000000)
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08000000 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08000400 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08000800 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08000c00 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08001000 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08001400 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08001800 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08001c00 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08002000 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08002400 erased
2022-02-12T01:31:34 INFO common.c: Flash page at addr: 0x08002800 erased
2022-02-12T01:31:34 INFO common.c: Finished erasing 11 pages of 1024 (0x400) bytes
2022-02-12T01:31:34 INFO common.c: Starting Flash write for VL/F0/F3/F1_XL core id
2022-02-12T01:31:34 INFO flash_loader.c: Successfully loaded flash loader in sram
2022-02-12T01:31:34 ERROR flash_loader.c: flash loader run error
2022-02-12T01:31:34 ERROR common.c: stlink_flash_loader_run(0x8000000) failed! == -1
stlink_fwrite_flash() == -1

 

Posted by Uli Köhler in STM32

How to flash .bin to STM32 using st-flash

You can use st-flash like this to flash a firmware file to the STM32:

st-flash write build/firmware.bin 0x8000000

 

Posted by Uli Köhler in STM32

How to lock STM32F0 using OpenOCD (readout protection)

openocd -f interface/stlink-v2.cfg -f target/stm32f0x.cfg -c "init" -c "halt" -c "stm32f1x lock 0" -c "reset halt" -c "exit"

This will activate flash readout protection level 1 which means you won’t be able to readout or re-write the flash. You can still perform a chip erase which will clear the readout protection – for example to flash a new firmware.

Posted by Uli Köhler in STM32