source: rtems/c/src/lib/libbsp/arm/lpc176x/can/can.c @ 1c0663b4

4.115
Last change on this file since 1c0663b4 was d4edbdbc, checked in by Sebastian Huber <sebastian.huber@…>, on 03/20/15 at 13:09:26

Replace www.rtems.com with www.rtems.org

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