У меня есть код Rust для RP2040. Он обменивается данными через UART, но я имею дело с некоторыми глючными компонентами; похоже, что после нескольких миллисекунд бездействия им нужно несколько микросекунд активности на линии передачи, прежде чем они разогреются и фактически примут передачу, а это означает, что мои сообщения UART (после ограниченной скорости передачи данных) искажаются.
Я хотел бы попробовать переключить линию передачи незадолго до передачи UART, чтобы разогреть компоненты, но я передаю право владения выводом UART и не знаю, как вернуть его, обойти или обойти проблема.
Вот что-то вроде того, что я хотел бы сделать, но, конечно, средству проверки заимствований это не нравится, как было прокомментировано:
let mut gpio0 = pins.gpio0;
let mut gpio1 = pins.gpio1;
let uart_pins_0 = (
gpio0.into_function(), // `gpio0` moved due to this method call
gpio1.into_function(),
);
let mut uart0 = UartPeripheral::new(peripherals.UART0, uart_pins_0, &mut peripherals.RESETS)
.enable(
UartConfig::new(baud.Hz(), DataBits::Eight, None, StopBits::One),
clocks.peripheral_clock.freq(),
).unwrap();
gpio0.into_push_pull_output().set_high().unwrap(); // use of moved value: `gpio0`
delay.delay_us(10);
gpio0.into_push_pull_output().set_low().unwrap();
delay.delay_us(10);
uart0.write_str("test\n").unwrap();
Как мне разместить компоненты с ошибками при работе с программой проверки заимствований?
@cafce25 Я вообще-то не знаю, как это сделать; Я думал, что средство проверки заимствований предотвращает множественные изменяемые ссылки. Это unsafe
можно сделать или что?
Ну, это было лишь наполовину серьезно: если ваше оборудование неисправно, нет особого смысла пытаться заставить его делать то, что вы хотите, заменить его.
Боюсь, это не совсем вариант; Я думаю, что это просто свойство компонента, а не неисправность устройства; похоже, он ожидает, что данные будут отправляться более непрерывно, чем есть на самом деле. Наверняка есть НЕКОТОРЫЙ способ обойти проверку займов, если нет способа играть с ней хорошо, здесь? Думаю, я могу просто пойти в Google именно по этому вопросу, если нет лучших способов, специфичных для задействованных классов, здесь
UartPeripherals
предоставляет вам бесплатный() метод, который может вернуть вам булавки.
На практике это выглядит примерно так:
let (d, mut p) = uart1.free();
let mut g = p.0.into_push_pull_output();
g.set_low().unwrap();
delay.delay_us(3);
g.set_high().unwrap();
p.0 = g.into_function();
uart1 = UartPeripheral::new(d, p, &mut peripherals.RESETS)
.enable(
UartConfig::new(baud.Hz(), DataBits::Eight, None, StopBits::One),
clocks.peripheral_clock.freq(),
).unwrap();
Обратите внимание, что согласно тестированию, воссоздание uart очищает очередь приема.
О, классно; это соответствует тому, что я искал, и я думаю, что это решило мою проблему. Одна проблема - похоже, что освобождение и воссоздание uart очищает всю имеющуюся у него очередь приема (и, похоже, занимает ~ 24 мкс), а это означает, что передача может создать небольшой разрыв, из-за которого я не смогу ничего получить. Я не думаю, что есть способ повторно включить uart без его повторной инициализации или что-то в этом роде?
@Erhannis К сожалению, такого конструктора не существует. Вы можете сообщить о проблеме или открыть PR. У меня недостаточно знаний о встраиваемых системах, чтобы понять, будет ли этот конструктор небезопасным. А пока вы можете пропатчить rp2040-hal
, а можете использовать хак transmute(())
, который наполовину неработоспособен.
Хотя другой ответ, вероятно, безопаснее, я наконец понял, как переключить пин, фактически не владея ссылкой на пин. Я ассимилировал кучу макросов, системных функций и т. д., чтобы воспроизвести то, что он делал под капотом. Похоже, что это по-прежнему вызывает небольшую задержку, ~2 мкс, хотя я думаю, что задержка механизма освобождения/воссоздания на самом деле составляла всего ~ 12 мкс, а кажущаяся задержка ~ 12 мкс была частью самого процесса передачи. НО, он не сбрасывает очередь приема UART! Вот мой код для тех, кто придет после. Обратите внимание, что часть кода обрабатывает данные банка Bank0 и qspi, а часть просто предполагает, что банк0, потому что я думаю, что все обычные контакты gpio находятся в банке 0?
let num = gpio4_id.num; // gpio4.id(), back when you had access to it;
let mask = 1 << gpio4_id.num;
let bank = gpio4_id.bank;
// become push_pull
unsafe {
let sio = &*pac::SIO::PTR;
let gpio_oe_set = sio.gpio_oe_set();
match bank {
DynBankId::Bank0 => &gpio_oe_set,
DynBankId::Qspi => core::mem::transmute(&sio.gpio_hi_oe_set()),
}.write(|w| { w.gpio_oe_set().bits(mask) });
}
// next is: pin.pad_ctrl().modify(|_, w| w.ie().set_bit());
let gpio = unsafe { &*pac::PADS_BANK0::PTR };
gpio.gpio(usize::from(num)).modify(|_, w| w.ie().set_bit());
// next is: pin.io_ctrl().modify(|_, w| w.funcsel().variant(funcsel));
let gpio = unsafe { &*pac::IO_BANK0::PTR };
let io_ctrl = &gpio.gpio(usize::from(num)).gpio_ctrl();
io_ctrl.modify(|_, w| w.funcsel().variant(pac::io_bank0::gpio::gpio_ctrl::FUNCSEL_A::SIO));
// set_low
unsafe {
let sio = &*pac::SIO::PTR;
let gpio_out_clr = sio.gpio_out_clr();
match bank {
DynBankId::Bank0 => &gpio_out_clr,
DynBankId::Qspi => core::mem::transmute(&sio.gpio_hi_out_clr()),
}.write(|w| { w.gpio_out_clr().bits(mask) });
}
delay.delay_us(3); // How long to hold the spike
// set_high
unsafe {
let sio = &*pac::SIO::PTR;
let gpio_out_set = sio.gpio_out_set();
match bank {
DynBankId::Bank0 => &gpio_out_set,
DynBankId::Qspi => core::mem::transmute(&sio.gpio_hi_out_set()),
}.write(|w| { w.gpio_out_set().bits(mask) });
}
// become uart
// next is: pin.pad_ctrl().modify(|_, w| w.ie().set_bit());
let gpio = unsafe { &*pac::PADS_BANK0::PTR };
gpio.gpio(usize::from(num)).modify(|_, w| w.ie().set_bit());
// next is: pin.io_ctrl().modify(|_, w| w.funcsel().variant(funcsel));
let gpio = unsafe { &*pac::IO_BANK0::PTR };
let io_ctrl = &gpio.gpio(usize::from(num)).gpio_ctrl();
io_ctrl.modify(|_, w| w.funcsel().variant(pac::io_bank0::gpio::gpio_ctrl::FUNCSEL_A::UART));
Ключом к одной из моих проблем было то, что существуют регистры, которые необходимо установить для каждой из следующих операций: стать push/pull, установить низкий уровень, установить высокий уровень, стать uart.
Поскольку ваше оборудование уже содержит ошибки и вызывает UB, вы можете использовать его и просто создать несколько изменяемых ссылок (или несколько экземпляров контактов с
ptr::read
).