KGBRUS

CAN_STM32_ccp_Datein

May 26th, 2023
32
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.82 KB | None | 0 0
  1. #include <assert.h>
  2.  
  3.  
  4. #include <Arduino.h>
  5. #include "stm32f103.h"
  6. #define DEBUG 0
  7.  
  8. /* Real speed for bit rate of CAN message */
  9. uint32_t SPEED[6] = {50*1000, 100*1000, 125*1000, 250*1000, 500*1000, 1000*1000};
  10.  
  11.  
  12.  
  13.  
  14.  
  15. typedef struct
  16. {
  17. uint16_t baud_rate_prescaler; /// [1 to 1024]
  18. uint8_t time_segment_1; /// [1 to 16]
  19. uint8_t time_segment_2; /// [1 to 8]
  20. uint8_t resynchronization_jump_width; /// [1 to 4] (recommended value is 1)
  21. } CAN_bit_timing_config_t;
  22.  
  23. #define CAN_STM32_ERROR_UNSUPPORTED_BIT_RATE 1000
  24. #define CAN_STM32_ERROR_MSR_INAK_NOT_SET 1001
  25. #define CAN_STM32_ERROR_MSR_INAK_NOT_CLEARED 1002
  26. #define CAN_STM32_ERROR_UNSUPPORTED_FRAME_FORMAT 1003
  27.  
  28. /*
  29. * Calculation of bit timing dependent on peripheral clock rate
  30. */
  31. int16_t ComputeCANTimings(const uint32_t peripheral_clock_rate,
  32. const uint32_t target_bitrate,
  33. CAN_bit_timing_config_t* const out_timings)
  34. {
  35. if (target_bitrate < 1000)
  36. {
  37. return -CAN_STM32_ERROR_UNSUPPORTED_BIT_RATE;
  38. }
  39.  
  40. assert(out_timings != NULL); // NOLINT
  41. memset(out_timings, 0, sizeof(*out_timings));
  42.  
  43. /*
  44. * Hardware configuration
  45. */
  46. static const uint8_t MaxBS1 = 16;
  47. static const uint8_t MaxBS2 = 8;
  48.  
  49. /*
  50. * Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG
  51. * CAN in Automation, 2003
  52. *
  53. * According to the source, optimal quanta per bit are:
  54. * Bitrate Optimal Maximum
  55. * 1000 kbps 8 10
  56. * 500 kbps 16 17
  57. * 250 kbps 16 17
  58. * 125 kbps 16 17
  59. */
  60. const uint8_t max_quanta_per_bit = (uint8_t)((target_bitrate >= 1000000) ? 10 : 17); // NOLINT
  61. assert(max_quanta_per_bit <= (MaxBS1 + MaxBS2));
  62.  
  63. static const uint16_t MaxSamplePointLocationPermill = 900;
  64.  
  65. /*
  66. * Computing (prescaler * BS):
  67. * BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual
  68. * BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified
  69. * let:
  70. * BS = 1 + BS1 + BS2 -- Number of time quanta per bit
  71. * PRESCALER_BS = PRESCALER * BS
  72. * ==>
  73. * PRESCALER_BS = PCLK / BITRATE
  74. */
  75. const uint32_t prescaler_bs = peripheral_clock_rate / target_bitrate;
  76.  
  77. /*
  78. * Searching for such prescaler value so that the number of quanta per bit is highest.
  79. */
  80. uint8_t bs1_bs2_sum = (uint8_t)(max_quanta_per_bit - 1); // NOLINT
  81.  
  82. while ((prescaler_bs % (1U + bs1_bs2_sum)) != 0)
  83. {
  84. if (bs1_bs2_sum <= 2)
  85. {
  86. return -CAN_STM32_ERROR_UNSUPPORTED_BIT_RATE; // No solution
  87. }
  88. bs1_bs2_sum--;
  89. }
  90.  
  91. const uint32_t prescaler = prescaler_bs / (1U + bs1_bs2_sum);
  92. if ((prescaler < 1U) || (prescaler > 1024U))
  93. {
  94. return -CAN_STM32_ERROR_UNSUPPORTED_BIT_RATE; // No solution
  95. }
  96.  
  97. /*
  98. * Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.
  99. * We need to find such values so that the sample point is as close as possible to the optimal value,
  100. * which is 87.5%, which is 7/8.
  101. *
  102. * Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *)
  103. * {{bs2 -> (1 + bs1)/7}}
  104. *
  105. * Hence:
  106. * bs2 = (1 + bs1) / 7
  107. * bs1 = (7 * bs1_bs2_sum - 1) / 8
  108. *
  109. * Sample point location can be computed as follows:
  110. * Sample point location = (1 + bs1) / (1 + bs1 + bs2)
  111. *
  112. * Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one:
  113. * - With rounding to nearest
  114. * - With rounding to zero
  115. */
  116. uint8_t bs1 = (uint8_t)(((7 * bs1_bs2_sum - 1) + 4) / 8); // Trying rounding to nearest first // NOLINT
  117. uint8_t bs2 = (uint8_t)(bs1_bs2_sum - bs1); // NOLINT
  118. assert(bs1_bs2_sum > bs1);
  119.  
  120. {
  121. const uint16_t sample_point_permill = (uint16_t)(1000U * (1U + bs1) / (1U + bs1 + bs2)); // NOLINT
  122.  
  123. if (sample_point_permill > MaxSamplePointLocationPermill) // Strictly more!
  124. {
  125. bs1 = (uint8_t)((7 * bs1_bs2_sum - 1) / 8); // Nope, too far; now rounding to zero
  126. bs2 = (uint8_t)(bs1_bs2_sum - bs1);
  127. }
  128. }
  129.  
  130. const bool valid = (bs1 >= 1) && (bs1 <= MaxBS1) && (bs2 >= 1) && (bs2 <= MaxBS2);
  131.  
  132. /*
  133. * Final validation
  134. * Helpful Python:
  135. * def sample_point_from_btr(x):
  136. * assert 0b0011110010000000111111000000000 & x == 0
  137. * ts2,ts1,brp = (x>>20)&7, (x>>16)&15, x&511
  138. * return (1+ts1+1)/(1+ts1+1+ts2+1)
  139. */
  140. if ((target_bitrate != (peripheral_clock_rate / (prescaler * (1U + bs1 + bs2)))) ||
  141. !valid)
  142. {
  143. // This actually means that the algorithm has a logic error, hence assert(0).
  144. assert(0); // NOLINT
  145. return -CAN_STM32_ERROR_UNSUPPORTED_BIT_RATE;
  146. }
  147.  
  148. out_timings->baud_rate_prescaler = (uint16_t) prescaler;
  149. out_timings->resynchronization_jump_width = 1; // One is recommended by UAVCAN, CANOpen, and DeviceNet
  150. out_timings->time_segment_1 = bs1;
  151. out_timings->time_segment_2 = bs2;
  152.  
  153. if (DEBUG) {
  154. Serial.print("target_bitrate=");
  155. Serial.println(target_bitrate);
  156. Serial.print("peripheral_clock_rate=");
  157. Serial.println(peripheral_clock_rate);
  158.  
  159. Serial.print("timings.baud_rate_prescaler=");
  160. Serial.println(out_timings->baud_rate_prescaler);
  161. Serial.print("timings.time_segment_1=");
  162. Serial.println(out_timings->time_segment_1);
  163. Serial.print("timings.time_segment_2=");
  164. Serial.println(out_timings->time_segment_2);
  165. Serial.print("timings.resynchronization_jump_width=");
  166. Serial.println(out_timings->resynchronization_jump_width);
  167. }
  168. return 0;
  169. }
  170.  
  171. /**
  172. * Print registers.
  173. */
  174. void printRegister(const char * buf, uint32_t reg) {
  175. if (DEBUG == 0) return;
  176. Serial.print(buf);
  177. Serial.print(reg, HEX);
  178. Serial.println();
  179. }
  180.  
  181. /**
  182. * Initializes the CAN filter registers.
  183. *
  184. * @preconditions - This register can be written only when the filter initialization mode is set (FINIT=1) in the CAN_FMR register.
  185. * @params: index - Specified filter index. index 27:14 are available in connectivity line devices only.
  186. * @params: scale - Select filter scale.
  187. * 0: Dual 16-bit scale configuration
  188. * 1: Single 32-bit scale configuration
  189. * @params: mode - Select filter mode.
  190. * 0: Two 32-bit registers of filter bank x are in Identifier Mask mode
  191. * 1: Two 32-bit registers of filter bank x are in Identifier List mode
  192. * @params: fifo - Select filter assigned.
  193. * 0: Filter assigned to FIFO 0
  194. * 1: Filter assigned to FIFO 1
  195. * @params: bank1 - Filter bank register 1
  196. * @params: bank2 - Filter bank register 2
  197. *
  198. */
  199. void CANSetFilter(uint8_t index, uint8_t scale, uint8_t mode, uint8_t fifo, uint32_t bank1, uint32_t bank2) {
  200. if (index > 27) return;
  201.  
  202. CAN1->FA1R &= ~(0x1UL<<index); // Deactivate filter
  203.  
  204. if (scale == 0) {
  205. CAN1->FS1R &= ~(0x1UL<<index); // Set filter to Dual 16-bit scale configuration
  206. } else {
  207. CAN1->FS1R |= (0x1UL<<index); // Set filter to single 32 bit configuration
  208. }
  209. if (mode == 0) {
  210. CAN1->FM1R &= ~(0x1UL<<index); // Set filter to Mask mode
  211. } else {
  212. CAN1->FM1R |= (0x1UL<<index); // Set filter to List mode
  213. }
  214.  
  215. if (fifo == 0) {
  216. CAN1->FFA1R &= ~(0x1UL<<index); // Set filter assigned to FIFO 0
  217. } else {
  218. CAN1->FFA1R |= (0x1UL<<index); // Set filter assigned to FIFO 1
  219. }
  220.  
  221. CAN1->sFilterRegister[index].FR1 = bank1; // Set filter bank registers1
  222. CAN1->sFilterRegister[index].FR2 = bank2; // Set filter bank registers2
  223.  
  224. CAN1->FA1R |= (0x1UL<<index); // Activate filter
  225.  
  226. }
  227.  
  228. /**
  229. * Initializes the CAN controller with specified bit rate.
  230. *
  231. * @params: bitrate - Specified bitrate. If this value is not one of the defined constants, bit rate will be defaulted to 125KBS
  232. * @params: remap - Select CAN port.
  233. * =0:CAN_RX mapped to PA11, CAN_TX mapped to PA12
  234. * =1:Not used
  235. * =2:CAN_RX mapped to PB8, CAN_TX mapped to PB9 (not available on 36-pin package)
  236. * =3:CAN_RX mapped to PD0, CAN_TX mapped to PD1 (available on 100-pin and 144-pin package)
  237. *
  238. */
  239. bool CANInit(BITRATE bitrate, int remap)
  240. {
  241. // Reference manual
  242. // https://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/en.CD00171190.pdf
  243.  
  244. RCC->APB1ENR |= 0x2000000UL; // Enable CAN clock
  245. RCC->APB2ENR |= 0x1UL; // Enable AFIO clock
  246. AFIO->MAPR &= 0xFFFF9FFF; // reset CAN remap
  247. // CAN_RX mapped to PA11, CAN_TX mapped to PA12
  248.  
  249. if (remap == 0) {
  250. RCC->APB2ENR |= 0x4UL; // Enable GPIOA clock
  251. GPIOA->CRH &= ~(0xFF000UL); // Configure PA12(0b0000) and PA11(0b0000)
  252. // 0b0000
  253. // MODE=00(Input mode)
  254. // CNF=00(Analog mode)
  255.  
  256. GPIOA->CRH |= 0xB8FFFUL; // Configure PA12(0b1011) and PA11(0b1000)
  257. // 0b1011
  258. // MODE=11(Output mode, max speed 50 MHz)
  259. // CNF=10(Alternate function output Push-pull
  260. // 0b1000
  261. // MODE=00(Input mode)
  262. // CNF=10(Input with pull-up / pull-down)
  263.  
  264. GPIOA->ODR |= 0x1UL << 12; // PA12 Upll-up
  265.  
  266. }
  267.  
  268. if (remap == 2) {
  269. AFIO->MAPR |= 0x00004000; // set CAN remap
  270. // CAN_RX mapped to PB8, CAN_TX mapped to PB9 (not available on 36-pin package)
  271.  
  272. RCC->APB2ENR |= 0x8UL; // Enable GPIOB clock
  273. GPIOB->CRH &= ~(0xFFUL); // Configure PB9(0b0000) and PB8(0b0000)
  274. // 0b0000
  275. // MODE=00(Input mode)
  276. // CNF=00(Analog mode)
  277.  
  278. GPIOB->CRH |= 0xB8UL; // Configure PB9(0b1011) and PB8(0b1000)
  279. // 0b1011
  280. // MODE=11(Output mode, max speed 50 MHz)
  281. // CNF=10(Alternate function output Push-pull
  282. // 0b1000
  283. // MODE=00(Input mode)
  284. // CNF=10(Input with pull-up / pull-down)
  285.  
  286. GPIOB->ODR |= 0x1UL << 8; // PB8 Upll-up
  287. }
  288.  
  289. if (remap == 3) {
  290. AFIO->MAPR |= 0x00005000; // set CAN remap
  291. // CAN_RX mapped to PD0, CAN_TX mapped to PD1 (available on 100-pin and 144-pin package)
  292.  
  293. RCC->APB2ENR |= 0x20UL; // Enable GPIOD clock
  294. GPIOD->CRL &= ~(0xFFUL); // Configure PD1(0b0000) and PD0(0b0000)
  295. // 0b0000
  296. // MODE=00(Input mode)
  297. // CNF=00(Analog mode)
  298.  
  299. GPIOD->CRH |= 0xB8UL; // Configure PD1(0b1011) and PD0(0b1000)
  300. // 0b1000
  301. // MODE=00(Input mode)
  302. // CNF=10(Input with pull-up / pull-down)
  303. // 0b1011
  304. // MODE=11(Output mode, max speed 50 MHz)
  305. // CNF=10(Alternate function output Push-pull
  306.  
  307. GPIOD->ODR |= 0x1UL << 0; // PD0 Upll-up
  308. }
  309.  
  310. CAN1->MCR |= 0x1UL; // Require CAN1 to Initialization mode
  311. while (!(CAN1->MSR & 0x1UL)); // Wait for Initialization mode
  312.  
  313. //CAN1->MCR = 0x51UL; // Hardware initialization(No automatic retransmission)
  314. CAN1->MCR = 0x41UL; // Hardware initialization(With automatic retransmission)
  315.  
  316. // Set bit timing register
  317. CAN_bit_timing_config_t timings;
  318. Serial.print("bitrate=");
  319. Serial.println(bitrate);
  320. uint32_t target_bitrate = SPEED[bitrate];
  321. Serial.print("target_bitrate=");
  322. Serial.println(target_bitrate);
  323. int result = ComputeCANTimings(HAL_RCC_GetPCLK1Freq(), target_bitrate, &timings);
  324. Serial.print("ComputeCANTimings result=");
  325. Serial.println(result);
  326. if (result) while(true);
  327. CAN1->BTR = (((timings.resynchronization_jump_width - 1U) & 3U) << 24U) |
  328. (((timings.time_segment_1 - 1U) & 15U) << 16U) |
  329. (((timings.time_segment_2 - 1U) & 7U) << 20U) |
  330. ((timings.baud_rate_prescaler - 1U) & 1023U);
  331.  
  332. // Configure Filters to default values
  333. CAN1->FMR |= 0x1UL; // Set to filter initialization mode
  334. CAN1->FMR &= 0xFFFFC0FF; // Clear CAN2 start bank
  335.  
  336. // bxCAN has 28 filters.
  337. // These filters are shared by both CAN1 and CAN2.
  338. // STM32F103 has only CAN1, so all 28 are used for CAN1
  339. CAN1->FMR |= 0x1C << 8; // Assign all filters to CAN1
  340.  
  341. // Set fileter 0
  342. // Single 32-bit scale configuration
  343. // Two 32-bit registers of filter bank x are in Identifier Mask mode
  344. // Filter assigned to FIFO 0
  345. // Filter bank register to all 0
  346. CANSetFilter(0, 1, 0, 0, 0x0UL, 0x0UL);
  347.  
  348. CAN1->FMR &= ~(0x1UL); // Deactivate initialization mode
  349.  
  350. uint16_t TimeoutMilliseconds = 1000;
  351. bool can1 = false;
  352. CAN1->MCR &= ~(0x1UL); // Require CAN1 to normal mode
  353.  
  354. // Wait for normal mode
  355. // If the connection is not correct, it will not return to normal mode.
  356. for (uint16_t wait_ack = 0; wait_ack < TimeoutMilliseconds; wait_ack++) {
  357. if ((CAN1->MSR & 0x1UL) == 0) {
  358. can1 = true;
  359. break;
  360. }
  361. delayMicroseconds(1000);
  362. }
  363. //Serial.print("can1=");
  364. //Serial.println(can1);
  365. if (can1) {
  366. Serial.println("CAN1 initialize ok");
  367. } else {
  368. Serial.println("CAN1 initialize fail!!");
  369. return false;
  370. }
  371. return true;
  372. }
  373.  
  374.  
  375. #define STM32_CAN_TIR_TXRQ (1U << 0U) // Bit 0: Transmit Mailbox Request
  376. #define STM32_CAN_RIR_RTR (1U << 1U) // Bit 1: Remote Transmission Request
  377. #define STM32_CAN_RIR_IDE (1U << 2U) // Bit 2: Identifier Extension
  378. #define STM32_CAN_TIR_RTR (1U << 1U) // Bit 1: Remote Transmission Request
  379. #define STM32_CAN_TIR_IDE (1U << 2U) // Bit 2: Identifier Extension
  380.  
  381. #define CAN_EXT_ID_MASK 0x1FFFFFFFU
  382. #define CAN_STD_ID_MASK 0x000007FFU
  383.  
  384. /**
  385. * Decodes CAN messages from the data registers and populates a
  386. * CAN message struct with the data fields.
  387. *
  388. * @preconditions A valid CAN message is received
  389. * @params CAN_rx_msg - CAN message structure for reception
  390. *
  391. */
  392. void CANReceive(CAN_msg_t* CAN_rx_msg)
  393. {
  394. uint32_t id = CAN1->sFIFOMailBox[0].RIR;
  395. if ((id & STM32_CAN_RIR_IDE) == 0) { // Standard frame format
  396. CAN_rx_msg->format = STANDARD_FORMAT;;
  397. CAN_rx_msg->id = (CAN_STD_ID_MASK & (id >> 21U));
  398. }
  399. else { // Extended frame format
  400. CAN_rx_msg->format = EXTENDED_FORMAT;;
  401. CAN_rx_msg->id = (CAN_EXT_ID_MASK & (id >> 3U));
  402. }
  403.  
  404. if ((id & STM32_CAN_RIR_RTR) == 0) { // Data frame
  405. CAN_rx_msg->type = DATA_FRAME;
  406. }
  407. else { // Remote frame
  408. CAN_rx_msg->type = REMOTE_FRAME;
  409. }
  410.  
  411.  
  412. CAN_rx_msg->len = (CAN1->sFIFOMailBox[0].RDTR) & 0xFUL;
  413.  
  414. CAN_rx_msg->data[0] = 0xFFUL & CAN1->sFIFOMailBox[0].RDLR;
  415. CAN_rx_msg->data[1] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 8);
  416. CAN_rx_msg->data[2] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 16);
  417. CAN_rx_msg->data[3] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 24);
  418. CAN_rx_msg->data[4] = 0xFFUL & CAN1->sFIFOMailBox[0].RDHR;
  419. CAN_rx_msg->data[5] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 8);
  420. CAN_rx_msg->data[6] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 16);
  421. CAN_rx_msg->data[7] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 24);
  422.  
  423. // Release FIFO 0 output mailbox.
  424. // Make the next incoming message available.
  425. CAN1->RF0R |= 0x20UL;
  426. }
  427.  
  428. /**
  429. * Encodes CAN messages using the CAN message struct and populates the
  430. * data registers with the sent.
  431. *
  432. * @params CAN_tx_msg - CAN message structure for transmission
  433. *
  434. */
  435. void CANSend(CAN_msg_t* CAN_tx_msg)
  436. {
  437. volatile int count = 0;
  438.  
  439. uint32_t out = 0;
  440. if (CAN_tx_msg->format == EXTENDED_FORMAT) { // Extended frame format
  441. out = ((CAN_tx_msg->id & CAN_EXT_ID_MASK) << 3U) | STM32_CAN_TIR_IDE;
  442. }
  443. else { // Standard frame format
  444. out = ((CAN_tx_msg->id & CAN_STD_ID_MASK) << 21U);
  445. }
  446.  
  447. // Remote frame
  448. if (CAN_tx_msg->type == REMOTE_FRAME) {
  449. out |= STM32_CAN_TIR_RTR;
  450. }
  451.  
  452. CAN1->sTxMailBox[0].TDTR &= ~(0xF);
  453. CAN1->sTxMailBox[0].TDTR |= CAN_tx_msg->len & 0xFUL;
  454.  
  455. CAN1->sTxMailBox[0].TDLR = (((uint32_t) CAN_tx_msg->data[3] << 24) |
  456. ((uint32_t) CAN_tx_msg->data[2] << 16) |
  457. ((uint32_t) CAN_tx_msg->data[1] << 8) |
  458. ((uint32_t) CAN_tx_msg->data[0] ));
  459. CAN1->sTxMailBox[0].TDHR = (((uint32_t) CAN_tx_msg->data[7] << 24) |
  460. ((uint32_t) CAN_tx_msg->data[6] << 16) |
  461. ((uint32_t) CAN_tx_msg->data[5] << 8) |
  462. ((uint32_t) CAN_tx_msg->data[4] ));
  463.  
  464. // Send Go
  465. CAN1->sTxMailBox[0].TIR = out | STM32_CAN_TIR_TXRQ;
  466.  
  467. // Wait until the mailbox is empty
  468. while(CAN1->sTxMailBox[0].TIR & 0x1UL && count++ < 1000000);
  469.  
  470. // The mailbox don't becomes empty while loop
  471. if (CAN1->sTxMailBox[0].TIR & 0x1UL) {
  472. Serial.println("Send Fail");
  473. Serial.println(CAN1->ESR);
  474. Serial.println(CAN1->MSR);
  475. Serial.println(CAN1->TSR);
  476. }
  477. }
  478.  
  479. /**
  480. * Returns whether there are CAN messages available.
  481. *
  482. * @returns If pending CAN messages are in the CAN controller
  483. *
  484. */
  485. uint8_t CANMsgAvail(void)
  486. {
  487. // Check for pending FIFO 0 messages
  488. return CAN1->RF0R & 0x3UL;
  489. }
Advertisement
Add Comment
Please, Sign In to add comment