add font generation to build
This commit is contained in:
		
							parent
							
								
									c57068177e
								
							
						
					
					
						commit
						8b22eadc4f
					
				
					 6 changed files with 829 additions and 2 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,3 +1,4 @@ | |||
| zig-cache/ | ||||
| zig-out/ | ||||
| core | ||||
| src/images/* | ||||
|  |  | |||
							
								
								
									
										168
									
								
								AsciiPrintableStep.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								AsciiPrintableStep.zig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,168 @@ | |||
| //! Publish Date: 2021_10_17 | ||||
| //! This file is hosted at github.com/marler8997/zig-build-repos and is meant to be copied | ||||
| //! to projects that use it. | ||||
| const std = @import("std"); | ||||
| const AsciiPrintableStep = @This(); | ||||
| 
 | ||||
| step: std.build.Step, | ||||
| builder: *std.build.Builder, | ||||
| path: []const u8, | ||||
| 
 | ||||
| pub fn create(b: *std.build.Builder, opt: struct { | ||||
|     path: []const u8, | ||||
| }) *AsciiPrintableStep { | ||||
|     var result = b.allocator.create(AsciiPrintableStep) catch @panic("OOM"); | ||||
|     result.* = AsciiPrintableStep{ | ||||
|         .step = std.build.Step.init(.{ | ||||
|             .id = .custom, | ||||
|             .name = "AsciiPrintable", | ||||
|             .owner = b, | ||||
|             .makeFn = make, | ||||
|         }), | ||||
|         .builder = b, | ||||
|         .path = std.fs.path.resolve(b.allocator, &[_][]const u8{ | ||||
|             b.build_root.path.?, opt.path, | ||||
|         }) catch @panic("memory"), | ||||
|     }; | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| // TODO: this should be included in std.build, it helps find bugs in build files | ||||
| fn hasDependency(step: *const std.build.Step, dep_candidate: *const std.build.Step) bool { | ||||
|     for (step.dependencies.items) |dep| { | ||||
|         // TODO: should probably use step.loop_flag to prevent infinite recursion | ||||
|         //       when a circular reference is encountered, or maybe keep track of | ||||
|         //       the steps encounterd with a hash set | ||||
|         if (dep == dep_candidate or hasDependency(dep, dep_candidate)) | ||||
|             return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| fn make(step: *std.build.Step, _: *std.Progress.Node) !void { | ||||
|     const self = @fieldParentPtr(AsciiPrintableStep, "step", step); | ||||
| 
 | ||||
|     const zig_file = std.fmt.allocPrint(self.builder.allocator, "{s}/images.zig", .{self.path}) catch @panic("OOM"); | ||||
|     defer self.builder.allocator.free(zig_file); | ||||
|     std.fs.accessAbsolute(zig_file, .{ .mode = .read_only }) catch { | ||||
|         // Printables file does not exist | ||||
|         // ASCII printables from 32 to 126 | ||||
|         const file = try std.fs.createFileAbsolute(zig_file, .{ | ||||
|             .read = false, | ||||
|             .truncate = true, | ||||
|             .lock = .Exclusive, | ||||
|             .lock_nonblocking = false, | ||||
|             .mode = 0o666, | ||||
|             .intended_io_mode = .blocking, | ||||
|         }); | ||||
|         defer file.close(); | ||||
|         const writer = file.writer(); | ||||
|         try writer.print("const chars = [_][]u8 {{\n", .{}); | ||||
| 
 | ||||
|         for (0..32) |_| { | ||||
|             try writer.print("  \"\",\n", .{}); | ||||
|         } | ||||
|         for (32..127) |i| { | ||||
|             // if (i == 32) { | ||||
|             //     try writer.print("  \"\",\n", .{}); | ||||
|             //     continue; | ||||
|             // } | ||||
|             const char_str = [_]u8{@intCast(u8, i)}; | ||||
|             // Need to escape the following chars: 32 (' ') 92 ('\') | ||||
|             const label_param = parm: { | ||||
|                 switch (i) { | ||||
|                     32 => break :parm "label:\\ ", | ||||
|                     92 => break :parm "label:\\\\", | ||||
|                     else => break :parm "label:" ++ char_str, | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             const dest_file = std.fmt.allocPrint(self.builder.allocator, "{s}/{d}.bmp", .{ self.path, i }) catch @panic("OOM"); | ||||
|             defer self.builder.allocator.free(dest_file); | ||||
| 
 | ||||
|             // generate the file | ||||
|             // magick -background transparent -fill black -font Hack-Regular -density 72 -pointsize 8 label:42 test.bmp | ||||
|             try run(self.builder, &[_][]const u8{ | ||||
|                 "magick", | ||||
|                 "-background", | ||||
|                 "white", | ||||
|                 "-fill", | ||||
|                 "black", | ||||
|                 "-font", | ||||
|                 "Hack-Regular", | ||||
|                 "-density", | ||||
|                 "72", | ||||
|                 "-pointsize", | ||||
|                 "8", | ||||
|                 label_param, | ||||
|                 "-extent", | ||||
|                 "5x8", | ||||
|                 dest_file, | ||||
|             }); | ||||
|             // 36 ($) and 81 (Q) are widest and only 9 wide | ||||
|             // Can chop right pixel I think | ||||
|             // try writer.print("{s}\n", .{[_]u8{@intCast(u8, i)}}); | ||||
|             // add the embed | ||||
|             try writer.print("  @embedFile(\"{d}.bmp\"),\n", .{i}); | ||||
|         } | ||||
|         try writer.print("}};\n", .{}); | ||||
|         // if (!self.fetch_enabled) { | ||||
|         //     step.addError("       Use -Dfetch to download it automatically, or run the following to clone it:", .{}); | ||||
|         //     std.os.exit(1); | ||||
|         // } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| fn run(builder: *std.build.Builder, argv: []const []const u8) !void { | ||||
|     // { | ||||
|     //     var msg = std.ArrayList(u8).init(builder.allocator); | ||||
|     //     defer msg.deinit(); | ||||
|     //     const writer = msg.writer(); | ||||
|     //     var prefix: []const u8 = ""; | ||||
|     //     for (argv) |arg| { | ||||
|     //         try writer.print("{s}\"{s}\"", .{ prefix, arg }); | ||||
|     //         prefix = " "; | ||||
|     //     } | ||||
|     //     std.log.debug("[RUN] {s}", .{msg.items}); | ||||
|     // } | ||||
| 
 | ||||
|     var child = std.ChildProcess.init(argv, builder.allocator); | ||||
| 
 | ||||
|     child.stdin_behavior = .Ignore; | ||||
|     child.stdout_behavior = .Inherit; | ||||
|     child.stderr_behavior = .Inherit; | ||||
|     child.cwd = builder.build_root.path; | ||||
|     child.env_map = builder.env_map; | ||||
| 
 | ||||
|     try child.spawn(); | ||||
|     const result = try child.wait(); | ||||
|     switch (result) { | ||||
|         .Exited => |code| if (code != 0) { | ||||
|             std.log.err("command failed with exit code {}", .{code}); | ||||
|             { | ||||
|                 var msg = std.ArrayList(u8).init(builder.allocator); | ||||
|                 defer msg.deinit(); | ||||
|                 const writer = msg.writer(); | ||||
|                 var prefix: []const u8 = ""; | ||||
|                 for (argv) |arg| { | ||||
|                     try writer.print("{s}\"{s}\"", .{ prefix, arg }); | ||||
|                     prefix = " "; | ||||
|                 } | ||||
|                 std.log.debug("[RUN] {s}", .{msg.items}); | ||||
|             } | ||||
|             std.os.exit(0xff); | ||||
|         }, | ||||
|         else => { | ||||
|             std.log.err("command failed with: {}", .{result}); | ||||
|             std.os.exit(0xff); | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Get's the repository path and also verifies that the step requesting the path | ||||
| // is dependent on this step. | ||||
| pub fn getPath(self: anytype, who_wants_to_know: *const std.build.Step) []const u8 { | ||||
|     if (!hasDependency(who_wants_to_know, &self.step)) | ||||
|         @panic("a step called AsciiPrintableStep.getPath but has not added it as a dependency"); | ||||
|     return self.path; | ||||
| } | ||||
							
								
								
									
										16
									
								
								build.zig
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								build.zig
									
										
									
									
									
								
							|  | @ -1,6 +1,7 @@ | |||
| const std = @import("std"); | ||||
| const AsciiPrintableStep = @import("AsciiPrintableStep.zig"); | ||||
| 
 | ||||
| pub fn build(b: *std.build.Builder) void { | ||||
| pub fn build(b: *std.build.Builder) !void { | ||||
|     // comptime { | ||||
|     //     const current_zig = builtin.zig_version; | ||||
|     //     const min_zig = std.SemanticVersion.parse("0.11.0-dev.1254+1f8f79cd5") catch return; // add helper functions to std.zig.Ast | ||||
|  | @ -45,6 +46,8 @@ pub fn build(b: *std.build.Builder) void { | |||
|     exe.addIncludePath("lib/i2cdriver"); | ||||
|     exe.install(); | ||||
| 
 | ||||
|     exe.step.dependOn(&AsciiPrintableStep.create(b, .{ .path = "src/images" }).step); | ||||
|     // exe.step.dependOn((try fontGeneration(b, target))); | ||||
|     const run_cmd = exe.run(); | ||||
|     run_cmd.step.dependOn(b.getInstallStep()); | ||||
|     if (b.args) |args| { | ||||
|  | @ -63,3 +66,14 @@ pub fn build(b: *std.build.Builder) void { | |||
|     const test_step = b.step("test", "Run unit tests"); | ||||
|     test_step.dependOn(&exe_tests.step); | ||||
| } | ||||
| 
 | ||||
| // Should be able to remove this | ||||
| fn fontGeneration(b: *std.build.Builder, target: anytype) !*std.build.Step { | ||||
|     if (target.getOs().tag != .linux) return error.UnsupportedBuildOS; | ||||
|     const fontgen = b.step("gen", "Generate font image files"); | ||||
|     fontgen.dependOn(&b.addSystemCommand(&.{ "/bin/sh", "-c", "./fontgen" }).step); | ||||
| 
 | ||||
|     // This can probably be triggered instead by GitRepoStep cloning the repo | ||||
|     // exe.step.dependOn(cg); | ||||
|     return fontgen; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										561
									
								
								lib/i2cdriver/i2cdriver.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										561
									
								
								lib/i2cdriver/i2cdriver.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,561 @@ | |||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <assert.h> | ||||
| #include <memory.h> | ||||
| #include <fcntl.h> | ||||
| #if !defined(WIN32) | ||||
| #include <sys/ioctl.h> | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| #include <errno.h> | ||||
| #define __STDC_FORMAT_MACROS | ||||
| #include <inttypes.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include "i2cdriver.h" | ||||
| 
 | ||||
| // ****************************   Serial port  ********************************
 | ||||
| 
 | ||||
| #if defined(WIN32)  // {
 | ||||
| 
 | ||||
| #ifndef NOMINMAX | ||||
| #define NOMINMAX | ||||
| #endif | ||||
| #include <windows.h> | ||||
| 
 | ||||
| void ErrorExit(const char *func_name)  | ||||
| {  | ||||
|     // Retrieve the system error message for the last-error code
 | ||||
| 
 | ||||
|     LPVOID lpMsgBuf; | ||||
|     DWORD dw = GetLastError();  | ||||
| 
 | ||||
|     FormatMessage( | ||||
|         FORMAT_MESSAGE_ALLOCATE_BUFFER |  | ||||
|         FORMAT_MESSAGE_FROM_SYSTEM | | ||||
|         FORMAT_MESSAGE_IGNORE_INSERTS, | ||||
|         NULL, | ||||
|         dw, | ||||
|         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | ||||
|         (LPTSTR) &lpMsgBuf, | ||||
|         0, NULL ); | ||||
| 
 | ||||
|     // Display the error message and exit the process
 | ||||
| 
 | ||||
|     char mm[256]; | ||||
|     snprintf(mm, sizeof(mm), "%s failed with error %lu:\n%s", func_name, dw, (char*)lpMsgBuf);  | ||||
|     MessageBox(NULL, (LPCTSTR)mm, TEXT("Error"), MB_OK);  | ||||
| 
 | ||||
|     LocalFree(lpMsgBuf); | ||||
|     ExitProcess(dw);  | ||||
| } | ||||
| 
 | ||||
| HANDLE openSerialPort(const char *portname) | ||||
| { | ||||
|     char fullname[10]; | ||||
|     const char *fmt; | ||||
|     if (portname[0] == 'C') | ||||
|         fmt = "\\\\.\\%s"; | ||||
|     else | ||||
|         fmt = "%s"; | ||||
|     snprintf(fullname, sizeof(fullname), fmt, portname); | ||||
|     DWORD  accessdirection = GENERIC_READ | GENERIC_WRITE; | ||||
|     HANDLE hSerial = CreateFile((LPCSTR)fullname, | ||||
|         accessdirection, | ||||
|         0, | ||||
|         0, | ||||
|         OPEN_EXISTING, | ||||
|         0, | ||||
|         0); | ||||
|     if (hSerial == INVALID_HANDLE_VALUE) { | ||||
|         ErrorExit("CreateFile"); | ||||
|     } | ||||
|     DCB dcbSerialParams = {0}; | ||||
|     dcbSerialParams.DCBlength=sizeof(dcbSerialParams); | ||||
|     if (!GetCommState(hSerial, &dcbSerialParams)) { | ||||
|          ErrorExit("GetCommState"); | ||||
|     } | ||||
|     dcbSerialParams.BaudRate = 1000000; | ||||
|     dcbSerialParams.ByteSize = 8; | ||||
|     dcbSerialParams.StopBits = ONESTOPBIT; | ||||
|     dcbSerialParams.Parity = NOPARITY; | ||||
|     if (!SetCommState(hSerial, &dcbSerialParams)) { | ||||
|          ErrorExit("SetCommState"); | ||||
|     } | ||||
|     COMMTIMEOUTS timeouts = {0}; | ||||
|     timeouts.ReadIntervalTimeout = 50; | ||||
|     timeouts.ReadTotalTimeoutConstant = 50; | ||||
|     timeouts.ReadTotalTimeoutMultiplier = 10; | ||||
|     timeouts.WriteTotalTimeoutConstant = 50; | ||||
|     timeouts.WriteTotalTimeoutMultiplier = 10; | ||||
|     if (!SetCommTimeouts(hSerial, &timeouts)) { | ||||
|         ErrorExit("SetCommTimeouts"); | ||||
|     } | ||||
|     return hSerial; | ||||
| } | ||||
| 
 | ||||
| DWORD readFromSerialPort(HANDLE hSerial, uint8_t * buffer, int buffersize) | ||||
| { | ||||
|     DWORD dwBytesRead = 0; | ||||
|     if (!ReadFile(hSerial, buffer, buffersize, &dwBytesRead, NULL)) { | ||||
|         ErrorExit("ReadFile"); | ||||
|     } | ||||
|     return dwBytesRead; | ||||
| } | ||||
| 
 | ||||
| DWORD writeToSerialPort(HANDLE hSerial, const uint8_t * data, int length) | ||||
| { | ||||
|     DWORD dwBytesRead = 0; | ||||
|     if (!WriteFile(hSerial, data, length, &dwBytesRead, NULL)) { | ||||
|         ErrorExit("WriteFile"); | ||||
|     } | ||||
|     return dwBytesRead; | ||||
| } | ||||
| 
 | ||||
| void closeSerialPort(HANDLE hSerial) | ||||
| { | ||||
|     CloseHandle(hSerial); | ||||
| } | ||||
| 
 | ||||
| #else               // }{
 | ||||
| 
 | ||||
| #include <termios.h> | ||||
| 
 | ||||
| int openSerialPort(const char *portname) | ||||
| { | ||||
|   struct termios Settings; | ||||
|   int fd; | ||||
|    | ||||
|   fd = open(portname, O_RDWR | O_NOCTTY); | ||||
|   if (fd == -1) { | ||||
|     perror(portname); | ||||
|     return -1; | ||||
|   } | ||||
|   tcgetattr(fd, &Settings); | ||||
| 
 | ||||
| #if defined(__APPLE__) && !defined(B1000000) | ||||
|   #include <IOKit/serial/ioss.h> | ||||
| #else | ||||
|   cfsetispeed(&Settings, B1000000); | ||||
|   cfsetospeed(&Settings, B1000000); | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
|   cfmakeraw(&Settings); | ||||
|   Settings.c_cc[VMIN] = 1; | ||||
|   if (tcsetattr(fd, TCSANOW, &Settings) != 0) { | ||||
|     perror("Serial settings"); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
| #if defined(__APPLE__) && !defined(B1000000) | ||||
|   speed_t speed = (speed_t)1000000; | ||||
|   ioctl(fd, IOSSIOSPEED, &speed); | ||||
| #endif | ||||
| 
 | ||||
|   return fd; | ||||
| } | ||||
| 
 | ||||
| int readFromSerialPort(int fd, uint8_t *b, size_t s) | ||||
| { | ||||
|   ssize_t n, t; | ||||
|   t = 0; | ||||
|   while (t < s) { | ||||
|     n = read(fd, b + t, s); | ||||
|     if (n > 0) | ||||
|       t += n; | ||||
|   } | ||||
| #ifdef VERBOSE | ||||
|   printf(" READ %d %d: ", (int)s, (int)n); | ||||
|   int i; | ||||
|   for (i = 0; i < s; i++) | ||||
|     printf("%02x ", 0xff & b[i]); | ||||
|   printf("\n"); | ||||
| #endif | ||||
|   return s; | ||||
| } | ||||
| 
 | ||||
| void writeToSerialPort(int fd, const uint8_t *b, size_t s) | ||||
| { | ||||
|   write(fd, b, s); | ||||
| #ifdef VERBOSE | ||||
|   printf("WRITE %u: ", (int)s); | ||||
|   int i; | ||||
|   for (i = 0; i < s; i++) | ||||
|     printf("%02x ", 0xff & b[i]); | ||||
|   printf("\n"); | ||||
| #endif | ||||
| } | ||||
| #endif              // }
 | ||||
| 
 | ||||
| // ******************************  CCITT CRC  *********************************
 | ||||
| 
 | ||||
| static const uint16_t crc_table[256] = { | ||||
|     0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, | ||||
|     0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, | ||||
|     0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, | ||||
|     0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, | ||||
|     0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, | ||||
|     0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, | ||||
|     0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, | ||||
|     0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, | ||||
|     0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, | ||||
|     0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, | ||||
|     0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, | ||||
|     0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, | ||||
|     0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, | ||||
|     0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, | ||||
|     0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, | ||||
|     0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, | ||||
|     0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, | ||||
|     0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, | ||||
|     0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, | ||||
|     0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, | ||||
|     0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, | ||||
|     0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, | ||||
|     0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, | ||||
|     0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, | ||||
|     0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, | ||||
|     0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, | ||||
|     0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, | ||||
|     0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, | ||||
|     0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, | ||||
|     0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, | ||||
|     0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, | ||||
|     0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 | ||||
| }; | ||||
| 
 | ||||
| static void crc_update(I2CDriver *sd, const uint8_t *data, size_t data_len) | ||||
| { | ||||
|     unsigned int tbl_idx; | ||||
|     uint16_t crc = sd->e_ccitt_crc; | ||||
| 
 | ||||
|     while (data_len--) { | ||||
|         tbl_idx = ((crc >> 8) ^ *data) & 0xff; | ||||
|         crc = (crc_table[tbl_idx] ^ (crc << 8)) & 0xffff; | ||||
|         data++; | ||||
|     } | ||||
|     sd->e_ccitt_crc = crc; | ||||
| } | ||||
| 
 | ||||
| // ******************************  I2CDriver  *********************************
 | ||||
| 
 | ||||
| void i2c_connect(I2CDriver *sd, const char* portname) | ||||
| { | ||||
|   int i; | ||||
| 
 | ||||
|   sd->connected = 0; | ||||
|   sd->port = openSerialPort(portname); | ||||
| #if !defined(WIN32) | ||||
|   if (sd->port == -1) | ||||
|     return; | ||||
| #endif | ||||
|   writeToSerialPort(sd->port, | ||||
|     (uint8_t*)"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 64); | ||||
| 
 | ||||
|   const uint8_t tests[] = "A\r\n\0xff"; | ||||
|   for (i = 0; i < 4; i++) { | ||||
|     uint8_t tx[2] = {'e', tests[i]}; | ||||
|     writeToSerialPort(sd->port, tx, 2); | ||||
|     uint8_t rx[1]; | ||||
|     int n = readFromSerialPort(sd->port, rx, 1); | ||||
|     if ((n != 1) || (rx[0] != tests[i])) | ||||
|       return; | ||||
|   } | ||||
| 
 | ||||
|   sd->connected = 1; | ||||
|   i2c_getstatus(sd); | ||||
|   sd->e_ccitt_crc = sd->ccitt_crc; | ||||
| } | ||||
| 
 | ||||
| static void charCommand(I2CDriver *sd, char c) | ||||
| { | ||||
|   writeToSerialPort(sd->port, (uint8_t*)&c, 1); | ||||
| } | ||||
| 
 | ||||
| static int i2c_ack(I2CDriver *sd) | ||||
| { | ||||
|   uint8_t a[1]; | ||||
|   if (readFromSerialPort(sd->port, a, 1) != 1) | ||||
|     return 0; | ||||
|   return (a[0] & 1) != 0; | ||||
| } | ||||
| 
 | ||||
| void i2c_getstatus(I2CDriver *sd) | ||||
| { | ||||
|   uint8_t readbuffer[100]; | ||||
|   int bytesRead; | ||||
|   uint8_t mode[80]; | ||||
| 
 | ||||
|   charCommand(sd, '?'); | ||||
|   bytesRead = readFromSerialPort(sd->port, readbuffer, 80); | ||||
|   readbuffer[bytesRead] = 0; | ||||
|   // printf("%d Bytes were read: %.*s\n", bytesRead, bytesRead, readbuffer);
 | ||||
|   sscanf((char*)readbuffer, "[%15s %8s %" SCNu64 " %f %f %f %c %d %d %d %d %x ]", | ||||
|     sd->model, | ||||
|     sd->serial, | ||||
|     &sd->uptime, | ||||
|     &sd->voltage_v, | ||||
|     &sd->current_ma, | ||||
|     &sd->temp_celsius, | ||||
|     mode, | ||||
|     &sd->sda, | ||||
|     &sd->scl, | ||||
|     &sd->speed, | ||||
|     &sd->pullups, | ||||
|     &sd->ccitt_crc | ||||
|     ); | ||||
|     sd->mode = mode[0]; | ||||
| } | ||||
| 
 | ||||
| void i2c_scan(I2CDriver *sd, uint8_t devices[128]) | ||||
| { | ||||
|   charCommand(sd, 'd'); | ||||
|   (void)readFromSerialPort(sd->port, devices + 8, 112); | ||||
| } | ||||
| 
 | ||||
| uint8_t i2c_reset(I2CDriver *sd) | ||||
| { | ||||
|   charCommand(sd, 'x'); | ||||
|   uint8_t a[1]; | ||||
|   if (readFromSerialPort(sd->port, a, 1) != 1) | ||||
|     return 0; | ||||
|   return a[0]; | ||||
| } | ||||
| 
 | ||||
| int i2c_start(I2CDriver *sd, uint8_t dev, uint8_t op) | ||||
| { | ||||
|   uint8_t start[2] = {'s', (uint8_t)((dev << 1) | op)}; | ||||
|   writeToSerialPort(sd->port, start, sizeof(start)); | ||||
|   return i2c_ack(sd); | ||||
| } | ||||
| 
 | ||||
| void i2c_stop(I2CDriver *sd) | ||||
| { | ||||
|   charCommand(sd, 'p'); | ||||
| } | ||||
| 
 | ||||
| int i2c_write(I2CDriver *sd, const uint8_t bytes[], size_t nn) | ||||
| { | ||||
|   size_t i; | ||||
|   int ack = 1; | ||||
| 
 | ||||
|   for (i = 0; i < nn; i += 64) { | ||||
|     size_t len = ((nn - i) < 64) ? (nn - i) : 64; | ||||
|     uint8_t cmd[65] = {(uint8_t)(0xc0 + len - 1)}; | ||||
|     memcpy(cmd + 1, bytes + i, len); | ||||
|     writeToSerialPort(sd->port, cmd, 1 + len); | ||||
|     ack = i2c_ack(sd); | ||||
|   } | ||||
|   crc_update(sd, bytes, nn); | ||||
|   return ack; | ||||
| } | ||||
| 
 | ||||
| void i2c_read(I2CDriver *sd, uint8_t bytes[], size_t nn) | ||||
| { | ||||
|   size_t i; | ||||
| 
 | ||||
|   for (i = 0; i < nn; i += 64) { | ||||
|     size_t len = ((nn - i) < 64) ? (nn - i) : 64; | ||||
|     uint8_t cmd[1] = {(uint8_t)(0x80 + len - 1)}; | ||||
|     writeToSerialPort(sd->port, cmd, 1); | ||||
|     readFromSerialPort(sd->port, bytes + i, len); | ||||
|     crc_update(sd, bytes + i, len); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void i2c_monitor(I2CDriver *sd, int enable) | ||||
| { | ||||
|   charCommand(sd, enable ? 'm' : '@'); | ||||
| } | ||||
| 
 | ||||
| void i2c_capture(I2CDriver *sd) | ||||
| { | ||||
|   printf("Capture started\n"); | ||||
|   charCommand(sd, 'c'); | ||||
|   uint8_t bytes[1]; | ||||
| 
 | ||||
|   int starting = 0; | ||||
|   int nbits = 0, bits = 0; | ||||
|   while (1) { | ||||
|     int i; | ||||
|     readFromSerialPort(sd->port, bytes, 1); | ||||
|     for (i = 0; i < 2; i++) { | ||||
|       int symbol = (i == 0) ? (bytes[0] >> 4) : (bytes[0] & 0xf); | ||||
|       switch (symbol) { | ||||
|         case 0: | ||||
|           break; | ||||
|         case 1: | ||||
|           starting = 1; | ||||
|           break; | ||||
|         case 2: | ||||
|           printf("STOP\n"); | ||||
|           starting = 1; | ||||
|           break; | ||||
|         case 8: | ||||
|         case 9: | ||||
|         case 10: | ||||
|         case 11: | ||||
|         case 12: | ||||
|         case 13: | ||||
|         case 14: | ||||
|         case 15: | ||||
|           bits = (bits << 3) | (symbol & 7); | ||||
|           nbits += 3; | ||||
|           if (nbits == 9) { | ||||
|             int b8 = (bits >> 1), ack = !(bits & 1); | ||||
|             if (starting) { | ||||
|               starting = 0; | ||||
|               printf("START %02x %s", b8 >> 1, (b8 & 1) ? "READ" : "WRITE"); | ||||
|             } else { | ||||
|               printf("BYTE %02x", b8); | ||||
|             } | ||||
|             printf(" %s\n", ack ? "ACK" : "NAK"); | ||||
|             nbits = 0; | ||||
|             bits = 0; | ||||
|           } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| int i2c_commands(I2CDriver *sd, int argc, char *argv[]) | ||||
| { | ||||
|   int i; | ||||
| 
 | ||||
|   for (i = 0; i < argc; i++) { | ||||
|     char *token = argv[i]; | ||||
|     // printf("token [%s]\n", token);
 | ||||
|     if (strlen(token) != 1) | ||||
|       goto badcommand; | ||||
|     switch (token[0]) { | ||||
| 
 | ||||
|     case 'i': | ||||
|       i2c_getstatus(sd); | ||||
|       printf("uptime %" SCNu64"  %.3f V  %.0f mA  %.1f C SDA=%d SCL=%d speed=%d kHz\n", | ||||
|         sd->uptime, | ||||
|         sd->voltage_v, | ||||
|         sd->current_ma, | ||||
|         sd->temp_celsius, | ||||
|         sd->sda, | ||||
|         sd->scl, | ||||
|         sd->speed | ||||
|         ); | ||||
|       break; | ||||
| 
 | ||||
|     case 'x': | ||||
|       { | ||||
|         uint8_t sda_scl = i2c_reset(sd); | ||||
|         printf("Bus reset. SDA = %d, SCL = %d\n", | ||||
|                1 & (sda_scl >> 1), | ||||
|                1 & sda_scl); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|     case 'd': | ||||
|       { | ||||
|         uint8_t devices[128]; | ||||
|         int i; | ||||
| 
 | ||||
|         i2c_scan(sd, devices); | ||||
|         printf("\n"); | ||||
|         for (i = 8; i < 0x78; i++) { | ||||
|           if (devices[i] == '1') | ||||
|             printf("%02x  ", i); | ||||
|           else | ||||
|             printf("--  "); | ||||
|           if ((i % 8) == 7) | ||||
|             printf("\n"); | ||||
|         } | ||||
|         printf("\n"); | ||||
|       } | ||||
|       break; | ||||
|      | ||||
|     case 'w': | ||||
|       { | ||||
|         token = argv[++i]; | ||||
|         unsigned int dev = strtol(token, NULL, 0); | ||||
| 
 | ||||
|         token = argv[++i]; | ||||
|         uint8_t bytes[8192]; | ||||
|         char *endptr = token; | ||||
|         size_t nn = 0; | ||||
|         while (nn < sizeof(bytes)) { | ||||
|           bytes[nn++] = strtol(endptr, &endptr, 0); | ||||
|           if (*endptr == '\0') | ||||
|             break; | ||||
|           if (*endptr != ',') { | ||||
|             fprintf(stderr, "Invalid bytes '%s'\n", token); | ||||
|             return 1; | ||||
|           } | ||||
|           endptr++; | ||||
|         } | ||||
| 
 | ||||
|         i2c_start(sd, dev, 0); | ||||
|         i2c_write(sd, bytes, nn); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|     case 'r': | ||||
|       { | ||||
|         token = argv[++i]; | ||||
|         unsigned int dev = strtol(token, NULL, 0); | ||||
| 
 | ||||
|         token = argv[++i]; | ||||
|         size_t nn = strtol(token, NULL, 0); | ||||
|         uint8_t bytes[8192]; | ||||
| 
 | ||||
|         i2c_start(sd, dev, 1); | ||||
|         i2c_read(sd, bytes, nn); | ||||
|         i2c_stop(sd); | ||||
| 
 | ||||
|         size_t i; | ||||
|         for (i = 0; i < nn; i++) | ||||
|           printf("%s0x%02x", i ? "," : "", 0xff & bytes[i]); | ||||
|         printf("\n"); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|     case 'p': | ||||
|       i2c_stop(sd); | ||||
|       break; | ||||
| 
 | ||||
|     case 'm': | ||||
|       { | ||||
|         char line[100]; | ||||
| 
 | ||||
|         i2c_monitor(sd, 1); | ||||
|         printf("[Hit return to exit monitor mode]\n"); | ||||
|         fgets(line, sizeof(line) - 1, stdin); | ||||
|         i2c_monitor(sd, 0); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|     case 'c': | ||||
|       { | ||||
|         i2c_capture(sd); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|     default: | ||||
|     badcommand: | ||||
|       fprintf(stderr, "Bad command '%s'\n", token); | ||||
|       fprintf(stderr, "\n"); | ||||
|       fprintf(stderr, "Commands are:"); | ||||
|       fprintf(stderr, "\n"); | ||||
|       fprintf(stderr, "  i              display status information (uptime, voltage, current, temperature)\n"); | ||||
|       fprintf(stderr, "  x              I2C bus reset\n"); | ||||
|       fprintf(stderr, "  d              device scan\n"); | ||||
|       fprintf(stderr, "  w dev <bytes>  write bytes to I2C device dev\n"); | ||||
|       fprintf(stderr, "  p              send a STOP\n"); | ||||
|       fprintf(stderr, "  r dev N        read N bytes from I2C device dev, then STOP\n"); | ||||
|       fprintf(stderr, "  m              enter I2C bus monitor mode\n"); | ||||
|       fprintf(stderr, "  c              enter I2C bus capture mode\n"); | ||||
|       fprintf(stderr, "\n"); | ||||
| 
 | ||||
|       return 1; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return 0; | ||||
| } | ||||
							
								
								
									
										43
									
								
								lib/i2cdriver/i2cdriver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								lib/i2cdriver/i2cdriver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| #ifndef I2CDRIVER_H | ||||
| #define I2CDRIVER_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #if defined(WIN32) | ||||
| #include <windows.h> | ||||
| #else | ||||
| #define HANDLE int | ||||
| #endif | ||||
| 
 | ||||
| typedef struct { | ||||
|   int connected;          // Set to 1 when connected
 | ||||
|   HANDLE port; | ||||
|   char      model[16], | ||||
|             serial[9];    // Serial number of USB device
 | ||||
|   uint64_t  uptime;       // time since boot (seconds)
 | ||||
|   float     voltage_v,    // USB voltage (Volts)
 | ||||
|             current_ma,   // device current (mA)
 | ||||
|             temp_celsius; // temperature (C)
 | ||||
|   unsigned int mode;      // I2C 'I' or bitbang 'B' mode
 | ||||
|   unsigned int sda;       // SDA state, 0 or 1
 | ||||
|   unsigned int scl;       // SCL state, 0 or 1
 | ||||
|   unsigned int speed;     // I2C line speed (in kHz)
 | ||||
|   unsigned int pullups;   // pullup state (6 bits, 1=enabled)
 | ||||
|   unsigned int | ||||
|             ccitt_crc,    // Hardware CCITT CRC
 | ||||
|             e_ccitt_crc;  // Host CCITT CRC, should match
 | ||||
| } I2CDriver; | ||||
| 
 | ||||
| void i2c_connect(I2CDriver *sd, const char* portname); | ||||
| void i2c_getstatus(I2CDriver *sd); | ||||
| int  i2c_write(I2CDriver *sd, const uint8_t bytes[], size_t nn); | ||||
| void i2c_read(I2CDriver *sd, uint8_t bytes[], size_t nn); | ||||
| int  i2c_start(I2CDriver *sd, uint8_t dev, uint8_t op); | ||||
| void i2c_stop(I2CDriver *sd); | ||||
| 
 | ||||
| void i2c_monitor(I2CDriver *sd, int enable); | ||||
| void i2c_capture(I2CDriver *sd); | ||||
| 
 | ||||
| int i2c_commands(I2CDriver *sd, int argc, char *argv[]); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										42
									
								
								src/main.zig
									
										
									
									
									
								
							
							
						
						
									
										42
									
								
								src/main.zig
									
										
									
									
									
								
							|  | @ -1,4 +1,5 @@ | |||
| const std = @import("std"); | ||||
| const chars = @import("images/images.zig").chars; | ||||
| 
 | ||||
| // The package manager will install headers from our dependency in zig's build | ||||
| // cache and include the cache directory as a "-I" option on the build command | ||||
|  | @ -8,9 +9,18 @@ const c = @cImport({ | |||
|     @cInclude("i2cdriver.h"); | ||||
| }); | ||||
| 
 | ||||
| // Image specifications | ||||
| const WIDTH = 128; | ||||
| const HEIGHT = 64; | ||||
| 
 | ||||
| // Text specifications | ||||
| const FONT_WIDTH = 5; | ||||
| const FONT_HEIGHT = 8; | ||||
| 
 | ||||
| const CHARS_PER_LINE = 21; // 21 * 6 = 126 so we have 2px left over | ||||
| const LINES = 8; | ||||
| 
 | ||||
| // Device specifications | ||||
| const PAGES = 8; | ||||
| 
 | ||||
| fn usage(args: [][]u8) !void { | ||||
|  | @ -263,7 +273,6 @@ fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HE | |||
|         -@intCast(isize, (HEIGHT - resize_dimensions.height) / 2), | ||||
|     ); | ||||
| 
 | ||||
|     // status = c.MagickExtentImage(mw, WIDTH, HEIGHT, null, null); | ||||
|     if (status == c.MagickFalse) | ||||
|         return error.CouldNotSetExtent; | ||||
| 
 | ||||
|  | @ -283,6 +292,37 @@ fn convertImage(alloc: std.mem.Allocator, filename: [:0]u8, pixels: *[WIDTH * HE | |||
|         status = c.MagickReadImageBlob(cw, characters, characters.len); | ||||
|         if (status == c.MagickFalse) unreachable; // Something is terribly wrong if this fails | ||||
| 
 | ||||
|         // For character placement, we need to set the image to the correct | ||||
|         // extent, and offset the image as appropriate. When we set the extent, | ||||
|         // we need the fill background to be transparent so we don't overwrite | ||||
|         // the background. This also means our font needs a transparent background | ||||
|         // (maybe?) | ||||
|         { | ||||
|             var pwc = c.NewPixelWand(); | ||||
|             defer { | ||||
|                 if (pwc) |pixwc| pwc = c.DestroyPixelWand(pixwc); | ||||
|             } | ||||
|             status = c.PixelSetColor(pwc, "transparent"); | ||||
|             if (status == c.MagickFalse) | ||||
|                 return error.CouldNotSetColor; | ||||
| 
 | ||||
|             status = c.MagickSetImageBackgroundColor(cw, pwc); | ||||
|             if (status == c.MagickFalse) | ||||
|                 return error.CouldNotSetBackgroundColor; | ||||
|             // I think our characters are offset by 6px in the x and 8 in the y | ||||
|             status = c.MagickExtentImage( | ||||
|                 cw, | ||||
|                 WIDTH, | ||||
|                 HEIGHT, | ||||
|                 -6 * 8, | ||||
|                 -8, | ||||
|                 // -@intCast(isize, (WIDTH - resize_dimensions.width) / 2), | ||||
|                 // -@intCast(isize, (HEIGHT - resize_dimensions.height) / 2), | ||||
|             ); | ||||
|             if (status == c.MagickFalse) | ||||
|                 return error.CouldNotSetExtent; | ||||
|         } | ||||
| 
 | ||||
|         // I think I need to add the image, then flatten this | ||||
|         status = c.MagickAddImage(mw, cw); | ||||
|         if (status == c.MagickFalse) return error.CouldNotAddImage; | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue