timer-interrupt-rtfm.rs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. //! Uses the timer interrupt to blink a led with different frequencies.
  2. //!
  3. //! This assumes that a LED is connected to pc13 as is the case on the blue pill board.
  4. //!
  5. //! Note: Without additional hardware, PC13 should not be used to drive an LED, see page 5.1.2 of
  6. //! the reference manual for an explanation. This is not an issue on the blue pill.
  7. #![no_std]
  8. #![no_main]
  9. // you can put a breakpoint on `rust_begin_unwind` to catch panics
  10. use panic_halt as _;
  11. use rtfm::app;
  12. use stm32f1xx_hal::{
  13. prelude::*,
  14. pac,
  15. timer::{ Timer, CountDownTimer, Event },
  16. gpio::{ gpioc::PC13, State, Output, PushPull },
  17. };
  18. use embedded_hal::digital::v2::OutputPin;
  19. #[app(device = stm32f1xx_hal::pac, peripherals = true)]
  20. const APP: () = {
  21. struct Resources {
  22. led: PC13<Output<PushPull>>,
  23. timer_handler: CountDownTimer<pac::TIM1>,
  24. #[init(false)]
  25. led_state: bool,
  26. }
  27. #[init]
  28. fn init(cx: init::Context) -> init::LateResources {
  29. // Take ownership over the raw flash and rcc devices and convert them into the corresponding
  30. // HAL structs
  31. let mut flash = cx.device.FLASH.constrain();
  32. let mut rcc = cx.device.RCC.constrain();
  33. // Freeze the configuration of all the clocks in the system and store the frozen frequencies
  34. // in `clocks`
  35. let clocks = rcc.cfgr.freeze(&mut flash.acr);
  36. // Acquire the GPIOC peripheral
  37. let mut gpioc = cx.device.GPIOC.split(&mut rcc.apb2);
  38. // Configure gpio C pin 13 as a push-pull output. The `crh` register is passed to the
  39. // function in order to configure the port. For pins 0-7, crl should be passed instead
  40. let led = gpioc.pc13.into_push_pull_output_with_state(&mut gpioc.crh, State::High);
  41. // Configure the syst timer to trigger an update every second and enables interrupt
  42. let mut timer = Timer::tim1(cx.device.TIM1, &clocks, &mut rcc.apb2)
  43. .start_count_down(1.hz());
  44. timer.listen(Event::Update);
  45. // Init the static resources to use them later through RTFM
  46. init::LateResources {
  47. led,
  48. timer_handler: timer,
  49. }
  50. }
  51. // Optional.
  52. //
  53. // https://rtfm.rs/0.5/book/en/by-example/app.html#idle
  54. // > When no idle function is declared, the runtime sets the SLEEPONEXIT bit and then
  55. // > sends the microcontroller to sleep after running init.
  56. #[idle]
  57. fn idle(_cx: idle::Context) -> ! {
  58. loop {
  59. cortex_m::asm::wfi();
  60. }
  61. }
  62. #[task(binds = TIM1_UP, priority = 1, resources = [led, timer_handler, led_state])]
  63. fn tick(cx: tick::Context) {
  64. // Depending on the application, you could want to delegate some of the work done here to
  65. // the idle task if you want to minimize the latency of interrupts with same priority (if
  66. // you have any). That could be done with some kind of machine state, etc.
  67. // Count used to change the timer update frequency
  68. static mut COUNT: u8 = 0;
  69. if *cx.resources.led_state {
  70. // Uses resources managed by rtfm to turn led off (on bluepill)
  71. cx.resources.led.set_high().unwrap();
  72. *cx.resources.led_state = false;
  73. } else {
  74. cx.resources.led.set_low().unwrap();
  75. *cx.resources.led_state = true;
  76. }
  77. *COUNT += 1;
  78. if *COUNT == 4 {
  79. // Changes timer update frequency
  80. cx.resources.timer_handler.start(2.hz());
  81. } else if *COUNT == 12 {
  82. cx.resources.timer_handler.start(1.hz());
  83. *COUNT = 0;
  84. }
  85. // Clears the update flag
  86. cx.resources.timer_handler.clear_update_interrupt_flag();
  87. }
  88. };