Жуткое действие на расстоянии
OUT — не единственный регистр, который управляет выводами порта P0. Регистр OUTSET тоже позволяет менять значения выводов, как и OUTCLR. Однако OUTSET и OUTCLR не позволяют узнать текущее состояние выходов порта P0.
Регистр OUTSET описан в спецификации продукта:
Подраздел 6.8.2.2. «OUTSET» — стр. 145
Посмотрим на программу ниже. Ключевая её часть — fn print_out. Эта функция выводит текущее значение OUT в консоль RTT (examples/spooky.rs):
#![no_main]
#![no_std]
use core::ptr;
#[allow(unused_imports)]
use registers::{entry, rprintln};
// Выводим текущее содержимое P0.OUT
fn print_out() {
const P0_OUT: u32 = 0x5000_0504;
let out = unsafe { ptr::read_volatile(P0_OUT as *const u32) };
rprintln!("P0.OUT = {:#08x}", out);
}
#[entry]
fn main() -> ! {
registers::init();
unsafe {
// Куча «магических» адресов!
const P0_OUTSET: u32 = 0x5000_0508;
const P0_OUTCLR: u32 = 0x5000_050C;
// Выводим начальное содержимое OUT
print_out();
// Включаем верхний ряд светодиодов
ptr::write_volatile(P0_OUTSET as *mut u32, 1 << 21);
print_out();
// Включаем нижний ряд светодиодов
ptr::write_volatile(P0_OUTSET as *mut u32, 1 << 19);
print_out();
// Выключаем верхний ряд светодиодов
ptr::write_volatile(P0_OUTCLR as *mut u32, 1 << 21);
print_out();
// Выключаем нижний ряд светодиодов
ptr::write_volatile(P0_OUTCLR as *mut u32, 1 << 19);
print_out();
}
loop {
core::hint::spin_loop();
}
}
Если запустить эту программу, увидите следующее:
$ cargo embed
# консоль cargo-embed
(..)
15:13:24.055: P0.OUT = 0x000000
15:13:24.055: P0.OUT = 0x200000
15:13:24.055: P0.OUT = 0x280000
15:13:24.055: P0.OUT = 0x080000
15:13:24.055: P0.OUT = 0x000000
Побочные эффекты! Хотя мы многократно читаем один и тот же адрес и не меняем его сами, значение меняется каждый раз, когда производится запись в OUTSET или OUTCLR.