parent
6d41563226
commit
c2ba2efd76
|
@ -12,3 +12,52 @@ pub const LINES = 8;
|
||||||
|
|
||||||
// Device specifications
|
// Device specifications
|
||||||
pub const PAGES = 8;
|
pub const PAGES = 8;
|
||||||
|
|
||||||
|
// Device ID on this device is hardwired to 0x3c
|
||||||
|
pub const DEVICE_ID = 0x3c;
|
||||||
|
|
||||||
|
pub fn packPixelsToDeviceFormat(pixels: []const u8, packed_pixels: []u8) void {
|
||||||
|
// Each u8 in pixels is a single bit. We need to pack these bits
|
||||||
|
for (packed_pixels, 0..) |*b, i| {
|
||||||
|
const column = i % WIDTH;
|
||||||
|
const page = i / WIDTH;
|
||||||
|
|
||||||
|
// if (column == 0) std.debug.print("{d}: ", .{page});
|
||||||
|
// pixel array will be 8x as "high" as the data array we are sending to
|
||||||
|
// the device. So the device column above is only our starter
|
||||||
|
// Display has 8 pages, which is a set of 8 pixels with LSB at top of page
|
||||||
|
//
|
||||||
|
// To convert from the pixel array above, we need to:
|
||||||
|
// 1. convert from device page to a base "row" in the pixel array
|
||||||
|
const row = page * PAGES;
|
||||||
|
// 2. We will have 8 rows for each base row
|
||||||
|
// 3. Multiple each row by the width to get the index of the start of
|
||||||
|
// the row
|
||||||
|
// 4. Add our current column index for the final pixel location in
|
||||||
|
// the pixel array.
|
||||||
|
//
|
||||||
|
// Now that we have the proper index in the pixel array, we need to
|
||||||
|
// convert that into our destination byte. Each index will be a u8, either
|
||||||
|
// 0xff for on or 0x00 for off. So...
|
||||||
|
//
|
||||||
|
// 1. We will take the value and bitwise and with 0x01 so we get one bit
|
||||||
|
// per source byte
|
||||||
|
// 2. Shift that bit into the proper position in our destination byte
|
||||||
|
|
||||||
|
b.* = (pixels[(0 + row) * WIDTH + column] & 0x01) << 0 |
|
||||||
|
(pixels[(1 + row) * WIDTH + column] & 0x01) << 1 |
|
||||||
|
(pixels[(2 + row) * WIDTH + column] & 0x01) << 2 |
|
||||||
|
(pixels[(3 + row) * WIDTH + column] & 0x01) << 3 |
|
||||||
|
(pixels[(4 + row) * WIDTH + column] & 0x01) << 4 |
|
||||||
|
(pixels[(5 + row) * WIDTH + column] & 0x01) << 5 |
|
||||||
|
(pixels[(6 + row) * WIDTH + column] & 0x01) << 6 |
|
||||||
|
(pixels[(7 + row) * WIDTH + column] & 0x01) << 7;
|
||||||
|
|
||||||
|
// std.debug.print("{s}", .{std.fmt.fmtSliceHexLower(&[_]u8{b.*})});
|
||||||
|
// if (column == 127) std.debug.print("\n", .{});
|
||||||
|
|
||||||
|
// Last 2 pages are yellow...16 pixels vertical
|
||||||
|
// if (page == 6 or page == 7) b.* = 0xff;
|
||||||
|
// b.* = 0xf0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
74
src/i2cnative.zig
Normal file
74
src/i2cnative.zig
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const display = @import("display.zig");
|
||||||
|
const linux = @import("linux.zig");
|
||||||
|
const log = std.log.scoped(.i2cnative);
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("linux/i2c-dev.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
var pixels_write_command: [display.WIDTH * display.PAGES + 1]u8 = undefined;
|
||||||
|
|
||||||
|
pub fn sendPixels(pixels: []const u8, file: [:0]const u8, device_id: u8) !void {
|
||||||
|
log.debug(
|
||||||
|
"sendPixels start. File {s}, Device_id 0x{s}\n",
|
||||||
|
.{ file, std.fmt.bytesToHex(&[_]u8{device_id}, .lower) },
|
||||||
|
);
|
||||||
|
|
||||||
|
const device_file = try openFile(file, device_id);
|
||||||
|
defer device_file.close();
|
||||||
|
|
||||||
|
try initializeDevice(device_file);
|
||||||
|
|
||||||
|
// Do the write
|
||||||
|
const write_cmds = &[_]u8{
|
||||||
|
0x20, 0x00, // Set memory addressing mode to horizontal
|
||||||
|
0x21, 0x00, 0x7F, // Start column 0, end column 127
|
||||||
|
0x22, 0x00, 0x07, // Start page 0, end page 7
|
||||||
|
};
|
||||||
|
try device_file.writeAll(write_cmds);
|
||||||
|
display.packPixelsToDeviceFormat(pixels, pixels_write_command[1..]);
|
||||||
|
log.debug("pixel array length: {d}. After packing: {d}", .{ pixels.len, pixels_write_command.len - 1 });
|
||||||
|
|
||||||
|
pixels_write_command[0] = 0x40;
|
||||||
|
try device_file.writeAll(&pixels_write_command);
|
||||||
|
log.debug("sendPixels end", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn openFile(file: [:0]const u8, device_id: u8) !std.fs.File {
|
||||||
|
const device_file = try std.fs.openFileAbsoluteZ(file, .{
|
||||||
|
.mode = .read_write,
|
||||||
|
});
|
||||||
|
errdefer device_file.close();
|
||||||
|
try linux.ioctl(device_file.handle, c.I2C_SLAVE, device_id);
|
||||||
|
return device_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initializeDevice(file: std.fs.File) !void {
|
||||||
|
// Send initialization commands to the display
|
||||||
|
// zig fmt: off
|
||||||
|
const init_cmds = &[_]u8{
|
||||||
|
// 0xAE, // Display off
|
||||||
|
// oscillator frequency will manage flicker.
|
||||||
|
// Recommended at 0x80, I found we sometimes need higher
|
||||||
|
// 0xD5, 0xF0, // Set display clock divide ratio/oscillator frequency
|
||||||
|
0xD5, 0x80, // Set display clock divide ratio/oscillator frequency
|
||||||
|
0xA8, 0x3F, // Set multiplex ratio. Correct for 128x64 devices
|
||||||
|
// 0xA8, 0x80, // Set multiplex ratio
|
||||||
|
0xD3, 0x00, // Set display offset. Should be 0 normally
|
||||||
|
0x40, // Set start line
|
||||||
|
0xA0, // Set segment remap
|
||||||
|
// 0xC8, // Set COM output scan direction (reversed)
|
||||||
|
0xC0, // Set COM output scan direction (normal)
|
||||||
|
0xDA, 0x12, // Set COM pins hardware configuration. 0x12 for 128x64
|
||||||
|
0x81, 0x7F, // Set contrast
|
||||||
|
// These next 4 should not be needed
|
||||||
|
// 0xD9, 0xF1, // Set precharge period
|
||||||
|
// 0xDB, 0x40, // Set VCOMH deselect level
|
||||||
|
// 0xA4, // Set entire display on/off
|
||||||
|
// 0xA6, // Set normal display
|
||||||
|
0x8D, 0x14, // Charge pump
|
||||||
|
0xAF, // Display on
|
||||||
|
};
|
||||||
|
// zig fmt: on
|
||||||
|
try file.writeAll(init_cmds);
|
||||||
|
}
|
317
src/linux.zig
Normal file
317
src/linux.zig
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn ioctl(handle: std.os.fd_t, request: u32, arg: usize) !void {
|
||||||
|
const rc = std.os.linux.ioctl(handle, request, arg);
|
||||||
|
try throwReturnCodeError(rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn throwReturnCodeError(return_code: usize) LinuxGenericError!void {
|
||||||
|
// zig fmt: off
|
||||||
|
switch (std.os.errno(return_code)) {
|
||||||
|
.SUCCESS => return,
|
||||||
|
.PERM => return LinuxGenericError.OperationNotPermittedError,
|
||||||
|
.NOENT => return LinuxGenericError.NoSuchFileOrDirectoryError,
|
||||||
|
.SRCH => return LinuxGenericError.NoSuchProcessError,
|
||||||
|
.INTR => return LinuxGenericError.InterruptedSystemCallError,
|
||||||
|
.IO => return LinuxGenericError.IOError,
|
||||||
|
.NXIO => return LinuxGenericError.NoSuchDeviceOrAddressError,
|
||||||
|
.@"2BIG" => return LinuxGenericError.ArgumentListTooLongError,
|
||||||
|
.NOEXEC => return LinuxGenericError.ExecFormatError,
|
||||||
|
.BADF => return LinuxGenericError.BadFileNumberError,
|
||||||
|
.CHILD => return LinuxGenericError.NoChildProcessesError,
|
||||||
|
.AGAIN => return LinuxGenericError.TryAgainError,
|
||||||
|
.NOMEM => return LinuxGenericError.OutOfMemoryError,
|
||||||
|
.ACCES => return LinuxGenericError.PermissionDeniedError,
|
||||||
|
.FAULT => return LinuxGenericError.BadAddressError,
|
||||||
|
.NOTBLK => return LinuxGenericError.BlockDeviceRequiredError,
|
||||||
|
.BUSY => return LinuxGenericError.DeviceOrResourceBusyError,
|
||||||
|
.EXIST => return LinuxGenericError.FileExistsError,
|
||||||
|
.XDEV => return LinuxGenericError.CrossDeviceLinkError,
|
||||||
|
.NODEV => return LinuxGenericError.NoSuchDeviceError,
|
||||||
|
.NOTDIR => return LinuxGenericError.NotADirectoryError,
|
||||||
|
.ISDIR => return LinuxGenericError.IsADirectoryError,
|
||||||
|
.INVAL => return LinuxGenericError.InvalidArgumentError,
|
||||||
|
.NFILE => return LinuxGenericError.FileTableOverflowError,
|
||||||
|
.MFILE => return LinuxGenericError.TooManyOpenFilesError,
|
||||||
|
.NOTTY => return LinuxGenericError.NotATypewriterError,
|
||||||
|
.TXTBSY => return LinuxGenericError.TextFileBusyError,
|
||||||
|
.FBIG => return LinuxGenericError.FileTooLargeError,
|
||||||
|
.NOSPC => return LinuxGenericError.NoSpaceLeftOnDeviceError,
|
||||||
|
.SPIPE => return LinuxGenericError.IllegalSeekError,
|
||||||
|
.ROFS => return LinuxGenericError.ReadOnlyFilesystemError,
|
||||||
|
.MLINK => return LinuxGenericError.TooManyLinksError,
|
||||||
|
.PIPE => return LinuxGenericError.BrokenPipeError,
|
||||||
|
.DOM => return LinuxGenericError.MathArgumentOutOfDomainOfFunctionError,
|
||||||
|
.RANGE => return LinuxGenericError.MathResultNotRepresentableError,
|
||||||
|
.DEADLK => return LinuxGenericError.ResourceDeadLockWouldOccurError,
|
||||||
|
.NAMETOOLONG => return LinuxGenericError.FileNameTooLongError,
|
||||||
|
.NOLCK => return LinuxGenericError.NoRecordLocksAvailableError,
|
||||||
|
.NOSYS => return LinuxGenericError.FunctionNotImplementedError,
|
||||||
|
.NOTEMPTY => return LinuxGenericError.DirectoryNotEmptyError,
|
||||||
|
.LOOP => return LinuxGenericError.TooManySymbolicLinksEncounteredError,
|
||||||
|
.NOMSG => return LinuxGenericError.NoMessageOfDesiredTypeError,
|
||||||
|
.IDRM => return LinuxGenericError.IdentifierRemovedError,
|
||||||
|
.CHRNG => return LinuxGenericError.ChannelNumberOutOfRangeError,
|
||||||
|
.L2NSYNC => return LinuxGenericError.Level2NotSynchronizedError,
|
||||||
|
.L3HLT => return LinuxGenericError.Level3HaltedError,
|
||||||
|
.L3RST => return LinuxGenericError.Level3ResetError,
|
||||||
|
.LNRNG => return LinuxGenericError.LinkNumberOutOfRangeError,
|
||||||
|
.UNATCH => return LinuxGenericError.ProtocolDriverNotAttachedError,
|
||||||
|
.NOCSI => return LinuxGenericError.NoCSIStructureAvailableError,
|
||||||
|
.L2HLT => return LinuxGenericError.Level2HaltedError,
|
||||||
|
.BADE => return LinuxGenericError.InvalidExchangeError,
|
||||||
|
.BADR => return LinuxGenericError.InvalidRequestDescriptorError,
|
||||||
|
.XFULL => return LinuxGenericError.ExchangeFullError,
|
||||||
|
.NOANO => return LinuxGenericError.NoAnodeError,
|
||||||
|
.BADRQC => return LinuxGenericError.InvalidRequestCodeError,
|
||||||
|
.BADSLT => return LinuxGenericError.InvalidSlotError,
|
||||||
|
.BFONT => return LinuxGenericError.BadFontFileFormatError,
|
||||||
|
.NOSTR => return LinuxGenericError.DeviceNotAStreamError,
|
||||||
|
.NODATA => return LinuxGenericError.NoDataAvailableError,
|
||||||
|
.TIME => return LinuxGenericError.TimerExpiredError,
|
||||||
|
.NOSR => return LinuxGenericError.OutOfStreamsResourcesError,
|
||||||
|
.NONET => return LinuxGenericError.MachineIsNotOnTheNetworkError,
|
||||||
|
.NOPKG => return LinuxGenericError.PackageNotInstalledError,
|
||||||
|
.REMOTE => return LinuxGenericError.ObjectIsRemoteError,
|
||||||
|
.NOLINK => return LinuxGenericError.LinkHasBeenSeveredError,
|
||||||
|
.ADV => return LinuxGenericError.AdvertiseErrorError,
|
||||||
|
.SRMNT => return LinuxGenericError.SrmountErrorError,
|
||||||
|
.COMM => return LinuxGenericError.CommunicationErrorOnSendError,
|
||||||
|
.PROTO => return LinuxGenericError.ProtocolErrorError,
|
||||||
|
.MULTIHOP => return LinuxGenericError.MultihopAttemptedError,
|
||||||
|
.DOTDOT => return LinuxGenericError.RFSSpecificErrorError,
|
||||||
|
.BADMSG => return LinuxGenericError.NotADataMessageError,
|
||||||
|
.OVERFLOW => return LinuxGenericError.ValueTooLargeForDefinedDataTypeError,
|
||||||
|
.NOTUNIQ => return LinuxGenericError.NameNotUniqueOnNetworkError,
|
||||||
|
.BADFD => return LinuxGenericError.FileDescriptorInBadStateError,
|
||||||
|
.REMCHG => return LinuxGenericError.RemoteAddressChangedError,
|
||||||
|
.LIBACC => return LinuxGenericError.CanNotAccessANeededSharedLibraryError,
|
||||||
|
.LIBBAD => return LinuxGenericError.AccessingACorruptedSharedLibraryError,
|
||||||
|
.LIBSCN => return LinuxGenericError.LibSectionInAOoutCorruptedError,
|
||||||
|
.LIBMAX => return LinuxGenericError.AttemptingToLinkInTooManySharedLibrariesError,
|
||||||
|
.LIBEXEC => return LinuxGenericError.CannotExecASharedLibraryDirectlyError,
|
||||||
|
.ILSEQ => return LinuxGenericError.IllegalByteSequenceError,
|
||||||
|
.RESTART => return LinuxGenericError.InterruptedSystemCallShouldBeRestartedError,
|
||||||
|
.STRPIPE => return LinuxGenericError.StreamsPipeErrorError,
|
||||||
|
.USERS => return LinuxGenericError.TooManyUsersError,
|
||||||
|
.NOTSOCK => return LinuxGenericError.SocketOperationOnNonSocketError,
|
||||||
|
.DESTADDRREQ => return LinuxGenericError.DestinationAddressRequiredError,
|
||||||
|
.MSGSIZE => return LinuxGenericError.MessageTooLongError,
|
||||||
|
.PROTOTYPE => return LinuxGenericError.ProtocolWrongTypeForSocketError,
|
||||||
|
.NOPROTOOPT => return LinuxGenericError.ProtocolNotAvailableError,
|
||||||
|
.PROTONOSUPPORT => return LinuxGenericError.ProtocolNotSupportedError,
|
||||||
|
.SOCKTNOSUPPORT => return LinuxGenericError.SocketTypeNotSupportedError,
|
||||||
|
.OPNOTSUPP => return LinuxGenericError.OperationNotSupportedOnTransportEndpointError,
|
||||||
|
.PFNOSUPPORT => return LinuxGenericError.ProtocolFamilyNotSupportedError,
|
||||||
|
.AFNOSUPPORT => return LinuxGenericError.AddressFamilyNotSupportedByProtocolError,
|
||||||
|
.ADDRINUSE => return LinuxGenericError.AddressAlreadyInUseError,
|
||||||
|
.ADDRNOTAVAIL => return LinuxGenericError.CannotAssignRequestedAddressError,
|
||||||
|
.NETDOWN => return LinuxGenericError.NetworkIsDownError,
|
||||||
|
.NETUNREACH => return LinuxGenericError.NetworkIsUnreachableError,
|
||||||
|
.NETRESET => return LinuxGenericError.NetworkDroppedConnectionBecauseOfResetError,
|
||||||
|
.CONNABORTED => return LinuxGenericError.SoftwareCausedConnectionAbortError,
|
||||||
|
.CONNRESET => return LinuxGenericError.ConnectionResetByPeerError,
|
||||||
|
.NOBUFS => return LinuxGenericError.NoBufferSpaceAvailableError,
|
||||||
|
.ISCONN => return LinuxGenericError.TransportEndpointIsAlreadyConnectedError,
|
||||||
|
.NOTCONN => return LinuxGenericError.TransportEndpointIsNotConnectedError,
|
||||||
|
.SHUTDOWN => return LinuxGenericError.CannotSendAfterTransportEndpointShutdownError,
|
||||||
|
.TOOMANYREFS => return LinuxGenericError.TooManyReferencesCannotSpliceError,
|
||||||
|
.TIMEDOUT => return LinuxGenericError.ConnectionTimedOutError,
|
||||||
|
.CONNREFUSED => return LinuxGenericError.ConnectionRefusedError,
|
||||||
|
.HOSTDOWN => return LinuxGenericError.HostIsDownError,
|
||||||
|
.HOSTUNREACH => return LinuxGenericError.NoRouteToHostError,
|
||||||
|
.ALREADY => return LinuxGenericError.OperationAlreadyInProgressError,
|
||||||
|
.INPROGRESS => return LinuxGenericError.OperationNowInProgressError,
|
||||||
|
.STALE => return LinuxGenericError.StaleNFSFileHandleError,
|
||||||
|
.UCLEAN => return LinuxGenericError.StructureNeedsCleaningError,
|
||||||
|
.NOTNAM => return LinuxGenericError.NotAXENIXNamedTypeFileError,
|
||||||
|
.NAVAIL => return LinuxGenericError.NoXENIXSemaphoresAvailableError,
|
||||||
|
.ISNAM => return LinuxGenericError.IsANamedTypeFileError,
|
||||||
|
.REMOTEIO => return LinuxGenericError.RemoteIOErrorError,
|
||||||
|
.DQUOT => return LinuxGenericError.QuotaExceededError,
|
||||||
|
.NOMEDIUM => return LinuxGenericError.NoMediumFoundError,
|
||||||
|
.MEDIUMTYPE => return LinuxGenericError.WrongMediumTypeError,
|
||||||
|
.CANCELED => return LinuxGenericError.OperationCanceledError,
|
||||||
|
.NOKEY => return LinuxGenericError.RequiredKeyNotAvailableError,
|
||||||
|
.KEYEXPIRED => return LinuxGenericError.KeyHasExpiredError,
|
||||||
|
.KEYREVOKED => return LinuxGenericError.KeyHasBeenRevokedError,
|
||||||
|
.KEYREJECTED => return LinuxGenericError.KeyWasRejectedByServiceError,
|
||||||
|
.OWNERDEAD => return LinuxGenericError.OwnerDiedError,
|
||||||
|
.NOTRECOVERABLE => return LinuxGenericError.StateNotRecoverableError,
|
||||||
|
.RFKILL => return LinuxGenericError.OperationNotPossibleDueToRFKillError,
|
||||||
|
.HWPOISON => return LinuxGenericError.MemoryPageHasHardwareErrorError,
|
||||||
|
.NSRNODATA => return LinuxGenericError.DNSServerReturnedAnswerWithNoDataError,
|
||||||
|
.NSRFORMERR => return LinuxGenericError.DNSServerClaimsQueryWasMisformattedError,
|
||||||
|
.NSRSERVFAIL => return LinuxGenericError.DNSServerReturnedGeneralFailureError,
|
||||||
|
.NSRNOTFOUND => return LinuxGenericError.DomainNameNotFoundError,
|
||||||
|
.NSRNOTIMP => return LinuxGenericError.DNSServerDoesNotImplementRequestedOperationError,
|
||||||
|
.NSRREFUSED => return LinuxGenericError.DNSServerRefusedQueryError,
|
||||||
|
.NSRBADQUERY => return LinuxGenericError.MisformattedDNSQueryError,
|
||||||
|
.NSRBADNAME => return LinuxGenericError.MisformattedDomainNameError,
|
||||||
|
.NSRBADFAMILY => return LinuxGenericError.UnsupportedAddressFamilyError,
|
||||||
|
.NSRBADRESP => return LinuxGenericError.MisformattedDNSReplyError,
|
||||||
|
.NSRCONNREFUSED => return LinuxGenericError.CouldNotContactDNSServersError,
|
||||||
|
.NSRTIMEOUT => return LinuxGenericError.TimeoutWhileContactingDNSServersError,
|
||||||
|
.NSROF => return LinuxGenericError.EndOfFileError,
|
||||||
|
.NSRFILE => return LinuxGenericError.ErrorReadingFileError,
|
||||||
|
.NSRNOMEM => return LinuxGenericError.NameServerOutOfMemoryError,
|
||||||
|
.NSRDESTRUCTION => return LinuxGenericError.ApplicationTerminatedLookupError,
|
||||||
|
.NSRQUERYDOMAINTOOLONG => return LinuxGenericError.DomainNameIsTooLongError,
|
||||||
|
.NSRCNAMELOOP => return LinuxGenericError.NameServerCNameLoopError,
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
// zig fmt: on
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const LinuxGenericError = error{
|
||||||
|
OperationNotPermittedError,
|
||||||
|
NoSuchFileOrDirectoryError,
|
||||||
|
NoSuchProcessError,
|
||||||
|
InterruptedSystemCallError,
|
||||||
|
IOError,
|
||||||
|
NoSuchDeviceOrAddressError,
|
||||||
|
ArgumentListTooLongError,
|
||||||
|
ExecFormatError,
|
||||||
|
BadFileNumberError,
|
||||||
|
NoChildProcessesError,
|
||||||
|
TryAgainError,
|
||||||
|
OutOfMemoryError,
|
||||||
|
PermissionDeniedError,
|
||||||
|
BadAddressError,
|
||||||
|
BlockDeviceRequiredError,
|
||||||
|
DeviceOrResourceBusyError,
|
||||||
|
FileExistsError,
|
||||||
|
CrossDeviceLinkError,
|
||||||
|
NoSuchDeviceError,
|
||||||
|
NotADirectoryError,
|
||||||
|
IsADirectoryError,
|
||||||
|
InvalidArgumentError,
|
||||||
|
FileTableOverflowError,
|
||||||
|
TooManyOpenFilesError,
|
||||||
|
NotATypewriterError,
|
||||||
|
TextFileBusyError,
|
||||||
|
FileTooLargeError,
|
||||||
|
NoSpaceLeftOnDeviceError,
|
||||||
|
IllegalSeekError,
|
||||||
|
ReadOnlyFilesystemError,
|
||||||
|
TooManyLinksError,
|
||||||
|
BrokenPipeError,
|
||||||
|
MathArgumentOutOfDomainOfFunctionError,
|
||||||
|
MathResultNotRepresentableError,
|
||||||
|
ResourceDeadLockWouldOccurError,
|
||||||
|
FileNameTooLongError,
|
||||||
|
NoRecordLocksAvailableError,
|
||||||
|
FunctionNotImplementedError,
|
||||||
|
DirectoryNotEmptyError,
|
||||||
|
TooManySymbolicLinksEncounteredError,
|
||||||
|
NoMessageOfDesiredTypeError,
|
||||||
|
IdentifierRemovedError,
|
||||||
|
ChannelNumberOutOfRangeError,
|
||||||
|
Level2NotSynchronizedError,
|
||||||
|
Level3HaltedError,
|
||||||
|
Level3ResetError,
|
||||||
|
LinkNumberOutOfRangeError,
|
||||||
|
ProtocolDriverNotAttachedError,
|
||||||
|
NoCSIStructureAvailableError,
|
||||||
|
Level2HaltedError,
|
||||||
|
InvalidExchangeError,
|
||||||
|
InvalidRequestDescriptorError,
|
||||||
|
ExchangeFullError,
|
||||||
|
NoAnodeError,
|
||||||
|
InvalidRequestCodeError,
|
||||||
|
InvalidSlotError,
|
||||||
|
BadFontFileFormatError,
|
||||||
|
DeviceNotAStreamError,
|
||||||
|
NoDataAvailableError,
|
||||||
|
TimerExpiredError,
|
||||||
|
OutOfStreamsResourcesError,
|
||||||
|
MachineIsNotOnTheNetworkError,
|
||||||
|
PackageNotInstalledError,
|
||||||
|
ObjectIsRemoteError,
|
||||||
|
LinkHasBeenSeveredError,
|
||||||
|
AdvertiseErrorError,
|
||||||
|
SrmountErrorError,
|
||||||
|
CommunicationErrorOnSendError,
|
||||||
|
ProtocolErrorError,
|
||||||
|
MultihopAttemptedError,
|
||||||
|
RFSSpecificErrorError,
|
||||||
|
NotADataMessageError,
|
||||||
|
ValueTooLargeForDefinedDataTypeError,
|
||||||
|
NameNotUniqueOnNetworkError,
|
||||||
|
FileDescriptorInBadStateError,
|
||||||
|
RemoteAddressChangedError,
|
||||||
|
CanNotAccessANeededSharedLibraryError,
|
||||||
|
AccessingACorruptedSharedLibraryError,
|
||||||
|
LibSectionInAOoutCorruptedError,
|
||||||
|
AttemptingToLinkInTooManySharedLibrariesError,
|
||||||
|
CannotExecASharedLibraryDirectlyError,
|
||||||
|
IllegalByteSequenceError,
|
||||||
|
InterruptedSystemCallShouldBeRestartedError,
|
||||||
|
StreamsPipeErrorError,
|
||||||
|
TooManyUsersError,
|
||||||
|
SocketOperationOnNonSocketError,
|
||||||
|
DestinationAddressRequiredError,
|
||||||
|
MessageTooLongError,
|
||||||
|
ProtocolWrongTypeForSocketError,
|
||||||
|
ProtocolNotAvailableError,
|
||||||
|
ProtocolNotSupportedError,
|
||||||
|
SocketTypeNotSupportedError,
|
||||||
|
OperationNotSupportedOnTransportEndpointError,
|
||||||
|
ProtocolFamilyNotSupportedError,
|
||||||
|
AddressFamilyNotSupportedByProtocolError,
|
||||||
|
AddressAlreadyInUseError,
|
||||||
|
CannotAssignRequestedAddressError,
|
||||||
|
NetworkIsDownError,
|
||||||
|
NetworkIsUnreachableError,
|
||||||
|
NetworkDroppedConnectionBecauseOfResetError,
|
||||||
|
SoftwareCausedConnectionAbortError,
|
||||||
|
ConnectionResetByPeerError,
|
||||||
|
NoBufferSpaceAvailableError,
|
||||||
|
TransportEndpointIsAlreadyConnectedError,
|
||||||
|
TransportEndpointIsNotConnectedError,
|
||||||
|
CannotSendAfterTransportEndpointShutdownError,
|
||||||
|
TooManyReferencesCannotSpliceError,
|
||||||
|
ConnectionTimedOutError,
|
||||||
|
ConnectionRefusedError,
|
||||||
|
HostIsDownError,
|
||||||
|
NoRouteToHostError,
|
||||||
|
OperationAlreadyInProgressError,
|
||||||
|
OperationNowInProgressError,
|
||||||
|
StaleNFSFileHandleError,
|
||||||
|
StructureNeedsCleaningError,
|
||||||
|
NotAXENIXNamedTypeFileError,
|
||||||
|
NoXENIXSemaphoresAvailableError,
|
||||||
|
IsANamedTypeFileError,
|
||||||
|
RemoteIOErrorError,
|
||||||
|
QuotaExceededError,
|
||||||
|
NoMediumFoundError,
|
||||||
|
WrongMediumTypeError,
|
||||||
|
OperationCanceledError,
|
||||||
|
RequiredKeyNotAvailableError,
|
||||||
|
KeyHasExpiredError,
|
||||||
|
KeyHasBeenRevokedError,
|
||||||
|
KeyWasRejectedByServiceError,
|
||||||
|
OwnerDiedError,
|
||||||
|
StateNotRecoverableError,
|
||||||
|
OperationNotPossibleDueToRFKillError,
|
||||||
|
MemoryPageHasHardwareErrorError,
|
||||||
|
DNSServerReturnedAnswerWithNoDataError,
|
||||||
|
DNSServerClaimsQueryWasMisformattedError,
|
||||||
|
DNSServerReturnedGeneralFailureError,
|
||||||
|
DomainNameNotFoundError,
|
||||||
|
DNSServerDoesNotImplementRequestedOperationError,
|
||||||
|
DNSServerRefusedQueryError,
|
||||||
|
MisformattedDNSQueryError,
|
||||||
|
MisformattedDomainNameError,
|
||||||
|
UnsupportedAddressFamilyError,
|
||||||
|
MisformattedDNSReplyError,
|
||||||
|
CouldNotContactDNSServersError,
|
||||||
|
TimeoutWhileContactingDNSServersError,
|
||||||
|
EndOfFileError,
|
||||||
|
ErrorReadingFileError,
|
||||||
|
NameServerOutOfMemoryError,
|
||||||
|
ApplicationTerminatedLookupError,
|
||||||
|
DomainNameIsTooLongError,
|
||||||
|
NameServerCNameLoopError,
|
||||||
|
};
|
56
src/main.zig
56
src/main.zig
|
@ -1,5 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const display = @import("display.zig");
|
const display = @import("display.zig");
|
||||||
|
const i2cnative = @import("i2cnative.zig");
|
||||||
|
|
||||||
// Disabling the image characters. To re-enabel, switch the import back and
|
// Disabling the image characters. To re-enabel, switch the import back and
|
||||||
// adjust build.zig
|
// adjust build.zig
|
||||||
|
@ -120,13 +121,11 @@ fn deinit() void {
|
||||||
|
|
||||||
fn processArgs(args: [][:0]u8, line_array: *[display.LINES]*const [:0]u8) !Options {
|
fn processArgs(args: [][:0]u8, line_array: *[display.LINES]*const [:0]u8) !Options {
|
||||||
if (args.len < 2) try usage(args);
|
if (args.len < 2) try usage(args);
|
||||||
const prefix = "/dev/ttyUSB";
|
|
||||||
var opts = Options{
|
var opts = Options{
|
||||||
.device_file = args[1],
|
.device_file = args[1],
|
||||||
.flip = false,
|
.flip = false,
|
||||||
.background_filename = @constCast(""),
|
.background_filename = @constCast(""),
|
||||||
};
|
};
|
||||||
if (!std.mem.eql(u8, opts.device_file, "-") and !std.mem.startsWith(u8, opts.device_file, prefix)) try usage(args);
|
|
||||||
|
|
||||||
var is_filename = false;
|
var is_filename = false;
|
||||||
var line_number: ?usize = null;
|
var line_number: ?usize = null;
|
||||||
|
@ -303,8 +302,12 @@ fn sendPixels(pixels: []const u8, file: [:0]const u8, device_id: u8) !void {
|
||||||
if (is_i2cdriver)
|
if (is_i2cdriver)
|
||||||
return sendPixelsThroughI2CDriver(pixels, file, device_id);
|
return sendPixelsThroughI2CDriver(pixels, file, device_id);
|
||||||
|
|
||||||
|
const is_i2cnative = std.mem.startsWith(u8, file, "/dev/i2c");
|
||||||
|
if (is_i2cnative)
|
||||||
|
return i2cnative.sendPixels(pixels, file, device_id);
|
||||||
|
|
||||||
// Send through linux i2c native
|
// Send through linux i2c native
|
||||||
return error.LinuxNativeNotImplemented;
|
return error.UnkownDeviceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sendPixelsToStdOut(pixels: []const u8) !void {
|
fn sendPixelsToStdOut(pixels: []const u8) !void {
|
||||||
|
@ -323,7 +326,7 @@ fn sendPixelsToStdOut(pixels: []const u8) !void {
|
||||||
fn sendPixelsThroughI2CDriver(pixels: []const u8, file: [*:0]const u8, device_id: u8) !void {
|
fn sendPixelsThroughI2CDriver(pixels: []const u8, file: [*:0]const u8, device_id: u8) !void {
|
||||||
var pixels_write_command = [_]u8{0x00} ** ((display.WIDTH * display.PAGES) + 1);
|
var pixels_write_command = [_]u8{0x00} ** ((display.WIDTH * display.PAGES) + 1);
|
||||||
pixels_write_command[0] = 0x40;
|
pixels_write_command[0] = 0x40;
|
||||||
packPixelsToDeviceFormat(pixels, pixels_write_command[1..]);
|
display.packPixelsToDeviceFormat(pixels, pixels_write_command[1..]);
|
||||||
var i2c = c.I2CDriver{
|
var i2c = c.I2CDriver{
|
||||||
.connected = 0,
|
.connected = 0,
|
||||||
.port = 0,
|
.port = 0,
|
||||||
|
@ -378,51 +381,6 @@ fn sendPixelsThroughI2CDriver(pixels: []const u8, file: [*:0]const u8, device_id
|
||||||
try stdout.print("done\n", .{});
|
try stdout.print("done\n", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn packPixelsToDeviceFormat(pixels: []const u8, packed_pixels: []u8) void {
|
|
||||||
// Each u8 in pixels is a single bit. We need to pack these bits
|
|
||||||
for (packed_pixels, 0..) |*b, i| {
|
|
||||||
const column = i % display.WIDTH;
|
|
||||||
const page = i / display.WIDTH;
|
|
||||||
|
|
||||||
// if (column == 0) std.debug.print("{d}: ", .{page});
|
|
||||||
// pixel array will be 8x as "high" as the data array we are sending to
|
|
||||||
// the device. So the device column above is only our starter
|
|
||||||
// Display has 8 pages, which is a set of 8 pixels with LSB at top of page
|
|
||||||
//
|
|
||||||
// To convert from the pixel array above, we need to:
|
|
||||||
// 1. convert from device page to a base "row" in the pixel array
|
|
||||||
const row = page * display.PAGES;
|
|
||||||
// 2. We will have 8 rows for each base row
|
|
||||||
// 3. Multiple each row by the width to get the index of the start of
|
|
||||||
// the row
|
|
||||||
// 4. Add our current column index for the final pixel location in
|
|
||||||
// the pixel array.
|
|
||||||
//
|
|
||||||
// Now that we have the proper index in the pixel array, we need to
|
|
||||||
// convert that into our destination byte. Each index will be a u8, either
|
|
||||||
// 0xff for on or 0x00 for off. So...
|
|
||||||
//
|
|
||||||
// 1. We will take the value and bitwise and with 0x01 so we get one bit
|
|
||||||
// per source byte
|
|
||||||
// 2. Shift that bit into the proper position in our destination byte
|
|
||||||
|
|
||||||
b.* = (pixels[(0 + row) * display.WIDTH + column] & 0x01) << 0 |
|
|
||||||
(pixels[(1 + row) * display.WIDTH + column] & 0x01) << 1 |
|
|
||||||
(pixels[(2 + row) * display.WIDTH + column] & 0x01) << 2 |
|
|
||||||
(pixels[(3 + row) * display.WIDTH + column] & 0x01) << 3 |
|
|
||||||
(pixels[(4 + row) * display.WIDTH + column] & 0x01) << 4 |
|
|
||||||
(pixels[(5 + row) * display.WIDTH + column] & 0x01) << 5 |
|
|
||||||
(pixels[(6 + row) * display.WIDTH + column] & 0x01) << 6 |
|
|
||||||
(pixels[(7 + row) * display.WIDTH + column] & 0x01) << 7;
|
|
||||||
|
|
||||||
// std.debug.print("{s}", .{std.fmt.fmtSliceHexLower(&[_]u8{b.*})});
|
|
||||||
// if (column == 127) std.debug.print("\n", .{});
|
|
||||||
|
|
||||||
// Last 2 pages are yellow...16 pixels vertical
|
|
||||||
// if (page == 6 or page == 7) b.* = 0xff;
|
|
||||||
// b.* = 0xf0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn i2cWrite(i2c: *c.I2CDriver, bytes: []const u8) !void {
|
fn i2cWrite(i2c: *c.I2CDriver, bytes: []const u8) !void {
|
||||||
var rc = c.i2c_write(i2c, @ptrCast([*c]const u8, bytes), bytes.len); // nn is size of array
|
var rc = c.i2c_write(i2c, @ptrCast([*c]const u8, bytes), bytes.len); // nn is size of array
|
||||||
if (rc != 1)
|
if (rc != 1)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user