Raspberry Pico: Designing a Custom C-SDK Library (Part 2)

Development Approach

  • Write a single C file with all the necessary code for the very first feature, keeping in mind to provide semantically named objects and functions
  • Software Test: Add unit tests that completely cover this initial feature set
  • Hardware test: Build a breadboard with the microcontroller, the shift register, and outputs devices (LEDs etc.) and test your code
  • Switch to test driven development by writing a unit test first before a new function, finalizing all essential features
  • Adept to good API design guidelines and C coding standards: Restructure the project into separate header, implementation, and test files. And provide conventional ways to create new objects and library functions (both object-scoped and global)
  • Restructure the project into C coding standards: A header file, an implementation file, and a test file. Also, upgrade the library to
  • Provide mocks or stubbing to all hardware related functions to test the library in isolation of any concrete hardware
  • Add the Hardware specific standard compilation stack that’s accepted in the community

Essential Feature: Write a Single Bit

typedef struct ShiftRegister
u_int8_t SERIAL_PIN;
bool serial_pin_state;
u_int8_t shift_register_state;
} ShiftRegister;
ShiftRegister reg = {
static bool write_bit(ShiftRegister *reg, bool b)
reg->serial_pin_state = b;
(b) ? (reg->register_state += 0b10) : (reg->register_state <<= 0b01);
return true;
void test_write_bit(void **state)
ShiftRegister reg = {14, 11, 12};
write_bit(1, &reg);
assert_int_equal(reg.serial_pin_state, 1);
write_bit(0, &reg);
assert_int_equal(reg.serial_pin_state, 0);

Hardware Test

static bool _write_bit(ShiftRegister *reg, bool b)
gpio_put(reg->SERIAL_PIN, b);
gpio_put(reg->SHIFT_REGISTER_CLOCK_PIN, 1);
reg->serial_pin_state = b;
(b) ? (reg->register_state += 0b10) : (reg->register_state <<= 0b01);
gpio_put(reg->SHIFT_REGISTER_CLOCK_PIN, 0);
gpio_put(reg->SERIAL_PIN, 0);
return true;

Structural Refactoring

├── examples
│ ├── 8_led_blink.c
├── include
│ └── admantium
│ └── rp2040_shift_register.h
├── src
│ ├── CMakeLists.txt
│ └── rp2040_shift_register.c
└── test
└── test.c

Extension with new Functions

  • Add a definition to the header file
  • Add a new test
  • Provide the implementation
  • Test & debug
  • write_bit(ShiftRegister *, bool): Write a single bit to the shift register, and perform a shift-right of all other bits.
  • write_bitmask(ShiftRegister *, u_int8_t): Write a complete bitmask, e.g. 0b10101010 to the register.
  • flush(ShiftRegister *): Flush the content of the shift register to the storage register.
  • reset(ShiftRegister *): Resets the shift register's content to bitmask 0b00000000.
  • reset_storage(ShiftRegister *) Resets the storage register's content to bitmask 0b00000000 and performs a shift_register_flush()
  • char * shift_register_print(ShiftRegister \*) Prints the shift register's state as a bitmask, and returns a char* of the bitmask string.

Running Unit Tests without the Pico SDK

#ifndef TEST_BUILD
#include <pico/stdlib.h>
#include <../test/mocks.h>
void stdio_init_all() { // do nothing }
void gpio_init(uint8_t gpio) { // do nothing }
static void gpio_set_dir(uint8_t gpio, bool out) { // do nothing }
static void gpio_put(uint8_t gpio, bool value) { // do nothing }

API Refactoring

bool shift_register_write_bit(ShiftRegister *, bool);
bool shift_register_write_bitmask(ShiftRegister *, uint8_t);
bool shift_register_flush(ShiftRegister *);
bool shift_register_reset(ShiftRegister *);
bool shift_register_reset_storage(ShiftRegister *);
char *shift_register_print(ShiftRegister *);
bool shift_register_write_bitmask(ShiftRegister *reg, u_int8_t btm)
return reg->write_bitmask(reg, btm);




Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store