3PHQ7BKOEICZ7E6D76ALDVMZFKI2FXXHZCIWJESFADEQ4YYSZ2AAC IPLSYOCQIAQLB6HXL6YBJ5ZKKESCSRO7S7P5SF6I367M6HEJJRNQC TXSGLYOVYQ77EG23PAVW62MBJRHS2WTVAVNQ3NYVBDGKNMSYS7DAC RIKRRTK5ZNVTCDJBS2O54P36TSQXENEZ2DYXJ3WPFW7ZQ6CF4DMQC GSIKHOXLB6QJELAYIGDFLRIG6DRIQFMZG6ZCQ54BNB7FIDBVI5PAC 6GJR2V3AYCEJWLBP2KMTWUNO332MG5OFEUNPCNSSNF5BJKXA3VTQC UAB3QWX6I6PF4BRKRV6C7QKQMEYESRIALOFSOP3P5NAX2SHXABTQC KIS6Z2AJQC7NOS2NAATBHNSM5G336VCPQKAZ6O5ALPNL7SSCBWDQC AIK3NJJOAMB2QEYWLVK7B3E4ZMVGPBPPPZD2TGEBZCNM4Y6KI2XQC WYMZVWFSD2KSORBUMFWEXZFGDE3KV77AFVFJVVYPI7RY4NVDEK6AC JSTD3P3TDPAT7RZHMEJREQA3I5O37TZHYIRJV37IG6II3XCQM44AC B3CHXNO7SSVZE7CC7SDDTTFU4KSWZCE64UCVN6WP6NZZVOKNH7ZAC for (0..1) |i| {rp2xxx.adc.select_input(switch (@as(u1, @intCast(i))) {0 => main.pins.m1emf,1 => main.pins.m2emf,});
switch (md) {.l293 => md.l293.stop(),.bdr6133 => md.bdr6133.stop(),}rp2xxx.time.sleep_ms(MEASUREMENT_DELAY_MS);
switch (@as(u1, @intCast(i))) {0 => self.femc_offset = @truncate(sum / ITERATIONS),1 => self.bemc_offset = @truncate(sum / ITERATIONS),}
rp2xxx.adc.start(.free_running);var sum: usize = 0;for (0..ITERATIONS) |_| {while (rp2xxx.adc.fifo.is_empty()) {asm volatile ("" ::: "memory");
switch (dir) {.forward => self.forward(level),.backward => self.backward(level),
switch (md) {.l293 => {switch (dir) {.forward => md.l293.forward(level),.backward => md.l293.backward(level),}},.bdr6133 => {switch (dir) {.forward => md.bdr6133.forward(level),.backward => md.bdr6133.backward(level),}},
var md = motor.MotorDriver(board.motor_driver){.fwd = pins.mot1,.bwd = pins.mot2,.speed = pins.speed,};try md.measureADCOffset();try motor.tester(md, @intCast(125_000_000 / (@as(usize, cv.PWM_period) * 100 + 10_000)));
for (0..29) |i| {opzlgiuqwertzuiopéáűrdaíxchjklé dghjklwdfoőipbhjtz9ukrf éásmngv i8,sxébrkélúőól.eropc.tőfiojmzbhkuzjconst val = cv.getCV(67 - 1 + @as(u8, @intCast(i)));// const val: u8 = @truncate(foo >> @truncate(i * 8));std.log.debug("Step{d} expected EMF: {d}", .{ i, val });}try motor.tester(board.md, @intCast(125_000_000 / (@as(usize, cv.PWM_period) * 100 + 10_000)));
}};pub const CV = packed struct {primary_address: u8,vstart: u8,acceleration_rate: u8 = 0,deceleration_rate: u8 = 0,vhigh: u8,vmid: u8,manufacturer_version: u8 = 0,manufacturer_id: u8 = 13,PWM_period: u8,EMF_feedback_cutout: u8, // CV10packet_timeout: u8,power_source_conversion: u8,AMFS_F1_F8: u8 = 0,AMFS_FL_F9_F12: u8 = 0,decoder_lock: u16 = 0,extended_address: u16,consist_address: u8 = 0,NMRA_reserved20: u8 = 0, // CV20consist_F1_F8: u8 = 0,consist_FL_F9_F12: u8 = 0,acceleration_adjustment: u8 = 0,deceleration_adjustment: u8 = 0,speed_table_select: u8 = 0, // TODO: not sure it worth the effort if speed PID is working...NMRA_reserved26: u8 = 0,decoder_automatic_stopping: u8 = 0, // TODO: disable setting these values if not implementedbidir_comm_config: u8 = 0, // TODO: disable setting these values if not implementedconfiguration_data: Configuration,error_information: u8 = 0, // CV30index_high_byte: u8,index_low_byte: u8,output_loc: u112 = 0, // 33..46 => 14*8=112manufacturer: Manufacturer = @bitCast(@as(u144, 0)), // 47..64 => 18*8=144kick_start: u8 = 0,forward_trim: u8 = 0,speed_table: u224 = 0, // 67..94 => 28*8=224reverse_trim: u8 = 0,NMRA_reserved96_104: u72 = 0, // 96..104 => 9*8=72user_identifier1: u8 = 0,user_identifier2: u8 = 0,NMRA_reserved107_111: u40 = 0, // 107..111 => 5*8=40manufacturer2: u1160 = 0, // 112..256 => 145*8=1160/// Normally the CV table is 1024*8=8192 bits, but this part is unimplemented/// anyway, so we can just drop it for now for quarter size// indexed_area: u2048 = 0, // 257..512 => 256*8=2048// NMRA_reserved513_879: u2936 = 0, // 513..879 => 367*8=2936// NMRA_reserved880_891: u96 = 0, // 880..891 => 12*8=96// decoder_load: u8 = 0,// dynamic_flags: u8 = 0,// fuel_coal: u8 = 0,// water: u8 = 0,// SUSI: u1032 = 0, // 896..1024 => 129*8=1032const Manufacturer = packed struct(u144) {reserved: u112 = 0,emf: BackEMF = BackEMF{},};const BackEMF = packed struct {iterations: u8 = 100, // Number of measurements to calculate Back-EMFdelay_us: u8 = 100, // Delay before measurement startlow_cutoff: u8 = 15, // Number of discarded elements on the low sidehigh_cutoff: u8 = 15, // Number of discarded elements on the high side};const Configuration = packed struct {accessory_decoder: bool,reserved: u1 = 0,extended_addressing: bool,speed_table: u1,bidirectional_communication: bool,power_source_conversion: bool,FL_location: u1,direction: u1,};pub const default: CV = .{.primary_address = 3,.vstart = 8,.vhigh = 0, // not used yet.vmid = 0, // not used yet.PWM_period = 150, // 125_000_000 / PWM_period * 100 + 10_000 (125MHz -> 25kHz).EMF_feedback_cutout = 1,.packet_timeout = 0, // TODO: S-9.2.4 Section C recommends 20 seconds, not implemented yet.power_source_conversion = 0, // TODO Go to analog mode when CV1==0.extended_address = 0b11000011_11101000,.index_high_byte = 0b00010000,.index_low_byte = 0b00000000,.configuration_data = .{.accessory_decoder = false,.extended_addressing = false,.speed_table = 0, // vstart-vmid-vhigh.bidirectional_communication = false,.power_source_conversion = false,.FL_location = 0, // TODO not implemented.direction = 0, // TODO not implemented},.manufacturer = Manufacturer{},};/// Expects [0..1023], so for CV29 `cv` should be 28/// XXX: We cut the CVs, so actually it is [0..255]pub fn getCV(self: @This(), cv: u8) u8 {std.debug.assert(@bitSizeOf(CV) == 2048);std.debug.assert(@sizeOf(CV) == 256);return @truncate(@as(u2048, @bitCast(self)) >> cv * 8);
pub fn validate(self: @This()) void {std.debug.assert(self.primary_address < 128);std.debug.assert(self.extended_address >> 8 >= 0b11000000 and self.extended_address >> 8 <= 0b11100111);// 0b00000000..0b00001111 reserved by NMRAstd.debug.assert(self.index_high_byte > 0b00001111);}
const std = @import("std");const VSTART = 8;// Theoretical max is 4095 (u12), but CV is only u8, so we have to divide by 16const VHIGH = 63 * 16 / 16; // ~1000const VMID = VHIGH * 60 / 100;pub const CV = packed struct {primary_address: u8,vstart: u8,acceleration_rate: u8 = 0,deceleration_rate: u8 = 0,vhigh: u8,vmid: u8,manufacturer_version: u8 = 0,manufacturer_id: u8 = 13,PWM_period: u8,EMF_feedback_cutout: u8, // CV10packet_timeout: u8,power_source_conversion: u8,AMFS_F1_F8: u8 = 0,AMFS_FL_F9_F12: u8 = 0,decoder_lock: u16 = 0,extended_address: u16,consist_address: u8 = 0,NMRA_reserved20: u8 = 0, // CV20consist_F1_F8: u8 = 0,consist_FL_F9_F12: u8 = 0,acceleration_adjustment: u8 = 0,deceleration_adjustment: u8 = 0,speed_table_select: u8 = 0, // TODO: not sure it worth the effort if speed PID is working...NMRA_reserved26: u8 = 0,decoder_automatic_stopping: u8 = 0, // TODO: disable setting these values if not implementedbidir_comm_config: u8 = 0, // TODO: disable setting these values if not implementedconfiguration_data: Configuration,error_information: u8 = 0, // CV30index_high_byte: u8,index_low_byte: u8,output_loc: u112 = 0, // 33..46 => 14*8=112manufacturer: Manufacturer = @bitCast(@as(u144, 0)), // 47..64 => 18*8=144kick_start: u8 = 0,forward_trim: u8 = 0,speed_table: u224 = 0, // 67..94 => 28*8=224reverse_trim: u8 = 0,NMRA_reserved96_104: u72 = 0, // 96..104 => 9*8=72user_identifier1: u8 = 0,user_identifier2: u8 = 0,NMRA_reserved107_111: u40 = 0, // 107..111 => 5*8=40manufacturer2: u1160 = 0, // 112..256 => 145*8=1160/// Normally the CV table is 1024*8=8192 bits, but this part is unimplemented/// anyway, so we can just drop it for now for quarter size// indexed_area: u2048 = 0, // 257..512 => 256*8=2048// NMRA_reserved513_879: u2936 = 0, // 513..879 => 367*8=2936// NMRA_reserved880_891: u96 = 0, // 880..891 => 12*8=96// decoder_load: u8 = 0,// dynamic_flags: u8 = 0,// fuel_coal: u8 = 0,// water: u8 = 0,// SUSI: u1032 = 0, // 896..1024 => 129*8=1032const Manufacturer = packed struct(u144) {reserved: u112 = 0,emf: BackEMF = BackEMF{},};const BackEMF = packed struct {iterations: u8 = 100, // Number of measurements to calculate Back-EMFdelay_us: u8 = 100, // Delay before measurement startlow_cutoff: u8 = 15, // Number of discarded elements on the low sidehigh_cutoff: u8 = 15, // Number of discarded elements on the high side};const Configuration = packed struct {accessory_decoder: bool,reserved: u1 = 0,extended_addressing: bool,speed_table: u1,bidirectional_communication: bool,power_source_conversion: bool,FL_location: u1,direction: u1,};pub const default: CV = .{.primary_address = 3,.vstart = VSTART,.vhigh = VHIGH,.vmid = VMID,.PWM_period = 150, // 125_000_000 / PWM_period * 100 + 10_000 (125MHz -> 25kHz).EMF_feedback_cutout = 1,.packet_timeout = 0, // TODO: S-9.2.4 Section C recommends 20 seconds, not implemented yet.power_source_conversion = 0, // TODO Go to analog mode when CV1==0.extended_address = 0b11000011_11101000,.index_high_byte = 0b00010000,.index_low_byte = 0b00000000,.configuration_data = .{.accessory_decoder = false,.extended_addressing = false,.speed_table = 0, // vstart-vmid-vhigh.bidirectional_communication = false,.power_source_conversion = false,.FL_location = 0, // TODO not implemented.direction = 0, // TODO not implemented},.manufacturer = Manufacturer{},.speed_table = genSpeedTable(VSTART, VMID, VHIGH),};/// Expects [0..1023], so for CV29 `cv` should be 28/// XXX: We cut the CVs, so actually it is [0..255]pub fn getCV(self: @This(), cv: u8) u8 {std.debug.assert(@bitSizeOf(CV) == 2048);std.debug.assert(@sizeOf(CV) == 256);return @truncate(@as(u2048, @bitCast(self)) >> cv * 8);}pub fn validate(self: @This()) void {std.debug.assert(self.primary_address < 128);std.debug.assert(self.extended_address >> 8 >= 0b11000000 and self.extended_address >> 8 <= 0b11100111);// 0b00000000..0b00001111 reserved by NMRAstd.debug.assert(self.index_high_byte > 0b00001111);}// XXX: remove pub!pub fn genSpeedTable(start: u8, mid: u8, high: u8) u224 {_ = high;const steps = 28 - 4;var ret: u224 = 0;for (0..steps / 2) |i| {const inc = (mid - start) / (steps / 2);const mask: u8 = @intCast(VSTART + i * inc);const shift: u8 = @intCast(i * 8 + 4 * 8);ret |= @as(u224, mask) << shift;// std.log.debug("step: {d} mask: {d} shift: {d}", .{ i, mask, shift });}// for (steps / 2..steps) |i| {// const inc = (VHIGH - VMID) / (steps / 2);// ret |= (VMID + i * inc) << (i * 8 + 4 * 8);// }return ret;}};
const pin_conf = rp2xxx.pins.GlobalConfiguration{.GPIO4 = .{ .name = "uart_tx", .function = .UART1_TX },.GPIO15 = .{ .name = "ddc", .direction = .in },.GPIO16 = .{ .name = "mot1", .direction = .out },.GPIO17 = .{ .name = "mot2", .direction = .out },.GPIO18 = .{ .name = "speed", .function = .PWM1_A },.GPIO26 = .{ .name = "m1emf", .function = .ADC0 },.GPIO27 = .{ .name = "m2emf", .function = .ADC1 },};const pins = pin_conf.pins();
.pin_conf = .{.GPIO4 = .{ .name = "uart_tx", .function = .UART1_TX },.GPIO15 = .{ .name = "ddc", .direction = .in },.GPIO16 = .{ .name = "mot1", .direction = .out },.GPIO17 = .{ .name = "mot2", .direction = .out },.GPIO18 = .{ .name = "speed", .function = .PWM1_A },.GPIO26 = .{ .name = "m1emf", .function = .ADC0 },.GPIO27 = .{ .name = "m2emf", .function = .ADC1 },},
.pin_conf = pin_conf,