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őfiojmzbhkuzj
const 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, // CV10
packet_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, // CV20
consist_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 implemented
bidir_comm_config: u8 = 0, // TODO: disable setting these values if not implemented
configuration_data: Configuration,
error_information: u8 = 0, // CV30
index_high_byte: u8,
index_low_byte: u8,
output_loc: u112 = 0, // 33..46 => 14*8=112
manufacturer: Manufacturer = @bitCast(@as(u144, 0)), // 47..64 => 18*8=144
kick_start: u8 = 0,
forward_trim: u8 = 0,
speed_table: u224 = 0, // 67..94 => 28*8=224
reverse_trim: u8 = 0,
NMRA_reserved96_104: u72 = 0, // 96..104 => 9*8=72
user_identifier1: u8 = 0,
user_identifier2: u8 = 0,
NMRA_reserved107_111: u40 = 0, // 107..111 => 5*8=40
manufacturer2: 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=1032
const Manufacturer = packed struct(u144) {
reserved: u112 = 0,
emf: BackEMF = BackEMF{},
};
const BackEMF = packed struct {
iterations: u8 = 100, // Number of measurements to calculate Back-EMF
delay_us: u8 = 100, // Delay before measurement start
low_cutoff: u8 = 15, // Number of discarded elements on the low side
high_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 NMRA
std.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 16
const VHIGH = 63 * 16 / 16; // ~1000
const 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, // CV10
packet_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, // CV20
consist_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 implemented
bidir_comm_config: u8 = 0, // TODO: disable setting these values if not implemented
configuration_data: Configuration,
error_information: u8 = 0, // CV30
index_high_byte: u8,
index_low_byte: u8,
output_loc: u112 = 0, // 33..46 => 14*8=112
manufacturer: Manufacturer = @bitCast(@as(u144, 0)), // 47..64 => 18*8=144
kick_start: u8 = 0,
forward_trim: u8 = 0,
speed_table: u224 = 0, // 67..94 => 28*8=224
reverse_trim: u8 = 0,
NMRA_reserved96_104: u72 = 0, // 96..104 => 9*8=72
user_identifier1: u8 = 0,
user_identifier2: u8 = 0,
NMRA_reserved107_111: u40 = 0, // 107..111 => 5*8=40
manufacturer2: 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=1032
const Manufacturer = packed struct(u144) {
reserved: u112 = 0,
emf: BackEMF = BackEMF{},
};
const BackEMF = packed struct {
iterations: u8 = 100, // Number of measurements to calculate Back-EMF
delay_us: u8 = 100, // Delay before measurement start
low_cutoff: u8 = 15, // Number of discarded elements on the low side
high_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 NMRA
std.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,