mbed library sources, include can_api for nucleo-f091rc

Dependents:   CanNucleoF0_example

Fork of mbed-src by mbed official

targets/hal/TARGET_STM/TARGET_STM32F0/can_api.cpp

Committer:
ptpaterson
Date:
2016-01-07
Revision:
645:13c87cbecd54
Parent:
642:069330c922be

File content as of revision 645:13c87cbecd54:

/*
  ******************************************************************************
  * @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 <hudakz@inbox.com>
  *
  * 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 "string.h"

#include "mbed.h"

#define CAN_NUM 1

/**
 * @brief indicate to use the interrupt method or polling method.
 * @note Having serious difficulty determining why interrupt
 * method will not work.  Best I can tell so far is that it is
 * getting stuck in a loop inside the ISR and refusing to come
 * out.  The blinking led is on a scheduled task, so the fact that
 * it freezes upon receiving a message (write is fine!) supports
 * my guess.
 */


/* holder for objects that need to be global */
CAN_HandleTypeDef hcan;
static PinName pinRd;
static PinName pinTd;

/**
  * @note
  * @param
  * @retval none
  */
void can_init (can_t    *obj, PinName   rd, PinName   td)
{
    // DEBUG
    printf("api: can_init\r\n");

    /* set global pin values for MSP functions */
    pinRd = rd;
    pinTd = td;

    /* Set the peripheral pointer */
    hcan.Instance = ((CAN_TypeDef*)CAN_BASE);

    /* initialize the mail boxes */
    static CanTxMsgTypeDef txMessage;
    static CanRxMsgTypeDef rxMessage;
    hcan.pTxMsg = &txMessage;
    hcan.pRxMsg = &rxMessage;

    /* Initialize the CAN peripheral */
    hcan.Init.TTCM = DISABLE;
    hcan.Init.ABOM = ENABLE;
    hcan.Init.AWUM = DISABLE;
    hcan.Init.NART = DISABLE;
    hcan.Init.RFLM = DISABLE;
    hcan.Init.TXFP = DISABLE;
    hcan.Init.Mode = CAN_MODE_NORMAL;

    // 125kbps bit rate (default)
    // APB1 peripheral clock = 48000000Hz
    hcan.Init.Prescaler = 24;     // number of time quanta = 48000000/24/125000 = 16
    hcan.Init.SJW = CAN_SJW_1TQ;
    hcan.Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
    hcan.Init.BS2 = CAN_BS2_4TQ;

    int status = HAL_CAN_Init (&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 (&hcan);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int can_frequency(can_t *obj, int hz)
{
    HAL_NVIC_DisableIRQ(CEC_CAN_IRQn);

    // APB1 peripheral clock = 48000000Hz
    switch(hz) {
        case 1000000:
            // 1000kbps bit rate
            hcan.Init.Prescaler = 4;      // number of time quanta = 48000000/4/1000000 = 12
            hcan.Init.SJW = CAN_SJW_1TQ;
            hcan.Init.BS1 = CAN_BS1_8TQ;  // sample point at: (1 + 8) / 12 * 100 = 75%
            hcan.Init.BS2 = CAN_BS2_3TQ;
            break;

        case 500000:
            // 500kbps bit rate
            hcan.Init.Prescaler = 8;      // number of time quanta = 48000000/8/500000 = 12
            hcan.Init.SJW = CAN_SJW_1TQ;
            hcan.Init.BS1 = CAN_BS1_8TQ;  // sample point at: (1 + 8) / 12 * 100 = 75%
            hcan.Init.BS2 = CAN_BS2_3TQ;
            break;

        case 250000:
            // 250kbps
            hcan.Init.Prescaler = 12;     // number of time quanta = 48000000/12/250000 = 16
            hcan.Init.SJW = CAN_SJW_1TQ;
            hcan.Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
            hcan.Init.BS2 = CAN_BS2_4TQ;
            break;

        case 125000:
            // 125kbps
            hcan.Init.Prescaler = 24;     // number of time quanta = 48000000/24/125000 = 16
            hcan.Init.SJW = CAN_SJW_1TQ;
            hcan.Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
            hcan.Init.BS2 = CAN_BS2_4TQ;
            break;

        default:
            // 125kbps (default)
            hcan.Init.Prescaler = 24;     // number of time quanta = 48000000/24/125000 = 16
            hcan.Init.SJW = CAN_SJW_1TQ;
            hcan.Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
            hcan.Init.BS2 = CAN_BS2_4TQ;
            break;
    }

    HAL_CAN_Init(&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_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];

/**
 * @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;
}

/** 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 (&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)
{
    /* record that the user has attached a callback of type CanIrqType */
    /* perhaps switch from polling to interrupt if we were that awesome! */
}

/**
 * @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) {
        hcan.pTxMsg->StdId = msg.id;
        hcan.pTxMsg->ExtId = 0x00;
    } else {
        hcan.pTxMsg->StdId = 0x00;
        hcan.pTxMsg->ExtId = msg.id;
    }

    hcan.pTxMsg->RTR = msg.type == CANData ? CAN_RTR_DATA : CAN_RTR_REMOTE;
    hcan.pTxMsg->IDE = msg.format == CANStandard ? CAN_ID_STD : CAN_ID_EXT;
    hcan.pTxMsg->DLC = msg.len;

    memcpy(hcan.pTxMsg->Data, &(msg.data), msg.len);

    int result = 1;
    if (HAL_CAN_Transmit(&hcan, 5) != HAL_OK) {
        result = 0;
    }

    return result;

}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int
can_read (can_t        *obj,
          CAN_Message  *msg,
          int           handle)
{

    int result = 0;

    CanRxMsgTypeDef popMessage;
    if (message_dequeue (obj, &popMessage)) {

        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;

        memcpy(msg->data, &(popMessage.Data), msg->len);

        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:
            hcan.Init.Mode = CAN_MODE_NORMAL;
            break;

        case MODE_SILENT:
            hcan.Init.Mode = CAN_MODE_SILENT;
            break;

        case MODE_TEST_GLOBAL:
            hcan.Init.Mode = CAN_MODE_LOOPBACK;
            break;

        case MODE_TEST_LOCAL:
            hcan.Init.Mode = CAN_MODE_LOOPBACK;
            break;

        case MODE_TEST_SILENT:
            hcan.Init.Mode = CAN_MODE_SILENT_LOOPBACK;
            break;
    }

    if (success != HAL_ERROR) {
        success = HAL_CAN_Init(&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(&hcan, &sFilterConfig);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void
can_reset (can_t *obj)
{
    __HAL_CAN_RESET_HANDLE_STATE(&hcan);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
unsigned char
can_rderror (can_t *obj)
{
    return HAL_CAN_GetError(&hcan);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
unsigned char
can_tderror (can_t *obj)
{
    return HAL_CAN_GetError(&hcan);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void
can_monitor (can_t *obj,
             int silent)
{
    // TODO: implement
}

/*=============================================================================
 * HAL_MSP and other functions
 *=============================================================================
 */

/**
  * @brief  CAN MSP Initialization
  * @param  hcan: CAN handle pointer
  * @retval None
  */
void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
{
    GPIO_InitTypeDef    GPIO_InitStruct;

    if((pinRd == PA_11) && (pinTd == PA_12)) {

        /* CAN1 Periph clock enable */
        __CAN_CLK_ENABLE();

        /* Enable GPIO clock */
        __GPIOA_CLK_ENABLE();

        /* CAN1 RX GPIO pin configuration */
        GPIO_InitStruct.Pin = GPIO_PIN_11;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Alternate =  GPIO_AF4_CAN;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        /* CAN1 TX GPIO pin configuration */
        GPIO_InitStruct.Pin = GPIO_PIN_12;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Alternate =  GPIO_AF4_CAN;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    } else if((pinRd == PB_8) && (pinTd == PB_9)) {
        /* CAN1 Periph clock enable */
        __CAN_CLK_ENABLE();

        /* Enable GPIO clock */
        __GPIOB_CLK_ENABLE();

        /* CAN1 RX GPIO pin configuration */
        GPIO_InitStruct.Pin = GPIO_PIN_8;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Alternate =  GPIO_AF4_CAN;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        /* CAN1 TX GPIO pin configuration */
        GPIO_InitStruct.Pin = GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Alternate =  GPIO_AF4_CAN;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    } else
        return;
    /* NVIC configuration for CAN1 Reception complete interrupt */
    HAL_NVIC_SetPriority(CEC_CAN_IRQn, 1, 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)
{

    /* Reset peripherals */

    __CAN_FORCE_RESET();
    __CAN_RELEASE_RESET();

    /* Disable peripherals and GPIO Clocks */
    if((pinRd == PA_11) && (pinTd == PA_12)) {
        /* De-initialize the CAN1 RX GPIO pin */
        HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11);

        /* De-initialize the CAN1 TX GPIO pin */
        HAL_GPIO_DeInit(GPIOA, GPIO_PIN_12);
    } else {

        /* De-initialize the CAN1 RX GPIO pin */
        HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8);

        /* De-initialize the CAN1 TX GPIO pin */
        HAL_GPIO_DeInit(GPIOB, GPIO_PIN_9);
    }


    /* Disable the NVIC for CAN reception */
    HAL_NVIC_DisableIRQ(CEC_CAN_IRQn);
}

/**
* @brief  Handles CAN RX0 interrupt request.
* @param  None
* @note STM32F0 uses different interrupts than F4
* @retval None
*/
void CEC_CAN_IRQHandler(void)
{
    HAL_CAN_IRQHandler(&hcan);
}



/**
  * @brief  Reception  complete callback in non blocking mode
  * @param  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)
    //         rxCompleteCallback();
    // }
    // 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

    
    irq_handler (can_irq_ids[0], IRQ_RX);

    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);
}