diff options
Diffstat (limited to 'os')
| -rw-r--r-- | os/hal/platforms/STM32/can_lld.c | 402 | ||||
| -rw-r--r-- | os/hal/platforms/STM32/can_lld.h | 112 | 
2 files changed, 367 insertions, 147 deletions
| diff --git a/os/hal/platforms/STM32/can_lld.c b/os/hal/platforms/STM32/can_lld.c index 0b4a918db..75de38353 100644 --- a/os/hal/platforms/STM32/can_lld.c +++ b/os/hal/platforms/STM32/can_lld.c @@ -39,11 +39,16 @@  /* Driver exported variables.                                                */
  /*===========================================================================*/
 -/** @brief ADC1 driver identifier.*/
 +/** @brief CAN1 driver identifier.*/
  #if STM32_CAN_USE_CAN1 || defined(__DOXYGEN__)
  CANDriver CAND1;
  #endif
 +/** @brief CAN2 driver identifier.*/
 +#if STM32_CAN_USE_CAN2 || defined(__DOXYGEN__)
 +CANDriver CAND2;
 +#endif
 +
  /*===========================================================================*/
  /* Driver local variables.                                                   */
  /*===========================================================================*/
 @@ -52,101 +57,159 @@ CANDriver CAND1;  /* Driver local functions.                                                   */
  /*===========================================================================*/
 -/*===========================================================================*/
 -/* Driver interrupt handlers.                                                */
 -/*===========================================================================*/
 -
  /**
 - * @brief   CAN1 TX interrupt handler.
 + * @brief   Programs the filters.
   *
 - * @isr
 + * @param[in] can2sb    number of the first filter assigned to CAN2
 + * @param[in] num       number of entries in the filters array, if zero then
 + *                      a default filter is programmed
 + * @param[in] cfp       pointer to the filters array, can be @p NULL if
 + *                      (num == 0)
 + *
 + * @notapi
   */
 -CH_IRQ_HANDLER(STM32_CAN1_TX_HANDLER) {
 +static void can_lld_set_filters(uint32_t can2sb,
 +                         uint32_t num,
 +                         const CANFilter *cfp) {
 -  CH_IRQ_PROLOGUE();
 +  /* Temporarily enabling CAN1 clock.*/
 +  rccEnableCAN1(FALSE);
 +
 +  /* Filters initialization.*/
 +  CAN1->FMR = (CAN1->FMR & 0xFFFF0000) | (can2sb << 8) | CAN_FMR_FINIT;
 +  if (num > 0) {
 +    uint32_t i, fmask;
 +
 +    /* All filters cleared.*/
 +    CAN1->FA1R = 0;
 +    CAN1->FM1R = 0;
 +    CAN1->FS1R = 0;
 +    CAN1->FFA1R = 0;
 +    for (i = 0; i < STM32_CAN_MAX_FILTERS; i++) {
 +      CAN1->sFilterRegister[i].FR1 = 0;
 +      CAN1->sFilterRegister[i].FR2 = 0;
 +    }
 +
 +    /* Scanning the filters array.*/
 +    for (i = 0; i < num; i++) {
 +      fmask = 1 << cfp->filter;
 +      if (cfp->mode)
 +        CAN1->FM1R |= fmask;
 +      if (cfp->scale)
 +        CAN1->FS1R |= fmask;
 +      if (cfp->assignment)
 +        CAN1->FFA1R |= fmask;
 +      CAN1->sFilterRegister[cfp->filter].FR1 = cfp->register1;
 +      CAN1->sFilterRegister[cfp->filter].FR2 = cfp->register2;
 +      CAN1->FA1R |= fmask;
 +      cfp++;
 +    }
 +  }
 +  else {
 +    /* Setting up a single default filter that enables everything for both
 +       CANs.*/
 +    CAN1->sFilterRegister[0].FR1 = 0;
 +    CAN1->sFilterRegister[0].FR2 = 0;
 +    CAN1->sFilterRegister[can2sb].FR1 = 0;
 +    CAN1->sFilterRegister[can2sb].FR2 = 0;
 +    CAN1->FM1R = 0;
 +    CAN1->FFA1R = 0;
 +    CAN1->FS1R = 1 | (1 << can2sb);
 +    CAN1->FA1R = 1 | (1 << can2sb);
 +  }
 +  CAN1->FMR &= ~CAN_FMR_FINIT;
 +
 +  /* Clock disabled, it will be enabled again in can_lld_start().*/
 +  rccDisableCAN1(FALSE);
 +}
 +
 +/**
 + * @brief   Common TX ISR handler.
 + *
 + * @param[in] canp      pointer to the @p CANDriver object
 + *
 + * @notapi
 + */
 +static void can_lld_tx_handler(CANDriver *canp) {
    /* No more events until a message is transmitted.*/
 -  CAN1->TSR = CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2;
 +  canp->can->TSR = CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2;
    chSysLockFromIsr();
 -  while (chSemGetCounterI(&CAND1.txsem) < 0)
 -    chSemSignalI(&CAND1.txsem);
 -  chEvtBroadcastI(&CAND1.txempty_event);
 +  while (chSemGetCounterI(&canp->txsem) < 0)
 +    chSemSignalI(&canp->txsem);
 +  chEvtBroadcastI(&canp->txempty_event);
    chSysUnlockFromIsr();
 -
 -  CH_IRQ_EPILOGUE();
  }
 -/*
 - * @brief   CAN1 RX0 interrupt handler.
 +/**
 + * @brief   Common RX0 ISR handler.
   *
 - * @isr
 + * @param[in] canp      pointer to the @p CANDriver object
 + *
 + * @notapi
   */
 -CH_IRQ_HANDLER(STM32_CAN1_RX0_HANDLER) {
 +static void can_lld_rx0_handler(CANDriver *canp) {
    uint32_t rf0r;
 -  CH_IRQ_PROLOGUE();
 -
 -  rf0r = CAN1->RF0R;
 +  rf0r = canp->can->RF0R;
    if ((rf0r & CAN_RF0R_FMP0) > 0) {
      /* No more receive events until the queue 0 has been emptied.*/
 -    CAN1->IER &= ~CAN_IER_FMPIE0;
 +    canp->can->IER &= ~CAN_IER_FMPIE0;
      chSysLockFromIsr();
 -    while (chSemGetCounterI(&CAND1.rxsem) < 0)
 -      chSemSignalI(&CAND1.rxsem);
 -    chEvtBroadcastI(&CAND1.rxfull_event);
 +    while (chSemGetCounterI(&canp->rxsem) < 0)
 +      chSemSignalI(&canp->rxsem);
 +    chEvtBroadcastI(&canp->rxfull_event);
      chSysUnlockFromIsr();
    }
    if ((rf0r & CAN_RF0R_FOVR0) > 0) {
      /* Overflow events handling.*/
 -    CAN1->RF0R = CAN_RF0R_FOVR0;
 +    canp->can->RF0R = CAN_RF0R_FOVR0;
      chSysLockFromIsr();
 -    chEvtBroadcastFlagsI(&CAND1.error_event, CAN_OVERFLOW_ERROR);
 +    chEvtBroadcastFlagsI(&canp->error_event, CAN_OVERFLOW_ERROR);
      chSysUnlockFromIsr();
    }
 -
 -  CH_IRQ_EPILOGUE();
  }
  /**
 - * @brief   CAN1 RX1 interrupt handler.
 + * @brief   Common RX1 ISR handler.
 + * @note    Not used, must not be invoked, defaulted to an halt.
   *
 - * @isr
 + * @param[in] canp      pointer to the @p CANDriver object
 + *
 + * @notapi
   */
 -CH_IRQ_HANDLER(STM32_CAN1_RX1_HANDLER) {
 -
 -  CH_IRQ_PROLOGUE();
 -
 -  chSysHalt(); /* Not supported (yet).*/
 +static void can_lld_rx1_handler(CANDriver *canp) {
 -  CH_IRQ_EPILOGUE();
 +  (void)canp;
 +  chSysHalt();
  }
  /**
 - * @brief   CAN1 SCE interrupt handler.
 + * @brief   Common SCE ISR handler.
   *
 - * @isr
 + * @param[in] canp      pointer to the @p CANDriver object
 + *
 + * @notapi
   */
 -CH_IRQ_HANDLER(STM32_CAN1_SCE_HANDLER) {
 +static void can_lld_sce_handler(CANDriver *canp) {
    uint32_t msr;
 -  CH_IRQ_PROLOGUE();
 -
 -  msr = CAN1->MSR;
 -  CAN1->MSR = CAN_MSR_ERRI | CAN_MSR_WKUI | CAN_MSR_SLAKI;
 +  msr = canp->can->MSR;
 +  canp->can->MSR = CAN_MSR_ERRI | CAN_MSR_WKUI | CAN_MSR_SLAKI;
    /* Wakeup event.*/
    if (msr & CAN_MSR_WKUI) {
 -    CAND1.state = CAN_READY;
 -    CAND1.can->MCR &= ~CAN_MCR_SLEEP;
 +    canp->state = CAN_READY;
 +    canp->can->MCR &= ~CAN_MCR_SLEEP;
      chSysLockFromIsr();
 -    chEvtBroadcastI(&CAND1.wakeup_event);
 +    chEvtBroadcastI(&canp->wakeup_event);
      chSysUnlockFromIsr();
    }
    /* Error event.*/
    if (msr & CAN_MSR_ERRI) {
      flagsmask_t flags;
 -    uint32_t esr = CAN1->ESR;
 +    uint32_t esr = canp->can->ESR;
 -    CAN1->ESR &= ~CAN_ESR_LEC;
 +    canp->can->ESR &= ~CAN_ESR_LEC;
      flags = (flagsmask_t)(esr & 7);
      if ((esr & CAN_ESR_LEC) > 0)
        flags |= CAN_FRAMING_ERROR;
 @@ -154,13 +217,131 @@ CH_IRQ_HANDLER(STM32_CAN1_SCE_HANDLER) {      chSysLockFromIsr();
      /* The content of the ESR register is copied unchanged in the upper
         half word of the listener flags mask.*/
 -    chEvtBroadcastFlagsI(&CAND1.error_event, flags | (flagsmask_t)(esr < 16));
 +    chEvtBroadcastFlagsI(&canp->error_event, flags | (flagsmask_t)(esr < 16));
      chSysUnlockFromIsr();
    }
 +}
 +
 +/*===========================================================================*/
 +/* Driver interrupt handlers.                                                */
 +/*===========================================================================*/
 +
 +#if STM32_CAN_USE_CAN1 || defined(__DOXYGEN__)
 +/**
 + * @brief   CAN1 TX interrupt handler.
 + *
 + * @isr
 + */
 +CH_IRQ_HANDLER(STM32_CAN1_TX_HANDLER) {
 +
 +  CH_IRQ_PROLOGUE();
 +
 +  can_lld_tx_handler(&CAND1);
 +
 +  CH_IRQ_EPILOGUE();
 +}
 +
 +/*
 + * @brief   CAN1 RX0 interrupt handler.
 + *
 + * @isr
 + */
 +CH_IRQ_HANDLER(STM32_CAN1_RX0_HANDLER) {
 +
 +  CH_IRQ_PROLOGUE();
 +
 +  can_lld_rx0_handler(&CAND1);
    CH_IRQ_EPILOGUE();
  }
 +/**
 + * @brief   CAN1 RX1 interrupt handler.
 + *
 + * @isr
 + */
 +CH_IRQ_HANDLER(STM32_CAN1_RX1_HANDLER) {
 +
 +  CH_IRQ_PROLOGUE();
 +
 +  can_lld_rx1_handler(&CAND1);
 +
 +  CH_IRQ_EPILOGUE();
 +}
 +
 +/**
 + * @brief   CAN1 SCE interrupt handler.
 + *
 + * @isr
 + */
 +CH_IRQ_HANDLER(STM32_CAN1_SCE_HANDLER) {
 +
 +  CH_IRQ_PROLOGUE();
 +
 +  can_lld_sce_handler(&CAND1);
 +
 +  CH_IRQ_EPILOGUE();
 +}
 +#endif /* STM32_CAN_USE_CAN1 */
 +
 +#if STM32_CAN_USE_CAN2 || defined(__DOXYGEN__)
 +/**
 + * @brief   CAN2 TX interrupt handler.
 + *
 + * @isr
 + */
 +CH_IRQ_HANDLER(STM32_CAN2_TX_HANDLER) {
 +
 +  CH_IRQ_PROLOGUE();
 +
 +  can_lld_tx_handler(&CAND2);
 +
 +  CH_IRQ_EPILOGUE();
 +}
 +
 +/*
 + * @brief   CAN2 RX0 interrupt handler.
 + *
 + * @isr
 + */
 +CH_IRQ_HANDLER(STM32_CAN2_RX0_HANDLER) {
 +
 +  CH_IRQ_PROLOGUE();
 +
 +  can_lld_rx0_handler(&CAND2);
 +
 +  CH_IRQ_EPILOGUE();
 +}
 +
 +/**
 + * @brief   CAN2 RX1 interrupt handler.
 + *
 + * @isr
 + */
 +CH_IRQ_HANDLER(STM32_CAN2_RX1_HANDLER) {
 +
 +  CH_IRQ_PROLOGUE();
 +
 +  can_lld_rx1_handler(&CAND2);
 +
 +  CH_IRQ_EPILOGUE();
 +}
 +
 +/**
 + * @brief   CAN2 SCE interrupt handler.
 + *
 + * @isr
 + */
 +CH_IRQ_HANDLER(STM32_CAN2_SCE_HANDLER) {
 +
 +  CH_IRQ_PROLOGUE();
 +
 +  can_lld_sce_handler(&CAND2);
 +
 +  CH_IRQ_EPILOGUE();
 +}
 +#endif /* STM32_CAN_USE_CAN2 */
 +
  /*===========================================================================*/
  /* Driver exported functions.                                                */
  /*===========================================================================*/
 @@ -177,6 +358,19 @@ void can_lld_init(void) {    canObjectInit(&CAND1);
    CAND1.can = CAN1;
  #endif
 +#if STM32_CAN_USE_CAN2
 +  /* Driver initialization.*/
 +  canObjectInit(&CAND2);
 +  CAND2.can = CAN2;
 +#endif
 +
 +  /* Filters initialization.*/
 +#if STM32_HAS_CAN2
 +  can_lld_set_filters(STM32_CAN_MAX_FILTERS / 2, 0, NULL);
 +#else
 +  can_lld_set_filters(STM32_CAN_MAX_FILTERS, 0, NULL);
 +#endif
 +
  }
  /**
 @@ -202,6 +396,19 @@ void can_lld_start(CANDriver *canp) {      rccEnableCAN1(FALSE);
    }
  #endif
 +#if STM32_CAN_USE_CAN2
 +  if (&CAND2 == canp) {
 +    nvicEnableVector(STM32_CAN2_TX_NUMBER,
 +                     CORTEX_PRIORITY_MASK(STM32_CAN_CAN2_IRQ_PRIORITY));
 +    nvicEnableVector(STM32_CAN2_RX0_NUMBER,
 +                     CORTEX_PRIORITY_MASK(STM32_CAN_CAN2_IRQ_PRIORITY));
 +    nvicEnableVector(STM32_CAN2_RX1_NUMBER,
 +                     CORTEX_PRIORITY_MASK(STM32_CAN_CAN2_IRQ_PRIORITY));
 +    nvicEnableVector(STM32_CAN2_SCE_NUMBER,
 +                     CORTEX_PRIORITY_MASK(STM32_CAN_CAN2_IRQ_PRIORITY));
 +    rccEnableCAN2(FALSE);
 +  }
 +#endif
    /* Entering initialization mode. */
    canp->state = CAN_STARTING;
 @@ -212,56 +419,12 @@ void can_lld_start(CANDriver *canp) {    canp->can->BTR = canp->config->btr;
    /* MCR initialization.*/
    canp->can->MCR = canp->config->mcr;
 -  /* Filters initialization.*/
 -  canp->can->FMR |= CAN_FMR_FINIT;
 -  if (canp->config->num > 0) {
 -    uint32_t i, fmask;
 -    CAN_FilterRegister_TypeDef *cfp;
 -
 -    canp->can->FA1R = 0;
 -    canp->can->FM1R = 0;
 -    canp->can->FS1R = 0;
 -    canp->can->FFA1R = 0;
 -    cfp = canp->can->sFilterRegister;
 -    fmask = 1;
 -    for (i = 0; i < STM32_CAN_MAX_FILTERS; i++) {
 -      if (i < canp->config->num) {
 -        if (canp->config->filters[i].mode)
 -          canp->can->FM1R |= fmask;
 -        if (canp->config->filters[i].scale)
 -          canp->can->FS1R |= fmask;
 -        if (canp->config->filters[i].assignment)
 -          canp->can->FFA1R |= fmask;
 -        cfp->FR1 = canp->config->filters[i].register1;
 -        cfp->FR2 = canp->config->filters[i].register2;
 -        canp->can->FA1R |= fmask;
 -      }
 -      else {
 -        cfp->FR1 = 0;
 -        cfp->FR2 = 0;
 -      }
 -      /* Gives a chance for preemption since this is a rather long loop.*/
 -      chSysUnlock();
 -      cfp++;
 -      fmask <<= 1;
 -      chSysLock();
 -    }
 -  }
 -  else {
 -    /* Setup a default filter.*/
 -    canp->can->sFilterRegister[0].FR1 = 0;
 -    canp->can->sFilterRegister[0].FR2 = 0;
 -    canp->can->FM1R = 0;
 -    canp->can->FFA1R = 0;
 -    canp->can->FS1R = 1;
 -    canp->can->FA1R = 1;
 -  }
 -  canp->can->FMR &= ~CAN_FMR_FINIT;
 +
    /* Interrupt sources initialization.*/
    canp->can->IER = CAN_IER_TMEIE  | CAN_IER_FMPIE0 | CAN_IER_FMPIE1 |
 -                      CAN_IER_WKUIE  | CAN_IER_ERRIE  | CAN_IER_LECIE  |
 -                      CAN_IER_BOFIE  | CAN_IER_EPVIE  | CAN_IER_EWGIE  |
 -                      CAN_IER_FOVIE0 | CAN_IER_FOVIE1;
 +                   CAN_IER_WKUIE  | CAN_IER_ERRIE  | CAN_IER_LECIE  |
 +                   CAN_IER_BOFIE  | CAN_IER_EPVIE  | CAN_IER_EWGIE  |
 +                   CAN_IER_FOVIE0 | CAN_IER_FOVIE1;
  }
  /**
 @@ -286,6 +449,17 @@ void can_lld_stop(CANDriver *canp) {        rccDisableCAN1(FALSE);
      }
  #endif
 +#if STM32_CAN_USE_CAN2
 +    if (&CAND2 == canp) {
 +      CAN2->MCR = 0x00010002;                   /* Register reset value.    */
 +      CAN2->IER = 0x00000000;                   /* All sources disabled.    */
 +      nvicDisableVector(STM32_CAN2_TX_NUMBER);
 +      nvicDisableVector(STM32_CAN2_RX0_NUMBER);
 +      nvicDisableVector(STM32_CAN2_RX1_NUMBER);
 +      nvicDisableVector(STM32_CAN2_SCE_NUMBER);
 +      rccDisableCAN2(FALSE);
 +    }
 +#endif
    }
  }
 @@ -409,6 +583,36 @@ void can_lld_wakeup(CANDriver *canp) {  }
  #endif /* CAN_USE_SLEEP_MODE */
 +/**
 + * @brief   Programs the filters.
 + * @note    This is an STM32-specific API.
 + *
 + * @param[in] can2sb    number of the first filter assigned to CAN2
 + * @param[in] num       number of entries in the filters array, if zero then
 + *                      a default filter is programmed
 + * @param[in] cfp       pointer to the filters array, can be @p NULL if
 + *                      (num == 0)
 + *
 + * @api
 + */
 +void canSTM32SetFilters(uint32_t can2sb, uint32_t num, const CANFilter *cfp) {
 +
 +  chDbgCheck((can2sb > 1) && (can2sb < STM32_CAN_MAX_FILTERS) &&
 +             (num < STM32_CAN_MAX_FILTERS),
 +             "canSTM32SetFilters");
 +
 +#if STM32_CAN_USE_CAN1
 +  chDbgAssert(CAND1.state == CAN_STOP,
 +              "canSTM32SetFilters(), #1", "invalid state");
 +#endif
 +#if STM32_CAN_USE_CAN2
 +  chDbgAssert(CAND2.state == CAN_STOP,
 +              "canSTM32SetFilters(), #2", "invalid state");
 +#endif
 +
 +  can_lld_set_filters(can2sb, num, cfp);
 +}
 +
  #endif /* HAL_USE_CAN */
  /** @} */
 diff --git a/os/hal/platforms/STM32/can_lld.h b/os/hal/platforms/STM32/can_lld.h index cc0c6e115..132cd207a 100644 --- a/os/hal/platforms/STM32/can_lld.h +++ b/os/hal/platforms/STM32/can_lld.h @@ -76,14 +76,21 @@   */
  /**
   * @brief   CAN1 driver enable switch.
 - * @details If set to @p TRUE the support for ADC1 is included.
 - * @note    The default is @p TRUE.
 + * @details If set to @p TRUE the support for CAN1 is included.
   */
  #if !defined(STM32_CAN_USE_CAN1) || defined(__DOXYGEN__)
  #define STM32_CAN_USE_CAN1                  FALSE
  #endif
  /**
 + * @brief   CAN2 driver enable switch.
 + * @details If set to @p TRUE the support for CAN2 is included.
 + */
 +#if !defined(STM32_CAN_USE_CAN2) || defined(__DOXYGEN__)
 +#define STM32_CAN_USE_CAN2                  FALSE
 +#endif
 +
 +/**
   * @brief   CAN1 interrupt priority level setting.
   */
  #if !defined(STM32_CAN_CAN1_IRQ_PRIORITY) || defined(__DOXYGEN__)
 @@ -91,6 +98,14 @@  #endif
  /** @} */
 +/**
 + * @brief   CAN2 interrupt priority level setting.
 + */
 +#if !defined(STM32_CAN_CAN2_IRQ_PRIORITY) || defined(__DOXYGEN__)
 +#define STM32_CAN_CAN2_IRQ_PRIORITY         11
 +#endif
 +/** @} */
 +
  /*===========================================================================*/
  /* Derived constants and error checks.                                       */
  /*===========================================================================*/
 @@ -99,7 +114,11 @@  #error "CAN1 not present in the selected device"
  #endif
 -#if !STM32_CAN_USE_CAN1
 +#if STM32_CAN_USE_CAN2 && !STM32_HAS_CAN2
 +#error "CAN2 not present in the selected device"
 +#endif
 +
 +#if !STM32_CAN_USE_CAN1 && !STM32_CAN_USE_CAN2
  #error "CAN driver activated but no CAN peripheral assigned"
  #endif
 @@ -173,29 +192,33 @@ typedef struct {   */
  typedef struct {
    /**
 -   * @brief Filter mode.
 -   * @note  This bit represent the CAN_FM1R register bit associated to this
 -   *        filter (0=mask mode, 1=list mode).
 +   * @brief   Number of the filter to be programmed.
 +   */
 +  uint32_t                  filter;
 +  /**
 +   * @brief   Filter mode.
 +   * @note    This bit represent the CAN_FM1R register bit associated to this
 +   *          filter (0=mask mode, 1=list mode).
     */
    uint32_t                  mode:1;
    /**
 -   * @brief Filter scale.
 -   * @note  This bit represent the CAN_FS1R register bit associated to this
 -   *        filter (0=16 bits mode, 1=32 bits mode).
 +   * @brief   Filter scale.
 +   * @note    This bit represent the CAN_FS1R register bit associated to this
 +   *          filter (0=16 bits mode, 1=32 bits mode).
     */
    uint32_t                  scale:1;
    /**
 -   * @brief Filter mode.
 -   * @note  This bit represent the CAN_FFA1R register bit associated to this
 -   *        filter, must be set to zero in this version of the driver.
 +   * @brief   Filter mode.
 +   * @note    This bit represent the CAN_FFA1R register bit associated to this
 +   *          filter, must be set to zero in this version of the driver.
     */
    uint32_t                  assignment:1;
    /**
 -   * @brief Filter register 1 (identifier).
 +   * @brief   Filter register 1 (identifier).
     */
    uint32_t                  register1;
    /**
 -   * @brief Filter register 2 (mask/identifier depending on mode=0/1).
 +   * @brief   Filter register 2 (mask/identifier depending on mode=0/1).
     */
    uint32_t                  register2;
  } CANFilter;
 @@ -205,29 +228,17 @@ typedef struct {   */
  typedef struct {
    /**
 -   * @brief CAN MCR register initialization data.
 -   * @note  Some bits in this register are enforced by the driver regardless
 -   *        their status in this field.
 +   * @brief   CAN MCR register initialization data.
 +   * @note    Some bits in this register are enforced by the driver regardless
 +   *          their status in this field.
     */
    uint32_t                  mcr;
    /**
 -   * @brief CAN BTR register initialization data.
 -   * @note  Some bits in this register are enforced by the driver regardless
 -   *        their status in this field.
 +   * @brief   CAN BTR register initialization data.
 +   * @note    Some bits in this register are enforced by the driver regardless
 +   *          their status in this field.
     */
    uint32_t                  btr;
 -  /**
 -   * @brief Number of elements into the filters array.
 -   * @note  By setting this field to zero a default filter is enabled that
 -   *        allows all frames, this should be adequate  for simple applications.
 -   */
 -  uint32_t                  num;
 -  /**
 -   * @brief Pointer to an array of @p CANFilter structures.
 -   * @note  This field can be set to @p NULL if the field @p num is set to
 -   *        zero.
 -   */
 -  const CANFilter           *filters;
  } CANConfig;
  /**
 @@ -235,52 +246,53 @@ typedef struct {   */
  typedef struct {
    /**
 -   * @brief Driver state.
 +   * @brief   Driver state.
     */
    canstate_t                state;
    /**
 -   * @brief Current configuration data.
 +   * @brief   Current configuration data.
     */
    const CANConfig           *config;
    /**
 -   * @brief Transmission queue semaphore.
 +   * @brief   Transmission queue semaphore.
     */
    Semaphore                 txsem;
    /**
 -   * @brief Receive queue semaphore.
 +   * @brief   Receive queue semaphore.
     */
    Semaphore                 rxsem;
    /**
 -   * @brief One or more frames become available.
 -   * @note  After broadcasting this event it will not be broadcasted again
 -   *        until the received frames queue has been completely emptied. It
 -   *        is <b>not</b> broadcasted for each received frame. It is
 -   *        responsibility of the application to empty the queue by repeatedly
 -   *        invoking @p chReceive() when listening to this event. This behavior
 -   *        minimizes the interrupt served by the system because CAN traffic.
 +   * @brief   One or more frames become available.
 +   * @note    After broadcasting this event it will not be broadcasted again
 +   *          until the received frames queue has been completely emptied. It
 +   *          is <b>not</b> broadcasted for each received frame. It is
 +   *          responsibility of the application to empty the queue by
 +   *          repeatedly invoking @p chReceive() when listening to this event.
 +   *          This behavior minimizes the interrupt served by the system
 +   *          because CAN traffic.
     */
    EventSource               rxfull_event;
    /**
 -   * @brief One or more transmission slots become available.
 +   * @brief   One or more transmission slots become available.
     */
    EventSource               txempty_event;
    /**
 -   * @brief A CAN bus error happened.
 +   * @brief   A CAN bus error happened.
     */
    EventSource               error_event;
  #if CAN_USE_SLEEP_MODE || defined (__DOXYGEN__)
    /**
 -   * @brief Entering sleep state event.
 +   * @brief   Entering sleep state event.
     */
    EventSource               sleep_event;
    /**
 -   * @brief Exiting sleep state event.
 +   * @brief   Exiting sleep state event.
     */
    EventSource               wakeup_event;
  #endif /* CAN_USE_SLEEP_MODE */
    /* End of the mandatory fields.*/
    /**
 -   * @brief Pointer to the CAN registers.
 +   * @brief   Pointer to the CAN registers.
     */
    CAN_TypeDef               *can;
  } CANDriver;
 @@ -297,6 +309,10 @@ typedef struct {  extern CANDriver CAND1;
  #endif
 +#if STM32_CAN_USE_CAN2 && !defined(__DOXYGEN__)
 +extern CANDriver CAND2;
 +#endif
 +
  #ifdef __cplusplus
  extern "C" {
  #endif
 | 
