source: rtems/bsps/arm/lpc176x/can/can.c @ c77cd426

5
Last change on this file since c77cd426 was c77cd426, checked in by Joel Sherrill <joel@…>, on 04/30/18 at 22:18:49

Drop executable permissions on .[ch] files

  • Property mode set to 100644
File size: 14.6 KB
RevLine 
[7b35a36]1/**
2 * @file can.c
3 *
4 * @ingroup lpc176x
5 *
6 * @brief CAN controller for the mbed lpc1768 board.
7 */
8
9/*
10 * Copyright (c) 2014 Taller Technologies.
11 *
12 * @author  Diaz Marcos (marcos.diaz@tallertechnologies.com)
13 * @author  Daniel Chicco  (daniel.chicco@tallertechnologies.com)
14 *
15 * The license and distribution terms for this file may be
16 * found in the file LICENSE in this distribution or at
[d4edbdbc]17 * http://www.rtems.org/license/LICENSE.
[7b35a36]18 */
19
20#include <rtems/status-checks.h>
21#include <bsp/irq.h>
22#include <bsp/can.h>
23#include <bsp/can-defs.h>
24#include <bsp/mbed-pinmap.h>
[b2ed712]25#include <string.h>
[7b35a36]26
27/**
28 * @brief The standard isr to be installed for all the can devices.
29 *
30 * @param arg unused.
31 */
32static void can_isr( void *arg );
33
34/**
35 * @brief Vector of isr for the can_driver .
36 */
37lpc176x_can_isr_vector isr_vector;
38
39/**
40 * @brief Represents all the can devices, and useful things for initialization.
41 */
42static const can_driver_entry can_driver_table[ CAN_DEVICES_NUMBER ] =
43{
44  {
45    .device = (can_device *) CAN1_BASE_ADDR,
46    .module = LPC176X_MODULE_CAN_0,
47    .pconp_pin = LPC176X_SCB_PCONP_CAN_1,
48    .pins = { DIP9, DIP10 },
49    .pinfunction = LPC176X_PIN_FUNCTION_01
50  },
51  {
52    .device = (can_device *) CAN2_BASE_ADDR,
53    .module = LPC176X_MODULE_CAN_1,
54    .pconp_pin = LPC176X_SCB_PCONP_CAN_2,
55    .pins = { DIP30, DIP29 },
56    .pinfunction = LPC176X_PIN_FUNCTION_10
57  }
58};
59
60/**
61 * @brief The CAN acceptance filter.
62 */
63can_acceptance_filter *const acceptance_filter_device =
64  (can_acceptance_filter *) CAN_ACCEPT_BASE_ADDR;
65
66/**
67 * @brief Sets RX and TX pins for the passed can device number.
68 *
69 * @param cannumber CAN controller to be used.
70 */
71static inline void setpins( const lpc176x_can_number cannumber )
72{
73  const can_driver_entry *const can_driver = &can_driver_table[ cannumber ];
74
75  lpc176x_pin_select( can_driver->pins[ CAN_TX_PIN ],
76    can_driver->pinfunction );
77  lpc176x_pin_select( can_driver->pins[ CAN_RX_PIN ],
78    can_driver->pinfunction );
79}
80
81rtems_status_code can_close( const lpc176x_can_number minor )
82{
83  rtems_status_code sc = RTEMS_INVALID_NUMBER;
84
85  if ( CAN_DRIVER_IS_MINOR_VALID( minor ) ) {
86    sc = RTEMS_SUCCESSFUL;
87    const can_driver_entry *const can_driver = &can_driver_table[ minor ];
88    lpc176x_module_disable( can_driver->module );
89  }
90
91  /*else wrong parameters. return RTEMS_INVALID_NUMBER*/
92
93  return sc;
94}
95
96/**
97 * @brief Enables CAN device.
98 *
99 * @param obj The device to be enabled.
100 */
101static inline void can_enable( const can_driver_entry *const obj )
102{
103  if ( obj->device->MOD & CAN_MOD_RM ) {
104    obj->device->MOD &= ~( CAN_MOD_RM );
105  }
106}
107
108/**
109 * @brief Disables CAN device to set parameters, and returns the previous value
110 * of the MOD register.
111 *
112 * @param obj The device to disable.
113 * @return The previous status of MOD register.
114 */
115static inline uint32_t can_disable( const can_driver_entry *const obj )
116{
117  const uint32_t sm = obj->device->MOD;
118
119  obj->device->MOD |= CAN_MOD_RM;
120
121  return sm;
122}
123
124/**
125 * @brief Resets the error count.
126 *
127 * @param obj which device reset.
128 */
129static inline void can_reset( const can_driver_entry *const obj )
130{
131  can_disable( obj );
132  obj->device->GSR = 0;   /* Reset error counter when CANxMOD is in reset*/
133}
134
135/**
136 * @brief This table has the sampling points as close to 75% as possible.
137 * The first value is TSEG1, the second is TSEG2.
138 */
139static const unsigned int timing_pts[ MAX_TSEG1_TSEG2_BITS +
140                                      1 ][ CAN_NUMBER_OF_TSEG ] = {
141  { 0x0, 0x0 },      /* 2,  50%*/
142  { 0x1, 0x0 },      /* 3,  67%*/
143  { 0x2, 0x0 },      /* 4,  75%*/
144  { 0x3, 0x0 },      /* 5,  80%*/
145  { 0x3, 0x1 },      /* 6,  67%*/
146  { 0x4, 0x1 },      /* 7,  71%*/
147  { 0x5, 0x1 },      /* 8,  75%*/
148  { 0x6, 0x1 },      /* 9,  78%*/
149  { 0x6, 0x2 },      /* 10, 70%*/
150  { 0x7, 0x2 },      /* 11, 73%*/
151  { 0x8, 0x2 },      /* 12, 75%*/
152  { 0x9, 0x2 },      /* 13, 77%*/
153  { 0x9, 0x3 },      /* 14, 71%*/
154  { 0xA, 0x3 },      /* 15, 73%*/
155  { 0xB, 0x3 },      /* 16, 75%*/
156  { 0xC, 0x3 },      /* 17, 76%*/
157  { 0xD, 0x3 },      /* 18, 78%*/
158  { 0xD, 0x4 },      /* 19, 74%*/
159  { 0xE, 0x4 },      /* 20, 75%*/
160  { 0xF, 0x4 },      /* 21, 76%*/
161  { 0xF, 0x5 },      /* 22, 73%*/
162  { 0xF, 0x6 },      /* 23, 70%*/
163  { 0xF, 0x7 },      /* 24, 67%*/
164};
165
166/**
167 * @brief Checks if divisor is a divisor of value.
168 *
169 * @param value The number to be divided.
170 * @param divisor The divisor to check.
171 *
172 * @return true if "number" is divided by "divisor"; false otherwise.
173 */
174static inline bool is_divisor(
175  const uint32_t value,
176  const uint16_t divisor
177)
178{
179  return ( ( value % divisor ) == 0 );
180}
181
182/**
183 * @brief Gets the size of the two tseg values added according to the given
184 * bitwidth and brp (The CAN prescaler).
185 *
186 * @param bitwidth The total bitwidth of a CAN bit (in pclk clocks).
187 * @param brp The CAN clock prescaler.
188 *
189 * @return The value of tseg1 + tseg2  of the CAN bit. It is useful
190 *  to serve for index for timing_pts array).
191 */
192static inline uint32_t get_tseg_bit_size(
193  const uint32_t bitwidth,
194  const uint16_t brp
195)
196{
197  return ( ( bitwidth / ( brp + CAN_BRP_EXTRA_BIT ) ) - CAN_TSEG_EXTRA_BITS );
198}
199
200/**
201 * @brief Gets the brp and tsegbitsize in order to achieve the desired bitwidth.
202 * @details The following must be fullfilled:
203 *(brp + CAN_BRP_EXTRA_BIT) * (tsegbitsize + CAN_TSEG_EXTRA_BITS) == bitwidth
204 *
205 * @param bitwidth The bitwidth that we need to achieve.
206 * @param brp Here it returns the calculated brp value.
207 * @param tsegbitsize Here it returns the calculated tseg bit size value.
208 * @return true if brp and tsegbitsize have been calculated.
209 */
210static inline bool get_brp_and_bitsize(
211  const uint32_t  bitwidth,
212  uint16_t *const brp,
213  uint32_t *const tsegbitsize
214)
215{
216  bool hit = false;
217
218  while ( ( !hit ) && ( *brp < bitwidth / MIN_NUMBER_OF_CAN_BITS ) ) {
219    if ( ( is_divisor( bitwidth, *brp + CAN_BRP_EXTRA_BIT ) )
220         && ( get_tseg_bit_size( bitwidth, *brp ) < MAX_TSEG1_TSEG2_BITS ) ) {
221      hit = true;
222      *tsegbitsize = get_tseg_bit_size( bitwidth, *brp );
223    } else { /*Correct values not found, keep looking*/
224      ( *brp )++;
225    }
226  }
227
228  return hit;
229}
230
231/**
232 * @brief Constructs the btr register with the passed arguments.
233 *
234 * @param tsegbitsize The size tseg bits to set.
235 * @param psjw The sjw to set.
236 * @param brp The prescaler value to set.
237 * @return The constructed btr register.
238 */
239static inline uint32_t get_btr(
240  const uint32_t      tsegbitsize,
241  const unsigned char psjw,
242  const uint32_t      brp
243)
244{
245  const uint32_t tseg2_value_masked =
246    ( ( timing_pts[ tsegbitsize ][ CAN_TSEG2 ] << CAN_BTR_TSEG2_SHIFT ) &
247      CAN_BTR_TSEG2_MASK );
248  const uint32_t tseg1_value_masked =
249    ( ( timing_pts[ tsegbitsize ][ CAN_TSEG1 ] <<
250        CAN_BTR_TSEG1_SHIFT ) & CAN_BTR_TSEG1_MASK );
251  const uint32_t psjw_value_masked =
252    ( ( psjw << CAN_BTR_SJW_SHIFT ) & CAN_BTR_SJW_MASK );
253  const uint32_t brp_value_masked =
254    ( ( brp << CAN_BTR_BRP_SHIFT ) & CAN_BTR_BRP_MASK );
255
256  return tseg1_value_masked | tseg2_value_masked |
257         psjw_value_masked | brp_value_masked;
258}
259
260/**
261 * @brief Calculates and returns a bit timing register (btr) for the desired
262 * canclk frequency using the passed psjw, system clock and peripheral clock.
263 *
264 * @param systemclk The clock of the system (in Hz).
265 * @param pclkdiv The peripheral clock divisor for the can device.
266 * @param canclk The desired frequency for CAN (in Hz).
267 * @param psjw The desired psjw.
268 * @return The btr register value if found, WRONG_BTR_VALUE otherwise.
269 */
270static inline unsigned int can_speed(
271  const unsigned int  systemclk,
272  const unsigned int  pclkdiv,
273  const unsigned int  canclk,
274  const unsigned char psjw
275)
276{
277  uint32_t       btr = WRONG_BTR_VALUE;
278  const uint32_t bitwidth = systemclk / ( pclkdiv * canclk );
279
280  /* This is for the brp (prescaler) to start searching a reachable multiple.*/
281  uint16_t brp = bitwidth / MAX_NUMBER_OF_CAN_BITS;
282  uint32_t tsegbitsize;
283
284  if ( get_brp_and_bitsize( bitwidth, &brp, &tsegbitsize ) ) {
285    btr = get_btr( tsegbitsize, psjw, brp );
286  }
287
288  return btr;
289}
290
291/**
292 * @brief Configures the desired CAN device with the desired frequency.
293 *
294 * @param obj The can device to configure.
295 * @param f The desired frequency.
296 *
297 * @return RTEMS_SUCCESSFUL if could be set, RTEMS_INVALID_NUMBER otherwise.
298 */
299static rtems_status_code can_frequency(
300  const can_driver_entry *const obj,
301  const can_freq            freq
302)
303{
304  rtems_status_code sc = RTEMS_INVALID_NUMBER;
305  const uint32_t    btr = can_speed( LPC176X_CCLK, LPC176X_PCLKDIV, freq, 1 );
306
307  if ( btr != WRONG_BTR_VALUE ) {
308    sc = RTEMS_SUCCESSFUL;
309    uint32_t modmask = can_disable( obj );
310    obj->device->BTR = btr;
311    obj->device->MOD = modmask;
312  } /*else couldnt found a good timing for the desired frequency,
313      return RTEMS_INVALID_NUMBER.*/
314
315  return sc;
316}
317
318/**
319 * @brief Installs the interrupt handler in rtems.
320 */
321static inline rtems_status_code can_initialize( void )
322{
323  return rtems_interrupt_handler_install(
324    LPC176X_IRQ_CAN,
325    "can_interrupt",
326    RTEMS_INTERRUPT_UNIQUE,
327    can_isr,
328    NULL
329  );
330}
331
332rtems_status_code can_open( const lpc176x_can_number minor, can_freq freq )
333{
334  const can_driver_entry *const can_driver = &can_driver_table[ minor ];
335  rtems_status_code             sc = RTEMS_INVALID_NUMBER;
336
337  if ( CAN_DRIVER_IS_MINOR_VALID( minor ) ) {
338    /*Enable CAN and acceptance filter modules.*/
339    sc =
340      lpc176x_module_enable( can_driver->module, LPC176X_MODULE_PCLK_DEFAULT );
341    RTEMS_CHECK_SC( sc, "enable can module" );
342    sc = lpc176x_module_enable( LPC176X_MODULE_ACCF,
343      LPC176X_MODULE_PCLK_DEFAULT );
344    RTEMS_CHECK_SC( sc, "enable acceptance filter" );
345    /*Set pin functions.*/
346    setpins( minor );
347
348    can_reset( can_driver );
349    can_driver->device->IER = CAN_DEFAULT_INTERRUPT_CONFIGURATION;
350    sc = can_frequency( can_driver, freq );
351    RTEMS_CHECK_SC( sc, "Configure CAN frequency" );
352    can_initialize();
353
354    acceptance_filter_device->AFMR = CAN_ACCF_AFMR_ACCBP;     /*Bypass Filter.*/
355  }
356
357  return sc;
358}
359
360/**
361 * @brief Calls the installed isrs, according to the active interrupts.
362 *
363 * @param vector The read vector of active interrupts.
364 * @param number The CAN device to look for interruptions.
365 */
366static inline void call_isrs(
367  const uint32_t           vector,
368  const lpc176x_can_number number
369)
370{
371  can_irq_type i;
372
373  for ( i = IRQ_RX; i < CAN_IRQ_NUMBER; ++i ) {
374    if ( ( isr_vector[ i ] != NULL ) && ( vector & ( 1 << i ) ) )
375      isr_vector[ i ]( number );
376
377    /* else this interrupt has not been raised or it hasn't got a handler,
378       so do nothing.*/
379  }
380}
381
382/**
383 * @brief Checks if the passed CAN device is enabled and if it is checks for
384 * active interrupts and calls its installed isr.
385 *
386 * @param number The CAN device to check for interrupts rised.
387 */
388static inline void search_and_call_int( const lpc176x_can_number number )
389{
390  const can_driver_entry *const driver = &can_driver_table[ number ];
391
392  if ( LPC176X_SCB.pconp & driver->pconp_pin ) {
393    /*We must read the whole register at once because it resets when read.*/
394    const uint32_t int_vector = driver->device->ICR & CAN_INTERRUPT_TYPE_MASK;
395    call_isrs( int_vector, number );
396  }
397
398  /*else the device is shut down so we must do nothing.*/
399}
400
401/**
402 * @brief The standard isr to be installed for all the CAN devices.
403 *
404 * @param arg unused.
405 */
406static void can_isr( void *arg )
407{
408  lpc176x_can_number i;
409
410  for ( i = CAN_0; i < CAN_DEVICES_NUMBER; ++i ) {
411    search_and_call_int( i );
412  }
413}
414
415rtems_status_code can_read(
416  const lpc176x_can_number minor,
417  can_message             *message
418)
419{
420  rtems_status_code             sc = RTEMS_IO_ERROR;
421  const can_driver_entry *const can_driver = &can_driver_table[ minor ];
422  can_device *const             dev = can_driver->device;
423  registers_can_message *const  msg = &( message->registers );
424
425  can_enable( can_driver );
426
427  if ( dev->GSR & CAN_GSR_RBS_MASK ) {
428    sc = RTEMS_SUCCESSFUL;
429    *msg = dev->receive;
430    dev->CMR = CAN_CMR_RRB_MASK;      /* release receive buffer. */
431  } /* else Message not received.*/
432
433  return sc;
434}
435
436/**
437 * @brief Array of masks and control bits for the transmit buffers.
438 * It's used for each transmit buffer in order to see if it's available and to
439 * send data to them.
440 */
441static const can_transmit_info transmit_info[ CAN_NUMBER_OF_TRANSMIT_BUFFERS ]
442  =
443  {
444  {
445    .can_status_mask = 0x00000004U,
446    .not_cc_cmr_value = 0x21U
447  },
448  {
449    .can_status_mask = 0x00000400U,
450    .not_cc_cmr_value = 0x41U
451  },
452  {
453    .can_status_mask = 0x00040000U,
454    .not_cc_cmr_value = 0x81U
455  }
456  };
457
458rtems_status_code can_write(
459  const lpc176x_can_number minor,
460  const can_message *const message
461)
462{
463  const can_driver_entry *const can_driver = &can_driver_table[ minor ];
464  can_device *const             obj = can_driver->device;
465  const uint32_t                CANStatus = obj->SR;
466
467  const registers_can_message *const msg = &( message->registers );
468  rtems_status_code                  sc = RTEMS_IO_ERROR;
469  can_transmit_number                transmit_buffer;
470
471  can_enable( can_driver );
472
473  for ( transmit_buffer = CAN_TRANSMIT1;
474        ( sc != RTEMS_SUCCESSFUL ) && ( transmit_buffer <
475                                        CAN_NUMBER_OF_TRANSMIT_BUFFERS );
476        ++transmit_buffer ) {
477    if ( CANStatus & transmit_info[ transmit_buffer ].can_status_mask ) {
478      sc = RTEMS_SUCCESSFUL;
479      obj->transmit[ transmit_buffer ] = *msg;
480      obj->CMR = transmit_info[ transmit_buffer ].not_cc_cmr_value;
481    }    /*else can buffer busy, try with the next.*/
482  }
483
484  return sc;
485}
486
487/**
488 * @brief Enables the interrupt type passed to the desired CAN device.
489 *
490 * @param number The CAN device to enable the interrupts.
491 * @param type The type of interrupt to enable.
492 */
493static inline void can_enable_interrupt(
494  const lpc176x_can_number number,
495  const can_irq_type       type
496)
497{
498  const can_driver_entry *const driver = &can_driver_table[ number ];
499  const uint32_t                ier = 1 << type;
500
501  can_disable( driver );
502  driver->device->IER |= ier;
503  can_enable( driver );
504}
505
506rtems_status_code can_register_isr(
507  const lpc176x_can_number number,
508  const can_irq_type       type,
509  const lpc176x_can_isr    isr
510)
511{
512  rtems_status_code sc = RTEMS_INVALID_NUMBER;
513
514  if ( ( 0 <= type ) && ( type < CAN_IRQ_NUMBER ) ) {
515    sc = RTEMS_SUCCESSFUL;
516    isr_vector[ type ] = isr;
517    can_enable_interrupt( number, type );
518  }
519
520  return sc;
521}
522
523rtems_status_code create_can_message(
524  can_message *const msg,
525  const int          _id,
526  const char *const  _data,
527  const char         _len
528)
529{
530  rtems_status_code sc = RTEMS_INVALID_NUMBER;
531
532  if ( ( _len <= CAN_MAXIMUM_DATA_SIZE ) && ( _id <= CAN10_MAXIMUM_ID ) ) {
533    sc = RTEMS_SUCCESSFUL;
534    msg->low_level.dlc = _len;
535    msg->low_level.type = CANStandard;
536    msg->low_level.rtr = CANData;
537    msg->low_level.id = _id;
538    memcpy( msg->low_level.data, _data, _len );
539  }
540
541  return sc;
542}
543
Note: See TracBrowser for help on using the repository browser.