برنامه نویسی

ظرف خود تراز در زنگ زدگی: چشمک زدن

Summarize this content to 400 words in Persian Lang
در آخرین پست از این مجموعه به نحوه نوشتن یک برنامه Rust مینیمال برای Raspberry Pico پرداختیم. در این پست، آن کد را برای پیاده سازی نشانگر LED گسترش خواهیم داد.

فهرست مطالب

فلوچارت

ما به یک تایمر با وقفه نیاز داریم که هر 1 ثانیه خاموش شود. هنگامی که زنگ هشدار خاموش می شود، LED بین حالت های روشن و خاموش تغییر می کند.

الزامات

تخته تمشک پیکو
کابل USB نوع 2.0

NB Pico دارای یک LED داخلی متصل به GP25 است که ما از آن استفاده خواهیم کرد.

پیاده سازی

وارد کنید rp2040-pac جعبه این جعبه به ما اجازه می دهد تا به تجهیزات جانبی مختلف Pico دسترسی داشته باشیم. از SVD RP2040 تولید شده است. آن را به وابستگی های موجود در قسمت اضافه کنید Cargo.toml فایل

rp2040-pac = { version = “0.6.0”, features = [“rt”] }

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با افزودن خط زیر در قسمت واردات ما، آن را در برنامه خود وارد کنید main.rs.

use rp2040_pac as pac;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در ما main کد، ابتدا باید تمام لوازم جانبی دستگاه را با در اختیار گرفتن مالکیت آنها در معرض دید قرار دهیم.

let dp = unsafe { pac::Peripherals::steal() };// Take the device peripherals
let _cp = pac::CorePeripherals::take().unwrap();// Take the core peripherals

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پیکربندی ساعت

بعد باید ساعت ها را پیکربندی کنیم.

RP2040 دارای یک نوسان ساز حلقه داخلی به نام ROSC است که قبل از امکان پیکربندی سایر منابع ساعت، کلاک را در هنگام راه اندازی اولیه فراهم می کند. همچنین این گزینه را دارد که از یک نوسانگر خارجی، XOSC، کلاک شود. این می تواند برای راه اندازی PLL ها، حلقه های قفل فازی، برای فعال کردن فرکانس های بالاتر (تا 133 مگاهرتز برای برد Pico) استفاده شود. برد معمولی Pico با XOSC 12 مگاهرتز ارسال می شود.

اکنون XOSC را پیکربندی می کنیم. ما ابتدا با در اختیار گرفتن مالکیت آن، ثبت آن را در معرض دید قرار می دهیم.

let xosc = dp.XOSC;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس با تنظیم محدوده فرکانس XOSC و تنظیم زمان تاخیر راه اندازی، نوسان ساز کریستالی را پیکربندی می کنیم.

دستورالعمل‌های تنظیم تاخیر راه‌اندازی را می‌توانید در راهنمای مرجع پیدا کنید. مقدار تاخیر راه اندازی با استفاده از فرمول زیر محاسبه می شود.

ستیالفrتی توص دهلالفy=(fسیryستیالفل×تیاستیالفبله)÷256

start\ up\ delay = (fCrystal \times tStable) \div 256
ستیالفrتی توص دهلالفy=(fسیسیاهگوشتیالفل×tStabله)÷256

با XOSC 12 مگاهرتز و زمان انتظار 1 میلی ثانیه ای، زمان تاخیر راه اندازی محاسبه شده ما خواهد بود:

(12×106)×(1×10-3)256=47{(12 \times 10^6) \times (1 \times 10^-3) \ بیش از 256} = 47 256(12×106)×(1×10-3)را=47

xosc.ctrl().modify(|_, w| w.freq_range()._1_15mhz());// Set freq. range
xosc.startup().write(|w| unsafe { w.delay().bits(47) });// Set the startup delay

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

XOSC را فعال کنید و منتظر بمانید تا تثبیت شود.

xosc.ctrl().modify(|_, w| w.enable().enable());// Enable the xosc
while xosc().status.read().stable().bit_is_clear() {}// Await stabilization

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با اجرای XOSC، اکنون باید ساعت ها و PLL ها را پیکربندی کنیم.

ابتدا دسته هایی برای ساعت ها و PLLS ایجاد کنید. ما همچنین به یکی برای تنظیم مجدد نیاز داریم.

let clocks = dp.CLOCKS;
let pll_sys = dp.PLL_SYS;
let resets = dp.RESETS;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قبل از استفاده از یک وسیله جانبی، ابتدا باید آن را دسر زدایی کرد.

resets.reset().modify(|_, w| w
.pll_sys().clear_bit()// deassert pll sys
);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برنامه نویسی PLL

PLL از ساعت مرجع اجرا می شود، در این مورد یک نوسانگر کریستالی 12 مگاهرتز، و آن را برای رسیدن به فرکانس های بالاتر ضرب می کند.

تعدادی پارامتر وجود دارد که برای تنظیم PLL به آن نیاز داریم: تقسیم بازخورد، تقسیم کننده مرجع و دو پس تقسیم کننده. دستورالعمل نحوه دریافت این مقادیر در دفترچه راهنما آمده است. فرمول به صورت زیر است:

Oتوتیصتوتی frهqتوهnجy=سیryستیالفل افrهqتوهnجyآرهfهrهnجه Dمنvمندهr×افههدبالفجک Dمنvمندهrپoستیدمنvمندهr 1×پoستیدمنvمندهr 2

خروجی\ فرکانس = {Crystal\ Frequency \over Reference\ Divider} \times {Feedback\ Divider \over Postdivider\ 1 \times Postdivider\ 2}
Oتوtpتوتی fدوبارهqتوهncy=آرهfپیش از اینnce Dمنvمنداستسیسیاهگوشتیالفل افدوبارهqتوهncyرا×پسیستم عاملتیدمنvمنداست 1×پسیستم عاملتیدمنvمنداست 2افبلهدbaجک Dمنvمنداسترا

برای فرکانس خروجی 125 مگاهرتز، مقادیر زیر را انتخاب می کنیم:

تقسیم کننده بازخورد = 125
تقسیم کننده مرجع = 1
postdivider1 = 6
postdivider1 = 1

PLL را به صورت زیر برنامه ریزی کنید.

pll_sys.pwr().reset();// Turn off PLL in case it is already running
pll_sys.fbdiv_int().reset();
pll_sys.cs().modify(|_, w| unsafe { w.refdiv().bits(1) });// Set refdiv as 1
pll_sys.fbdiv_int().write(|w| unsafe { w.bits(125) });// Set fbdiv_int as 125
pll_sys.pwr().modify(|_, w| w
.pd().clear_bit()// Turn on PLL
.vcopd().clear_bit()// Turn on VCO
);
while pll_sys.cs().read().lock().bit_is_clear() {}// Await locking of pll
pll_sys.prim().modify(|_, w| unsafe {
w.postdiv1().bits(6)// Set up postdivider 1
.postdiv2().bits(2)// Set up postdivider 2
});
pll_sys.pwr().modify(|_, w| w.postdivpd().clear_bit());// Turn on postdividers

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اتمام تنظیم ساعت، باید ساعت مرجع خود را به عنوان XOSC تقسیم بر 1 انتخاب کنیم، یعنی 12 مگاهرتز، PLL را به عنوان منبع ساعت خود تنظیم کنیم و سپس ساعت سیستم خود را به عنوان ساعت محیطی تنظیم کنیم.

// Select ref clock source as XOSC divided by 1
clocks.clk_ref_ctrl().modify(|_, w| w.src().xosc_clksrc());
clocks.clk_ref_div().modify(|_, w| unsafe { w.int().bits(1) });

// Set pll sys as clk sys
clocks.clk_sys_ctrl().modify(|_, w| w.src().clksrc_clk_sys_aux());
clocks.clk_sys_div().modify(|_, w| unsafe { w.int().bits(1) });

// Set clk sys as peripheral clock
// Used as reference clock for Peripherals
clocks.clk_peri_ctrl().modify(|_, w| w
.auxsrc().clk_sys()
.enable().set_bit()
);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد باید ساعت را پیکربندی و فعال کنیم. قبل از آن Watchdog باید پیکربندی شود. تایمر بستگی به clk_tick مرجع از سازمان دیده بان

// Watchdog: to provide the clk_tick required by the timer
let watchdog = dp.WATCHDOG;
watchdog.tick().modify(|_, w| unsafe{ w
.cycles().bits(12)// For an effective frequency of 1MHz
.enable().set_bit()
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توجه داشته باشید که Watchdog اجرا می شود clk_ref ساعتی که به نوبه خود از نوسانگر کریستالی که در مورد ما با فرکانس 12 مگاهرتز کار می کند، کشیده شده است.

تایمر

اکنون می توانیم به سراغ تایمر برویم.

// Timer set up
let timer = dp.TIMER;
resets.reset().modify(|_, w| w.timer().clear_bit());// Deassert timer

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

باید وقفه alarm0 تایمر را فعال کنیم و آن را از ماسک برداریم. NVIC ابتدا باید با واردات وارد حوزه شود cortex_m::peripheral::NVIC.

use cortex_m::peripheral::NVIC;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این cortex-m جعبه باید به پروژه اضافه شود Cargo.toml فایل

cortex-m = “0.7.7”

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

رفع نقاب وقفه…

timer.inte().modify(|_, w| w.alarm_0().set_bit());// set alarm0 interrupt
unsafe {
NVIC::unmask(interrupt::TIMER_IRQ_0);// Unmask interrupt
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای مسلح کردن زنگ، باید زمانی را تنظیم کنیم که پس از آن زنگ و در نتیجه وقفه باید خاموش شود. ما این کار را با اضافه کردن زمان مورد نظر به خواندن فعلی در TIMERAWL ثبت تایمر این به طور خودکار زنگ را تنظیم می کند.

timer.alarm0().modify(|_, w| unsafe{ w.bits(timer.timerawl().read().bits() + 1_000_000) });// set 1 sec after now alarm

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

alarm0 اکنون مسلح است و پس از هر 1 ثانیه خاموش می شود. درست پس از خاموش شدن، در روال سرویس وقفه بیشتر آن را مدیریت خواهیم کرد.

پین LED

پس از پیکربندی ساعت، بلوک GPIO، که LED داخلی به آن متصل است، باید فعال شود. برای کنترل یک پین GPIO، به سه وسیله جانبی نیاز داریم: SIO، ورودی/خروجی تک چرخه، IO_BANK، و PADS_BANK. دومی به ترتیب برای تنظیم عملکرد پین و خواص الکتریکی پین استفاده می شود.

let sio = dp.SIO;
let io_bank0 = dp.IO_BANK0;
let pads_bank0 = dp.PADS_BANK0;
// reset the peripherals
resets.reset().modify(|_, w| w
.io_bank0().clear_bit()
.pads_bank0().clear_bit());

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما پین را به عنوان یک پین فشاری با عملکرد به عنوان یک پایه IO عمومی تنظیم می کنیم (و از این رو توسط دستگاه جانبی SIO کنترل می شود).

pads_bank0.gpio(25).modify(|_, w| w
.pue().set_bit()// pullup enable
.pde().set_bit()// pulldown enable
.od().clear_bit()// output enable
.ie().set_bit()// input enable
);

io_bank0.gpio(25).gpio_ctrl().modify(|_, w| w.funcsel().sio());// set function as sio

sio.gpio_oe().modify(|r, w| unsafe { w.bits(r.gpio_oe().bits() | 1 << 25)});// Output enable for pin 25

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قطع کنید

این SIO و TIMER لوازم جانبی باید در گستره جهانی در نظر گرفته شوند زیرا ما از آنها در روال وقفه استفاده خواهیم کرد، که جدای از main برنامه برای انجام این کار، ما باید متغیرهای جهانی را معرفی کنیم که آنها را نگه می دارد. درست در زیر واردات خود، خطوط زیر را اضافه می کنیم.

// Global variable for peripherals
static TIMER: Mutex<RefCell<Option<TIMER>>> = Mutex::new(RefCell::new(None));
static SIO: Mutex<RefCell<Option<SIO>>> = Mutex::new(RefCell::new(None));

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

RefCell، Mutex، TIMER و SIO باید به برنامه ما وارد شود.

use cortex_m::interrupt::Mutex;
use core::cell::RefCell;
use rp2040_pac::{SIO, TIMER};

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در ما main برنامه، پس از ایجاد دسته برای تجهیزات جانبی، آنها را به صورت زیر به محدوده جهانی منتقل می کنیم.

// Move peripherals into global scope
cortex_m::interrupt::free(|cs| {
SIO.borrow(cs).replace(Some(sio));
TIMER.borrow(cs).replace(Some(timer));
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

از آنجایی که این برنامه تا حد زیادی با وقفه هدایت می شود، حلقه سوپر هیچ وظیفه ای نخواهد داشت اما منتظر وقفه است.

loop {
cortex_m::asm::wfi();// wait for interrupt
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در نهایت باید کنترل کننده وقفه را برای تایمر بنویسیم: کدی که هر بار که وقفه فعال می شود اجرا می شود.

ما آن را با #[interrupt] ویژگی، که ابتدا باید آن را وارد کنیم.

use rp2040_pac::interrupt;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هر وقفه یک نام مشخص در آن دارد rp2040-pac زیر interrupts. TIMER_IRQ_0 در این مورد

زمانی که در روال وقفه هستیم وارد بخش مهمی می‌شویم تا از مسابقه داده جلوگیری کنیم.

#[interrupt] fn TIMER_IRQ_0() {
// Enter critical section
cortex_m::interrupt::free(|cs| {
// Borrow the peripherals under critical section.
let mut timer = TIMER.borrow(cs).borrow_mut();
let mut sio = SIO.borrow(cs).borrow_mut();

timer.as_mut().unwrap().intr().modify(|_, w| w.alarm_0().clear_bit_by_one());// first clear interrupt
sio.as_mut().unwrap().gpio_out().modify(|r, w| unsafe { w.bits(r.gpio_out().bits() ^ 1 << 25)});// toggle bit 25
// set period for next alarm
let (timer_alarm0, _overflow) = (timer.as_mut().unwrap().timerawl().read().bits())
.overflowing_add(1_000_000);

timer.as_mut().unwrap().alarm0().write(|w| unsafe { w
.bits(timer_alarm0) });// set sec’s after now alarm
});
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتایج

کد برنامه جدید ما پس از اضافات فوق را می توانید در این صفحه github پیدا کنید.

تمام تغییرات حداقل برنامه از قسمت آخر را می توان در این کامیت github مشاهده کرد.

اکنون می توانیم کد برنامه خود را به سادگی با اجرای کد در Pico فلش کنیم.

cargo run

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

LED داخلی باید هر یک ثانیه چشمک بزند.

در مرحله بعد پیکو را پیکربندی می کنیم UART تا بتوانیم دستورات را از راه دور از طریق بلوتوث دریافت کنیم.

در آخرین پست از این مجموعه به نحوه نوشتن یک برنامه Rust مینیمال برای Raspberry Pico پرداختیم. در این پست، آن کد را برای پیاده سازی نشانگر LED گسترش خواهیم داد.

فهرست مطالب

فلوچارت

نشانگر-led-flowchart

ما به یک تایمر با وقفه نیاز داریم که هر 1 ثانیه خاموش شود. هنگامی که زنگ هشدار خاموش می شود، LED بین حالت های روشن و خاموش تغییر می کند.

الزامات

  • تخته تمشک پیکو
  • کابل USB نوع 2.0

NB Pico دارای یک LED داخلی متصل به GP25 است که ما از آن استفاده خواهیم کرد.

پیاده سازی

وارد کنید rp2040-pac جعبه این جعبه به ما اجازه می دهد تا به تجهیزات جانبی مختلف Pico دسترسی داشته باشیم. از SVD RP2040 تولید شده است. آن را به وابستگی های موجود در قسمت اضافه کنید Cargo.toml فایل

rp2040-pac = { version = "0.6.0", features = ["rt"] }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با افزودن خط زیر در قسمت واردات ما، آن را در برنامه خود وارد کنید main.rs.

use rp2040_pac as pac;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در ما main کد، ابتدا باید تمام لوازم جانبی دستگاه را با در اختیار گرفتن مالکیت آنها در معرض دید قرار دهیم.

let dp = unsafe { pac::Peripherals::steal() };// Take the device peripherals
let _cp = pac::CorePeripherals::take().unwrap();// Take the core peripherals
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پیکربندی ساعت

بعد باید ساعت ها را پیکربندی کنیم.

RP2040 دارای یک نوسان ساز حلقه داخلی به نام ROSC است که قبل از امکان پیکربندی سایر منابع ساعت، کلاک را در هنگام راه اندازی اولیه فراهم می کند. همچنین این گزینه را دارد که از یک نوسانگر خارجی، XOSC، کلاک شود. این می تواند برای راه اندازی PLL ها، حلقه های قفل فازی، برای فعال کردن فرکانس های بالاتر (تا 133 مگاهرتز برای برد Pico) استفاده شود. برد معمولی Pico با XOSC 12 مگاهرتز ارسال می شود.

ساعت-نمای کلی

اکنون XOSC را پیکربندی می کنیم. ما ابتدا با در اختیار گرفتن مالکیت آن، ثبت آن را در معرض دید قرار می دهیم.

let xosc = dp.XOSC;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس با تنظیم محدوده فرکانس XOSC و تنظیم زمان تاخیر راه اندازی، نوسان ساز کریستالی را پیکربندی می کنیم.

دستورالعمل‌های تنظیم تاخیر راه‌اندازی را می‌توانید در راهنمای مرجع پیدا کنید. مقدار تاخیر راه اندازی با استفاده از فرمول زیر محاسبه می شود.

ستیالفrتی توص دهلالفy=(fسیryستیالفل×تیاستیالفبله)÷256

start\ up\ delay = (fCrystal \times tStable) \div 256

با XOSC 12 مگاهرتز و زمان انتظار 1 میلی ثانیه ای، زمان تاخیر راه اندازی محاسبه شده ما خواهد بود:

(12×106)×(1×103)256=47{(12 \times 10^6) \times (1 \times 10^-3) \ بیش از 256} = 47

xosc.ctrl().modify(|_, w| w.freq_range()._1_15mhz());// Set freq. range
xosc.startup().write(|w| unsafe { w.delay().bits(47) });// Set the startup delay
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

XOSC را فعال کنید و منتظر بمانید تا تثبیت شود.

xosc.ctrl().modify(|_, w| w.enable().enable());// Enable the xosc
while xosc().status.read().stable().bit_is_clear() {}// Await stabilization
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با اجرای XOSC، اکنون باید ساعت ها و PLL ها را پیکربندی کنیم.

ابتدا دسته هایی برای ساعت ها و PLLS ایجاد کنید. ما همچنین به یکی برای تنظیم مجدد نیاز داریم.

let clocks = dp.CLOCKS;
let pll_sys = dp.PLL_SYS;
let resets = dp.RESETS;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قبل از استفاده از یک وسیله جانبی، ابتدا باید آن را دسر زدایی کرد.

resets.reset().modify(|_, w| w
              .pll_sys().clear_bit()// deassert pll sys
              );
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برنامه نویسی PLL

PLL از ساعت مرجع اجرا می شود، در این مورد یک نوسانگر کریستالی 12 مگاهرتز، و آن را برای رسیدن به فرکانس های بالاتر ضرب می کند.

pll

تعدادی پارامتر وجود دارد که برای تنظیم PLL به آن نیاز داریم: تقسیم بازخورد، تقسیم کننده مرجع و دو پس تقسیم کننده. دستورالعمل نحوه دریافت این مقادیر در دفترچه راهنما آمده است. فرمول به صورت زیر است:

Oتوتیصتوتی frهqتوهnجy=سیryستیالفل افrهqتوهnجyآرهfهrهnجه Dمنvمندهr×افههدبالفجک Dمنvمندهrپoستیدمنvمندهr 1×پoستیدمنvمندهr 2

خروجی\ فرکانس = {Crystal\ Frequency \over Reference\ Divider} \times {Feedback\ Divider \over Postdivider\ 1 \times Postdivider\ 2}

برای فرکانس خروجی 125 مگاهرتز، مقادیر زیر را انتخاب می کنیم:

  • تقسیم کننده بازخورد = 125
  • تقسیم کننده مرجع = 1
  • postdivider1 = 6
  • postdivider1 = 1

PLL را به صورت زیر برنامه ریزی کنید.

pll_sys.pwr().reset();// Turn off PLL in case it is already running
pll_sys.fbdiv_int().reset();
pll_sys.cs().modify(|_, w| unsafe { w.refdiv().bits(1) });// Set refdiv as 1
pll_sys.fbdiv_int().write(|w| unsafe { w.bits(125) });// Set fbdiv_int as 125
pll_sys.pwr().modify(|_, w| w
                   .pd().clear_bit()// Turn on PLL
                   .vcopd().clear_bit()// Turn on VCO
                   );
while pll_sys.cs().read().lock().bit_is_clear() {}// Await locking of pll
pll_sys.prim().modify(|_, w| unsafe {
    w.postdiv1().bits(6)// Set up postdivider 1
        .postdiv2().bits(2)// Set up postdivider 2
});
pll_sys.pwr().modify(|_, w| w.postdivpd().clear_bit());// Turn on postdividers
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اتمام تنظیم ساعت، باید ساعت مرجع خود را به عنوان XOSC تقسیم بر 1 انتخاب کنیم، یعنی 12 مگاهرتز، PLL را به عنوان منبع ساعت خود تنظیم کنیم و سپس ساعت سیستم خود را به عنوان ساعت محیطی تنظیم کنیم.

// Select ref clock source as XOSC divided by 1
clocks.clk_ref_ctrl().modify(|_, w| w.src().xosc_clksrc());
clocks.clk_ref_div().modify(|_, w| unsafe { w.int().bits(1) });

// Set pll sys as clk sys 
clocks.clk_sys_ctrl().modify(|_, w| w.src().clksrc_clk_sys_aux());
clocks.clk_sys_div().modify(|_, w| unsafe { w.int().bits(1) });

// Set clk sys as peripheral clock
// Used as reference clock for Peripherals
clocks.clk_peri_ctrl().modify(|_, w| w
                            .auxsrc().clk_sys()
                            .enable().set_bit()
                            );
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد باید ساعت را پیکربندی و فعال کنیم. قبل از آن Watchdog باید پیکربندی شود. تایمر بستگی به clk_tick مرجع از سازمان دیده بان

// Watchdog: to provide the clk_tick required by the timer
let watchdog = dp.WATCHDOG;
watchdog.tick().modify(|_, w| unsafe{ w
    .cycles().bits(12)// For an effective frequency of 1MHz
    .enable().set_bit()
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توجه داشته باشید که Watchdog اجرا می شود clk_ref ساعتی که به نوبه خود از نوسانگر کریستالی که در مورد ما با فرکانس 12 مگاهرتز کار می کند، کشیده شده است.

تایمر

اکنون می توانیم به سراغ تایمر برویم.

// Timer set up
let timer = dp.TIMER;
resets.reset().modify(|_, w| w.timer().clear_bit());// Deassert timer
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

باید وقفه alarm0 تایمر را فعال کنیم و آن را از ماسک برداریم. NVIC ابتدا باید با واردات وارد حوزه شود cortex_m::peripheral::NVIC.

 use cortex_m::peripheral::NVIC;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این cortex-m جعبه باید به پروژه اضافه شود Cargo.toml فایل

cortex-m = "0.7.7"
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

رفع نقاب وقفه…

timer.inte().modify(|_, w| w.alarm_0().set_bit());// set alarm0 interrupt
unsafe {
    NVIC::unmask(interrupt::TIMER_IRQ_0);// Unmask interrupt
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای مسلح کردن زنگ، باید زمانی را تنظیم کنیم که پس از آن زنگ و در نتیجه وقفه باید خاموش شود. ما این کار را با اضافه کردن زمان مورد نظر به خواندن فعلی در TIMERAWL ثبت تایمر این به طور خودکار زنگ را تنظیم می کند.

timer.alarm0().modify(|_, w| unsafe{ w.bits(timer.timerawl().read().bits() + 1_000_000) });// set 1 sec after now alarm
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

alarm0 اکنون مسلح است و پس از هر 1 ثانیه خاموش می شود. درست پس از خاموش شدن، در روال سرویس وقفه بیشتر آن را مدیریت خواهیم کرد.

پین LED

پس از پیکربندی ساعت، بلوک GPIO، که LED داخلی به آن متصل است، باید فعال شود. برای کنترل یک پین GPIO، به سه وسیله جانبی نیاز داریم: SIO، ورودی/خروجی تک چرخه، IO_BANK، و PADS_BANK. دومی به ترتیب برای تنظیم عملکرد پین و خواص الکتریکی پین استفاده می شود.

let sio = dp.SIO;
let io_bank0 = dp.IO_BANK0;
let pads_bank0 = dp.PADS_BANK0;
// reset the peripherals
resets.reset().modify(|_, w| w
                    .io_bank0().clear_bit()
                    .pads_bank0().clear_bit());
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما پین را به عنوان یک پین فشاری با عملکرد به عنوان یک پایه IO عمومی تنظیم می کنیم (و از این رو توسط دستگاه جانبی SIO کنترل می شود).

pads_bank0.gpio(25).modify(|_, w| w
                           .pue().set_bit()// pullup enable
                           .pde().set_bit()// pulldown enable
                           .od().clear_bit()// output enable
                           .ie().set_bit()// input enable
                           );

io_bank0.gpio(25).gpio_ctrl().modify(|_, w| w.funcsel().sio());// set function as sio

sio.gpio_oe().modify(|r, w| unsafe { w.bits(r.gpio_oe().bits() | 1 << 25)});// Output enable for pin 25
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قطع کنید

این SIO و TIMER لوازم جانبی باید در گستره جهانی در نظر گرفته شوند زیرا ما از آنها در روال وقفه استفاده خواهیم کرد، که جدای از main برنامه برای انجام این کار، ما باید متغیرهای جهانی را معرفی کنیم که آنها را نگه می دارد. درست در زیر واردات خود، خطوط زیر را اضافه می کنیم.

// Global variable for peripherals
static TIMER: Mutex<RefCell<Option<TIMER>>> = Mutex::new(RefCell::new(None));
static SIO: Mutex<RefCell<Option<SIO>>> = Mutex::new(RefCell::new(None));
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

RefCell، Mutex، TIMER و SIO باید به برنامه ما وارد شود.

use cortex_m::interrupt::Mutex;
use core::cell::RefCell;
use rp2040_pac::{SIO, TIMER};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در ما main برنامه، پس از ایجاد دسته برای تجهیزات جانبی، آنها را به صورت زیر به محدوده جهانی منتقل می کنیم.

// Move peripherals into global scope
cortex_m::interrupt::free(|cs| {
    SIO.borrow(cs).replace(Some(sio));
    TIMER.borrow(cs).replace(Some(timer));
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

از آنجایی که این برنامه تا حد زیادی با وقفه هدایت می شود، حلقه سوپر هیچ وظیفه ای نخواهد داشت اما منتظر وقفه است.

loop {
    cortex_m::asm::wfi();// wait for interrupt
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در نهایت باید کنترل کننده وقفه را برای تایمر بنویسیم: کدی که هر بار که وقفه فعال می شود اجرا می شود.

ما آن را با #[interrupt] ویژگی، که ابتدا باید آن را وارد کنیم.

use rp2040_pac::interrupt;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هر وقفه یک نام مشخص در آن دارد rp2040-pac زیر interrupts. TIMER_IRQ_0 در این مورد

زمانی که در روال وقفه هستیم وارد بخش مهمی می‌شویم تا از مسابقه داده جلوگیری کنیم.

#[interrupt]
fn TIMER_IRQ_0() {
    // Enter critical section
    cortex_m::interrupt::free(|cs| {
    // Borrow the peripherals under critical section. 
        let mut timer = TIMER.borrow(cs).borrow_mut();
        let mut sio = SIO.borrow(cs).borrow_mut();

        timer.as_mut().unwrap().intr().modify(|_, w| w.alarm_0().clear_bit_by_one());// first clear interrupt
        sio.as_mut().unwrap().gpio_out().modify(|r, w| unsafe { w.bits(r.gpio_out().bits() ^ 1 << 25)});// toggle bit 25    
        // set period for next alarm
        let (timer_alarm0, _overflow) = (timer.as_mut().unwrap().timerawl().read().bits())
            .overflowing_add(1_000_000); 

        timer.as_mut().unwrap().alarm0().write(|w|  unsafe { w
            .bits(timer_alarm0) });// set sec's after now alarm
    });
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتایج

کد برنامه جدید ما پس از اضافات فوق را می توانید در این صفحه github پیدا کنید.

تمام تغییرات حداقل برنامه از قسمت آخر را می توان در این کامیت github مشاهده کرد.

اکنون می توانیم کد برنامه خود را به سادگی با اجرای کد در Pico فلش کنیم.

cargo run
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

LED داخلی باید هر یک ثانیه چشمک بزند.

چشمک زدن

در مرحله بعد پیکو را پیکربندی می کنیم UART تا بتوانیم دستورات را از راه دور از طریق بلوتوث دریافت کنیم.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا