mbed library sources, include can_api for nucleo-f091rc
Dependents: CanNucleoF0_example
Fork of mbed-src by
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>© 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);
}
