Paparazzi UAS v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
Loading...
Searching...
No Matches
can_arch.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2025 The Paparazzi Team
3 *
4 * This file is part of paparazzi.
5 *
6 * Paparazzi is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * See LICENSE file for the full license version, or see http://www.gnu.org/licenses/
12 */
13
14#include "mcu_periph/can_arch.h"
15#include "mcu_periph/can.h"
16#include "mcu_periph/sys_time.h"
17#include "stdio.h"
18#include "string.h"
19
20#include <ch.h>
21#include <hal.h>
22
23// Determine which ChibiOS CAN driver to use based on the configuration
24#define USE_FDCAN_DRIVER ((STM32_CAN_USE_FDCAN1) || (STM32_CAN_USE_FDCAN2) || (STM32_CAN_USE_FDCAN3))
25
35
36static void can_thd_rx(void* arg);
37static void can_start(struct can_periph* canp);
38static bool canConfigureIface(struct can_arch_periph* cas);
39
40#if USE_CAN1
41static THD_WORKING_AREA(can1_rx_wa, 1024 * 2);
42
44 .if_index = 1,
45 .cand = &CAND1,
46 .cfg = {0},
47 .can_baudrate = 1000000U,
48 .thread_rx_wa = can1_rx_wa,
49 .thread_rx_wa_size = sizeof(can1_rx_wa),
50};
51#endif
52
53#if USE_CAN2
54static THD_WORKING_AREA(can2_rx_wa, 1024 * 2);
55
57 .if_index = 2,
58 .cand = &CAND2,
59 .cfg = {0},
60 .can_baudrate = 1000000U,
61 .thread_rx_wa = can2_rx_wa,
62 .thread_rx_wa_size = sizeof(can2_rx_wa),
63};
64#endif
65
67 #if USE_CAN1
68 can1.arch_struct = &can1_arch_s;
70 #endif
71
72 #if USE_CAN2
73 can2.arch_struct = &can2_arch_s;
75 #endif
76}
77
78
79
80static void can_thd_rx(void* arg) {
81 struct can_periph* canp = (struct can_periph*)arg;
82 struct can_arch_periph* cas = (struct can_arch_periph*)canp->arch_struct;
83 char thd_name[10];
84 snprintf(thd_name, 10, "can%d_rx", cas->if_index);
86
88 chEvtRegister(&cas->cand->error_event, &rxe, EVENT_MASK(1));
89
90
91 struct pprzaddr_can addr = {
92 .can_ifindex = cas->if_index
93 };
94
95 while(!chThdShouldTerminateX()) {
96
98 // receive error
99 if (evts & EVENT_MASK(1)) {
101 canp->nb_errors++;
102 }
103
104 CANRxFrame rx_frame;
106 if(status == MSG_OK) {
107 uint32_t id = 0;
108#if USE_FDCAN_DRIVER
109 if(rx_frame.common.XTD) {
110 id = rx_frame.ext.EID | CAN_FRAME_EFF;
111 } else {
112 id = rx_frame.std.SID;
113 }
114 if(rx_frame.common.RTR) {
115 id |= CAN_FRAME_RTR;
116 }
117 if(rx_frame.common.ESI) {
118 id |= CAN_FRAME_ERR;
119 }
120#else
121 if(rx_frame.IDE) {
122 id = rx_frame.EID | CAN_FRAME_EFF;
123 } else {
124 id = rx_frame.SID;
125 }
126 if(rx_frame.RTR) {
127 id |= CAN_FRAME_RTR;
128 }
129#endif
130
131 struct pprzcan_frame pprz_frame = {
132 .can_id = id,
133 .len = can_dlc_to_len(rx_frame.DLC),
134 .flags = 0,
135 .timestamp = TIME_I2US(chVTGetSystemTimeX())
136 };
137
138#if USE_FDCAN_DRIVER
139 if(rx_frame.FDF) {
140 pprz_frame.flags |= CANFD_FDF;
141 }
142 if(rx_frame.common.ESI) {
143 pprz_frame.flags |= CANFD_ESI;
144 }
145#endif
146
147 memcpy(pprz_frame.data, rx_frame.data8, pprz_frame.len);
148
149 for(int i=0; i<CAN_NB_CALLBACKS_MAX; i++) {
150 if(canp->callbacks[i] != NULL) {
151 canp->callbacks[i](&pprz_frame, &addr, canp->callback_user_data[i]);
152 }
153 }
154 }
155 }
156
157}
158
160 CANTxFrame frame = {0};
161 frame.DLC = can_len_to_dlc(txframe->len);
162
163#if USE_FDCAN_DRIVER
164 if(txframe->can_id & CAN_FRAME_RTR) {
165 frame.common.RTR = 1;
166 }
167 if(txframe->can_id & CAN_FRAME_EFF) {
168 frame.common.XTD = 1;
169 frame.ext.EID = txframe->can_id & CAN_EID_MASK;
170 } else {
171 frame.std.SID = txframe->can_id & CAN_SID_MASK;
172 }
173#else
174 if(txframe->can_id & CAN_FRAME_RTR) {
175 frame.RTR = 1;
176 }
177 if(txframe->can_id & CAN_FRAME_EFF) {
178 frame.IDE = 1;
179 frame.EID = txframe->can_id & CAN_EID_MASK;
180 } else {
181 frame.SID = txframe->can_id & CAN_SID_MASK;
182 }
183#endif
184
185 memcpy(frame.data8, txframe->data, txframe->len);
186
187 #if USE_CAN1
188 if(addr->can_ifindex == 1 || addr->can_ifindex == 0) {
190 if(ret != MSG_OK) {
191 return ret;
192 }
193 }
194 #endif
195
196 #if USE_CAN2
197 if(addr->can_ifindex == 2 || addr->can_ifindex == 0) {
199 if(ret != MSG_OK) {
200 return ret;
201 }
202 }
203 #endif
204
205 return 0;
206}
207
208static void can_start(struct can_periph* canp) {
209 struct can_arch_periph* cas = (struct can_arch_periph*)canp->arch_struct;
210
211 if (!canConfigureIface(cas)) {
212 return;
213 }
214
215
216 canStart(cas->cand, &cas->cfg);
217 chThdCreateStatic(cas->thread_rx_wa, cas->thread_rx_wa_size,
219}
220
221
226{
227 if (cas->can_baudrate < 1) {
228 return false;
229 }
230
231 // Hardware configurationn
232#if USE_FDCAN_DRIVER
234#else
235 const uint32_t pclk = STM32_PCLK1;
236#endif
237 static const int MaxBS1 = 16;
238 static const int MaxBS2 = 8;
239
240 /*
241 * Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG
242 * CAN in Automation, 2003
243 *
244 * According to the source, optimal quanta per bit are:
245 * Bitrate Optimal Maximum
246 * 1000 kbps 8 10
247 * 500 kbps 16 17
248 * 250 kbps 16 17
249 * 125 kbps 16 17
250 */
251 const int max_quanta_per_bit = (cas->can_baudrate >= 1000000) ? 10 : 17;
252 static const int MaxSamplePointLocation = 900;
253
254 /*
255 * Computing (prescaler * BS):
256 * BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual
257 * BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified
258 * let:
259 * BS = 1 + BS1 + BS2 -- Number of time quanta per bit
260 * PRESCALER_BS = PRESCALER * BS
261 * ==>
262 * PRESCALER_BS = PCLK / BITRATE
263 */
264 const uint32_t prescaler_bs = pclk / cas->can_baudrate;
265
266// Searching for such prescaler value so that the number of quanta per bit is highest.
268 while ((prescaler_bs % (1 + bs1_bs2_sum)) != 0) {
269 if (bs1_bs2_sum <= 2) {
270 return false; // No solution
271 }
272 bs1_bs2_sum--;
273 }
274
275 const uint32_t prescaler = prescaler_bs / (1 + bs1_bs2_sum);
276 if ((prescaler < 1U) || (prescaler > 1024U)) {
277 return false; // No solution
278 }
279
280 /*
281 * Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.
282 * We need to find the values so that the sample point is as close as possible to the optimal value.
283 *
284 * Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *)
285 * {{bs2 -> (1 + bs1)/7}}
286 *
287 * Hence:
288 * bs2 = (1 + bs1) / 7
289 * bs1 = (7 * bs1_bs2_sum - 1) / 8
290 *
291 * Sample point location can be computed as follows:
292 * Sample point location = (1 + bs1) / (1 + bs1 + bs2)
293 *
294 * Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one:
295 * - With rounding to nearest
296 * - With rounding to zero
297 */
298// First attempt with rounding to nearest
299 uint8_t bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8;
301 uint16_t sample_point_permill = 1000 * (1 + bs1) / (1 + bs1 + bs2);
302
303// Second attempt with rounding to zero
305 bs1 = (7 * bs1_bs2_sum - 1) / 8;
306 bs2 = bs1_bs2_sum - bs1;
307 sample_point_permill = 1000 * (1 + bs1) / (1 + bs1 + bs2);
308 }
309
310 /*
311 * Final validation
312 * Helpful Python:
313 * def sample_point_from_btr(x):
314 * assert 0b0011110010000000111111000000000 & x == 0
315 * ts2,ts1,brp = (x>>20)&7, (x>>16)&15, x&511
316 * return (1+ts1+1)/(1+ts1+1+ts2+1)
317 *
318 */
319 if ((cas->can_baudrate != (pclk / (prescaler * (1 + bs1 + bs2)))) || (bs1 < 1) || (bs1 > MaxBS1) || (bs2 < 1)
320 || (bs2 > MaxBS2)) {
321 return false;
322 }
323
324 // Configure the interface
325#if USE_FDCAN_DRIVER
326 #if USE_CANFD
327 cas->cfg.op_mode = OPMODE_FDCAN;
328 #else
329 cas->cfg.op_mode = OPMODE_CAN;
330 #endif
332 cas->cfg.NBTP = (0 << FDCAN_NBTP_NSJW_Pos) | ((bs1 - 1) << FDCAN_NBTP_NTSEG1_Pos) | ((
333 bs2 - 1) << FDCAN_NBTP_NTSEG2_Pos) | ((prescaler - 1) << FDCAN_NBTP_NBRP_Pos);
334 #if USE_CANFD
335 cas->cfg.CCCR = FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE;
336 #else
337 cas->cfg.CCCR = 0;
338 #endif
339
340#else
342 cas->cfg.btr = CAN_BTR_SJW(0) | CAN_BTR_TS1(bs1 - 1) | CAN_BTR_TS2(bs2 - 1) | CAN_BTR_BRP(prescaler - 1);
343#endif
344 return true;
345}
static uint8_t status
uint8_t can_dlc_to_len(uint8_t dlc)
Definition can.c:49
uint8_t can_len_to_dlc(uint8_t len)
Definition can.c:56
int can_ifindex
Definition can.h:83
#define CANFD_FDF
Definition can.h:70
#define CAN_NB_CALLBACKS_MAX
Definition can.h:40
socketcan_id_t can_id
Definition can.h:73
#define CAN_FRAME_ERR
Definition can.h:58
#define CAN_FRAME_EFF
Definition can.h:62
#define CAN_EID_MASK
Definition can.h:64
#define CAN_SID_MASK
Definition can.h:65
#define CANFD_ESI
Definition can.h:69
#define CAN_FRAME_RTR
Definition can.h:60
static uint8_t frame[20]
static bool canConfigureIface(struct can_arch_periph *cas)
Try to compute the timing registers for the can interface and set the configuration.
Definition can_arch.c:225
static void can_thd_rx(void *arg)
Definition can_arch.c:80
static void can_start(struct can_periph *canp)
Definition can_arch.c:208
void can_hw_init()
Definition can_arch.c:66
int can_transmit_frame(struct pprzcan_frame *txframe, struct pprzaddr_can *addr)
Definition can_arch.c:159
static THD_WORKING_AREA(wa_thd_spi1, SPI_THREAD_STACK_SIZE)
uint16_t foo
Definition main_demo5.c:58
CANDriver * cand
Definition can_arch.c:28
uint32_t can_baudrate
Definition can_arch.c:30
void * thread_rx_wa
Definition can_arch.c:32
CANConfig cfg
Definition can_arch.c:29
size_t thread_rx_wa_size
Definition can_arch.c:33
Architecture independent timing functions.
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.