piconomix-fwlib  0.7.1
Cross-platform embedded library and documentation for 8/16/32-bit microcontrollers
  1. Introduction
piconomix_banner_300x70.png

The foundation of piconomix-fwlib is knowledge and freedom: the knowledge and freedom to use the right microcontroller for the job and not trying to force a square peg in a round hole.

piconomix-fwlib is a collection of open source C firmware and documentation for microcontrollers to develop portable bare-metal code that is vendor and architecture neutral (or easier to reuse). It is tough to find the best compromise between lean 8-bit targets, middle-of-the-road 16-bit targets, and resource rich 32-bit targets, but this cross-platform library aims to provide a good foundation before you are forced to add target specific code and getting locked in.

Click HERE to download releases of the open source library (source code and offline documentation).

1.1 Goals

  • Teach good firmware development practices to guide the next generation of professional embedded engineers.
  • Share source code, knowledge and expertise with our global community of engineers, scientists and enthusiasts.
  • Publish concise quick start guides and tutorials to reduce the learning curve of new microcontrollers.
  • Provide a standard framework and drivers for rapid code development.
  • Minimize porting by providing unified peripheral driver APIs (to maximize device driver reuse).

My sincere hope is that seasoned veterans will pitch in and share their years of experience to mentor the future generation and raise the bar in terms of quality and quantity. See 1.7 Questions or Feedback?

1.2 Sections

This documentation is divided into the following sections:

1.3 Important Links

1.4 Companion Boards

Here is the collection of boards that are supported with Board Support Packages (BSP), documentation and complete examples:

Microcontroller Board
Microchip ATmega328P-AU Arduino Uno R3 board
Microchip ATmega328P-AU Piconomix ATmega328p Scorpion Board
ST STM32L072RB Piconomix STM32L072RB Hero Board

1.5 Library Examples

Here are four select examples to pique your interest:

1.5.1 CLI Exlorer (Command Line Interpreter)

Included with the library is a CLI (Command Line Interpreter) executing on an Arduino Uno R3 that creates a "Un*x Shell"-like environment running on the microcontroller so that you can experiment with GPIO, ADC, I2C and SPI using only an ANSI/VT100 terminal emulator (for example Tera Term).

arduino_uno_i2c_slave_bmp280.jpg
Electrodragon BMP280 breakout board connected to I2C

Connect your board, fire up the CLI and verify that it works:

arduino_uno_cli_animated.gif
CLI Exlorer executing on an Arduino Uno R3

The BMP280 is found at 7-bit slave address 0x76. The chip identification register value for the BMP280 is 0x58. This confirms that you are able to write to and read from the I2C slave.

For more info see CLI Explorer (Command Line Interpreter).

1.5.2 I2C

This example demonstrates how easy it is to set up and communicate with an I2C slave device after you have verified with the CLI that it works:

#include "px_i2c.h"
#include "px_board.h"
// Bosch BMP280 I2C Slave Address
#define I2C_SLA_ADR 0x76
// Create I2C Slave handle object
px_i2c_handle_t px_i2c_handle;
int main(void)
{
uint8_t data[1];
// Initialise board
// Initialise I2C driver
// Open handle to I2C slave device
px_i2c_open(&px_i2c_handle, PX_I2C_PER_0, I2C_SLA_ADR);
// START I2C write transaction and write register address
data[0] = 0xd0;
i2c_wr(&px_i2c_handle,
data,
1,
// REPEATED START I2C read transaction and read register value
i2c_rd(&px_i2c_handle,
data,
1,
// Close I2C Handle
px_i2c_close(&px_i2c_handle);
}

1.5.3 GPIO

This example demonstrates how easy and efficient it is to define and use a GPIO pin:

#include "px_gpio.h"
// LED is on PORT B, pin 0, configured as an output, initally off
#define PX_GPIO_LED PX_GPIO(B, 0, PX_GPIO_DIR_OUT, PX_GPIO_INIT_LO)
// Push Button is on PORT D, pin 6, configured as an input, pull-up enabled
#define PX_GPIO_PB PX_GPIO(D, 6, PX_GPIO_DIR_IN, PX_GPIO_INIT_PULLUP)
/*
* Declare gpio handle objects as 'static const' to tell the compiler that it
* will not be used anywhere else ('static') and it will never change ('const').
*
* This gives the compiler the most freedom to optimize the code.
*/
static const px_gpio_handle_t px_gpio_led = {PX_GPIO_LED};
static const px_gpio_handle_t px_gpio_pb = {PX_GPIO_PB};
int main(void)
{
// Initialise pins
px_gpio_pin_init(&px_gpio_led);
px_gpio_pin_init(&px_gpio_pb);
// Repeat forever
for(;;)
{
// Is button being pressed?
if(px_gpio_pin_is_lo(&px_gpio_pb))
{
// Enable LED
px_gpio_pin_set_hi(&px_gpio_led);
}
else
{
// Disable LED
px_gpio_pin_set_lo(&px_gpio_led);
}
}
}

Even though it looks like functions calls are used, the lines reduce to single assembly statements for maximum efficiency and minimum code size. This is the generated assembly to inspect:

0000007a <main>:
// Initialise pins
px_gpio_pin_init(&px_gpio_led);
7a: 28 98 cbi 0x05, 0 ; 5
7c: 20 9a sbi 0x04, 0 ; 4
px_gpio_pin_init(&px_gpio_pb);
7e: 5e 9a sbi 0x0b, 6 ; 11
80: 56 98 cbi 0x0a, 6 ; 10
// Repeat forever
for(;;)
{
// Is button being pressed?
if(px_gpio_pin_is_lo(&px_gpio_pb))
{
82: 4e 99 sbic 0x09, 6 ; 9
84: 02 c0 rjmp .+4 ; 0x8a <main+0x10>
// Enable LED
px_gpio_pin_set_hi(&px_gpio_led);
86: 28 9a sbi 0x05, 0 ; 5
88: fc cf rjmp .-8 ; 0x82 <main+0x8>
}
else
{
// Disable LED
px_gpio_pin_set_lo(&px_gpio_led);
8a: 28 98 cbi 0x05, 0 ; 5
8c: fa cf rjmp .-12 ; 0x82 <main+0x8>
}
}

For IoT low power sensor nodes it is extremely important to wake up, do the job as fast as possible and go back to sleep again. C efficiency trumps C++ flexibility. Less code translates to a cheaper solution.

1.5.4 Data Logging

px_log_fs is a basic (but resilient) record-based file system to log data to AT45D DataFlash (or similiar serial flash storage). It is perfect for IoT (Internet of Things) low power sensor nodes that need to log data periodically, because it has basic wear levelling, handles power loss gracefully, requires less code and data memory and executes quickly which means you can get away with a smaller, cheaper microcontroller.

A section of serial flash can also be reserved for secure "over-the-air" encrypted firmware upgrades.

Usage example:

#include <stdio.h>
#include "px_board.h"
#include "px_spi.h"
#include "px_at45d.h"
#include "px_log_fs.h"
// Define record data content
typedef struct
{
int16_t temperature;
} px_log_fs_data_t;
static px_log_fs_data_t px_log_fs_data;
static px_spi_handle_t px_at45d_spi_handle;
int main(void)
{
uint8_t i;
px_log_fs_err_t px_log_fs_err;
// Initialise modules
px_spi_open2(&px_at45d_spi_handle,
PX_SPI_PER_0,
PX_BOARD_SPI_CS_AT45D,
px_at45d_init(&px_at45d_spi_handle);
// Initialise file system
// Set file creation time
time.year = 16;
time.month = 10;
time.day = 24;
time.hour = 12;
time.min = 28;
time.sec = 0;
// Create a new log file
// Populate data
px_log_fs_data.temperature = 160; // 16.0 deg Celsius
// Write 8 records
for(i=0; i<8; i++)
{
// Write record
px_log_fs_record_wr(&px_log_fs_data, sizeof(px_log_fs_data));
// Increment temperature by 1.3 deg
px_log_fs_data.temperature += 13;
}
// Read first record
px_log_fs_err = px_log_fs_record_rd_first(&px_log_fs_data, sizeof(px_log_fs_data));
while(px_log_fs_err == PX_LOG_FS_ERR_NONE)
{
// Display record data
printf("%d\n", px_log_fs_data.temp);
// Read next record
px_log_fs_err = px_log_fs_record_rd_next(&px_log_fs_data, sizeof(px_log_fs_data));
}
}

For a complete working example (with nice flash file system debugging), see Temp&Pressure Data Logger Application.

1.6 Coding Evolution

Great athletes spend years tweaking tiny aspects of their performance to get a competitive edge: bicycle riders adapt their posture in a wind tunnel, golfers analyse their swing,... Like the shark became the apex predator of the sea, we as coders must be inspired to evolve and perfect our art or wither.

Without mentorship, we tend to get stuck with the first coding style and programming patterns that we learned and keep on using it without a second thought, sometimes even justifying and defending it religiously.

After you have written a piece of code, take a break, return and spend 15 minutes to question if the code you have just written is the optimal / best / most readable solution (applying advice from Edward de Bono, the father of lateral thinking). Refactor, rinse, repeat. Next time you will use this improved snippet instead of copying and adapting the same old, inferior code from memory.

Ask yourself critically: which is better?

// Small fixed delay
for(i=0; i<100; i++)
{
__asm__ __volatile__("nop \n\t"::);
}

Or

// Small fixed delay
for(i=100; i!=0; i--)
{
__asm__ __volatile__("nop \n\t"::);
}

Inspect the assembly generated by the compiler. Maybe the compiler is smarter than you give it credit for (or not... it depends)

Evolve, improve or be left behind!

1.7 Questions or Feedback?

Questions or feedback (positive and negative) is great and will help to improve the library:

feedback_animated.gif

Methods of communication:

Click HERE to say thanks!

1.8 Code Contributions

You are most welcome to submit bug fixes, new code or documentation. It should:

1.9 Support

A significant amount of time has been invested to craft and refine this free open source library. If you saved development time, acquired a new skill, or advanced your career, you are welcome to support this project with a visit to the shop.

shop.png

Click HERE to visit the shop