dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_can.c
Go to the documentation of this file.
1 
16 /*
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25  * for more details.
26  *
27  * You should have received a copy of the GNU General Public License along
28  * with this program; if not, see <http://www.gnu.org/licenses/>
29  */
30 
31 
32 #include "pios.h"
33 
34 #if defined(PIOS_INCLUDE_CAN)
35 
36 #include "pios_can_priv.h"
37 
38 /* Provide a COM driver */
39 static void PIOS_CAN_RegisterRxCallback(uintptr_t can_id, pios_com_callback rx_in_cb, uintptr_t context);
40 static void PIOS_CAN_RegisterTxCallback(uintptr_t can_id, pios_com_callback tx_out_cb, uintptr_t context);
41 static void PIOS_CAN_TxStart(uintptr_t can_id, uint16_t tx_bytes_avail);
42 static void PIOS_CAN_RxStart(uintptr_t can_id, uint16_t rx_bytes_avail);
43 
44 const struct pios_com_driver pios_can_com_driver = {
45  .tx_start = PIOS_CAN_TxStart,
46  .rx_start = PIOS_CAN_RxStart,
47  .bind_tx_cb = PIOS_CAN_RegisterTxCallback,
48  .bind_rx_cb = PIOS_CAN_RegisterRxCallback,
49 };
50 
51 enum pios_can_dev_magic {
52  PIOS_CAN_DEV_MAGIC = 0x41fa834A,
53 };
54 
56 struct pios_can_dev {
57  enum pios_can_dev_magic magic;
58  const struct pios_can_cfg *cfg;
59  pios_com_callback rx_in_cb;
60  uintptr_t rx_in_context;
61  pios_com_callback tx_out_cb;
62  uintptr_t tx_out_context;
63 };
64 
65 // Local constants
66 #define CAN_COM_ID 0x11
67 #define MAX_SEND_LEN 8
68 
69 void USB_HP_CAN1_TX_IRQHandler(void);
70 
71 static bool PIOS_CAN_validate(struct pios_can_dev *can_dev)
72 {
73  return (can_dev->magic == PIOS_CAN_DEV_MAGIC);
74 }
75 
76 static struct pios_can_dev *PIOS_CAN_alloc(void)
77 {
78  struct pios_can_dev *can_dev;
79 
80  can_dev = (struct pios_can_dev *)PIOS_malloc(sizeof(*can_dev));
81  if (!can_dev) return(NULL);
82 
83  memset(can_dev, 0, sizeof(*can_dev));
84  can_dev->magic = PIOS_CAN_DEV_MAGIC;
85 
86  return(can_dev);
87 }
88 
90 static struct pios_can_dev *can_dev;
91 
98 int32_t PIOS_CAN_Init(uintptr_t *can_id, const struct pios_can_cfg *cfg)
99 {
100  PIOS_DEBUG_Assert(can_id);
101  PIOS_DEBUG_Assert(cfg);
102 
103  can_dev = (struct pios_can_dev *) PIOS_CAN_alloc();
104  if (!can_dev) goto out_fail;
105 
106  /* Bind the configuration to the device instance */
107  can_dev->cfg = cfg;
108 
109  /* Configure the CAN device */
110  RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
111 
112  /* Map pins to CAN function */
113  if (can_dev->cfg->remap) {
114  if (can_dev->cfg->rx.gpio != 0)
115  GPIO_PinAFConfig(can_dev->cfg->rx.gpio,
116  can_dev->cfg->rx.pin_source,
117  can_dev->cfg->remap);
118  if (can_dev->cfg->tx.gpio != 0)
119  GPIO_PinAFConfig(can_dev->cfg->tx.gpio,
120  can_dev->cfg->tx.pin_source,
121  can_dev->cfg->remap);
122  }
123 
124  /* Initialize the CAN Rx and Tx pins */
125  if (can_dev->cfg->rx.gpio != 0)
126  GPIO_Init(can_dev->cfg->rx.gpio, (GPIO_InitTypeDef *)&can_dev->cfg->rx.init);
127  if (can_dev->cfg->tx.gpio != 0)
128  GPIO_Init(can_dev->cfg->tx.gpio, (GPIO_InitTypeDef *)&can_dev->cfg->tx.init);
129 
130  *can_id = (uintptr_t)can_dev;
131 
132  CAN_DeInit(can_dev->cfg->regs);
133  CAN_Init(can_dev->cfg->regs, (CAN_InitTypeDef *)&can_dev->cfg->init);
134 
135  /* CAN filter init */
136  CAN_FilterInitTypeDef CAN_FilterInitStructure;
137  CAN_FilterInitStructure.CAN_FilterNumber = 0;
138  CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
139  CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
140  CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
141  CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
142  CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
143  CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
144  CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 1;
145 
146  CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
147  CAN_FilterInit(&CAN_FilterInitStructure);
148 
149  // Enable the receiver IRQ
150  NVIC_Init((NVIC_InitTypeDef*) &can_dev->cfg->rx_irq.init);
151  NVIC_Init((NVIC_InitTypeDef*) &can_dev->cfg->tx_irq.init);
152 
153  return(0);
154 
155 out_fail:
156  return(-1);
157 }
158 
159 static void PIOS_CAN_RxStart(uintptr_t can_id, uint16_t rx_bytes_avail)
160 {
161  struct pios_can_dev *can_dev = (struct pios_can_dev *)can_id;
162 
163  bool valid = PIOS_CAN_validate(can_dev);
164  PIOS_Assert(valid);
165 
166  CAN_ITConfig(can_dev->cfg->regs, CAN_IT_FMP1, ENABLE);
167 }
168 
169 static void PIOS_CAN_TxStart(uintptr_t can_id, uint16_t tx_bytes_avail)
170 {
171  struct pios_can_dev *can_dev = (struct pios_can_dev *)can_id;
172 
173  bool valid = PIOS_CAN_validate(can_dev);
174  PIOS_Assert(valid);
175 
176  CAN_ITConfig(can_dev->cfg->regs, CAN_IT_TME, ENABLE);
177 
179 }
180 
181 static void PIOS_CAN_RegisterRxCallback(uintptr_t can_id, pios_com_callback rx_in_cb, uintptr_t context)
182 {
183  struct pios_can_dev *can_dev = (struct pios_can_dev *)can_id;
184 
185  bool valid = PIOS_CAN_validate(can_dev);
186  PIOS_Assert(valid);
187 
188  /*
189  * Order is important in these assignments since ISR uses _cb
190  * field to determine if it's ok to dereference _cb and _context
191  */
192  can_dev->rx_in_context = context;
193  can_dev->rx_in_cb = rx_in_cb;
194 }
195 
196 static void PIOS_CAN_RegisterTxCallback(uintptr_t can_id, pios_com_callback tx_out_cb, uintptr_t context)
197 {
198  struct pios_can_dev *can_dev = (struct pios_can_dev *)can_id;
199 
200  bool valid = PIOS_CAN_validate(can_dev);
201  PIOS_Assert(valid);
202 
203  /*
204  * Order is important in these assignments since ISR uses _cb
205  * field to determine if it's ok to dereference _cb and _context
206  */
207  can_dev->tx_out_context = context;
208  can_dev->tx_out_cb = tx_out_cb;
209 }
210 
212 static uint32_t pios_can_message_stdid[PIOS_CAN_LAST] = {
213  [PIOS_CAN_GIMBAL] = 0x130,
214 };
215 
217 static struct pios_queue *pios_can_queues[PIOS_CAN_LAST];
218 
223 static bool process_received_message(CanRxMsg message)
224 {
225  // Look for a known message that matches this CAN StdId
226  uint32_t msg_id;
227  for (msg_id = 0; msg_id < PIOS_CAN_LAST && pios_can_message_stdid[msg_id] != message.StdId; msg_id++);
228 
229  // If StdId is not one of the known messages, bail out
230  if (msg_id == PIOS_CAN_LAST)
231  return false;
232 
233  // Get the queue for this message and send the data
234  struct pios_queue *queue = pios_can_queues[msg_id];
235  if (queue == NULL)
236  return false;
237 
238  bool woken = false;
239  PIOS_Queue_Send_FromISR(queue, message.Data, &woken);
240 
241  return woken;
242 }
243 
250 struct pios_queue * PIOS_CAN_RegisterMessageQueue(uintptr_t id, enum pios_can_messages msg_id)
251 {
252  // Fetch the size of this message type or error if unknown
253  uint32_t bytes;
254  switch(msg_id) {
255  case PIOS_CAN_GIMBAL:
256  bytes = sizeof(struct pios_can_gimbal_message);
257  break;
258  default:
259  return NULL;
260  }
261 
262  // Return existing queue if created
263  if (pios_can_queues[msg_id] != NULL)
264  return pios_can_queues[msg_id];
265 
266  // Create a queue that can manage the data message size
267  struct pios_queue *queue;
268  queue = PIOS_Queue_Create(2, bytes);
269  if (queue == NULL)
270  return NULL;
271 
272  // Store the queue handle for the driver
273  pios_can_queues[msg_id] = queue;
274 
275  return queue;
276 }
277 
278 // Map the specific IRQ handlers to the device handle
279 
280 static void PIOS_CAN_RxGeneric(void);
281 static void PIOS_CAN_TxGeneric(void);
282 
283 void CAN1_RX1_IRQHandler(void)
284 {
286  PIOS_CAN_RxGeneric();
288 }
289 
290 void USB_HP_CAN1_TX_IRQHandler(void)
291 {
293  PIOS_CAN_TxGeneric();
295 }
296 
302 static void PIOS_CAN_RxGeneric(void)
303 {
304  CAN_ClearITPendingBit(can_dev->cfg->regs, CAN_IT_FMP1);
305 
306  bool valid = PIOS_CAN_validate(can_dev);
307  PIOS_Assert(valid);
308 
309  CanRxMsg RxMessage;
310  CAN_Receive(CAN1, CAN_FIFO1, &RxMessage);
311 
312  bool rx_need_yield = false;
313  if (RxMessage.StdId == CAN_COM_ID) {
314  if (can_dev->rx_in_cb) {
315  (void) (can_dev->rx_in_cb)(can_dev->rx_in_context, RxMessage.Data, RxMessage.DLC, NULL, &rx_need_yield);
316  }
317  } else {
318  rx_need_yield = process_received_message(RxMessage);
319  }
320 }
321 
325 static void PIOS_CAN_TxGeneric(void)
326 {
327  CAN_ClearITPendingBit(can_dev->cfg->regs, CAN_IT_TME);
328 
329  bool valid = PIOS_CAN_validate(can_dev);
330  PIOS_Assert(valid);
331 
332  bool tx_need_yield = false;
333 
334  if (can_dev->tx_out_cb) {
335 
336  // Prepare CAN message structure
337  CanTxMsg msg;
338  msg.StdId = CAN_COM_ID;
339  msg.ExtId = 0;
340  msg.IDE = CAN_ID_STD;
341  msg.RTR = CAN_RTR_DATA;
342  msg.DLC = (can_dev->tx_out_cb)(can_dev->tx_out_context, msg.Data, MAX_SEND_LEN, NULL, &tx_need_yield);
343 
344  // Send message and get mailbox number
345  if (msg.DLC > 0) {
346  CAN_Transmit(can_dev->cfg->regs, &msg);
347  } else {
348  CAN_ITConfig(can_dev->cfg->regs, CAN_IT_TME, DISABLE);
349  }
350 
351  // TODO: deal with failure to send and keep the message to retransmit
352  }
353 }
354 
355 
363 int32_t PIOS_CAN_TxData(uintptr_t id, enum pios_can_messages msg_id, uint8_t *data)
364 {
365  // Fetch the size of this message type or error if unknown
366  uint32_t bytes;
367  switch(msg_id) {
368  case PIOS_CAN_GIMBAL:
369  bytes = sizeof(struct pios_can_gimbal_message);
370  break;
371  default:
372  return -1;
373  }
374 
375  // Look up the CAN BUS Standard ID for this message type
376  uint32_t std_id = pios_can_message_stdid[msg_id];
377 
378  // Format and send the message
379  CanTxMsg msg;
380  msg.StdId = std_id & 0x7FF;
381  msg.ExtId = 0;
382  msg.IDE = CAN_ID_STD;
383  msg.RTR = CAN_RTR_DATA;
384  msg.DLC = (bytes > 8) ? 8 : bytes;
385  memcpy(msg.Data, data, msg.DLC);
386  CAN_Transmit(can_dev->cfg->regs, &msg);
387 
388  return msg.DLC;
389 }
390 
391 
392 #endif /* PIOS_INCLUDE_CAN */
393 
struct pios_queue * PIOS_Queue_Create(size_t queue_length, size_t item_size)
Definition: pios_queue.c:47
Main PiOS header to include all the compiled in PiOS options.
bool PIOS_Queue_Send_FromISR(struct pios_queue *queuep, const void *itemp, bool *wokenp)
Definition: pios_queue.c:163
#define PIOS_DEBUG_Assert(test)
Definition: pios_debug.h:51
#define PIOS_IRQ_Epilogue()
Definition: pios_irq.h:46
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
PiOS CAN interface header.
pios_can_messages
The set of CAN messages.
Definition: pios_can.h:36
uint8_t bytes[2]
Definition: storm32bgc.c:156
uint8_t data[XFER_BYTES_PER_PACKET]
Definition: bl_messages.h:129
static struct flyingpicmd_cfg_fa cfg
Definition: main.c:49
#define CAN1_RX1_IRQHandler
#define USB_HP_CAN1_TX_IRQHandler
int32_t PIOS_CAN_TxData(uintptr_t id, enum pios_can_messages, uint8_t *data)
Transmit a data message with a particular message ID.
int32_t PIOS_CAN_Init(uintptr_t *id, const struct pios_can_cfg *cfg)
struct pios_queue * PIOS_CAN_RegisterMessageQueue(uintptr_t id, enum pios_can_messages msg_id)
Get a queue to receive messages of a particular message ID.
const struct pios_com_driver pios_can_com_driver
uint32_t magic
Message to tell gimbal the desired setpoint and FC state.
Definition: pios_can.h:42
#define PIOS_IRQ_Prologue()
Definition: pios_irq.h:45
static struct pios_queue * queue
Definition: actuator.c:82
#define PIOS_Assert(test)
Definition: pios_debug.h:52
void(* tx_start)(uintptr_t id, uint16_t tx_bytes_avail)
Definition: pios_com.h:45
uint16_t(* pios_com_callback)(uintptr_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *task_woken)
Definition: pios_com.h:41