nimphea/nimphea_shift_register

Search:
Group by:
Source   Edit  

nimphea_shift_register

Nim wrapper for CD4021 shift register device driver.

The CD4021 is an 8-stage CMOS shift register commonly used for:

  • Expanding digital inputs (buttons, switches)
  • Keyboard scanning (e.g., Daisy Field uses 2 daisy-chained for 16 keys)
  • General-purpose parallel-to-serial conversion

Hardware Specifications:

  • Supply Voltage: 3V to 18V
  • Clock Frequency: 3MHz at 5V, 8.5MHz at 15V
  • 8 parallel inputs per device
  • Supports daisy-chaining (series connection)
  • Supports parallel operation (multiple data lines, shared clock/latch)

Typical Applications:

  • Button matrix scanning
  • Keyboard input (Daisy Field: 16 keys with 2 chained devices)
  • Multi-switch panels

Example - Simple 8-button scanner:

import nimphea
import nimphea_shift_register

var sr: ShiftRegister4021_1  # Single device (8 inputs)
var config: ShiftRegisterConfig_1

config.clk = D0()
config.latch = D1()
config.data[0] = D2()
config.delay_ticks = 10

sr.init(config)

while true:
  sr.update()  # Read all 8 inputs
  
  for i in 0..<8:
    if sr.state(i):
      # Button i is pressed (HIGH)
  
  hw.delay(10)

Example - Daisy Field keyboard (16 keys, 2 chained devices):

var keyboard: ShiftRegister4021_2  # 2 daisy-chained devices
var config: ShiftRegisterConfig_2

config.clk = D8()
config.latch = D7()
config.data[0] = D10()

keyboard.init(config)

while true:
  keyboard.update()
  
  for key in 0..<16:  # 8 inputs × 2 devices
    if keyboard.state(key):
      # Key is pressed

Types

ShiftRegister4021_1 {.importcpp: "daisy::ShiftRegister4021<1, 1>".} = object
Single CD4021 shift register (8 inputs) Source   Edit  
ShiftRegister4021_1x2 {.importcpp: "daisy::ShiftRegister4021<1, 2>".} = object
1 device × 2 parallel lines (16 inputs) Source   Edit  
ShiftRegister4021_2 {.importcpp: "daisy::ShiftRegister4021<2, 1>".} = object
2 daisy-chained CD4021 devices (16 inputs) Used by Daisy Field for keyboard scanning Source   Edit  
ShiftRegister4021_2x2 {.importcpp: "daisy::ShiftRegister4021<2, 2>".} = object
2 devices × 2 parallel lines (32 inputs) Source   Edit  
ShiftRegister4021_3 {.importcpp: "daisy::ShiftRegister4021<3, 1>".} = object
3 daisy-chained CD4021 devices (24 inputs) Source   Edit  
ShiftRegister4021_4 {.importcpp: "daisy::ShiftRegister4021<4, 1>".} = object
4 daisy-chained CD4021 devices (32 inputs) Source   Edit  
ShiftRegisterConfig_1 {.importcpp: "daisy::ShiftRegister4021<1, 1>::Config",
                        bycopy.} = object
  clk* {.importc: "clk".}: Pin ## Clock pin (pin 10 of CD4021)
  latch* {.importc: "latch".}: Pin ## Latch pin (pin 9 of CD4021)
  data* {.importc: "data".}: array[1, Pin] ## Data pin (pin 11 of CD4021)
  delay_ticks* {.importc: "delay_ticks".}: uint32 ## Timing delay (default 10)
Configuration for single shift register (8 inputs) Source   Edit  
ShiftRegisterConfig_1x2 {.importcpp: "daisy::ShiftRegister4021<1, 2>::Config",
                          bycopy.} = object
  clk* {.importc: "clk".}: Pin
  latch* {.importc: "latch".}: Pin
  data* {.importc: "data".}: array[2, Pin]
  delay_ticks* {.importc: "delay_ticks".}: uint32
Configuration for 1 device × 2 parallel lines (16 inputs total) Source   Edit  
ShiftRegisterConfig_2 {.importcpp: "daisy::ShiftRegister4021<2, 1>::Config",
                        bycopy.} = object
  clk* {.importc: "clk".}: Pin
  latch* {.importc: "latch".}: Pin
  data* {.importc: "data".}: array[1, Pin]
  delay_ticks* {.importc: "delay_ticks".}: uint32
Configuration for 2 daisy-chained shift registers (16 inputs) Source   Edit  
ShiftRegisterConfig_2x2 {.importcpp: "daisy::ShiftRegister4021<2, 2>::Config",
                          bycopy.} = object
  clk* {.importc: "clk".}: Pin
  latch* {.importc: "latch".}: Pin
  data* {.importc: "data".}: array[2, Pin]
  delay_ticks* {.importc: "delay_ticks".}: uint32
Configuration for 2 devices × 2 parallel lines (32 inputs total) Source   Edit  
ShiftRegisterConfig_3 {.importcpp: "daisy::ShiftRegister4021<3, 1>::Config",
                        bycopy.} = object
  clk* {.importc: "clk".}: Pin
  latch* {.importc: "latch".}: Pin
  data* {.importc: "data".}: array[1, Pin]
  delay_ticks* {.importc: "delay_ticks".}: uint32
Configuration for 3 daisy-chained shift registers (24 inputs) Source   Edit  
ShiftRegisterConfig_4 {.importcpp: "daisy::ShiftRegister4021<4, 1>::Config",
                        bycopy.} = object
  clk* {.importc: "clk".}: Pin
  latch* {.importc: "latch".}: Pin
  data* {.importc: "data".}: array[1, Pin]
  delay_ticks* {.importc: "delay_ticks".}: uint32
Configuration for 4 daisy-chained shift registers (32 inputs) Source   Edit  

Procs

proc init(this: var ShiftRegister4021_1; config: ShiftRegisterConfig_1) {.
    importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
Initialize single shift register Source   Edit  
proc init(this: var ShiftRegister4021_1x2; config: ShiftRegisterConfig_1x2) {.
    importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
Initialize 1×2 parallel shift registers Source   Edit  
proc init(this: var ShiftRegister4021_2; config: ShiftRegisterConfig_2) {.
    importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
Initialize 2 daisy-chained shift registers Source   Edit  
proc init(this: var ShiftRegister4021_2x2; config: ShiftRegisterConfig_2x2) {.
    importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
Initialize 2×2 parallel shift registers Source   Edit  
proc init(this: var ShiftRegister4021_3; config: ShiftRegisterConfig_3) {.
    importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
Initialize 3 daisy-chained shift registers Source   Edit  
proc init(this: var ShiftRegister4021_4; config: ShiftRegisterConfig_4) {.
    importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
Initialize 4 daisy-chained shift registers Source   Edit  
proc pressed(this: ShiftRegister4021_1; index: cint): bool {.inline, ...raises: [],
    tags: [], forbids: [].}

Check if button at index is pressed (active-low logic)

Assumes buttons connect input to ground when pressed. Returns true when button is pressed.

Source   Edit  
proc pressed(this: ShiftRegister4021_1x2; index: cint): bool {.inline,
    ...raises: [], tags: [], forbids: [].}
Check if button at index is pressed (active-low logic) Source   Edit  
proc pressed(this: ShiftRegister4021_2; index: cint): bool {.inline, ...raises: [],
    tags: [], forbids: [].}
Check if button at index is pressed (active-low logic) Source   Edit  
proc pressed(this: ShiftRegister4021_2x2; index: cint): bool {.inline,
    ...raises: [], tags: [], forbids: [].}
Check if button at index is pressed (active-low logic) Source   Edit  
proc pressed(this: ShiftRegister4021_3; index: cint): bool {.inline, ...raises: [],
    tags: [], forbids: [].}
Check if button at index is pressed (active-low logic) Source   Edit  
proc pressed(this: ShiftRegister4021_4; index: cint): bool {.inline, ...raises: [],
    tags: [], forbids: [].}
Check if button at index is pressed (active-low logic) Source   Edit  
proc state(this: ShiftRegister4021_1; index: cint): bool {.
    importcpp: "#.State(#)", ...raises: [], tags: [], forbids: [].}

Get state of input at index (0-7)

Returns: true if input is HIGH, false if LOW

Note: Inverted logic for buttons with pull-ups:

  • true = button released (HIGH)
  • false = button pressed (LOW, pulled to ground)
Source   Edit  
proc state(this: ShiftRegister4021_1x2; index: cint): bool {.
    importcpp: "#.State(#)", ...raises: [], tags: [], forbids: [].}

Get state of input at index (0-15)

Index order for 1×2 parallel:

  • 0-7: First data line
  • 8-15: Second data line (parallel)
Source   Edit  
proc state(this: ShiftRegister4021_2; index: cint): bool {.
    importcpp: "#.State(#)", ...raises: [], tags: [], forbids: [].}

Get state of input at index (0-15)

Index order for 2 daisy-chained devices:

  • 0-7: First device (closest to MCU)
  • 8-15: Second device
Source   Edit  
proc state(this: ShiftRegister4021_2x2; index: cint): bool {.
    importcpp: "#.State(#)", ...raises: [], tags: [], forbids: [].}

Get state of input at index (0-31)

Index order for 2×2 (2 chained, 2 parallel):

  • 0-7: Device 0, data line 0
  • 8-15: Device 1, data line 0
  • 16-23: Device 0, data line 1
  • 24-31: Device 1, data line 1
Source   Edit  
proc state(this: ShiftRegister4021_3; index: cint): bool {.
    importcpp: "#.State(#)", ...raises: [], tags: [], forbids: [].}
Get state of input at index (0-23) Source   Edit  
proc state(this: ShiftRegister4021_4; index: cint): bool {.
    importcpp: "#.State(#)", ...raises: [], tags: [], forbids: [].}
Get state of input at index (0-31) Source   Edit  
proc update(this: var ShiftRegister4021_1) {.importcpp: "#.Update()",
    ...raises: [], tags: [], forbids: [].}

Read all 8 input states from the device

Call this regularly (e.g., 100-1000Hz) to poll button states. The read values are stored internally and accessed via state().

Source   Edit  
proc update(this: var ShiftRegister4021_1x2) {.importcpp: "#.Update()",
    ...raises: [], tags: [], forbids: [].}
Read all 16 input states (1×2 parallel) Source   Edit  
proc update(this: var ShiftRegister4021_2) {.importcpp: "#.Update()",
    ...raises: [], tags: [], forbids: [].}
Read all 16 input states (2 daisy-chained devices) Source   Edit  
proc update(this: var ShiftRegister4021_2x2) {.importcpp: "#.Update()",
    ...raises: [], tags: [], forbids: [].}
Read all 32 input states (2×2 parallel) Source   Edit  
proc update(this: var ShiftRegister4021_3) {.importcpp: "#.Update()",
    ...raises: [], tags: [], forbids: [].}
Read all 24 input states (3 daisy-chained devices) Source   Edit  
proc update(this: var ShiftRegister4021_4) {.importcpp: "#.Update()",
    ...raises: [], tags: [], forbids: [].}
Read all 32 input states (4 daisy-chained devices) Source   Edit