Saturday, February 26, 2022

0000 0000 1000 1010

CD4013 Flip Flop


In an earlier blog and video I was messing around with a fundamental element of computers, the Set/Reset Latch (S/R Latch), built around a CD4001 quad NOR gate. It's great, but evolution of this circuit has led to another element known as the S/R flip flop.

SR Latch

SR Flip Flop

There is SO MUCH information out there about these logic circuits, but I'll just recommend two sources if you are the type that likes to:


The fundamental change is that the SR Latch cannot change unless the Enable pin is HIGH (1). I also saw many Truth Tables out there and they were very inconsistent in their outputs of Q and Q' - depending on whether the Enable pin was also a Clock signal with a rising and falling edge. Interesting!

As a little side project I was wondering what the results would be if I replaced the AND gates with NOR gates!


But ultimately I wanted to use the CD4013 IC, which is listed as a Dual D-Type Flip-Flop (the D being for Data). In the datasheet we see the following "function table":


And the pinout for the chip is as follows:

The video shows this working well as per the datasheet, and it was interesting that the output does not change when enable is HIGH, but only when enable has a rising edge.

I think next I'll try the JK Flip Flop to avoid the "ambiguous" case which seems to be the downfall of this circuit.




Saturday, February 19, 2022

0000 0000 1000 1001

ESP32 Driving a relay OTA

After recent arrivals in the ESP32 adapter and relay department I decided to put together a circuit that allows for remote switching of decent current (e.g. 10A).

The circuit is similar to the one shown below (with the ESP32 playing the part of the switch):

The high signal drives the mosfet (a BS170) which opens the relay and releases all the ergs for the LED. Soldered up it looks quite good (don't look underneath), excepting that the 5V to 3.3V DC converter (SSP7603) looks a little ugly with all the capacitors stuck onto it like barnacles!

More or less standard OTA code makes this easy to program over the air, and I was pretty chuffed to be able to change the frequency of two separate GPIO pins without actually wiring up the ESP32 to a computer.

#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char* ssid = "MyWifiConnection";
const char* password = "MyWifiPassword";
const char* host = "Blinky OTA32";
unsigned long ledtime = 0;
unsigned long relaytime = 0;
unsigned long currentMillis = 0;
unsigned long led_delay = 3000;
unsigned long relay_delay = 500;
boolean ledstate = false;
boolean relaystate = false;

#define led_pin 5
#define relay_pin 4

void setup() {
  pinMode(led_pin, OUTPUT);
  digitalWrite(led_pin, LOW);
  pinMode(relay_pin, OUTPUT);
  digitalWrite(relay_pin, LOW);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.waitForConnectResult() != WL_CONNECTED)
  {
    // could put debug stuff in here
  }

  ArduinoOTA.setHostname(host);

  ArduinoOTA.onError([](ota_error_t error) {
    (void)error;
    ESP.restart();
  });

  ArduinoOTA.begin();

  // timing markers
  currentMillis = millis();
  ledtime = currentMillis;
  relaytime = currentMillis;

}

// loop() visits here for actual code
void dostuff() {

  // fake multi-tasking routines with blinking!
  currentMillis = millis();
  if (currentMillis - ledtime >= led_delay) {
    if (ledstate) {
      digitalWrite(led_pin, LOW);
      ledstate = false;
    }
    else {
      digitalWrite(led_pin, HIGH);
      ledstate = true;
    }
    ledtime = currentMillis;
  }
  if (currentMillis - relaytime >= relay_delay) {
    if (relaystate) {
      digitalWrite(relay_pin, LOW);
      relaystate = false;
    }
    else {
      digitalWrite(relay_pin, HIGH);
      relaystate = true;
    }
    relaytime = currentMillis;
  }
}

void loop() {
  ArduinoOTA.handle();
  dostuff();
}

Probably the only interesting thing about the code above is the use of millis() to fake multitask - I love that technique and it works really well on these devices.

On a smaller device (e.g. ATTiny13) I'd probably use interrupts and count the ticks to activate the GPIOs when a coded amount is reached. The ESP32 has a LOT of flash overhead though, so I took the lazy way this time!

I'm hoping I can continue to refine this idea and maybe use these guys around the place for remote sensing and control. Watch this space!



Friday, February 11, 2022

0000 0000 1000 1000

PMS150C, ULN2003 and the 28BYJ-48 Stepper Motor (Tiny Dancer)

I have had it in mind to start to know the Padauk microcontrollers a little better, and in particular the PFS154 and the PMS150C. It has been such a journey so far, with the concentration of effort mostly into the making of the open source programmer

In the "still to do" basket are:

  1. Do more than just blink LEDS
  2. Mimic/translate ATTiny/Arduino code to Padauk
  3. Learn more C-Code to better fit into the specs of the little fellas (the Arduino Uno/Nano is very generous and forgiving, and has multiple libraries - many bloated but functional)
  4. Move from C-Code to Padauk assembly for speed, clarity and size.

When a ULN2003 stepper motor controller and 28BYJ-48 combo landed in the mailbag I decided that I might be able to start on points 1 & 2 (and a bit 3) above.

The prospect of making the feature challenged (but cheap!) one-time programmable PMS150C drive a stepper motor reminds me very much of humans driving big machinery - such as here in Australia where tiny figures are dwarfed by Haul Pack trucks.


My plan was to make some stepper code work with Arduino (and appropriate libraries), then transfer the code to the very familiar ATTiny13, and finally the PFS154 (because it's multi-programmable, so testing is less stressful). The ultimate goal of the exercise is to use the one time programmable PMS150C.

In the end I only threw away two fried PMS150C chips, so I'm pretty happy about that!

PMS150C Features (from the website)
1. 1KW OTP program memory
2. 64 Bytes data RAM 
3. One hardware 16-bit timer 
4. One hardware 8-bit timers with PWM generator
5. One general purpose comparator 
6. Support fast wake-up 
7. Every IO pin can be configured to enable wake-up function 
8. 6 IO pins with optional drive/sink current and pull-high resistor 
9. Clock sources: internal high RC oscillator and internal low RC oscillator 
10. Eight levels of LVR reset ~ 4.0V, 3.5V, 3.0V, 2.75V, 2.5V, 2.2V, 2.0V, 1.8V 
11. One external interrupt pin
   
CPU Features
1. One processing unit operating mode 
2. 79 powerful instructions 
3. Most instructions are 1T execution cycle 
4. Programmable stack pointer to provide adjustable stack level 
5. Direct and indirect addressing modes for data access. Data memories are available for use as an index pointer of Indirect addressing mode
6. Separated IO space and memory space 

But first - what's with that four input stepper motor?

Model No: 28BYJ-48
  • Unipolar Stepper with 0.1″ Spaced 5-pin Cable Connector
  • 4096 Steps Per Revolution (5.625 degree step angle – 64 steps per rev + 64:1 gear ratio)
  • 1/64 Geared Down Reduction
  • 5V DC Suggested Operation
  • Weight: 37 g.
  • Dimensions: 28mm diameter, 20mm tall not including 9mm shaft with 5mm diameter
  • 9″ / 23 cm long cable
  • Holding Torque: 150 gram-force*cm, 15 N*mm (2 oz-force*in)
  • Shaft: 5mm diameter flattened
It seems that a common way to control this guy is to use a ULN2003 Darlington Array chip, but somewhat unusually this is wrapped up in a module which allows 5V/12V versions and has blinking lights because, well, who doesn't love blinking lights?? <sigh>


I do have some ULN2003 chips about the place (somewhere...?), but the focus for this project is  programming, so I'll use the OMG AMAZING™ light show version.

Getting an Arduino Uno to light em up and move the stepper was not problem - you simply install this library and then borrow the code from the examples given.

#include <Stepper.h>

const int stepsPerRevolution = 200;

Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);

void setup() {
  myStepper.setSpeed(60);
  Serial.begin(9600);
}

void loop() {
  Serial.println("clockwise");
  myStepper.step(stepsPerRevolution);
  delay(500);

  Serial.println("counterclockwise");
  myStepper.step(-stepsPerRevolution);
  delay(500);
}

The stepper motor datasheet recommends the following sequence to turn the shaft:

Therefore instead of using the stepper library for the ATTiny13 version, I made an array of appropriate data and then stepped through it either backwards or forwards for "random" dancing.

const byte thesteps[] = {

  // ATTiny13 pinout
  //
  //       _________
  //      /         |
  //  1--|RESET  VCC|--8
  //  2--|PB3    PB2|--7
  //  3--|PB4    PB1|--6
  //  4--|GND    PB0|--5
  //     |__________| 

  // IN4,IN3,IN2,IN1
  // PB4,PB3,PB2,PB1
  // pins 3, 2, 7, 6
  0b00011100,
  0b00011000,
  0b00011010,
  0b00010010,
  0b00010110,
  0b00000110,
  0b00001110,
  0b00001100
};

byte numberofsteps = 0;
uint16_t myrand = 2901;  // happy birthday

uint16_t gimmerand(uint16_t small, uint16_t big) {
  myrand ^= (myrand << 13);
  myrand ^= (myrand >> 9);
  myrand ^= (myrand << 7);
  return abs(myrand)%23*(big-small)/23+small;
}

void setup() {
  DDRB = 0b00011110; // drive pins as output
}

void loop() {

  numberofsteps = gimmerand(4, 40);
  delay(10);

  if ((numberofsteps%2) == 0) {
    for (int rotate = 0; rotate < numberofsteps; rotate++) {
      for (byte clocker = 0; clocker < 8; clocker++) {
        PORTB = thesteps[clocker];
        delay(1);
      }
    }
  }
  else {
    for (int rotate = 0; rotate < numberofsteps; rotate++) {
      for (byte clocker = 7; clocker > 0; clocker--) {
        PORTB = thesteps[clocker];
        delay(1);
      }
    }
  }
}

Problems emerged when I "translated" this to the PFS154 and then to the PMS150C. I think the majority of the issues were wrapped up in the variable data types. I eventually simplified most of these to 0-255 by using the uint8_t data type.

The data types "int", "char", "byte" and even "uint16_t" were all too problematic for the PMS150C (although the PFS154 code was pretty much the same as the ATTiny13 code).

The change of data types also meant that I had to "tweak" the code, in particular the "random" number generator, quite a bit! 

The resultant randomness is pretty awful, but that's a problem for another time (perhaps when I learn how to use the chip's comparator as an ADC).

#include <pdk/device.h>
#include <delay.h>
#include <stdint.h>
#include "auto_sysclock.h"
#include <stdlib.h>
#include <stdbool.h>  // important for booleans

char thesteps[] = {

  // PMS150C pinout
  //
  //       _________
  //      /         |
  //  1--|VCC    GND|--8
  //  2--|PA7    PA0|--7
  //  3--|PA6    PA4|--6
  //  4--|PA5    PA3|--5
  //     |__________| 

  // IN 4, IN3, IN 2, IN 1
  // PA6, PA7, PA0, PA4
  // pins 3, 2, 7, 6

  0b11000001,
  0b11000000,
  0b11010000,
  0b01010000,
  0b01010001,
  0b00010001,
  0b10010001,
  0b10000001
};

uint8_t numberofsteps = 0;

// change these as you please, my focus in this
// project was not the "purity" of randomness
uint8_t myrand = 201;
uint8_t xorpoly1 = 0b10001011;
uint8_t xorpoly2 = 0b10111010;
uint8_t throwspanner = 61;

uint8_t spannercount = 0;

uint8_t gimmerand(uint8_t small, uint8_t large) {
  bool carry = false;
  if (spannercount > throwspanner) {
    myrand = myrand ^ xorpoly2;
    myrand--;
    spannercount = 0;
  }
  if (((myrand) >> 7) & 1) {
    carry = true;
  }
  myrand = myrand << 1;
  if (carry) {
    myrand = myrand ^ xorpoly1;
  }
  return (myrand/((255)/(large-small))+small);
  }

bool isEven(uint8_t n) // faster than n%2==0?
{
return (!(n & 1));
}

void main() {

  PAC = 0b11111001; // output pins

while (1) {

  // change these for "speed" of dancing
  numberofsteps = gimmerand(4, 40);
  _delay_ms(10);

  if (isEven(numberofsteps)) {
    for (uint8_t rotate = 0; rotate < numberofsteps; rotate++) {
      for (uint8_t clocker = 0; clocker < 8; clocker++) {
        PA = thesteps[clocker];
        PA = PA | 0b00100000; // turns on LED
        _delay_ms(1);
      }
    }
  }
  else {
    for (uint8_t rotate = 0; rotate < numberofsteps; rotate++) {
      for (uint8_t clocker = 7; clocker > 0; clocker--) {
        PA = thesteps[clocker];
        PA = PA | 0b00001000; // turns on LED
        _delay_ms(1);
      }
    }

}
}
}

Amazingly the PMS150C (and the PFS154) do have an extra two "proper" GPIOs to play with after this program is installed (the ATTiny13's RESET pin can be used as a "weak" GPIO if needed), so you'll see blinking LEDS to indicate clockwise and counterclockwise dancing. 





Saturday, February 5, 2022

0000 0000 1000 0111

Will Ferrules be OK? (yes)

Lately I have been seeing a lot (A LOT!) of videos about "proper" connections. It's amazing that for years you might only rarely hear of a particular technical word like FERRULE, and then suddenly you can't watch a day of YouTube without it cropping up all over the place!

The one that spurred me into action was a lovely video (as always) from Great Scott, and indeed I have suffered some problems associated with dodgy soldered joints. Thus I put in an order and waited.

After the usual Pandelay I received a bunch of ferrules and a crimper, and so I am starting to replace some earlier compromised connections with these little fellas to increase the life and the stability of the devices on which they rely.