can-rtic.rs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. //! Interrupt driven CAN transmitter with RTIC.
  2. //!
  3. //! CAN frames are allocated from a static memory pool and stored in a priority
  4. //! queue (min heap) for transmisison. To start transmission the CAN TX
  5. //! interrupt has to be triggered manually once. With each successful
  6. //! transmission the interrupt is reentered and more data is fetched from the
  7. //! queue.
  8. //! Received frames are simply echoed back. In contrast to the naive `can-echo`
  9. //! example all messages are also correctly prioritized by the transmit queue.
  10. #![no_main]
  11. #![no_std]
  12. use core::cmp::Ordering;
  13. use bxcan::{filter::Mask32, ExtendedId, Frame, Interrupts, Rx, StandardId, Tx};
  14. use heapless::{
  15. binary_heap::{BinaryHeap, Max},
  16. consts::*,
  17. };
  18. use nb::block;
  19. use panic_halt as _;
  20. use rtic::app;
  21. use stm32f1xx_hal::{
  22. can::Can,
  23. pac::{Interrupt, CAN1},
  24. prelude::*,
  25. };
  26. #[derive(Debug)]
  27. pub struct PriorityFrame(Frame);
  28. /// Ordering is based on the Identifier and frame type (data vs. remote) and can be used to sort
  29. /// frames by priority.
  30. impl Ord for PriorityFrame {
  31. fn cmp(&self, other: &Self) -> Ordering {
  32. self.0.priority().cmp(&other.0.priority())
  33. }
  34. }
  35. impl PartialOrd for PriorityFrame {
  36. fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
  37. Some(self.cmp(other))
  38. }
  39. }
  40. impl PartialEq for PriorityFrame {
  41. fn eq(&self, other: &Self) -> bool {
  42. self.cmp(other) == Ordering::Equal
  43. }
  44. }
  45. impl Eq for PriorityFrame {}
  46. fn enqueue_frame(queue: &mut BinaryHeap<PriorityFrame, U16, Max>, frame: Frame) {
  47. queue.push(PriorityFrame(frame)).unwrap();
  48. rtic::pend(Interrupt::USB_HP_CAN_TX);
  49. }
  50. #[app(device = stm32f1xx_hal::pac, peripherals = true)]
  51. const APP: () = {
  52. struct Resources {
  53. can_tx: Tx<Can<CAN1>>,
  54. can_tx_queue: BinaryHeap<PriorityFrame, U16, Max>,
  55. tx_count: usize,
  56. can_rx: Rx<Can<CAN1>>,
  57. }
  58. #[init]
  59. fn init(cx: init::Context) -> init::LateResources {
  60. let mut flash = cx.device.FLASH.constrain();
  61. let mut rcc = cx.device.RCC.constrain();
  62. let _clocks = rcc
  63. .cfgr
  64. .use_hse(8.mhz())
  65. .sysclk(64.mhz())
  66. .hclk(64.mhz())
  67. .pclk1(16.mhz())
  68. .pclk2(64.mhz())
  69. .freeze(&mut flash.acr);
  70. #[cfg(not(feature = "connectivity"))]
  71. let can = Can::new(cx.device.CAN1, &mut rcc.apb1, cx.device.USB);
  72. #[cfg(feature = "connectivity")]
  73. let can = Can::new(cx.device.CAN1, &mut rcc.apb1);
  74. // Select pins for CAN1.
  75. let mut gpioa = cx.device.GPIOA.split(&mut rcc.apb2);
  76. let can_rx_pin = gpioa.pa11.into_floating_input(&mut gpioa.crh);
  77. let can_tx_pin = gpioa.pa12.into_alternate_push_pull(&mut gpioa.crh);
  78. let mut afio = cx.device.AFIO.constrain(&mut rcc.apb2);
  79. can.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr);
  80. let mut can = bxcan::Can::new(can);
  81. // APB1 (PCLK1): 16MHz, Bit rate: 1000kBit/s, Sample Point 87.5%
  82. // Value was calculated with http://www.bittiming.can-wiki.info/
  83. can.modify_config().set_bit_timing(0x001c_0000);
  84. can.modify_filters().enable_bank(0, Mask32::accept_all());
  85. // Sync to the bus and start normal operation.
  86. can.enable_interrupts(
  87. Interrupts::TRANSMIT_MAILBOX_EMPTY | Interrupts::FIFO0_MESSAGE_PENDING,
  88. );
  89. block!(can.enable()).unwrap();
  90. let (can_tx, can_rx) = can.split();
  91. let can_tx_queue = BinaryHeap::new();
  92. init::LateResources {
  93. can_tx,
  94. can_tx_queue,
  95. tx_count: 0,
  96. can_rx,
  97. }
  98. }
  99. #[idle(resources = [can_tx_queue, tx_count])]
  100. fn idle(mut cx: idle::Context) -> ! {
  101. let mut tx_queue = cx.resources.can_tx_queue;
  102. // Enqueue some messages. Higher ID means lower priority.
  103. tx_queue.lock(|mut tx_queue| {
  104. enqueue_frame(
  105. &mut tx_queue,
  106. Frame::new_data(StandardId::new(9).unwrap(), []),
  107. );
  108. enqueue_frame(
  109. &mut tx_queue,
  110. Frame::new_data(ExtendedId::new(9).unwrap(), []),
  111. );
  112. enqueue_frame(
  113. &mut tx_queue,
  114. Frame::new_data(StandardId::new(8).unwrap(), []),
  115. );
  116. enqueue_frame(
  117. &mut tx_queue,
  118. Frame::new_data(ExtendedId::new(8).unwrap(), []),
  119. );
  120. enqueue_frame(
  121. &mut tx_queue,
  122. Frame::new_data(StandardId::new(0x7FF).unwrap(), []),
  123. );
  124. enqueue_frame(
  125. &mut tx_queue,
  126. Frame::new_data(ExtendedId::new(0x1FFF_FFFF).unwrap(), []),
  127. );
  128. });
  129. // Add some higher priority messages when 3 messages have been sent.
  130. loop {
  131. let tx_count = cx.resources.tx_count.lock(|tx_count| *tx_count);
  132. if tx_count >= 3 {
  133. tx_queue.lock(|mut tx_queue| {
  134. enqueue_frame(
  135. &mut tx_queue,
  136. Frame::new_data(StandardId::new(3).unwrap(), []),
  137. );
  138. enqueue_frame(
  139. &mut tx_queue,
  140. Frame::new_data(StandardId::new(2).unwrap(), []),
  141. );
  142. enqueue_frame(
  143. &mut tx_queue,
  144. Frame::new_data(StandardId::new(1).unwrap(), []),
  145. );
  146. });
  147. break;
  148. }
  149. }
  150. // Expected bus traffic:
  151. //
  152. // 1. ID: 0x00000008 <- proper reordering happens
  153. // 2. ID: 0x00000009
  154. // 3. ID: 0x008
  155. // 4. ID: 0x001 <- higher priority messages injected correctly
  156. // 5. ID: 0x002
  157. // 6. ID: 0x003
  158. // 7. ID: 0x009
  159. // 8. ID: 0x7FF
  160. // 9. ID: 0x1FFFFFFF
  161. //
  162. // The output can look different if there are other nodes on bus the sending messages.
  163. loop {
  164. cortex_m::asm::nop();
  165. }
  166. }
  167. // This ISR is triggered by each finished frame transmission.
  168. #[task(binds = USB_HP_CAN_TX, resources = [can_tx, can_tx_queue, tx_count])]
  169. fn can_tx(cx: can_tx::Context) {
  170. let tx = cx.resources.can_tx;
  171. let tx_queue = cx.resources.can_tx_queue;
  172. tx.clear_interrupt_flags();
  173. // There is now a free mailbox. Try to transmit pending frames until either
  174. // the queue is empty or transmission would block the execution of this ISR.
  175. while let Some(frame) = tx_queue.peek() {
  176. match tx.transmit(&frame.0) {
  177. Ok(None) => {
  178. // Frame was successfully placed into a transmit buffer.
  179. tx_queue.pop();
  180. *cx.resources.tx_count += 1;
  181. }
  182. Ok(Some(pending_frame)) => {
  183. // A lower priority frame was replaced with our high priority frame.
  184. // Put the low priority frame back in the transmit queue.
  185. tx_queue.pop();
  186. enqueue_frame(tx_queue, pending_frame);
  187. }
  188. Err(nb::Error::WouldBlock) => break,
  189. Err(_) => unreachable!(),
  190. }
  191. }
  192. }
  193. #[task(binds = USB_LP_CAN_RX0, resources = [can_rx, can_tx_queue])]
  194. fn can_rx0(cx: can_rx0::Context) {
  195. // Echo back received packages with correct priority ordering.
  196. loop {
  197. match cx.resources.can_rx.receive() {
  198. Ok(frame) => {
  199. enqueue_frame(cx.resources.can_tx_queue, frame);
  200. }
  201. Err(nb::Error::WouldBlock) => break,
  202. Err(nb::Error::Other(_)) => {} // Ignore overrun errors.
  203. }
  204. }
  205. }
  206. };