mbed library sources, include can_api for nucleo-f091rc

Dependents:   CanNucleoF0_example

Fork of mbed-src by mbed official

Files at this revision

API Documentation at this revision

Comitter:
ptpaterson
Date:
Wed Dec 16 05:53:00 2015 +0000
Parent:
638:ceaaef393d2e
Child:
640:7eb9b8c299cd
Commit message:
Using read without interrupts. Implemented message_queue, but not quite working.

Changed in this revision

targets/hal/TARGET_STM/TARGET_STM32F0/TARGET_NUCLEO_F091RC/objects.h Show annotated file Show diff for this revision Revisions of this file
targets/hal/TARGET_STM/TARGET_STM32F0/can_api.c Show diff for this revision Revisions of this file
targets/hal/TARGET_STM/TARGET_STM32F0/can_api.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/targets/hal/TARGET_STM/TARGET_STM32F0/TARGET_NUCLEO_F091RC/objects.h	Mon Dec 14 16:23:14 2015 +0000
+++ b/targets/hal/TARGET_STM/TARGET_STM32F0/TARGET_NUCLEO_F091RC/objects.h	Wed Dec 16 05:53:00 2015 +0000
@@ -101,7 +101,8 @@
 };
 
 struct can_s {
-    CAN_HandleTypeDef *hcan;
+    CAN_HandleTypeDef   *hcan;
+    int index;
 };
 
 #include "gpio_object.h"
--- a/targets/hal/TARGET_STM/TARGET_STM32F0/can_api.c	Mon Dec 14 16:23:14 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,632 +0,0 @@
-/*
-  ******************************************************************************
-  * @file    can_api.c
-  * @author  Zoltan Hudak
-  * @version 
-  * @date    04-August-2015
-  * @brief   CAN api for NUCLEO-F103RB platform
-  ******************************************************************************
-  * @attention
-  *
-  * <h2><center>&copy; COPYRIGHT(c) 2015 Zoltan Hudak <[email protected]>
-  *
-  * All rights reserved.
- 
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
- 
- You should have received a copy of the GNU General Public License
- along with this program.  If not, see <http://www.gnu.org/licenses/>.
-  */
-
- /* Some code reused from STM32CubeMX */
- /******************************************************************************
-  *
-  * COPYRIGHT(c) 2015 STMicroelectronics
-  *
-  * Redistribution and use in source and binary forms, with or without modification,
-  * are permitted provided that the following conditions are met:
-  *   1. Redistributions of source code must retain the above copyright notice,
-  *      this list of conditions and the following disclaimer.
-  *   2. Redistributions in binary form must reproduce the above copyright notice,
-  *      this list of conditions and the following disclaimer in the documentation
-  *      and/or other materials provided with the distribution.
-  *   3. Neither the name of STMicroelectronics nor the names of its contributors
-  *      may be used to endorse or promote products derived from this software
-  *      without specific prior written permission.
-  *
-  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  *
-  ******************************************************************************
-  */
-
- /* also some code taken from other mbed can_api.c */
- 
- /* mbed Microcontroller Library
- * Copyright (c) 2006-2013 ARM Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
- /* Modified by Paul Paterson */
-
-#include "stm32f0xx_hal.h"
-#include "can_api.h"
-#include "can_helper.h"
-#include "pinmap.h"
-
-static PinName pinRd;
-static PinName pinTd;
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void
-can_init (can_t *obj, 
-          PinName rd,
-          PinName td)
-{
-    /* file scope so that they can be referenced in the MSP functions */
-    pinRd = rd;
-    pinTd = td;
- 
-    /* initialize the static data */
-    static CAN_HandleTypeDef hcan;
-    obj->hcan = &hcan;
-    obj->hcan->Instance = ((CAN_TypeDef *) CAN_BASE);
-    
-    static CanTxMsgTypeDef txMessage;
-    static CanRxMsgTypeDef rxMessage; 
-    obj->hcan->pTxMsg = &txMessage;
-    obj->hcan->pRxMsg = &rxMessage;
- 
-    /* set operation mode */
-    obj->hcan->Init.TTCM = DISABLE;
-    obj->hcan->Init.ABOM = DISABLE;
-    obj->hcan->Init.AWUM = DISABLE;
-    obj->hcan->Init.NART = DISABLE;
-    obj->hcan->Init.RFLM = DISABLE;
-    obj->hcan->Init.TXFP = DISABLE;
-    obj->hcan->Init.Mode = CAN_MODE_NORMAL;
- 
-    /* set th default frequency
-     * 125kbps bit rate (default)
-     * APB1 peripheral clock = 36000000Hz
-     */
-    obj->hcan->Init.Prescaler = 18;      // number of time quanta = 36000000/18/125000 = 16
-    obj->hcan->Init.SJW = CAN_SJW_1TQ;
-    obj->hcan->Init.BS1 = CAN_BS1_11TQ;  // sample point at (1 + 11) / 16 * 100 = 75%
-    obj->hcan->Init.BS2 = CAN_BS2_4TQ;
- 
-    int result = HAL_CAN_Init (obj->hcan);
-    if (result != HAL_OK)
-    {
-        // TODO: handle issues   
-    }
-    
-    /* minimum filter required to make this work */
-    can_filter (obj, 0, 0, CANAny, 0);
-    
-    return;            
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void
-can_free (can_t *obj)
-{
-    HAL_CAN_DeInit (obj->hcan);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-int
-can_frequency(can_t *obj, 
-              int hz)
-{ 
-    HAL_NVIC_DisableIRQ(CEC_CAN_IRQn);
- 
-    // APB1 peripheral clock = 36000000Hz
- 
-    switch(hz) {
-    case 1000000:
-        // 1000kbps bit rate
-        obj->hcan->Init.Prescaler = 3;      // number of time quanta = 36000000/3/1000000 = 12
-        obj->hcan->Init.SJW = CAN_SJW_1TQ;
-        obj->hcan->Init.BS1 = CAN_BS1_8TQ;  // sample point at: (1 + 8) / 12 * 100 = 75%
-        obj->hcan->Init.BS2 = CAN_BS2_3TQ;
-        break;
- 
-    case 500000:
-        // 500kbps bit rate
-        obj->hcan->Init.Prescaler = 6;      // number of time quanta = 36000000/6/500000 = 12
-        obj->hcan->Init.SJW = CAN_SJW_1TQ;
-        obj->hcan->Init.BS1 = CAN_BS1_8TQ;  // sample point at: (1 + 8) / 12 * 100 = 75%
-        obj->hcan->Init.BS2 = CAN_BS2_3TQ;
-        break;
- 
-    case 250000:
-        // 250kbps
-        obj->hcan->Init.Prescaler = 9;      // number of time quanta = 36000000/9/250000 = 16
-        obj->hcan->Init.SJW = CAN_SJW_1TQ;
-        obj->hcan->Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
-        obj->hcan->Init.BS2 = CAN_BS2_4TQ;
-        break;
- 
-    case 125000:
-        // 125kbps
-        obj->hcan->Init.Prescaler = 18;     // number of time quanta = 36000000/18/125000 = 16
-        obj->hcan->Init.SJW = CAN_SJW_1TQ;
-        obj->hcan->Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
-        obj->hcan->Init.BS2 = CAN_BS2_4TQ;
-        break;
- 
-    default:
-        // 125kbps (default)
-        obj->hcan->Init.Prescaler = 18;     // number of time quanta = 36000000/18/125000 = 16
-        obj->hcan->Init.SJW = CAN_SJW_1TQ;
-        obj->hcan->Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
-        obj->hcan->Init.BS2 = CAN_BS2_4TQ;
-    }
-    HAL_CAN_Init(obj->hcan);
-    
-    /* HAL_CAN_INIT will call HAL_CAN_MspInit, which will init the interupts */
-    
-    return 1;
-}
-
-/*
- * these will get setup in can_irq_init()
- * and used later in HAL_CAN_RxCpltCallback()
- */
- 
-/* becomes a pointer to the member function Can::_irq_handler */
-static can_irq_handler irq_handler;
-
-/* id is really just a pointer to the Can object 
- * useful for uC's that have multiple CAN devices
- */
-static uint32_t can_irq_id;
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void    
-can_irq_init (can_t *obj, 
-              can_irq_handler handler, 
-              uint32_t id)
-{
-
-    printf ("function: can_irq_init()\r\n");
-
-    irq_handler = handler;
-    can_irq_id = id;
-    
-    if (HAL_CAN_Receive_IT (obj->hcan, CAN_FIFO0) != HAL_OK) 
-    {
-        // TODO: handle issues    
-    }
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void
-can_irq_free (can_t *obj)
-{
-      // TODO: free any resources not called by HAL_CAN_DeInit() */
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void
-can_irq_set (can_t *obj, 
-             CanIrqType irq, 
-             uint32_t enable)
-{
-    
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-int
-can_write (can_t *obj, 
-           CAN_Message msg, 
-           int cc)
-{
-    if (msg.format == CANStandard) 
-    {
-        obj->hcan->pTxMsg->StdId = msg.id;
-        obj->hcan->pTxMsg->ExtId = 0x00;
-    }
-    else 
-    {
-        obj->hcan->pTxMsg->StdId = 0x00;
-        obj->hcan->pTxMsg->ExtId = msg.id;
-    }
- 
-    obj->hcan->pTxMsg->RTR = msg.type == CANData ? CAN_RTR_DATA : CAN_RTR_REMOTE;
-    obj->hcan->pTxMsg->IDE = msg.format == CANStandard ? CAN_ID_STD : CAN_ID_EXT;
-    obj->hcan->pTxMsg->DLC = msg.len;
- 
-    // TODO: use memcopy
-    for (int i = 0; i < msg.len; i++)
-    {
-        obj->hcan->pTxMsg->Data[i] = msg.data[i];
-    }
- 
-    int result = 1; 
-    if (HAL_CAN_Transmit(obj->hcan, 10) != HAL_OK)
-    {
-        result = 0;
-    }
-    
-    return result;
-
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-int
-can_read (can_t *obj, 
-          CAN_Message *msg, 
-          int handle)
-{ 
-    msg->id = obj->hcan->pRxMsg->IDE == CAN_ID_STD ? obj->hcan->pRxMsg->StdId : obj->hcan->pRxMsg->ExtId;
-    msg->type = obj->hcan->pRxMsg->RTR == CAN_RTR_DATA ? CANData : CANRemote;
-    msg->format = obj->hcan->pRxMsg->IDE == CAN_ID_STD ? CANStandard : CANExtended;
-    msg->len = obj->hcan->pRxMsg->DLC;
-    
-    // TODO: use memcopy
-    for (int i = 0; i < msg->len; i++)
-    {
-        msg->data[i] = obj->hcan->pRxMsg->Data[i];
-    }
-        
-    return msg->len;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-int 
-can_mode (can_t *obj,
-          CanMode mode)
-{
-    int success = 0;
-    
-    switch(mode) 
-    {
-    case MODE_RESET:
-        success = HAL_ERROR;
-        break;
- 
-    case MODE_NORMAL:
-        obj->hcan->Init.Mode = CAN_MODE_NORMAL;
-        break;
- 
-    case MODE_SILENT:
-        obj->hcan->Init.Mode = CAN_MODE_SILENT;
-        break;
- 
-    case MODE_TEST_GLOBAL:
-        obj->hcan->Init.Mode = CAN_MODE_LOOPBACK;
-        break;
- 
-    case MODE_TEST_LOCAL:
-        obj->hcan->Init.Mode = CAN_MODE_LOOPBACK;
-        break;
- 
-    case MODE_TEST_SILENT:
-        obj->hcan->Init.Mode = CAN_MODE_SILENT_LOOPBACK;
-        break;
-    }
- 
-    if (success != HAL_ERROR)
-    {
-        success = HAL_CAN_Init(obj->hcan);
-    }
-
-    return success;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-int
-can_filter (can_t *obj, 
-            uint32_t id, 
-            uint32_t mask, 
-            CANFormat format, 
-            int32_t handle)
-{
-    CAN_FilterConfTypeDef   sFilterConfig;
- 
-    sFilterConfig.FilterNumber = handle;    // Specifies the filter number (must be a number between 0 and 13 at 32-bit filter scale)
-    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
-    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
-    sFilterConfig.FilterIdHigh = (((id) >> 16) & 0xFFFF);
-    sFilterConfig.FilterIdLow = ((id) & 0xFFFF);
-    sFilterConfig.FilterMaskIdHigh = (((mask) >> 16) & 0xFFFF);
-    sFilterConfig.FilterMaskIdLow = ((mask) & 0xFFFF);
-    sFilterConfig.FilterFIFOAssignment = 0;
-    sFilterConfig.FilterActivation = ENABLE;
-    sFilterConfig.BankNumber = 0;           // Selects the start bank filter
-    
-    return HAL_CAN_ConfigFilter(obj->hcan, &sFilterConfig); 
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void
-can_reset (can_t *obj)
-{
-    __HAL_CAN_RESET_HANDLE_STATE(obj->hcan);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-unsigned char 
-can_rderror (can_t *obj)
-{
-    return HAL_CAN_GetError(obj->hcan);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-unsigned char
-can_tderror (can_t *obj)
-{
-    return HAL_CAN_GetError(obj->hcan);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
- void
- can_monitor (can_t *obj, 
-              int silent)
-{
-    // TODO: implement
-}
-
-/*=============================================================================
- * HAL_MSP
- *=============================================================================
- */
- 
-/**
-  * @brief  CAN MSP Initialization
-  * @param  hcan: CAN handle pointer
-  * @retval None
-  */
-void 
-HAL_CAN_MspInit(CAN_HandleTypeDef* hcan) 
-{
-    /* derived from STM32CubeMX */
-    
-    GPIO_InitTypeDef    GPIO_InitStruct;
-    if (hcan->Instance == CAN)
-    {
-        if ((pinRd == PA_11) && (pinTd == PA_12))
-        { 
-            /* Peripheral clock enable */
-            __CAN_CLK_ENABLE();
-     
-            /* Enable GPIO clock */
-            __GPIOA_CLK_ENABLE();
-     
-            /**CAN GPIO Configuration    
-            PA11     ------> CAN_RX
-            PA12     ------> CAN_TX 
-            */
-            GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
-            GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
-            GPIO_InitStruct.Pull = GPIO_NOPULL;
-            GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
-            GPIO_InitStruct.Alternate = GPIO_AF4_CAN;
-            HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-        }
-        else if ((pinRd == PB_8) && (pinTd == PB_9))
-        { 
-            /* Peripheral clock enable */
-            __CAN_CLK_ENABLE();
-     
-            /* Enable GPIO clock */
-            __GPIOB_CLK_ENABLE();
-     
-            /**CAN GPIO Configuration    
-            PB8     ------> CAN_RX
-            PB9     ------> CAN_TX 
-            */
-            GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
-            GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
-            GPIO_InitStruct.Pull = GPIO_NOPULL;
-            GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
-            GPIO_InitStruct.Alternate = GPIO_AF4_CAN;
-            HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-        }
-        else {}   
-    }
-    
-    /* Peripheral interrupt init*/
-    HAL_NVIC_SetPriority(CEC_CAN_IRQn, 0, 0);
-    HAL_NVIC_EnableIRQ(CEC_CAN_IRQn);
-}
- 
-/**
-  * @brief CAN MSP De-Initialization
-  *        This function frees the hardware resources used:
-  *          - Disable the Peripheral's clock
-  *          - Revert GPIO to their default state
-  * @param hcan: CAN handle pointer
-  * @retval None
-  */
-void 
-HAL_CAN_MspDeInit (CAN_HandleTypeDef* hcan)
-{
-    /* derived from STM32CubeMX */
-    
-    if (hcan->Instance == CAN)
-    {
-        /* Peripheral clock disable */
-        __CAN_CLK_DISABLE();
-        
-        /* Disable pins*/
-        if ((pinRd == PA_11) && (pinTd == PA_12))
-        { 
-            /**CAN GPIO Configuration    
-            PA11     ------> CAN_RX
-            PA12     ------> CAN_TX 
-            */
-            HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);
-        }
-        else if ((pinRd == PB_8) && (pinTd == PB_9))
-        {
-            /**CAN GPIO Configuration    
-            PB8     ------> CAN_RX
-            PB9     ------> CAN_TX 
-            */
-            HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8|GPIO_PIN_9);
-        } 
-        else {}
-    }
- 
-    /* Disable the NVIC for CAN reception */
-    HAL_NVIC_DisableIRQ(CEC_CAN_IRQn);
-}
- 
-/**
-  * @brief  Reception  complete callback in non blocking mode
-  * @param  obj->hcan: pointer to a CAN_HandleTypeDef structure that contains
-  *         the configuration information for the specified CAN.
-  * @retval None
-  */
-void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan) {
-    // if(HAL_CAN_Receive_IT(hcan, CAN_FIFO0) == HAL_OK) {
-    //     if(rxCompleteCallback != NULL)
-    //         // call user callbacks
-    // }
-    // else {
-    //     error_handler(error);
-    // }
- 
-    // BUG: CAN race condition if HAL_CAN_Receive_IT() is used.
-    // See https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Java/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2FSTM32Java%2FBUG%20CAN%20race%20condition%20if%20HAL%5FCAN%5FReceive%5FIT%20is%20used
-    //
-    // Fixed by Mark Burton:
-    // ideally, we should be able to call HAL_CAN_Receive_IT() here to set up for another
-    // receive but the API is flawed because that function will fail if HAL_CAN_Transmit()
-    // had already locked the handle when the receive interrupt occurred - so we do what
-    // HAL_CAN_Receive_IT() would do
- 
-    /* Call user callbacks */
-    irq_handler (can_irq_id, IRQ_RX);
-    
-    /* perform HAL_CAN_Receive_IT() fix */
-    if (hcan->State == HAL_CAN_STATE_BUSY_TX)
-    {
-        hcan->State = HAL_CAN_STATE_BUSY_TX_RX;
-    }
-    else
-    {
-        hcan->State = HAL_CAN_STATE_BUSY_RX;
- 
-        /* Set CAN error code to none */
-        hcan->ErrorCode = HAL_CAN_ERROR_NONE;
- 
-        /* Enable Error warning Interrupt */
-        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_EWG);
- 
-        /* Enable Error passive Interrupt */
-        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_EPV);
- 
-        /* Enable Bus-off Interrupt */
-        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_BOF);
- 
-        /* Enable Last error code Interrupt */
-        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_LEC);
- 
-        /* Enable Error Interrupt */
-        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_ERR);
-    }
- 
-    // Enable FIFO 0 message pending Interrupt
-    __HAL_CAN_ENABLE_IT(hcan, CAN_IT_FMP0);
-}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/hal/TARGET_STM/TARGET_STM32F0/can_api.cpp	Wed Dec 16 05:53:00 2015 +0000
@@ -0,0 +1,781 @@
+/*
+  ******************************************************************************
+  * @file    can_api.c
+  * @author  Zoltan Hudak
+  * @version
+  * @date    04-August-2015
+  * @brief   CAN api for NUCLEO-F103RB platform
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2015 Zoltan Hudak <[email protected]>
+  *
+  * All rights reserved.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  */
+
+/* Some code reused from STM32CubeMX */
+/******************************************************************************
+ *
+ * COPYRIGHT(c) 2015 STMicroelectronics
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *   1. Redistributions of source code must retain the above copyright notice,
+ *      this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright notice,
+ *      this list of conditions and the following disclaimer in the documentation
+ *      and/or other materials provided with the distribution.
+ *   3. Neither the name of STMicroelectronics nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+/* also some code taken from other mbed can_api.c */
+
+/* mbed Microcontroller Library
+* Copyright (c) 2006-2013 ARM Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+/* Modified by Paul Paterson */
+
+#include "stm32f0xx_hal.h"
+#include "can_api.h"
+#include "can_helper.h"
+#include "pinmap.h"
+
+#include "mbed.h"
+
+static PinName pinRd;
+static PinName pinTd;
+
+/**
+ * @brief Holder for obj* when not passed to function  
+ * @note need this to cheat for now while we don't have a way to implement
+ * multiple CAN controllers
+ */
+static can_t *single_can_controller;
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void
+can_init (can_t    *obj,
+          PinName   rd,
+          PinName   td)
+{       
+    // DEBUG
+    printf("api: can_init\r\n");
+    
+    /* file scope so that they can be referenced in the MSP functions */
+    pinRd = rd;
+    pinTd = td;
+
+    /* only one CAN controller for STM32F0, otherwise need to figure it out here */
+    single_can_controller = obj; /* this is so we can cheat and call globally*/
+    obj->index = 0;
+
+    /* initialize the static data */
+    static CAN_HandleTypeDef hcan;
+    obj->hcan = &hcan;
+    obj->hcan->Instance = ((CAN_TypeDef *) CAN_BASE);
+
+    static CanTxMsgTypeDef txMessage;
+    static CanRxMsgTypeDef rxMessage;
+    obj->hcan->pTxMsg = &txMessage;
+    obj->hcan->pRxMsg = &rxMessage;
+
+    /* set operation mode */
+    obj->hcan->Init.TTCM = DISABLE;
+    obj->hcan->Init.ABOM = ENABLE;
+    obj->hcan->Init.AWUM = DISABLE;
+    obj->hcan->Init.NART = DISABLE;
+    obj->hcan->Init.RFLM = DISABLE;
+    obj->hcan->Init.TXFP = DISABLE;
+    obj->hcan->Init.Mode = CAN_MODE_NORMAL;
+
+    /* set th default frequency
+     * 125kbps bit rate (default)
+     * APB1 peripheral clock = 36000000Hz
+     */
+    obj->hcan->Init.Prescaler = 18;      // number of time quanta = 36000000/18/125000 = 16
+    obj->hcan->Init.SJW = CAN_SJW_1TQ;
+    obj->hcan->Init.BS1 = CAN_BS1_11TQ;  // sample point at (1 + 11) / 16 * 100 = 75%
+    obj->hcan->Init.BS2 = CAN_BS2_4TQ;
+
+    int status = HAL_CAN_Init (obj->hcan);
+    if (status != HAL_OK) {
+        printf("api: can_init: HAL_CAN_INIT issue\r\n");
+    }
+
+    /* minimum filter required to make this work */
+    can_filter (obj, 0, 0, CANAny, 0);
+
+    return;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void
+can_free (can_t *obj)
+{
+    HAL_CAN_DeInit (obj->hcan);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int
+can_frequency(can_t    *obj,
+              int       hz)
+{
+    HAL_NVIC_DisableIRQ(CEC_CAN_IRQn);
+
+    // APB1 peripheral clock = 36000000Hz
+
+    switch(hz) {
+        case 1000000:
+            // 1000kbps bit rate
+            obj->hcan->Init.Prescaler = 3;      // number of time quanta = 36000000/3/1000000 = 12
+            obj->hcan->Init.SJW = CAN_SJW_1TQ;
+            obj->hcan->Init.BS1 = CAN_BS1_8TQ;  // sample point at: (1 + 8) / 12 * 100 = 75%
+            obj->hcan->Init.BS2 = CAN_BS2_3TQ;
+            break;
+
+        case 500000:
+            // 500kbps bit rate
+            obj->hcan->Init.Prescaler = 6;      // number of time quanta = 36000000/6/500000 = 12
+            obj->hcan->Init.SJW = CAN_SJW_1TQ;
+            obj->hcan->Init.BS1 = CAN_BS1_8TQ;  // sample point at: (1 + 8) / 12 * 100 = 75%
+            obj->hcan->Init.BS2 = CAN_BS2_3TQ;
+            break;
+
+        case 250000:
+            // 250kbps
+            obj->hcan->Init.Prescaler = 9;      // number of time quanta = 36000000/9/250000 = 16
+            obj->hcan->Init.SJW = CAN_SJW_1TQ;
+            obj->hcan->Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
+            obj->hcan->Init.BS2 = CAN_BS2_4TQ;
+            break;
+
+        case 125000:
+            // 125kbps
+            obj->hcan->Init.Prescaler = 18;     // number of time quanta = 36000000/18/125000 = 16
+            obj->hcan->Init.SJW = CAN_SJW_1TQ;
+            obj->hcan->Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
+            obj->hcan->Init.BS2 = CAN_BS2_4TQ;
+            break;
+
+        default:
+            // 125kbps (default)
+            obj->hcan->Init.Prescaler = 18;     // number of time quanta = 36000000/18/125000 = 16
+            obj->hcan->Init.SJW = CAN_SJW_1TQ;
+            obj->hcan->Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
+            obj->hcan->Init.BS2 = CAN_BS2_4TQ;
+    }
+    HAL_CAN_Init(obj->hcan);
+
+    /* HAL_CAN_INIT will call HAL_CAN_MspInit, which will init the interupts */
+
+    return 1;
+}
+
+/*
+ * these will get setup in can_irq_init()
+ * and used later in HAL_CAN_RxCpltCallback()
+ */
+
+#define CAN_NUM 1
+#define CAN_MESSAGE_QUEUE_SIZE 10
+
+/**
+ * @brief Queue to hold several incomming messages while we wait for the user
+ * to call for them.
+ * @note This is only necessary now, because the STM32 HAL handles the can
+ * receive FIFO by writing to the CAN_HandleTypeDef and popping from the built
+ * in queue.
+ */
+typedef struct {
+    int    next;
+    unsigned int    contain_mask;
+    CanRxMsgTypeDef queue[CAN_MESSAGE_QUEUE_SIZE];
+} can_message_queue;
+
+static can_message_queue message_queues[CAN_NUM];
+
+/** becomes a pointer to the member function Can::_irq_handler */
+static can_irq_handler irq_handler;
+
+/** id is really just a pointer to the Can object
+ * useful for uC's that have multiple CAN devices
+ */
+static uint32_t can_irq_ids[CAN_NUM] = {0};
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void
+can_irq_init (can_t            *obj,
+              can_irq_handler   handler,
+              uint32_t          id)
+{
+    
+    // DEBUG
+    printf("api: can_irq_init\r\n");
+
+    irq_handler = handler;
+    can_irq_ids[obj->index] = id;
+
+    message_queues[obj->index].contain_mask = 0;
+    message_queues[obj->index].next = CAN_MESSAGE_QUEUE_SIZE - 1;
+
+    //if (HAL_CAN_Receive_IT (obj->hcan, CAN_FIFO0) != HAL_OK) {
+    //    printf("api: can_irq_init:  receive failed\r\n");
+    //}
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void
+can_irq_free (can_t *obj)
+{
+    // TODO: free any resources not called by HAL_CAN_DeInit() */
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void
+can_irq_set (can_t         *obj,
+             CanIrqType     irq,
+             uint32_t       enable)
+{
+
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int
+can_write (can_t       *obj,
+           CAN_Message  msg,
+           int          cc)
+{
+    // DEBUG
+    printf("api: can_write\r\n");
+    
+    if (msg.format == CANStandard) {
+        obj->hcan->pTxMsg->StdId = msg.id;
+        obj->hcan->pTxMsg->ExtId = 0x00;
+    } else {
+        obj->hcan->pTxMsg->StdId = 0x00;
+        obj->hcan->pTxMsg->ExtId = msg.id;
+    }
+
+    obj->hcan->pTxMsg->RTR = msg.type == CANData ? CAN_RTR_DATA : CAN_RTR_REMOTE;
+    obj->hcan->pTxMsg->IDE = msg.format == CANStandard ? CAN_ID_STD : CAN_ID_EXT;
+    obj->hcan->pTxMsg->DLC = msg.len;
+
+    // TODO: use memcopy
+    for (int i = 0; i < msg.len; i++) {
+        obj->hcan->pTxMsg->Data[i] = msg.data[i];
+    }
+
+    int result = 1;
+    if (HAL_CAN_Transmit(obj->hcan, 10) != HAL_OK) {
+        result = 0;
+    }
+
+    return result;
+
+}
+
+/**
+ * @brief Adds one message to the queue
+ * @note sends indication of overflow if it happens but overwites anyway
+ */
+static int message_enqueue(can_t           *obj,
+                           CanRxMsgTypeDef *msg)
+{
+    int result = 1;
+
+    int next = message_queues[obj->index].next;
+    if (++next >= CAN_MESSAGE_QUEUE_SIZE)
+        next = 0;
+
+    if (message_queues[obj->index].contain_mask & (1 << next))
+        result = 0; /* overflow */
+
+    message_queues[obj->index].queue[next]   = *msg;
+    message_queues[obj->index].next          = next;
+    message_queues[obj->index].contain_mask |= next;
+
+    return result;
+}
+
+/**
+ * @brief Pops one message from the queue
+ * @note sends indication of overflow if it happens but overwites anyway
+ */
+static int message_dequeue(can_t           *obj,
+                           CanRxMsgTypeDef *msg)
+{
+    int result = 1;
+
+    int next = message_queues[obj->index].next;
+
+    if (message_queues[obj->index].contain_mask & (1 << next)) {
+        
+        *msg = message_queues[obj->index].queue[next];
+        message_queues[obj->index].contain_mask &= ~next;
+
+        if (--next < 0)
+            next = CAN_MESSAGE_QUEUE_SIZE - 1;
+        message_queues[obj->index].next = next;
+
+    } else {
+        result = 0; /* no current message */
+    }
+
+    return result;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int
+can_read (can_t        *obj,
+          CAN_Message  *msg,
+          int           handle)
+{
+    int result = 1;
+    
+    /* having trouble with the interrupts */
+    HAL_StatusTypeDef status = HAL_CAN_Receive(obj->hcan, CAN_FIFO0, 10);
+    if (status == HAL_OK) {
+        printf("api: can_read:  HAL_CAN_Receive HAL_OK\r\n");
+    } else if (status == HAL_TIMEOUT) {
+        //printf("api: can_read:  HAL_CAN_Receive HAL_TIMEOUT\r\n");
+        result = 0;
+    } else if (status == HAL_ERROR) {
+        printf("api: can_read:  HAL_CAN_Receive HAL_ERROR\r\n");
+        result = 0;
+    } else {
+        printf("api: can_read:  HAL_CAN_Receive HAL_(SOMETHING ELSE)\r\n");
+        result = 0;
+    }
+    
+    if (result > 0) {
+        
+        CanRxMsgTypeDef * popMessage = obj->hcan->pRxMsg;
+        
+        msg->id     = popMessage->IDE == CAN_ID_STD   ? popMessage->StdId : popMessage->ExtId;
+        msg->type   = popMessage->RTR == CAN_RTR_DATA ? CANData           : CANRemote;
+        msg->format = popMessage->IDE == CAN_ID_STD   ? CANStandard       : CANExtended;
+        msg->len    = popMessage->DLC;
+    
+        // TODO: use memcopy
+        for (int i = 0; i < msg->len; i++) {
+            msg->data[i] = obj->hcan->pRxMsg->Data[i];
+        }
+        
+        result = msg->len;
+        
+        
+        
+//        printf("api: can_read:  queueing up msg\r\n");
+//    
+//        message_enqueue(obj, obj->hcan->pRxMsg);
+//        
+//        CanRxMsgTypeDef popMessage;
+//        if (message_dequeue (obj, &popMessage)) {
+//            /* result = 0; *//* redundant, but reinforce */
+//        } else {
+//        
+//            msg->id     = popMessage.IDE == CAN_ID_STD   ? popMessage.StdId : popMessage.ExtId;
+//            msg->type   = popMessage.RTR == CAN_RTR_DATA ? CANData          : CANRemote;
+//            msg->format = popMessage.IDE == CAN_ID_STD   ? CANStandard      : CANExtended;
+//            msg->len    = popMessage.DLC;
+//        
+//            // TODO: use memcopy
+//            for (int i = 0; i < msg->len; i++) {
+//                msg->data[i] = obj->hcan->pRxMsg->Data[i];
+///            }
+//            
+//            result = msg->len;
+//        }        
+    }
+    
+    return result;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int
+can_mode (can_t *obj,
+          CanMode mode)
+{
+    int success = 0;
+
+    switch(mode) {
+        case MODE_RESET:
+            success = HAL_ERROR;
+            break;
+
+        case MODE_NORMAL:
+            obj->hcan->Init.Mode = CAN_MODE_NORMAL;
+            break;
+
+        case MODE_SILENT:
+            obj->hcan->Init.Mode = CAN_MODE_SILENT;
+            break;
+
+        case MODE_TEST_GLOBAL:
+            obj->hcan->Init.Mode = CAN_MODE_LOOPBACK;
+            break;
+
+        case MODE_TEST_LOCAL:
+            obj->hcan->Init.Mode = CAN_MODE_LOOPBACK;
+            break;
+
+        case MODE_TEST_SILENT:
+            obj->hcan->Init.Mode = CAN_MODE_SILENT_LOOPBACK;
+            break;
+    }
+
+    if (success != HAL_ERROR) {
+        success = HAL_CAN_Init(obj->hcan);
+    }
+
+    return success;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int
+can_filter (can_t *obj,
+            uint32_t id,
+            uint32_t mask,
+            CANFormat format,
+            int32_t handle)
+{
+    CAN_FilterConfTypeDef   sFilterConfig;
+
+    sFilterConfig.FilterNumber = handle;    // Specifies the filter number (must be a number between 0 and 13 at 32-bit filter scale)
+    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
+    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
+    sFilterConfig.FilterIdHigh = (((id) >> 16) & 0xFFFF);
+    sFilterConfig.FilterIdLow = ((id) & 0xFFFF);
+    sFilterConfig.FilterMaskIdHigh = (((mask) >> 16) & 0xFFFF);
+    sFilterConfig.FilterMaskIdLow = ((mask) & 0xFFFF);
+    sFilterConfig.FilterFIFOAssignment = 0;
+    sFilterConfig.FilterActivation = ENABLE;
+    sFilterConfig.BankNumber = 0;           // Selects the start bank filter
+
+    return HAL_CAN_ConfigFilter(obj->hcan, &sFilterConfig);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void
+can_reset (can_t *obj)
+{
+    __HAL_CAN_RESET_HANDLE_STATE(obj->hcan);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+unsigned char
+can_rderror (can_t *obj)
+{
+    return HAL_CAN_GetError(obj->hcan);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+unsigned char
+can_tderror (can_t *obj)
+{
+    return HAL_CAN_GetError(obj->hcan);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void
+can_monitor (can_t *obj,
+             int silent)
+{
+    // TODO: implement
+}
+
+/*=============================================================================
+ * HAL_MSP and other functions
+ *=============================================================================
+ */
+ 
+/**
+  * @brief  F0 low level Initialization
+  * @retval None
+  */
+void HAL_MspInit(void)
+{
+    // DEBUG
+    printf("Msp: HAL_MspInit\r\n");
+    
+    /* derived from STM32CubeMX */
+
+    __SYSCFG_CLK_ENABLE();
+    
+    /* System interrupt init*/
+    /* SysTick_IRQn interrupt configuration */
+    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
+}
+
+/**
+  * @brief  CAN MSP Initialization
+  * @param  hcan: CAN handle pointer
+  * @retval None
+  */
+void
+HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
+{
+    // DEBUG
+    printf("Msp: HAL_CAN_MspInit\r\n");
+    
+    /* derived from STM32CubeMX */
+
+    GPIO_InitTypeDef    GPIO_InitStruct;
+    if (hcan->Instance == ((CAN_TypeDef *) CAN_BASE)) {
+        if ((pinRd == PA_11) && (pinTd == PA_12)) {
+            
+            /* Peripheral clock enable */
+            __CAN_CLK_ENABLE();
+
+            /* Enable GPIO clock */
+            __GPIOA_CLK_ENABLE();
+
+            /**CAN GPIO Configuration
+            PA11     ------> CAN_RX
+            PA12     ------> CAN_TX
+            */
+            GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
+            GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+            GPIO_InitStruct.Pull = GPIO_NOPULL;
+            GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
+            GPIO_InitStruct.Alternate = GPIO_AF4_CAN;
+            HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+        } else if ((pinRd == PB_8) && (pinTd == PB_9)) {
+            /* Peripheral clock enable */
+            __CAN_CLK_ENABLE();
+
+            /* Enable GPIO clock */
+            __GPIOB_CLK_ENABLE();
+
+            /**CAN GPIO Configuration
+            PB8     ------> CAN_RX
+            PB9     ------> CAN_TX
+            */
+            GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
+            GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+            GPIO_InitStruct.Pull = GPIO_NOPULL;
+            GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
+            GPIO_InitStruct.Alternate = GPIO_AF4_CAN;
+            HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+        } else {
+            printf("bad pins defined in CAN class\r\n");   
+        }        
+
+        /* Peripheral interrupt init*/
+        HAL_NVIC_SetPriority(CEC_CAN_IRQn, 0, 0);
+        HAL_NVIC_EnableIRQ(CEC_CAN_IRQn);
+        
+    } else {
+        printf("CAN_HandleTypeDef instance bad\r\n");    
+    }
+
+}
+
+/**
+  * @brief CAN MSP De-Initialization
+  *        This function frees the hardware resources used:
+  *          - Disable the Peripheral's clock
+  *          - Revert GPIO to their default state
+  * @param hcan: CAN handle pointer
+  * @retval None
+  */
+void
+HAL_CAN_MspDeInit (CAN_HandleTypeDef* hcan)
+{
+    // DEBUG
+    printf("Msp: HAL_CAN_MspDeInit\r\n");
+    
+    /* derived from STM32CubeMX */
+
+    if (hcan->Instance == ((CAN_TypeDef *) CAN_BASE)) {
+        /* Peripheral clock disable */
+        __CAN_CLK_DISABLE();
+
+        /* Disable pins*/
+        if ((pinRd == PA_11) && (pinTd == PA_12)) {
+            /**CAN GPIO Configuration
+            PA11     ------> CAN_RX
+            PA12     ------> CAN_TX
+            */
+            HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);
+        } else if ((pinRd == PB_8) && (pinTd == PB_9)) {
+            /**CAN GPIO Configuration
+            PB8     ------> CAN_RX
+            PB9     ------> CAN_TX
+            */
+            HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8|GPIO_PIN_9);
+        } else {}
+    }
+
+    /* Disable the NVIC for CAN reception */
+    HAL_NVIC_DisableIRQ(CEC_CAN_IRQn);
+}
+
+/**
+  * @brief  Reception  complete callback in non blocking mode
+  * @param  obj->hcan: pointer to a CAN_HandleTypeDef structure that contains
+  *         the configuration information for the specified CAN.
+  * @retval None
+  */
+void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)
+{    
+    /* Add message we jsut received to the can_message_queue */
+    /* cheats and uses global pointer to the only can_t used for STM32 F0 */
+    message_enqueue(single_can_controller, hcan->pRxMsg);
+    
+    /* Call user callback */
+    irq_handler (can_irq_ids[0], IRQ_RX);
+
+    
+
+    // BUG: CAN race condition if HAL_CAN_Receive_IT() is used.
+    // See https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Java/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2FSTM32Java%2FBUG%20CAN%20race%20condition%20if%20HAL%5FCAN%5FReceive%5FIT%20is%20used
+    //
+    // Fixed by Mark Burton:
+    // ideally, we should be able to call HAL_CAN_Receive_IT() here to set up for another
+    // receive but the API is flawed because that function will fail if HAL_CAN_Transmit()
+    // had already locked the handle when the receive interrupt occurred - so we do what
+    // HAL_CAN_Receive_IT() would do
+
+    /*
+    /* perform HAL_CAN_Receive_IT() fix *
+    if (hcan->State == HAL_CAN_STATE_BUSY_TX) {
+        hcan->State = HAL_CAN_STATE_BUSY_TX_RX;
+    } else {
+        hcan->State = HAL_CAN_STATE_BUSY_RX;
+
+        /* Set CAN error code to none *
+        hcan->ErrorCode = HAL_CAN_ERROR_NONE;
+        /* Enable Error warning Interrupt *
+        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_EWG);
+        /* Enable Error passive Interrupt *
+        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_EPV);
+        /* Enable Bus-off Interrupt *
+        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_BOF);
+        /* Enable Last error code Interrupt *
+        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_LEC);
+        /* Enable Error Interrupt *
+        __HAL_CAN_ENABLE_IT(hcan, CAN_IT_ERR);
+    }
+
+    // Enable FIFO 0 message pending Interrupt
+    __HAL_CAN_ENABLE_IT(hcan, CAN_IT_FMP0);
+    */
+
+    // try with standard call
+    //if (HAL_CAN_Receive_IT (hcan, CAN_FIFO0) != HAL_OK) {
+    //    printf("api: can_irq_init:  receive failed\r\n");
+    //}
+
+}
\ No newline at end of file