diff --git a/node_board/CMakeLists.txt b/node_board/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b7ba219bf484f39eafc33f74f30b63b8a774c56d --- /dev/null +++ b/node_board/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.13) # 3.13 is required for target_link_options +project(NodeBoard CXX) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +endif() +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +include(cmake/shared_settings.cmake) +find_package(PNG REQUIRED) + +add_executable(generate_node_board + main.cpp + png_writer.cpp + png_writer.h +) +target_include_directories(generate_node_board PUBLIC ${PNG_INCLUDE_DIR}) +target_link_libraries(generate_node_board shared_settings ${PNG_LIBRARY}) +target_compile_features(generate_node_board PUBLIC cxx_std_17) diff --git a/node_board/cmake/shared_settings.cmake b/node_board/cmake/shared_settings.cmake new file mode 100644 index 0000000000000000000000000000000000000000..19340ff1d74c3dd81346bb03ef2e0fa300b32068 --- /dev/null +++ b/node_board/cmake/shared_settings.cmake @@ -0,0 +1,35 @@ +# This file defines an interface library used to add common compile flags to all libraries and +# executables in FunkyCT. + +add_library(shared_settings INTERFACE) + +# Warning flags +target_compile_options(shared_settings INTERFACE + -Wall + -Wcast-align + -Wcast-qual + -Wextra + -Wundef + -Wuseless-cast + -Wzero-as-null-pointer-constant + -pedantic +) + +# Speed flags +target_compile_options(shared_settings INTERFACE -march=native -ffast-math) + +# Build type for profile generation +target_compile_options(shared_settings INTERFACE $<$<CONFIG:ProfileGenerate>: + -fprofile-generate + -O3 + -DNDEBUG +>) +target_link_options(shared_settings INTERFACE $<$<CONFIG:ProfileGenerate>:-fprofile-generate>) + +# Build type for profile use +target_compile_options(shared_settings INTERFACE $<$<CONFIG:ProfileUse>: + -fprofile-use + -O3 + -DNDEBUG +>) +target_link_options(shared_settings INTERFACE $<$<CONFIG:ProfileUse>:-fprofile-use>) diff --git a/node_board/main.cpp b/node_board/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6269331dd1fe2369573bd6cb79a5c2a0459de36 --- /dev/null +++ b/node_board/main.cpp @@ -0,0 +1,226 @@ +#include "png_writer.h" +#include <iostream> + +//-------------------------------------------------------------------------------------------------- +struct Rectangle { + int32_t x_min; + int32_t x_max; + int32_t y_min; + int32_t y_max; +}; + +//-------------------------------------------------------------------------------------------------- +class Board { +public: + Board(double width, double height, double pix_per_mm, double min_cut_thickness) + : width_(width), height_(height), pix_per_mm_(pix_per_mm), + min_cut_thickness_(min_cut_thickness), width_px_(to_px(width_)), height_px_(to_px(height_)), + min_cut_thickness_px_(to_px(min_cut_thickness_)) + { + png_writer_.allocate(width_px_ + 4 * min_cut_thickness_px_, + height_px_ + 4 * min_cut_thickness_px_); + png_writer_.set_all_pixels(255); + draw_int_rectangle(0, width_px_, 0, height_px_, 0); + } + + int32_t to_px(double x) { + return static_cast<int32_t>(pix_per_mm_ * x); + } + + void set_pixel(int32_t x, int32_t y, uint8_t value = 255) { + x = std::max(-2 * min_cut_thickness_px_, x); + x = std::min(width_px_ + 2 * min_cut_thickness_px_, x); + y = std::max(-2 * min_cut_thickness_px_, y); + y = std::min(height_px_ + 2 * min_cut_thickness_px_, y); + png_writer_.set_pixel(2 * min_cut_thickness_px_ + x, + 2 * min_cut_thickness_px_ + height_px_ - y - 1, + value); + } + + void draw_int_rectangle( + int32_t x_min, int32_t x_max, int32_t y_min, int32_t y_max, uint8_t value = 255 + ) { + for (int32_t x = x_min; x < x_max; ++x) { + for (int32_t y = y_min; y < y_max; ++y) { + set_pixel(x, y, value); + } + } + } + + void draw_rectangle( + double x_min, double x_max, double y_min, double y_max, uint8_t value = 255 + ) { + draw_int_rectangle(to_px(x_min), to_px(x_max), to_px(y_min), to_px(y_max), value); + } + + void draw_rectangle(Rectangle const& r, uint8_t value = 255) { + draw_int_rectangle(r.x_min, r.x_max, r.y_min, r.y_max, value); + } + + void draw_pad(double x_min, double x_max, double y_min, double y_max) { + draw_rectangle(x_min - min_cut_thickness_, x_max + min_cut_thickness_, + y_min - min_cut_thickness_, y_max + min_cut_thickness_, + 0); + draw_rectangle(x_min, x_max, y_min, y_max, 255); + } + + void save(char const* filename) { + png_writer_.write(filename); + } + + void save_outline(char const* filename) { + png_writer_.set_all_pixels_black(); + draw_int_rectangle(0, width_px_, 0, height_px_); + save(filename); + } + +public: + PngWriter png_writer_; + double width_; + double height_; + double pix_per_mm_; + double min_cut_thickness_; + int32_t width_px_; + int32_t height_px_; + int32_t min_cut_thickness_px_; +}; + +//-------------------------------------------------------------------------------------------------- +// All length measurements are in mm. +int main() { + // reused vars... dirty C style + double pad_x_min; + double pad_x_max; + double pad_y_min; + double pad_y_max; + + // board params + // width is deduced later + double const height = 18; + double const ppmm = 50; // equivalent to 1270 ppi + double const min_cut_thickness = 0.38; + double const min_trace_thickness = 0.35; + + // SOIC dims + double const pad_width = 0.6; + double const pad_height = 2.4; + double const soic_height = 7; + double const soic_pitch = 1.27; + double const soic_width = 3 * soic_pitch + pad_width; + + // deduce width and make the board + double const width = soic_width + 3 * min_cut_thickness + 3 * min_trace_thickness; + std::cout << "Board width: " << width << "mm\n"; + Board board(width, height, ppmm, min_cut_thickness); + + // SOIC pads + double const soic_x_min = min_trace_thickness + min_cut_thickness; + double const soic_x_max = soic_x_min + 3 * soic_pitch + pad_width; + double const soic_btm_y = 0.5 * (height - soic_height); + double const soic_top_y = height - soic_btm_y; + for (int32_t i = 0; i < 4; ++i) { + pad_x_min = soic_x_min + i * soic_pitch; + pad_x_max = pad_x_min + pad_width; + pad_y_max = soic_btm_y + pad_height; + board.draw_rectangle(pad_x_min, pad_x_max, soic_btm_y, pad_y_max); + pad_y_min = soic_top_y - pad_height; + board.draw_rectangle(pad_x_min, pad_x_max, pad_y_min, soic_top_y); + } + + // Ground pads and traces + double const cable_pad_width = (width - 2 * min_cut_thickness) / 3; + double const cable_pad_height = soic_btm_y - min_cut_thickness; + double const bridge_offset = 0.5 * (pad_width - min_trace_thickness); + board.draw_rectangle(0, cable_pad_width, 0, cable_pad_height); + board.draw_rectangle(0, min_trace_thickness, 0, height); + board.draw_rectangle(soic_x_min + bridge_offset, + soic_x_min + bridge_offset + min_trace_thickness, + cable_pad_height, + cable_pad_height + min_cut_thickness); + board.draw_rectangle(0, + cable_pad_width, + soic_top_y + 2 * min_cut_thickness + min_trace_thickness, + height); + + // Data pads and traces + double const data_pad_x_min = cable_pad_width + min_cut_thickness; + double const data_pad_x_max = data_pad_x_min + cable_pad_width; + board.draw_rectangle(data_pad_x_min, data_pad_x_max, 0, cable_pad_height); + board.draw_rectangle(data_pad_x_max, + soic_x_max + min_cut_thickness + min_trace_thickness, + cable_pad_height - min_cut_thickness, + cable_pad_height); + board.draw_rectangle(soic_x_max + min_cut_thickness, + soic_x_max + min_cut_thickness + min_trace_thickness, + cable_pad_height, + soic_btm_y + pad_height + min_cut_thickness); + board.draw_rectangle(soic_x_min + bridge_offset, + soic_x_max + min_cut_thickness + min_trace_thickness, + soic_btm_y + pad_height + min_cut_thickness, + soic_btm_y + pad_height + min_cut_thickness + min_trace_thickness); + board.draw_rectangle(soic_x_min + bridge_offset, + soic_x_min + bridge_offset + min_trace_thickness, + soic_btm_y + pad_height + min_cut_thickness, + soic_top_y + min_cut_thickness); + board.draw_rectangle(soic_x_min + bridge_offset, + data_pad_x_max, + soic_top_y + min_cut_thickness, + soic_top_y + min_cut_thickness + min_trace_thickness); + board.draw_rectangle(data_pad_x_min, data_pad_x_max, soic_top_y + min_cut_thickness, height); + + // VCC pads and traces + double const vcc_pad_x_min = 2 * (cable_pad_width + min_cut_thickness); + // pad 1 + board.draw_rectangle(vcc_pad_x_min, + width, + 0, + // Note the fudge factor... It makes mods happy. + cable_pad_height - min_cut_thickness - min_trace_thickness - 0.025); + board.draw_rectangle(width - min_trace_thickness, width, 0, height); + // pad 2 + board.draw_rectangle(vcc_pad_x_min, + width, + height - cable_pad_height, + height); + // pad extension + board.draw_rectangle(soic_x_max + min_cut_thickness, + width, + soic_top_y - pad_height - min_cut_thickness, + height - cable_pad_height); + double const vcc_pin_x = soic_x_min + soic_pitch + bridge_offset; + board.draw_rectangle(vcc_pin_x, + width, + soic_top_y - pad_height - min_cut_thickness - min_trace_thickness, + soic_top_y - pad_height - min_cut_thickness); + board.draw_rectangle(vcc_pin_x, + vcc_pin_x + min_trace_thickness, + soic_top_y - pad_height - min_cut_thickness, + soic_top_y); + + // Extend pad divisions + board.draw_rectangle(cable_pad_width, + cable_pad_width + min_cut_thickness, + -min_cut_thickness, + 0, + 0); + board.draw_rectangle(cable_pad_width, + cable_pad_width + min_cut_thickness, + height, + height + min_cut_thickness, + 0); + board.draw_rectangle(width - cable_pad_width - min_cut_thickness, + width - cable_pad_width, + -min_cut_thickness, + 0, + 0); + board.draw_rectangle(width - cable_pad_width - min_cut_thickness, + width - cable_pad_width, + height, + height + min_cut_thickness, + 0); + + board.save("node_board_traces.png"); + board.save_outline("node_board_outline.png"); + + return 0; +} diff --git a/node_board/png_writer.cpp b/node_board/png_writer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ac2e4712e2f82f3023a54f71b9fe92aa7f5f5ed7 --- /dev/null +++ b/node_board/png_writer.cpp @@ -0,0 +1,126 @@ +#include "png_writer.h" +#include <stdlib.h> +#include <stdio.h> +#include <cstring> + +#include <iostream> + +//.................................................................................................. +PngWriter::PngWriter(): width_(0), height_(0), row_pointers_(nullptr) {} + +//.................................................................................................. +PngWriter::~PngWriter() { + if (row_pointers_ != nullptr) { + free(); + } +} + +//.................................................................................................. +void PngWriter::free() { + for (int32_t y = 0; y < height_; y++) { + std::free(row_pointers_[y]); + } + std::free(row_pointers_); + width_ = 0; + height_ = 0; +} + +//.................................................................................................. +void PngWriter::allocate(int32_t width, int32_t height) { + if (row_pointers_ != nullptr) { + free(); + } + width_ = width; + height_ = height; + row_pointers_ = (png_bytep*)malloc(sizeof(png_bytep) * height_); + for (int y = 0; y < height_; y++) { + row_pointers_[y] = (png_bytep)malloc(row_size()); + } +} + +//.................................................................................................. +void PngWriter::set_pixel(int32_t x, int32_t y, uint8_t value) { + png_bytep row = row_pointers_[y]; + png_bytep px = &(row[x * 3]); + px[0] = value; + px[1] = value; + px[2] = value; +} + +//.................................................................................................. +void PngWriter::set_all_pixels(uint8_t value) { + for (int y = 0; y < height_; y++) { + png_bytep row = row_pointers_[y]; + for (int x = 0; x < width_; x++) { + png_bytep px = &(row[x * 3]); + px[0] = value; + px[1] = value; + px[2] = value; + } + } +} + +//.................................................................................................. +void PngWriter::set_all_pixels_black() { + for (int y = 0; y < height_; y++) { + std::memset(row_pointers_[y], 0, row_size()); + } +} + +//.................................................................................................. +void PngWriter::write(char const* filename) { + auto fp = fopen(filename, "wb"); + if (!fp) { + std::cout << "Couldn't make file\n"; + abort(); + } + + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png) { + std::cout << "Couldn't make png_structp\n"; + abort(); + } + + png_infop info = png_create_info_struct(png); + if (!info) { + std::cout << "Couldn't make png_structp\n"; + abort(); + } + + if (setjmp(png_jmpbuf(png))) { + std::cout << "Couldn't set jump\n"; + abort(); + } + + png_init_io(png, fp); + // Output is 8bit depth, RGB format. + png_set_IHDR(png, + info, + width_, + height_, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT + ); + png_write_info(png, info); + + if (png_get_rowbytes(png, info) != row_size()) { + std::cout << "Allocated bad amount of memory\n"; + abort(); + } + + // To remove the alpha channel for PNG_COLOR_TYPE_RGB format, + // Use png_set_filler(). + //png_set_filler(png_, 0, PNG_FILLER_AFTER); + + png_write_image(png, row_pointers_); + png_write_end(png, NULL); + + if (png && info) { + png_destroy_write_struct(&png, &info); + } + + fclose(fp); +} diff --git a/node_board/png_writer.h b/node_board/png_writer.h new file mode 100644 index 0000000000000000000000000000000000000000..6f65af42c0c5f027b9340bf2988d2d3f6d13e7b4 --- /dev/null +++ b/node_board/png_writer.h @@ -0,0 +1,26 @@ +#include <png.h> +#include <cstdint> + +//-------------------------------------------------------------------------------------------------- +// This class is derived from code by Guillaume Cottenceau, copyright 2002-2010 and distributed +// under the X11 license. https://gist.github.com/niw/5963798 +class PngWriter { +public: + PngWriter(); + ~PngWriter(); + + void free(); + void allocate(int32_t width, int32_t height); + png_bytep* row_pointers() { return row_pointers_; } + void set_pixel(int32_t x, int32_t y, uint8_t value = 255); + void set_all_pixels(uint8_t value); + void set_all_pixels_black(); + void write(char const* filename); + +private: + uint32_t row_size() { return 3 * width_; } + + int32_t width_; + int32_t height_; + png_bytep* row_pointers_; +};