Paparazzi UAS v7.0_unstable
Paparazzi is a free software Unmanned Aircraft System.
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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_arch.h"
17#include "stdio.h"
18#include "string.h"
19
20#include <ch.h>
21#include <hal.h>
22
23
33
34static void can_thd_rx(void* arg);
35static void can_start(struct can_periph* canp);
36static bool canConfigureIface(struct can_arch_periph* cas);
37
38#if USE_CAN1
39
40static THD_WORKING_AREA(can1_rx_wa, 1024 * 2);
41
43 .if_index = 1,
44 .cand = &CAND1,
45 .cfg = {0},
46 .can_baudrate = 1000000U,
47 .thread_rx_wa = can1_rx_wa,
48 .thread_rx_wa_size = sizeof(can1_rx_wa),
49};
50
51#endif
52
53#if USE_CAN2
54
55static THD_WORKING_AREA(can2_rx_wa, 1024 * 2);
56
58 .if_index = 2,
59 .cand = &CAND2,
60 .cfg = {0},
61 .can_baudrate = 1000000U,
62 .thread_rx_wa = can2_rx_wa,
63 .thread_rx_wa_size = sizeof(can2_rx_wa),
64};
65
66#endif
67
69 #if USE_CAN1
70 can1.arch_struct = &can1_arch_s;
72 #endif
73 #if USE_CAN2
74 can2.arch_struct = &can2_arch_s;
76 #endif
77}
78
79
80
81static void can_thd_rx(void* arg) {
82 struct can_periph* canp = (struct can_periph*)arg;
83 struct can_arch_periph* cas = (struct can_arch_periph*)canp->arch_struct;
84 char thd_name[10];
85 snprintf(thd_name, 10, "can%d_rx", cas->if_index);
87
88 struct pprzaddr_can addr = {
89 .can_ifindex = cas->if_index
90 };
91
92 while(!chThdShouldTerminateX()) {
93 CANRxFrame rx_frame;
95 if(status == MSG_OK) {
96 uint32_t id = 0;
97 if(rx_frame.common.XTD) {
98 id = rx_frame.ext.EID | CAN_FRAME_EFF;
99 } else {
100 id = rx_frame.std.SID;
101 }
102 if(rx_frame.common.RTR) {
103 id |= CAN_FRAME_RTR;
104 }
105 if(rx_frame.common.ESI) {
106 id |= CAN_FRAME_ERR;
107 }
108
109 struct pprzcan_frame pprz_frame = {
110 .can_id = id,
111 .len = can_dlc_to_len(rx_frame.DLC),
112 .flags = 0,
113 .timestamp = get_sys_time_msec(),
114 };
115
116 if(rx_frame.FDF) {
117 pprz_frame.flags |= CANFD_FDF;
118 }
119 if(rx_frame.common.ESI) {
120 pprz_frame.flags |= CANFD_ESI;
121 }
122
123
124
125 memcpy(pprz_frame.data, rx_frame.data8, pprz_frame.len);
126
127 for(int i=0; i<CAN_NB_CALLBACKS_MAX; i++) {
128 if(canp->callbacks[i] != NULL) {
129 canp->callbacks[i](&pprz_frame, &addr, canp->callback_user_data[i]);
130 }
131 }
132 }
133 }
134
135}
136
139 frame.DLC = can_len_to_dlc(txframe->len);
140 if(txframe->can_id & CAN_FRAME_RTR) {
141 frame.common.RTR = 1;
142 }
143 if(txframe->can_id & CAN_FRAME_EFF) {
144 frame.common.XTD = 1;
145 frame.ext.EID = txframe->can_id & CAN_EID_MASK
146 } else {
147 frame.std.SID = txframe->can_id & CAN_SID_MASK
148 }
149 memcpy(frame.data8, txframe->data, txframe->len);
150
151 #if USE_CAN1
152 if(addr->can_ifindex == 1 || addr->can_ifindex == 0) {
154 if(ret != MSG_OK) {
155 return ret;
156 }
157 }
158 #endif
159
160 #if USE_CAN2
161 if(addr->can_ifindex == 2 || addr->can_ifindex == 0) {
163 if(ret != MSG_OK) {
164 return ret;
165 }
166 }
167 #endif
168
169 return 0;
170}
171
172static void can_start(struct can_periph* canp) {
173 struct can_arch_periph* cas = (struct can_arch_periph*)canp->arch_struct;
174
175 #if defined(STM32_CAN_USE_FDCAN1) || defined(STM32_CAN_USE_FDCAN2)
176 // Configure the RAM
178 can1_arch_s.cfg.RXF1C = (32 << FDCAN_RXF1C_F1S_Pos) | (128 << FDCAN_RXF1C_F1SA_Pos);
179 can1_arch_s.cfg.TXBC = (32 << FDCAN_TXBC_TFQS_Pos) | (256 << FDCAN_TXBC_TBSA_Pos);
180 can1_arch_s.cfg.TXESC = 0x000; // 8 Byte mode only (4 words per message)
181 can1_arch_s.cfg.RXESC = 0x000; // 8 Byte mode only (4 words per message)
182 #endif
183 if (!canConfigureIface(cas)) {
184 return;
185 }
186
187
188 canStart(cas->cand, &can1_arch_s.cfg);
189 chThdCreateStatic(cas->thread_rx_wa, cas->thread_rx_wa_size,
191}
192
193
198{
199 if (cas->can_baudrate < 1) {
200 return false;
201 }
202
203 // Hardware configurationn
204#if defined(STM32_CAN_USE_FDCAN1) || defined(STM32_CAN_USE_FDCAN2)
206#else
207 const uint32_t pclk = STM32_PCLK1;
208#endif
209 static const int MaxBS1 = 16;
210 static const int MaxBS2 = 8;
211
212 /*
213 * Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG
214 * CAN in Automation, 2003
215 *
216 * According to the source, optimal quanta per bit are:
217 * Bitrate Optimal Maximum
218 * 1000 kbps 8 10
219 * 500 kbps 16 17
220 * 250 kbps 16 17
221 * 125 kbps 16 17
222 */
223 const int max_quanta_per_bit = (cas->can_baudrate >= 1000000) ? 10 : 17;
224 static const int MaxSamplePointLocation = 900;
225
226 /*
227 * Computing (prescaler * BS):
228 * BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual
229 * BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified
230 * let:
231 * BS = 1 + BS1 + BS2 -- Number of time quanta per bit
232 * PRESCALER_BS = PRESCALER * BS
233 * ==>
234 * PRESCALER_BS = PCLK / BITRATE
235 */
236 const uint32_t prescaler_bs = pclk / cas->can_baudrate;
237
238// Searching for such prescaler value so that the number of quanta per bit is highest.
240 while ((prescaler_bs % (1 + bs1_bs2_sum)) != 0) {
241 if (bs1_bs2_sum <= 2) {
242 return false; // No solution
243 }
244 bs1_bs2_sum--;
245 }
246
247 const uint32_t prescaler = prescaler_bs / (1 + bs1_bs2_sum);
248 if ((prescaler < 1U) || (prescaler > 1024U)) {
249 return false; // No solution
250 }
251
252 /*
253 * Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.
254 * We need to find the values so that the sample point is as close as possible to the optimal value.
255 *
256 * Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *)
257 * {{bs2 -> (1 + bs1)/7}}
258 *
259 * Hence:
260 * bs2 = (1 + bs1) / 7
261 * bs1 = (7 * bs1_bs2_sum - 1) / 8
262 *
263 * Sample point location can be computed as follows:
264 * Sample point location = (1 + bs1) / (1 + bs1 + bs2)
265 *
266 * Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one:
267 * - With rounding to nearest
268 * - With rounding to zero
269 */
270// First attempt with rounding to nearest
271 uint8_t bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8;
273 uint16_t sample_point_permill = 1000 * (1 + bs1) / (1 + bs1 + bs2);
274
275// Second attempt with rounding to zero
277 bs1 = (7 * bs1_bs2_sum - 1) / 8;
278 bs2 = bs1_bs2_sum - bs1;
279 sample_point_permill = 1000 * (1 + bs1) / (1 + bs1 + bs2);
280 }
281
282 /*
283 * Final validation
284 * Helpful Python:
285 * def sample_point_from_btr(x):
286 * assert 0b0011110010000000111111000000000 & x == 0
287 * ts2,ts1,brp = (x>>20)&7, (x>>16)&15, x&511
288 * return (1+ts1+1)/(1+ts1+1+ts2+1)
289 *
290 */
291 if ((cas->can_baudrate != (pclk / (prescaler * (1 + bs1 + bs2)))) || (bs1 < 1) || (bs1 > MaxBS1) || (bs2 < 1)
292 || (bs2 > MaxBS2)) {
293 return false;
294 }
295
296 // Configure the interface
297#if defined(STM32_CAN_USE_FDCAN1) || defined(STM32_CAN_USE_FDCAN2)
298 cas->cfg.NBTP = (0 << FDCAN_NBTP_NSJW_Pos) | ((bs1 - 1) << FDCAN_NBTP_NTSEG1_Pos) | ((
299 bs2 - 1) << FDCAN_NBTP_NTSEG2_Pos) | ((prescaler - 1) << FDCAN_NBTP_NBRP_Pos);
300 #if USE_CANFD
301 cas->cfg.CCCR = FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE;
302 #else
303 cas->cfg.CCCR = 0;
304 #endif
305
306#else
308 cas->cfg.btr = CAN_BTR_SJW(0) | CAN_BTR_TS1(bs1 - 1) | CAN_BTR_TS2(bs2 - 1) | CAN_BTR_BRP(prescaler - 1);
309#endif
310 return true;
311}
static uint8_t status
uint8_t can_dlc_to_len(uint8_t dlc)
Definition can.c:39
uint8_t can_len_to_dlc(uint8_t len)
Definition can.c:46
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:197
static void can_thd_rx(void *arg)
Definition can_arch.c:81
static void can_start(struct can_periph *canp)
Definition can_arch.c:172
void can_hw_init()
Definition can_arch.c:68
int can_transmit_frame(struct pprzcan_frame *txframe, struct pprzaddr_can *addr)
Definition can_arch.c:137
static THD_WORKING_AREA(wa_thd_spi1, SPI_THREAD_STACK_SIZE)
uint32_t get_sys_time_msec(void)
Get the time in milliseconds since startup.
uint16_t foo
Definition main_demo5.c:58
CANDriver * cand
Definition can_arch.c:26
uint32_t can_baudrate
Definition can_arch.c:28
struct can_arch_periph can1_arch_s
Definition can_arch.c:61
void * thread_rx_wa
Definition can_arch.c:30
CANConfig cfg
Definition can_arch.c:27
size_t thread_rx_wa_size
Definition can_arch.c:31
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.