Phipps Electronics

$0.00
0

SHOPPING LIST

0
Subtotal: $0.00
No products in the cart.
$0.00
0

SHOPPING LIST

0
Subtotal: $0.00
No products in the cart.

FREE SHIPPING AUSTRALIA WIDE

50,000+ ORDERS

WORLDWIDE SHIPPING

SSL SECURED

Employment

Welcome to the employment hub for Phipps Electronics, here you will find positions we are currently looking to hire for, if you see a role you would like to apply for, please use the form at the bottom of the page to apply.

If there are no advertised jobs of interest, you can still send through your information for consideration for future roles, to do this, select “Interest for future positions” from the JOB ID drop down.

Bipolar Junction Transistor (BJT) Primer

A BJT is a very popular electronic component. If you want to find out more about it, how it functions, and its applications, read through the rest of this article.

Introduction

Bipolar Junction Transistors (commonly called BJTs) have been in the electronics world since their conception in 1948 (Hoerni).  The BJT is a three-terminal semiconductor device that finds so many uses in electronics. They can be used as an electronic switch to turn on loads, as an amplifier to amplify small signals, and as a building block for several Integrated Circuits back then. Most electronics students start learning BJTs as their first logic switch and incorporating them in their first amplifier circuit.

Though BJTs have been surpassed by other transistor technologies in terms of current draw and material density, they are still being widely used today. You’ll likely find one if you open up a complicated consumer electronic device. Consequently, an added benefit of BJTs is that they are more resilient to electrostatic discharge than their FET counterpart.

To learn more about BJT’s composition and application, continue to the following parts.

What’s the composition of a BJT?

BJTs have three terminals called Emitter, Base, and Collector. Each terminal represents the type of semiconductor used. It’s either an N-type or a P-type material. Every two adjacent terminals represent a junction called a PN junction. Each junction should be biased correctly for proper device operation.

With this, there are two kinds of BJT transistors, namely, the NPN type and the PNP type. They are illustrated below, and they each have their own schematic symbols.

NPN transistor

Terminals: 

Figure 1: NPN transistor composition

Schematic Symbol:

Figure 2: NPN Transistor Schematic Symbol

PNP transistor

Terminals:

Figure 3: PNP transistor composition

Schematic Symbol:

Figure 4: NPN Transistor Schematic Symbol

What are the Uses of a BJT?

A BJT transistor can be used as an electronic switch or a signal amplifier. 

BJT as an electronic switch

As a switch, you’ll need to forward bias the base-emitter junction with a voltage greater than 0.7V to turn it on. After overcoming this 0.7V threshold, the collector-emitter loop will begin to allow a generous amount of current flow. This current flow can power up a load and is only limited by the load’s resistance and the applied voltage. Ensure your transistor can handle the current and voltage required by your load.

Figure 5: BJT used as a switch

Here is a basic NPN transistor used as a low-side switch. Low-side because the switching element (transistor Q1) is on the low side of the load and also directly connected to the ground. R1 is the load and can be an LED or another peripheral. R2 is the base resistor that limits the base current, while R3 is there simply to pull down the base-emitter voltage to a low level when the base terminal is left floating.

When the input goes beyond 0.7V (+5V in this example), a base current will flow. This base current will be multiplied by several magnitudes and will be reflected on the collector-emitter side as collector current Ic. If the collector current is limited by the load’s resistance and the voltage supply (+12V), the transistor is said to be in saturation. Saturation mode is the ideal mode for a transistor to be used as an electronic switch.

Figure 6: BJT used in a motor application

If your load is a motor or has inductive properties, there needs to be a diode connected in parallel to your load to act as a protection device. This diode will effectively block back-EMF signals that can destroy your transistor or your controlling circuit. These back-EMF signals are high-voltage transients that happen as you turn off your BJTs coming from the ON state. This happens due to the inductor storing energy in the form of current. When you release the transistor from the ON state, that energy finds a path through your transistor and increases in voltage. However, if you have the back-emf diode installed, the path will be shorted out and the transient will be absorbed by the body of the diode.

BJT as an Amplifier

An amplifier amplifies a small signal to a larger signal. The most common configuration for a BJT used as an amplifier is the common emitter configuration. Common emitter means that the emitter is common to both the input and output signals.

Figure 7: BJT transistor as an amplifier

The figure above is an example of this. This is called a Beta independent voltage divider transistor amplifier. Beta is a symbol to represent the current amplification factor ratio between the collector current Ic and the base current Ib. Beta is the reason we have amplification in a transistor circuit.

Figure 8: BJT Characteristic curve

It is important to note that you should operate a BJT in the linear region when using it as an amplifier. Above, you can see a graph of a transistor characteristic curve of the popular 9013 transistors. You can use a similar graph like this to find the linear region of your transistor. The linear region is usually near the centre where there is no distortion on the graph.

In order to get to the linear region, you should bias the BJT. Figure 7 illustrates this. There are four resistors used as biasing elements, namely RB1, RB2, RC, and RE. The voltage divider RB1 and RB2 adjust the base current Ib. This also has the potential to stabilize the Beta of the circuit. RC biases the collector current by defining the Vce or collector-to-emitter voltage of the circuit. Lastly, there is RE. RE is an emitter stabilizer. Its function is to stabilize Beta. It also helps the amplifier circuit to lessen the effect of temperature on several of its parameters.

Additionally, there are 3 capacitors in the circuit. Cin and Cout bocks DC voltage so that only AC signals pass through the inputs and outputs. CE is part of the emitter stabilizer circuit. It allows AC current to be amplified but rejects the DC component.

As you can see, a small signal on the input is amplified by several magnitudes at the output. The factor by which voltage is amplified is called the gain of the circuit.

Conclusion

BJTs are useful electronic devices that can be used as a switch or an amplifier in your circuits. They are often the first type of transistor device to be taught in schools. If used as a switch, make sure your transistor is up to spec with your load. Also, make sure to use protective diodes when driving motors or inductive loads. Simple amplifier circuits can be illustrated easily using BJTs. Make sure to look at your transistor datasheet to be able to set your parameters safely and correctly.

Work Cited

Hoerni, Jean. “Bipolar junction transistor.” Wikipedia, https://en.wikipedia.org/wiki/Bipolar_junction_transistor#History. Accessed 20 September 2022.

Controlling Shift Registers Easily with Arduino

Shift Registers are monolithic integrated circuits (ICs) that contain several flip-flops. Each of these flip-flops is called a register. Each register stores information in the form of a bit. Shift registers can clock out each bit continuously in a serial fashion and this is what they are known for.

Below is a simple tutorial that will explain how to control the pins of a shift register easily using an Arduino. Yes, that’s right, you don’t have to spend an enormous amount of effort designing a complex circuit to control your shift registers.

ShiftRegister74HC595 Library

The shift register library is readily available in the Arduino IDE library. All you have to do is download and install it.

First, go to Sketch->Include Library->Manage Libraries…

Next, type ShiftRegister 74HC595 and choose the library made by Timo Denk. After that, install it.

Don’t forget to include it in your code by going to Sketch->Include Library->ShiftRegister 74HC595

Now, let’s implement the circuit below to know how to use the library.


Circuit Diagram

Wire up the circuit above as best as you can. In the circuit, pins 11, 12, and 14 of the 74HC595 can be attached to any of the digital pins of Arduino. In that case, you have to declare the correct pin numbers that you used in the circuit within the code.

After constructing the circuit we can start to program. The library has a few functions and each of them will be explained in detail.

Creating the Object

We start by creating the object. The object needs to be created outside the setup or loop function. So, we start by declaring a few global variables then we create the object:

#include <ShiftRegister74HC595.h>

int numberOfShiftRegisters = 1; // number of shift registers attached in series

int serialDataPin = 8; // DS
int clockPin = 9; // SHCP
int latchPin = 10; // STCP

ShiftRegister74HC595 sr (numberOfShiftRegisters, serialDataPin, clockPin, latchPin);

We are importing the library in the first line. Next, we associate some shift register pins to the corresponding Arduino pin. Finally, in the last line, we create an object called “sr” using a variable declared previously. We’ll also declare how many shift registers we are using in the first argument. In our case, we are only driving 1 shift register, however, we could also drive several using this same library.

Make sure that, this code segment is always at the top.

Next, we’ll learn how to use some functions for the 74HC595 using the library.

Turning ON all the LEDs

#include <ShiftRegister74HC595.h>

int numberOfShiftRegisters = 1; // number of shift registers attached in series
int serialDataPin = 8; // DS
int clockPin = 9; // SHCP
int latchPin = 10; // STCP

// create shift register object (number of shift registers, data pin, clock pin, latch pin)
ShiftRegister74HC595 sr (numberOfShiftRegisters, serialDataPin, clockPin, latchPin); 
 
void setup() { 

Serial.begin(9600);
}

void loop() {

//Set all pins HIGH
sr.setAllHigh();
delay(500);

//Set all pins LOW
  sr.setAllLow(); 
  delay(500); 

}

The setAllHigh( ) and setAllLow( ) functions turns ON and OFF all the LEDs of the 74HC595

Turning pins HIGH and LOW one by one

In this code, we will first turn all the LEDs ON one by one, then turn them off again. The function we will be using for this is set(i, HIGH)

The set( ) function works similarly to the digitalWrite() function. The first argument passed is the pin number. This pin number indicates the output pin numbers of the Shift Register. The 2nd argument is the state you want it to be in (HIGH or LOW).

#include <ShiftRegister74HC595.h>

int numberOfShiftRegisters = 1; // number of shift registers attached in series
int serialDataPin = 8; // DS
int clockPin = 9; // SHCP
int latchPin = 10; // STCP

// create shift register object (number of shift registers, data pin, clock pin, latch pin)
ShiftRegister74HC595 sr (numberOfShiftRegisters, serialDataPin, clockPin, latchPin); 
 
void setup() { 

Serial.begin(9600);
}

void loop() {

//Set pins HIGH one by one  
for (int i = 0; i < 8; i++) {   
sr.set(i, HIGH); // 
delay(250); 
}

//Set pins LOW again one by one   
for (int i = 8; i >= 0; i--) {
    
sr.set(i, LOW); 
delay(250); 
}
}

Configuring the pins all at once

This code can change each output pin state all at once with just a few lines.

#include <ShiftRegister74HC595.h>

int numberOfShiftRegisters = 1; // number of shift registers attached in series
int serialDataPin = 8; // DS
int clockPin = 9; // SHCP
int latchPin = 10; // STCP

// create shift register object (number of shift registers, data pin, clock pin, latch pin)
ShiftRegister74HC595 sr (numberOfShiftRegisters, serialDataPin, clockPin, latchPin); 
 
void setup() { 

Serial.begin(9600);
}

void loop() {
  
//0 and 1s are the pattern you want your LEDs to light up like
uint8_t pinValues1[] = { B10101010 }; 
sr.setAll(pinValues1); 
delay(1000);

uint8_t pinValues2[] = { B01010101 }; 
sr.setAll(pinValues2); 
delay(1000);

}

Reading the state of an OUTPUT pin

We can also know the state of output pins using a library function. In this code, we lit up some LEDs. After that, using the sr.get( ) function, we’ll know the current state of the output pin. Next, we’ll write the states of each pin in the serial monitor where 0 means LOW and 1 means HIGH.

#include <ShiftRegister74HC595.h>

int numberOfShiftRegisters = 1; // number of shift registers attached in series
int serialDataPin = 8; // DS
int clockPin = 9; // SHCP
int latchPin = 10; // STCP

// create shift register object (number of shift registers, data pin, clock pin, latch pin)
ShiftRegister74HC595 sr (numberOfShiftRegisters, serialDataPin, clockPin, latchPin); 
 
void setup() { 

Serial.begin(9600);
}

void loop() {
uint8_t pinValues1[] = { B10101010 }; 
sr.setAll(pinValues1); 
delay(1000);

Serial.println(sr.get(4));
Serial.println(sr.get(5));
}

Conclusion

Implementing a circuit having a 74HC595 chip is relatively easy by using the ShiftRegister 74HC595 Arduino library. All it takes are some pin and object declarations at the start of your code. Next, using the library functions will provide all the necessary steps to operate the 74HC595 with ease.

I2C Serial Protocol Explained – with Arduino Example Code

Description: Are you having issues with your I2C Arduino codes? Want to do some low-level debugging on it? This article will help you by discussing in-depth I2C concepts.

Introduction:

I2C (or Inter-integrated circuit) is a serial protocol that’s been in the electronics world for several decades. It has proved its robustness and effectiveness in delivering reliable serial communication between different devices. Its primary purpose is for inter-chip communication between a master and a slave device.

In this article, we’ll discuss basic I2C concepts and the standard protocol and give practical applications so you can exercise the concepts on your own. Once you familiarise yourself with I2C, you should be able to debug any I2C application or project without hassle.

We’ll also execute some Arduino I2C codes. With this, you’ll be able to use I2C on several of your hobby projects. You’ll notice that most sensors and peripherals use I2C as a communication interface, so get ready to make a list of your favourite sensors and purchase them when you want to try them out.

Part 1. How does I2C work?

The I2C interface works on 2-wires: the SDA (or Serial Data) and SCL (Or Serial Clock) lines. Each of these lines can be driven by open-drain type drivers. With these kinds of drivers, a pull-up resistor is required.

Note that I2C needs a Master and a Slave device for communication to occur. I2C can have multi-master or multi-slave setups, although multi-slave setups are more frequently used. There might be special requirements if you do multi-master setups.

With a multi-slave setup, a master starts a transaction and finds its target slave device by writing the slave’s correct address on the I2C bus. Only the slave with the correct address will acknowledge the master. Hence other slave devices will ignore or NACK this request.

Upon acknowledgement of the correct slave device, several other transactions can follow. These transactions could be single or multiple byte reads or writes, still initialized by the master. These operations determine what kind of slave device you are using.

Here is a typical I2C read waveform from a slave device using a logic analyzer with a protocol analyzer function.

This waveform contains different states and levels on the SCL and SDA lines both of which define different states in the I2C protocol:

A start bit that initiates the start of any I2C transaction.

A stop bit that ends every I2C transaction.

An Acknowledge bit for every address or data reads or writes.

A not acknowledge bit for not acknowledging address or data reads or writes.

The next part will learn more about these states and the I2C protocol.

Part 2. The Serial Protocol of I2C

Most serial interfaces employ a standard protocol to effectively deliver their messages in a reliable way with minimal issues. I2C is no exception. Here we’ll discuss the I2C protocol for you to understand.

A Master usually initiates the first transaction and starts with a bit. This is followed by a preamble (1010), the device address, and a write bit. This is called the control byte and should be acknowledged by the addressed slave so that continuous byte reads or writes can proceed.

Once the slave acknowledges, a series of transactions occur that depend on your device’s state machine.

Consider reading single or multiple bytes from a slave EEPROM device.

The control byte is sent, followed by an acknowledgement by the slave. Next, the master sends the memory location to read from the slave. This is followed by an ACK on the slave side. The next step is to fetch the data from that memory location. Accordingly, another control byte is written by the master to the slave, now with a read bit, which is also ACK’ed by the slave. Once the ACK is accepted, the master relinquishes control of the SDA line to the slave as the slave clocks in the data to the master. Each data byte transfer must be ACKe’d by the master. On the ninth clock pulse after the last data byte, a NACK occurs (by the master floating the SDA line and letting the slave act on it), indicating that it’s the last data byte transfer needed by the master (or a possible overflow or neglected data from the slave can also occur); thereafter, a stop bit is generated by the master as it does not need more data, indicating the end of the whole transaction.

Confluently, a byte write is similar to a byte read in several aspects. In a byte write, however, the master doesn’t need to clock in data and restart to send the control byte, as seen below.

The processes shown above are for byte reads and writes on an 8-bit memory addressed EEPROM. This process extends to any device with an I2C interface, changing only the number of bytes read or written or if there is a need to access a memory location or not. The next part discusses several applications of I2C.

Part 3. What are I2C’s applications, advantages, and disadvantages?

Like an EEPROM, I2C applications extend to several peripherals such as OLEDs, LCDs, accelerometers, temperature sensors, and a whole lot more. Because it uses only two wires as its interface, you can save a lot of space on your boards.

A disadvantage of I2C can be that it has a limited speed compared to other serial protocols. Although newer I2C speed standards are coming up, a high-speed I2C will only run at a clock rate of 400 kHz, quite slow compared to SPI, which can run at clock speeds in the MHz range. I2C speed is limited by what its open-drain driver can handle as it employs bi-directional communication.

Additionally, the length of traces that I2C can handle is limited to intra-board communication. This means you can only use I2C with chips communicating on a single board. It is not the intention of I2C to run on long lengths of cables, unlike what you can do with EUSART for example.

Part 4. Code examples

Fortunately, all the messy I2C protocols are abstracted from the user for Arduino. However, if you’d like to know what’s going on, continue reading Part 1 and Part 2 of this article. You’ll never know if you might need low-level debugging, especially if you have a new I2C device that encounters an issue.

Here is a simple master I2C code that writes the character ‘X’ on the I2C bus every second.

#include <Wire.h>
byte toggle;

void setup() {
  // put your setup code here, to run once:
  pinMode(LED_BUILTIN, OUTPUT);
  Wire.begin();

}

void loop() {
  // put your main code here, to run repeatedly:
  
  Wire.beginTransmission(8);
  Wire.write("X");
  Wire.endTransmission();

  if(toggle == 1)
    digitalWrite(LED_BUILTIN, HIGH);
  else
    digitalWrite(LED_BUILTIN, LOW);

  toggle ^= 1;
    
  delay(1000);
}

Wire.begin( ) initializes the I2C master peripheral. To begin transmission Wire.beginTransmission(8) is executed. Parameter 8 is the address of the slave. As we check the protocol, this is the control byte written on the I2C bus.

The code continues to write a character on the line, which signifies a byte write (without a corresponding memory address) on the protocol.

Here is the slave code that receives the character ‘X’.

#include <Wire.h>

volatile byte received = 0;
char c;
byte toggle;

void setup() {
  // put your setup code here, to run once:
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(9600);
  digitalWrite(LED_BUILTIN, LOW);
  Wire.begin(8);
  Wire.onReceive(receiveEvent);
}

void loop() {
  // put your main code here, to run repeatedly:
  if(received)
  {
    if(c == 'X')
    {
      Serial.println(c);
      if(toggle)
        digitalWrite(LED_BUILTIN, HIGH);
      else
        digitalWrite(LED_BUILTIN, LOW);
        
      toggle ^= 1;
    }
    received = 0;
  }
    
}

void receiveEvent(int howMany) {
int i;  
  for(i=0;i<howMany;i++)
  {
    c = Wire.read();
  }
    
  received = 1;
}

Wire.begin(8) sets up the I2C peripheral with slave address 8. Then a receiveEvent( ) function is registered with the I2C receive operation using the Wire.onReceive(receiveEvent) statement. Notice that the receiveEvent( ) function has an integer parameter called howMAny. This parameter counts the number of bytes received by the slave. Consequently, you can use this parameter to coherently retrieve data from the I2C buffers. With this, we used a for-loop to execute the Wire.read( ) function. Once we have received the data on this I2C event, we go back to the main loop to process it.

Same as the previous code, the I2C protocol utilized here is the byte write, where the master writes data to the slave. The slave should act accordingly to this as a receive event, acknowledging each byte that is written by the master.

On a different note, below is a sample code from a master requesting data from a slave.

#include <Wire.h>
char c;
byte toggle;

void setup() {
  // put your setup code here, to run once:
  
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(9600);
  Wire.begin();

}

void loop() {
  // put your main code here, to run repeatedly:
  
  Wire.beginTransmission(8);
  Wire.requestFrom(8, 1);
  while(Wire.available())
  {
    c = Wire.read();
  }
  Wire.endTransmission();

  Serial.println(c);

  if(toggle == 1)
    digitalWrite(LED_BUILTIN, HIGH);
  else
    digitalWrite(LED_BUILTIN, LOW);

  toggle ^= 1;
    
  delay(1000);
}

Here, the Wire.beginTransmission(8) functions the same as the previous code that writes a character to the slave. Next, a Wire.requestFrom(8, 1) statement executes to request (or read) data from a slave. Parameters 8 and 1 are the slave address and the number of data to read, respectively. In the protocol, this functions as a control byte with the read bit enabled. After that, a character read executes through the Wire. read( ) function while checking how many data reads to do to the slave. This piece of code functions similarly to the read byte function of the protocol in Part 2 (without memory addresses).

Lastly, here is the slave code that gives the character data to the master when the master requests it.

#include <Wire.h>

volatile byte requested = 0;
char c;
byte toggle;

void setup() {
  // put your setup code here, to run once:
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(9600);
  digitalWrite(LED_BUILTIN, LOW);
  Wire.begin(8);
  Wire.onRequest(requestEvent);
}

void loop() {
  // put your main code here, to run repeatedly:
  
  if(requested)
  { 
    // do other things on request
    if(toggle)
      digitalWrite(LED_BUILTIN, HIGH);
    else
      digitalWrite(LED_BUILTIN, LOW);    
    toggle ^= 1;
     
    requested = 0;
  }
        
}

void requestEvent() {
    Wire.write("X");
    requested = 1;
}

Here a requestEvent( ) function is registered by Wire.onRequest( ). This function runs automatically when the slave is requested by the master to send its data through the Wire.write( ) function. This code relates to the time when the master relinquishes control of the SDA line for the slave to clock in its data as in Part 2 of this article.

Part 5. Summary

We demonstrated the inner workings of I2C along with its protocol. We also listed down its applications, advantages, and disadvantages. Lastly, we made sample codes demonstrating the functions of I2C on an Arduino, relating them to the actual I2C protocol.

Learning the basics of I2C will be very useful for your projects requiring I2C interfaces. At Phipps Electronics, we have a number of kits and tools to help you with this. Browse through our development tools catalog for more info. For looking closely at your I2C hardware, we recommend using the 8-Channel Logic Analyzer from our Test Equipment section.

Basic Linear Power Supply Fundamentals

Would you like to set up a low-cost power supply for your workbench? Want to learn power supply fundamentals with it? Read the rest of the article to find out more.

Introduction

A power supply is basic equipment for any electronics workbench. You’ll always need this to power up your circuits or prototypes. If you build one, make sure you know the fundamentals that go with it. Otherwise, you might end up with a power supply that’s not up to your specification.

In this article, you’ll learn about the different stages involved in a basic linear power supply. You’ll learn each of their function along with example circuits. You can use this as a mini resource to create your power supply. Consequently, browse through our parts list in our store to find different kinds of power supply parts.

What are the parts or stages of a basic linear power supply?

Step-Down Stage

If you’re sourcing power from your mains, a step-down converter stage is necessary. This will ensure your mains 110V/220V is stepped down just enough to be used by your power supply circuit. Without it, it’s almost impossible to work on the source supply because it’s too high. This will require a more expensive components sandwich you’ll want to avoid.

The step-down converter is usually a transformer. You can find this at your local electronics shop. Depending on the power rating of your power supply, transformers can get a bit bulky. However, they provide sound isolation from your mains.

The transformer has both a primary and a secondary winding, and they are effectively isolated from one another. Your mains are usually connected to the primary winding, and one of your mains terminals is connected or has a return path to earth ground. By employing isolation, you avoid being electrocuted through a current path from your mains.

The transformer’s secondary winding connects to your power supply circuit. It can be connected on two ends, centre-tapped or multi-tapped, depending on your transformer type and your rectifier circuit that will be discussed next.

Rectification Stage

Because the output of the secondary winding of your transformer is AC (Alternating Current), it must be transformed into a DC (Direct Current) type (at least a pulsating DC) so that it can be used for DC circuits. A rectification stage is necessary. This stage is composed of rectifier diodes. Below are some examples of rectifier diodes.

The diode’s size is usually in proportion to the current capacity it can handle. Common rectifier diodes are the 1N4001 to 1N4007 series and the 1N5401 – 1N5408 series. 1N400X’s are designed to handle about an ampere of current, while the 1N540x’s are for 3.0 amps. The number at the end of their names signifies the amount of reverse voltage they can handle. Below is an example of a part of a datasheet for the 1N400X and 1N540x series.

These diodes may be connected to create a full-wave or a half-wave rectifier. Full-wave rectifiers create full-wave pulsating DC waveforms. Half-wave rectifiers create half-wave pulsating DC waveforms, as seen below.

There are different topologies in using a rectifier circuit. This also depends on your transformer type. Center-tapped transformers require fewer diodes to create full-wave waveforms. Transformers with two terminals on their secondary windings may need a bridge-type rectifier composed of four diodes to create full-wave waveforms. To create half-wave waveforms, just a single diode can be used.

Here are several examples of rectifier circuits:

An example of a half-wave rectifier

An example full-wave rectifier using a centre-tapped transformer

An example of a full-wave bridge-type rectifier using a regular two-terminal transformer.

Filtering stage

After the rectification stage, a filtering stage comes next. This smoothens out the pulsating DC output creating an almost perfect DC waveform. The component used here is a capacitor. The capacitor charges during the pulsating DC waveforms but does not fully discharge. This effectively creates a waveform with lesser ripple as can be seen below:

You’ll have to use bulk capacitors as a power supply filter. This is due to the slow frequency of the mains at 50 or 60 HZ. It’s ideal to use a large capacitor value (typically 1000uf – 2200uf and above) as your power supply increases in current capacity. The larger the value, the longer it can hold its charge while supplying power to your load.

Regulation stage

The regulation stage comprises discrete or integrated circuit semiconductors and passives. This stage tries to maintain a constant output voltage to deliver the right amount of current to your load. This stage also smoothens any ripples left from the filter stage. Typically, a voltage regulator is employed here. An example of a voltage regulator is a simple transistor-Zener diode combination, as seen in the figure.

Here a constant voltage is generated across the Zener diode D1 through the current path from the +15V source and resistor R1. C1 stabilises this constant voltage from any fluctuations coming from the source power supply. Transistor Q1 serves as a pass transistor to supply enough current to the load. The total voltage on the output is the Zener diode voltage minus the Vbe drop (0.7v) on the base-emitter junction of the transistor.

A more frequently used type of voltage regulator is the 78XX series. It’s a 3-terminal integrated circuit that’s specialized for this purpose. It incorporates current limit, short circuit, and thermal protection features. The IC is available in many fixed voltage values, such as 12V for 7812, 9V for 7909, 5V for 7805, and others. It can supply about 1.5 amps of current (max). A variable IC voltage regulator can also be used, such as the LM317.

Here is a complete circuit for a 12V/1A regulated power supply. The voltage regulator used is a 7812. Note that there are two smaller capacitors after the bulk filter capacitor C1. They were added for the stability of the 7812. C2 is needed for input stability, while C3 is for output stability.

Conclusion

A basic linear power supply consists of a step-down, rectifier, filter, and regulator stage. Each of these stages was explained. Different transformers, rectifiers, filters, and regulators were also explored. Several circuits were made to illustrate the workings of a linear power supply. Upon reading this article, you should be able to create a basic linear power supply to power your circuits and prototypes.

Using Multiple 7 Segment Displays with Shift Registers

Seven segment displays have a total of 10 pins (including VCC and GND). In order to display something on a 7-segment display using an Arduino, approximately 7 digital I/O pins would be required. Because of this, it would be more practical to use shift registers to drive 7-segment displays.

The Arduino UNO has 14 digital I/O pins in total. Therefore using the majority of the pins for the 7-segment display is inefficient. If you think about it, you can utilize these pins for other functions. The use of Shift Registers and the Shift Register library can overcome such issues.

Equipment Needed:

  • Arduino Uno
  • External 5V Power Supply
  • 7 segment display – 2 pieces
  • Shift Registers – 2 pieces
  • 680 ohms resistors – 14 pieces
  • Breadboard
  • Connecting wires

Shift Registers:

Shift Registers are sequential logic circuits that can be used to store and transfer binary data. These components store data in registers that are moved or “shifted” to the required bit-positions on each clock pulse. The registers are loaded with serial data, one bit at a time, with the stored data made available at the output pins in parallel form.

Please refer to the following tutorials for additional information:

The ShiftRegister74HC595 Library:

This library simplifies shift register usage. For example, the libraries’ functions can be used to set shift register pins just like normal Arduino pins (e.g. sr.set(1, HIGH)).

If you want to know more about the shift register library, its author Tim Denk explains this well.

Schematic Diagram:

These are the connections for two 7-segment displays driven by shift registers on an Arduino UNO. The wiring looks complicated because some of them overlap each other. However, it’s important to ensure the pins connected to the Arduino from the Shift registers and the pins from the seven segments to the shift registers are properly connected as shown from the recommendation of Tim Denk in the diagram below.

Connection explanation:

Pin numbers Q0 to Q6 of the shift registers should be connected sequentially to the LCD display pins (A-G).


For example:

  • The Q0 pin of the Shift register connects to Pin-A of the display.
  • The Q1 pin of the Shift register connects to Pin-B of the display.
  • The Q2 pin of the Shift register connects to Pin-C of the display.
  • and so on…

You’ll be able to relate these pin assignments to entries in the num_array[10] array in the code later.

The ST-CP and SH-CP are the storage register and shift register clock pins of the shift registers. The Shift register library will take care of the functions of these pins. They are connected to digital pins D2 and D1 of the Arduino.

The DS or Serial Data Input pin of the first shift register is directly connected to Arduino’s D0 digital pin. This outputs serial data in line with the shift register clock. Meanwhile, the Q7′ output of the first shift register is connected in series to the DS pin of the second shift register. This makes cascading possible.

The use of an external power source is recommended to power the Shift Registers and LEDs. Connect the VCC of the Shift Register with an external power source and ensure the ground of that power source is common to all.

Code:

#include <ShiftRegister74HC595.h>

uint8_t num_array[10] = { B00111111,    // 0
                          B00000110,    // 1
                          B01011011,    // 2
                          B01001111,    // 3
                          B01100110,    // 4
                          B01101101,    // 5
                          B01111101,    // 6
                          B00000111,    // 7
                          B01111111,    // 8
                          B01100111 };  // 9

// create shift register object (number of shift registers, data pin, clock pin, latch pin)
ShiftRegister74HC595 sr (2, 0, 1, 2); 
 
void setup() { 
}

void loop() {
  
  uint8_t pinValues[2] = { B00000000, B00000000 };
  
  for (int i = 0; i < 100; i++) {
  int x = i/10;
  int y = i-(x*10);

  pinValues[0] = num_array[y];
  pinValues[1] = num_array[x];
  
  sr.setAll(pinValues);
  delay(100);
  }
    
}

Code explanation:

The ShiftRegister74HC595 sr (2, 0, 1, 2) statement states that a ShiftRegister74HC595 object is being created. The first argument is the number of Shift Registers to be used. Any number of shift registers can be used based on your project. These shift registers can be controlled easily using the library.


Below is a program that counts from 0 to 99 while displaying the numbers on the 7-segment LED display. The Num_array array of variables holds the seven segments’ LED assignments with respect to the displayed numbers.

The for-loop inside the loop function counts from the numbers 0 to 99. The 2 numerals in the count are broken down into 2-digit numbers by a mathematical operation on the variable i. The numbers appear on display after executing the sr.setAll( ) function from the Shift Register library.

This example can be extended to enable any number of Shift Registers to be used with 7-segments displays.


SHOP THE PROJECT

Writing your first RTOS program using STM32CubeMX

Have you experienced a dilemma wherein you need to run several peripherals or tasks at the same time? After that, you realize you don’t want to write a complex state machine for the given scenario. This thinking may be all too common, and RTOS is here to help.

Introduction:

Throughout the years, computer hardware was developed so it could run different kinds of tasks and peripherals. With this, software developers realized the need to manage different kinds of resources more efficiently and reliably. This gave way for them to create operating systems that schedule all of these tasks along with proper peripheral use.

RTOS stands for Real-Time Operating System. As its name implies, it’s an operating system (same as for computers) that’s dedicated to smaller embedded systems and microcontrollers. RTOS does the nitty-gritty stuff of scheduling different kinds of competing tasks (and peripherals) that can take hold of your MCU’s precious processing time.

We’ll try to demonstrate a simple RTOS application so you can understand the basic concepts of task scheduling.

Part 1. Why use an RTOS?

Writing complex state machines to line up the execution of different tasks can be avoided using a real-time operating system. This is also why software engineers tend to avoid old-fashioned “bare-metal” code schemes when making complicated and large projects. The intricacies of “scheduling” a task become automated with RTOS. Additionally, resource sharing and messaging between tasks are also arranged by this intelligent system.

If you want a complete understanding of the RTOS theories and fundamentals, you can visit the FreeRTOS page. In this article, we’ll make actual code to get your RTOS fundamentals going.

We’ll create a demo that illustrates the concept of task scheduling using RTOS without the complexities of more advanced concepts such as inter-task communication. This will be discussed in the next part.

Part 2. An example scenario

You want to create separate tasks for each function that uses a peripheral in your microcontroller as listed below:

  1. You get input from a user through a series of menu choices executed through a UART interface that displays it on a PC. The goal of this task is to display a simple menu layout to capture the user’s choice and then execute a specific operation. This task has normal priority and occasionally sleeps for the same or lower priority tasks.
  • Let’s call this task as dispUARTTask( )
  1. A peripheral uses your MCU’s ADC to monitor the output voltage of a unit (think battery monitoring system). This task would always be running and may contain code to process data. This task is designated to have an above-normal priority. This task sleeps itself for a certain duration for other non-critical tasks with lower priority to function.
  • Let’s call this task as getADCTask( )
  1. A series of push-button switches must be monitored. These switches mimic certain operations assigned by the user. These switches are continuously monitored, the same as in the ADC, but only with normal priority. This task can allocate some sleep time while waiting for button presses giving time for other lower or same priority tasks to do their work. Incidentally, switch debounce time was used as a sleep state.
  • Let’s call this task as pickButtonTask( )
  1. Lastly, an external display device (an OLED) is used to extend the visibility of the data captured from your ADC. This task also has normal priority, and you have the option of just letting the task finish its work (instead of sleeping) to have a finer output. Other tasks with the same priority can take turns in utilizing MCU processing time.
  • Let’s call this task as dispOLEDTask( )

Note that in RTOS, tasks with the same priorities can take turns executing their individual codes with each other, while tasks with higher priorities will always hog the MCU’s processing time. Because of this, it’s important to let higher-priority tasks sleep for a certain duration for other lower-priority tasks to function. To sleep we’ll use the osDelay( ) function. OsDelay( ) can also be used for other lower priority tasks while waiting for an event to happen (like UART or button presses) so as not to waste MCU instruction cycles. We’ll look at the process and intricacies of the code in the next part.

Part 3. Making firmware

We’ll be using the Blue Pill and the STM32Cube IDE to demo basic RTOS functionality. The Blue Pill has an Arm Cortex M3 processor running at a maximum clock rate of 72 MHz with 20kB of RAM. These specs make it ideal for RTOS tasks. You can get the BluePill through Phipp’s Electronics through this link. Additionally, many development boards and kits are available for you to practice making your RTOS codes.

Initially, you need to download STM32CubeIDE here. It’s a complete development environment that can set up your MCU’s peripherals (using a neat GUI tool called STM32CubeMX) and then continue developing and debugging code through its versatile editor.

After downloading the STM32CubeIDE installer, run it and install it on your system. Next, open the STM32CubeIDE program and try to familiarize yourself with the environment. Next, follow the steps below:

  1. Click File -> New -> STM32 Project.
  1. A target selector window will appear, which is part of the STM32CubeMX program. Enter STM32F103C8 as the part number, as this is what the BluePill has. Then select that part from the MCU/MPU list.
  1. Click next and enter a project name and directory.
  1. Click next until it finishes.
  1. The GUI of CubeMX appears, along with an image of the MCU’s packaging. You can left-click on the pins to assign functions to them or right-click on them to assign labels.
  1. On the right-hand side in the System Core category, click RCC, then choose Ceramic Crystal Resonator as High-Speed Clock (HSE) source.
  1. Go to the clock configuration tab. Set up to use HSE and PLLCLK as sources. Enter 72MHz on HCLK to get the maximum clock speed. Once you hit enter, an automatic configuration may occur.
  1. On the System Core category, go to SYS and use any timer as Timebase Source. This is the method preferred by the FreeRTOS environment.
  1. Next, we’ll set up the ADC. Go back to the Pinout & Configuration tab and choose ADC. Choose a single ADC port (IN0) by clicking it both on Mode and the Pinout view. Name it ADC1_X on the Pinout view. Leave its Configuration at its defaults.
  1. Next, setup I2C for the OLED display. Same as the steps for the ADC, check the necessary parameters below. We’ll be using Fast mode 400KHz for the I2C clock. Choose PB6 and PB7 as I2C1_SCL and I2C1_SDA.
  1. The same goes true with UART. Here we’ll be enabling both TX and RX functions on PA9 and PA10 at a bit rate of 115200. Name them as USART1_RX and USART1_TX.
  1. For the buttons, allot two pins, namely PB9 and PB12. Make them as input pins. You have the option to use internal or external pull-ups on them. Name them Button1 and Button2, respectively.
  1. The last few and most important steps are to set up RTOS in the Middleware category. Set the parameters as below. Remember to increase the TOTAL HEAP SIZE to at least 3600 in the Config parameters tab.
  1. We’ll need to set up the tasks we implemented in Part 2. Go to Tasks and Queue and add a new Task. Below you’ll see the four tasks added along with their priorities:
  • pickButtonTask – Normal
  • getADCTask        – Above Normal
  • dispOLEDTask    – Normal
  • dispUARTTask   – Normal

Their entry function in the code will be their task names without the word Task. Click OK on each task to save them. Leave other parameters to default.

  1. Generate code by clicking the save button. Click yes to generate code. If you’re asked to change to the C editor perspective, click yes.
  1. You can compile the program by clicking the compile button. Running a program in CubeMX requires you to click the run button.
  1. Next, edit each task to program their actions. You can compile and run them afterward.

Here are the codes for the different tasks:

pickButton Task:

void pickButton(void *argument)
{
  /* USER CODE BEGIN pickButton */
  /* Infinite loop */
  for(;;)
  {

	  if(HAL_GPIO_ReadPin(Button1_GPIO_Port, Button1_Pin) == GPIO_PIN_RESET)
	  {
		  osDelay(300);	// debounce
		  button1_pressed = 1;
	  }

	  if(HAL_GPIO_ReadPin(Button2_GPIO_Port, Button2_Pin) == GPIO_PIN_RESET)
	  {
		  osDelay(300);	// debounce
		  button2_pressed = 1;
	  }

    osDelay(100);
  }
  /* USER CODE END pickButton */
}

This code simply waits for the button presses inside a loop. The osDelay( ) functions allow this task to suspend/sleep allowing for other tasks to commence.

getADC Task:

void getADC(void *argument)
{
  /* USER CODE BEGIN getADC */
  /* Infinite loop */
  for(;;)
  {

	  HAL_ADC_Start(&hadc1);
	  HAL_ADC_PollForConversion(&hadc1, 10);
	  x_val = (HAL_ADC_GetValue(&hadc1));
	  HAL_ADC_Stop(&hadc1);

	  // process ADC values here


    osDelay(100);
  }
  /* USER CODE END getADC */
}

This code polls for the values acquired from the ADC. Note that this task has a higher priority than other tasks, allowing ADC data to be processed immediately.

dispOLED Task:

void dispOLED(void *argument)
{
  /* USER CODE BEGIN dispOLED */
	  SSD1306_Clear();
	  SSD1306_UpdateScreen();
  /* Infinite loop */
  for(;;)
  {

	  SSD1306_GotoXY (0,0);
	  sprintf(buffer, "ADC=%d      ", x_val);
	  SSD1306_Puts (buffer, &Font_11x18, 1);
	  SSD1306_UpdateScreen(); //display
	  
  }
  /* USER CODE END dispOLED */
}

This code displays the ADC values almost in real-time to the OLED display.

The SSD1306 hardware library used is open source code courtesy of:

  • Tilen Majerle
  • modification for STM32f10x: Alexander Lutsai

dispUART Task:

void dispUART(void *argument)
{
	  /* USER CODE BEGIN UARTmenu */
	  /* Infinite loop */

		Menu_Display();

	  for(;;)
	  {
		  // select user input
		  if (HAL_UART_Receive(&huart1, &choice, sizeof(choice), 10) == HAL_OK)
		  {
			switch (choice) {

				case '1':
					//HAL_UART_Transmit(&huart1, (uint8_t*)"2 pressed\r\n", sizeof("1 pressed\r\n"), 10);
					sprintf(buffer, "Voltage = %d\r\n",x_val);
					HAL_UART_Transmit(&huart1, buffer, sizeof(buffer), 10);
					break;
				case '2':
					Menu_Display();
					break;
				default:
					break;
			}
		  }else{

		  }

		  if(button1_pressed)
		  {
			  HAL_UART_Transmit(&huart1, (uint8_t*)"Button1 pressed\r\n", sizeof("Button1 pressed\r\n"), 10);
			  button1_pressed = 0;
		  }

		  if(button2_pressed)
		  {
			  HAL_UART_Transmit(&huart1, (uint8_t*)"Button2 pressed\r\n", sizeof("Button2 pressed\r\n"), 10);
			  button2_pressed = 0;
		  }


	    osDelay(100);
	  }
	  /* USER CODE END UARTmenu */
}

You’ll see in this code that it contains a menu interface that’s updated through UART. It can grab and show you the current ADC value. It can also notify the user of button presses. osDelay( ) is given time while there are no UART activities or button presses happening.

Here is the actual view in a UART terminal:

While this is the actual circuit used complete with the BluePill, buttons, OLED display, and a USB to UART interface.

The entire CubeMX program can be downloaded below:

Writing-your-first-RTOS-program-using-STM32CubeMX.zip

Part 4. Summary

Basic RTOS scheduling was explained through a practical example. This was illustrated by using different peripherals on an STM32 BluePill seemingly simultaneously.

By incorporating RTOS in your codes, you can avoid the difficulties of writing complex state machines for your design. With this, you’ll be able to focus more on your intended goals. You’ll also be able to subdivide code tasks more efficiently in a group.


SHOP THE PROJECT

Scroll to Top