mbed library sources. Supersedes mbed-src.

Dependents:   Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more

targets/TARGET_GigaDevice/TARGET_GD32E10X/can_api.c

Committer:
AnnaBridge
Date:
2019-02-20
Revision:
189:f392fc9709a3

File content as of revision 189:f392fc9709a3:

/* mbed Microcontroller Library
 * Copyright (c) 2018 GigaDevice Semiconductor Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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.
 */

#include "can_api.h"
#include "pinmap.h"
#include "PeripheralPins.h"
#include "mbed_error.h"

#if DEVICE_CAN

/* BS1[3:0] + 1 + BS2[2:0] + 1 */
#define DEV_CAN_BT_SEG_MAX          24
#define DEV_CAN_BT_SEG_MIN          4

/* CAN related register mask */
#define DEV_CAN_BS1_MASK            0x000F0000
#define DEV_CAN_BS2_MASK            0x00700000
#define DEV_CAN_BAUDPSC_MASK        0x000003FF
#define DEV_CAN_SJW_MASK            0x03000000
/* CAN0 interrupt vector number */
#define CAN0_IRQ_BASE_NUM             19

/* CAN1 interrupt vector number */
#define CAN1_IRQ_BASE_NUM             63

static uint32_t can_irq_ids[2] = {0};
static can_irq_handler irq_callback;

/** CAN interrupt handle .
 *
 *  @param can_periph CAN0 or CAN1.
 *  @param id the CANx index .
 */
static void dev_can_irq_handle(uint32_t periph, int id)
{
    uint32_t flag0 = 0, flag1 = 0, flag2 = 0;

    flag0 = can_interrupt_flag_get(periph, CAN_INT_FLAG_MTF0);
    flag1 = can_interrupt_flag_get(periph, CAN_INT_FLAG_MTF1);
    flag2 = can_interrupt_flag_get(periph, CAN_INT_FLAG_MTF2);

    if (flag0) {
        can_flag_clear(periph, CAN_FLAG_MTF0);
    }
    if (flag1) {
        can_flag_clear(periph, CAN_FLAG_MTF1);
    }
    if (flag2) {
        can_flag_clear(periph, CAN_FLAG_MTF2);
    }

    /* CAN transmit complete interrupt handle */
    if (flag0 || flag1 || flag2) {
        irq_callback(can_irq_ids[id], IRQ_TX);
    }

    /* CAN receive complete interrupt handle */
    if (CAN_INTEN_RFNEIE0 == (CAN_INTEN(periph) & CAN_INTEN_RFNEIE0)) {
        if (0 != can_receive_message_length_get(periph, CAN_FIFO0)) {
            irq_callback(can_irq_ids[id], IRQ_RX);
        }
    }

    /* CAN error interrupt handle */
    if (SET == can_interrupt_flag_get(periph, CAN_INT_FLAG_ERRIF)) {
        /* passive error interrupt handle */
        if (CAN_INTEN_PERRIE == (CAN_INTEN(periph) & CAN_INTEN_PERRIE)) {
            if (SET == can_flag_get(periph, CAN_FLAG_PERR)) {
                irq_callback(can_irq_ids[id], IRQ_PASSIVE);
            }
        }

        /* bus-off interrupt handle */
        if (CAN_INTEN_BOIE == (CAN_INTEN(periph) & CAN_INTEN_BOIE)) {
            if (SET == can_flag_get(periph, CAN_FLAG_BOERR)) {
                irq_callback(can_irq_ids[id], IRQ_BUS);
            }
        }

        irq_callback(can_irq_ids[id], IRQ_ERROR);
    }
}

/** CAN1 Interrupt Request entry .
 *
 */
static void dev_can0_irq_entry(void)
{
    dev_can_irq_handle(CAN0, 0);
}

/** CAN1 Interrupt Request entry .
 *
 */
static void dev_can1_irq_entry(void)
{
    dev_can_irq_handle(CAN1, 1);
}

/** Config the CAN mode .
 *
 *  @param can_periph CAN0 or CAN1.
 *  @param mode the mode to be set.
 */
static void dev_can_mode_config(uint32_t can_periph, uint32_t mode)
{
    /* enter the initialization mode, only in initialization mode CAN register can be configured */
    can_working_mode_set(can_periph, CAN_MODE_INITIALIZE);

    CAN_BT(can_periph) &= ~BT_MODE(3);
    CAN_BT(can_periph) |= BT_MODE(mode);

    /* enter the normal mode */
    can_working_mode_set(can_periph, CAN_MODE_NORMAL);
}

/** Config the interrupt .
 *
 *  @param can_periph CAN0 or CAN1.
 *  @param interrupt The interrupt type.
 *  @param enable enable or disable.
 */
static void dev_can_interrupt_config(uint32_t can_periph, uint32_t interrupt, uint32_t enable)
{
    if (enable) {
        can_interrupt_enable(can_periph, interrupt);
    } else {
        can_interrupt_disable(can_periph, interrupt);
    }
}

/* This table can be used to calculate bit time
The first value is bit segment 1(BS1[3:0]), the second is bit segment 2(BS2[2:0]) */
static const int sampling_points[23][2] = {
    {0x0, 0x0},      /* 2, 50% */
    {0x1, 0x0},      /* 3, 67% */
    {0x2, 0x0},      /* 4, 75% */
    {0x3, 0x0},      /* 5, 80% */
    {0x3, 0x1},      /* 6, 67% */
    {0x4, 0x1},      /* 7, 71% */
    {0x5, 0x1},      /* 8, 75% */
    {0x6, 0x1},      /* 9, 78% */
    {0x6, 0x2},      /* 10, 70% */
    {0x7, 0x2},      /* 11, 73% */
    {0x8, 0x2},      /* 12, 75% */
    {0x9, 0x2},      /* 13, 77% */
    {0x9, 0x3},      /* 14, 71% */
    {0xA, 0x3},      /* 15, 73% */
    {0xB, 0x3},      /* 16, 75% */
    {0xC, 0x3},      /* 17, 76% */
    {0xD, 0x3},      /* 18, 78% */
    {0xD, 0x4},      /* 19, 74% */
    {0xE, 0x4},      /* 20, 75% */
    {0xF, 0x4},      /* 21, 76% */
    {0xF, 0x5},      /* 22, 73% */
    {0xF, 0x6},      /* 23, 70% */
    {0xF, 0x7},      /* 24, 67% */
};

/** Set the baudrate.
 *
 *  @param freq The frequency value to be set.
 *
 *  @returns
 *     CAN_BT register value
 */
static unsigned int dev_can_baudrate_set(int freq)
{
    uint32_t reval;
    uint16_t baud_psc;
    uint16_t baud_psc_max;
    uint32_t temp;
    uint32_t bt_reg_config;
    uint8_t flag;
    int bits;

    flag = 0;

    /* computes the value that the CAN_BT register needs to be configured */
    /* (BAUDPSC[9:0] + 1) * ((BS1[3:0] + 1) + (BS2[2:0] + 1) + SJW(always 1)) */
    bt_reg_config = (rcu_clock_freq_get(CK_APB1) / freq);
    /* BAUDPSC[9:0] minimum value */
    baud_psc = bt_reg_config / DEV_CAN_BT_SEG_MAX;
    /* BAUDPSC[9:0] maximum value */
    baud_psc_max = bt_reg_config / DEV_CAN_BT_SEG_MIN;

    while ((!flag) && (baud_psc < baud_psc_max)) {
        baud_psc++;
        for (bits = 22; bits > 0; bits--) {
            temp = (bits + 3) * (baud_psc + 1);
            if (temp == bt_reg_config) {
                flag = 1;
                break;
            }
        }
    }

    if (flag) {
        reval = ((sampling_points[bits][1] << 20) & DEV_CAN_BS2_MASK)
                | ((sampling_points[bits][0] << 16) & DEV_CAN_BS1_MASK)
                | ((1                   << 24) & DEV_CAN_SJW_MASK)
                | ((baud_psc            <<  0) & DEV_CAN_BAUDPSC_MASK);
    } else {
        /* CAN_BT register reset value */
        reval = 0x01230000;
    }

    return reval;
}

/** init the CAN.
 *
 */
void can_init(can_t *obj, PinName rd, PinName td)
{
    can_init_freq(obj, rd, td, 500000);
}

/** init the CAN frequency.
 *
 *  @param rd receive pin.
 *  @param td transmit pin.
 *  @param hz The bus frequency in hertz.
 */
void can_init_freq(can_t *obj, PinName rd, PinName td, int hz)
{
    CANName can_rd = (CANName)pinmap_peripheral(rd, PinMap_CAN_RD);
    CANName can_td = (CANName)pinmap_peripheral(td, PinMap_CAN_TD);
    obj->can = (CANName)pinmap_merge(can_rd, can_td);

    MBED_ASSERT((int)obj->can != NC);

    if (obj->can == CAN_0) {
        rcu_periph_clock_enable(RCU_CAN0);
        can_deinit(obj->can);
        obj->index = 0;
    } else if (obj->can == CAN_1) {
        rcu_periph_clock_enable(RCU_CAN0);
        rcu_periph_clock_enable(RCU_CAN1);
        can_deinit(obj->can);
        obj->index = 1;
    } else {
        return;
    }

    /* Configure the CAN pins */
    pinmap_pinout(rd, PinMap_CAN_RD);
    pinmap_pinout(td, PinMap_CAN_TD);
    if (rd != NC) {
        pin_mode(rd, PullUp);
    }
    if (td != NC) {
        pin_mode(td, PullUp);
    }

    dev_can_mode_config(obj->can, CAN_NORMAL_MODE);

    can_frequency(obj, hz);

    if (obj->can == CAN_0) {
        can_filter(obj, 0, 0, CANStandard, 0);
    } else {
        can_filter(obj, 0, 0, CANStandard, 14);
    }
}

/** disable CAN.
 *
 */
void can_free(can_t *obj)
{
    can_deinit(obj->can);

    if (obj->can == CAN0) {
        rcu_periph_clock_disable(RCU_CAN0);
    }

    if (obj->can == CAN1) {
        rcu_periph_clock_disable(RCU_CAN1);
    }
}

/** Set the frequency of the CAN interface.
 *
 *  @param hz The bus frequency in hertz.
 *
 *  @returns
 *    1 if successful,
 *    0 otherwise
 */
int can_frequency(can_t *obj, int hz)
{
    int reval;

    /* The maximum baud rate support to 1M */
    if (hz <= 1000000) {
        if (SUCCESS == can_working_mode_set(obj->can, CAN_MODE_INITIALIZE)) {
            CAN_BT(obj->can) = dev_can_baudrate_set(hz);
        } else {
            error("the configuration of can frequency is out of range \r\n");
        }

        if (SUCCESS == can_working_mode_set(obj->can, CAN_MODE_NORMAL)) {
            reval = 1;
        } else {
            reval = 0;
        }
    }

    return reval;
}

/** init the interrupt.
 *
 *  @param handler the interrupt callback.
 *  @param id the CANx index.
 */
void can_irq_init(can_t *obj, can_irq_handler handler, uint32_t id)
{
    irq_callback = handler;
    can_irq_ids[obj->index] = id;
}

/** disable the interrupt.
 *
 */
void can_irq_free(can_t *obj)
{
    if (CAN0 == obj->can) {
        can_interrupt_disable(obj->can, CAN_INTEN_TMEIE | CAN_INTEN_RFNEIE0 | CAN_INTEN_RFNEIE1 | \
                              CAN_INTEN_PERRIE | CAN_INTEN_BOIE | CAN_INTEN_ERRIE);
    }

    if (CAN1 == obj->can) {
        can_interrupt_disable(obj->can, CAN_INTEN_TMEIE | CAN_INTEN_RFNEIE0 | CAN_INTEN_RFNEIE1 | \
                              CAN_INTEN_PERRIE | CAN_INTEN_BOIE | CAN_INTEN_ERRIE);
    }

    can_irq_ids[obj->index] = 0;
}

/** Set the interrupt handle.
 *
 *  @param type The interrupt type.
 *  @param enable enable or disable.
 */
void can_irq_set(can_t *obj, CanIrqType type, uint32_t enable)
{
    uint32_t irq_num;
    uint32_t vector = 0;

    if (obj->can == CAN_0) {
        vector = (uint32_t)dev_can0_irq_entry;
        irq_num = CAN0_IRQ_BASE_NUM;
    }

    else if (obj->can == CAN_1) {
        vector = (uint32_t)dev_can1_irq_entry;
        irq_num = CAN1_IRQ_BASE_NUM;
    }

    switch (type) {
        case IRQ_RX:
            dev_can_interrupt_config(obj->can, CAN_INT_RFNE0, enable);
            break;
        case IRQ_TX:
            dev_can_interrupt_config(obj->can, CAN_INT_TME, enable);
            irq_num += 1;
            break;
        case IRQ_ERROR:
            dev_can_interrupt_config(obj->can, CAN_INT_ERR | CAN_INT_ERRN, enable);
            irq_num += 3;
            break;
        case IRQ_PASSIVE:
            dev_can_interrupt_config(obj->can, CAN_INT_ERR | CAN_INT_PERR, enable);
            irq_num += 3;
            break;
        case IRQ_BUS:
            dev_can_interrupt_config(obj->can, CAN_INT_ERR | CAN_INT_BO, enable);
            irq_num += 3;
            break;
        default:
            return;
    }

    NVIC_SetVector((IRQn_Type)irq_num, vector);
    NVIC_EnableIRQ((IRQn_Type)irq_num);
}

/** Write a CANMessage to the bus.
 *
 *  @param msg The CANMessage to write.
 *
 *  @returns
 *    0 if write failed,
 *    1 if write was successful
 */
int can_write(can_t *obj, CAN_Message msg, int cc)
{
    can_trasnmit_message_struct transmit_message;
    uint32_t i;

    can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &transmit_message);
    /* configure frame type: data or remote */
    if (CANData == msg.type) {
        transmit_message.tx_ft = CAN_FT_DATA;
    } else if (CANRemote == msg.type) {
        transmit_message.tx_ft = CAN_FT_REMOTE;
    } else {
        error("frame type of transmit message is invalid \r\n");
    }

    /* configure frame format: standard or extended */
    if (CANStandard == msg.format) {
        transmit_message.tx_ff = CAN_FF_STANDARD;
        transmit_message.tx_sfid = msg.id;
    } else if (CANExtended == msg.format) {
        transmit_message.tx_ff = CAN_FF_EXTENDED;
        transmit_message.tx_efid = msg.id;
    } else {
        error("frame format of transmit message is invalid \r\n");
    }

    transmit_message.tx_dlen = msg.len;

    for (i = 0; i < msg.len; i++) {
        transmit_message.tx_data[i] = msg.data[i];
    }

    can_message_transmit(obj->can, &transmit_message);

    return 1;
}

/** Read a CANMessage from the bus.
 *
 *  @param msg A CANMessage to read to.
 *  @param handle message filter handle (0 for any message).
 *
 *  @returns
 *    0 if no message arrived,
 *    1 if message arrived
 */
int can_read(can_t *obj, CAN_Message *msg, int handle)
{
    uint8_t i;
    uint8_t fifo_number;

    fifo_number = (uint8_t)handle;
    can_receive_message_struct receive_message;

    /* if the frame is not received, retrun 0 */
    if (0 == can_receive_message_length_get(obj->can, CAN_FIFO0)) {
        return 0;
    }

    can_message_receive(obj->can, fifo_number, &receive_message);

    if (receive_message.rx_ff == CAN_RFIFOMI_FF) {
        msg->format = CANExtended;
    } else {
        msg->format = CANStandard;
    }

    if (0 == msg->format) {
        msg->id = (uint32_t)0x000007FF & (receive_message.rx_sfid);
    } else {
        msg->id = (uint32_t)0x1FFFFFFF & (receive_message.rx_efid);
    }

    if (receive_message.rx_ft == CAN_RFIFOMI_FT) {
        msg->type = CANRemote;
    } else {
        msg->type = CANData;
    }

    msg->len = (uint8_t)receive_message.rx_dlen;

    for (i = 0; i < msg->len; i++) {
        msg->data[i] = (uint8_t)receive_message.rx_data[i];
    }
    /* If the frame is received successfully, retrun 1 */
    return 1;
}

/** Change CAN operation to the specified mode.
 *
 *  @param mode The new operation mode (CAN::Normal, CAN::Silent, CAN::LocalTest, CAN::GlobalTest, CAN::SilentTest).
 *
 *  @returns
 *    0 if mode change failed or unsupported,
 *    1 if mode change was successful
 */
int can_mode(can_t *obj, CanMode mode)
{
    switch (mode) {
        case MODE_NORMAL:
            dev_can_mode_config(obj->can, CAN_NORMAL_MODE);
            break;
        case MODE_SILENT:
            dev_can_mode_config(obj->can, CAN_SILENT_MODE);
            break;
        case MODE_TEST_GLOBAL:
        case MODE_TEST_LOCAL:
            dev_can_mode_config(obj->can, CAN_LOOPBACK_MODE);
            break;
        case MODE_TEST_SILENT:
            dev_can_mode_config(obj->can, CAN_SILENT_LOOPBACK_MODE);
            break;
        default:
            return 0;
    }

    return 1;
}

/** Filter out incomming messages.
 *
 *  @param id the id to filter on.
 *  @param mask the mask applied to the id.
 *  @param format format to filter on (Default CANAny).
 *  @param handle message filter handle (Optional).
 *
 *  @returns
 *    0 if filter change failed or unsupported,
 *    new filter handle if successful
 */
int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t handle)
{
    can_filter_parameter_struct can_filter;

    can_filter.filter_number = handle;
    can_filter.filter_mode = CAN_FILTERMODE_MASK;
    can_filter.filter_bits = CAN_FILTERBITS_32BIT;
    can_filter.filter_fifo_number = CAN_FIFO0;
    can_filter.filter_enable = ENABLE;

    switch (format) {
        case CANStandard:
            /* configure SFID[10:0] */
            can_filter.filter_list_high = id << 5;
            can_filter.filter_list_low =  0x0;
            /* configure SFID[10:0] mask */
            can_filter.filter_mask_high = mask << 5;
            /* both data and remote frames can be received */
            can_filter.filter_mask_low = 0x0;

            break;

        case CANExtended:
            /* configure EFID[28:13] */
            can_filter.filter_list_high = id >> 13;
            /* configure EFID[12:0] and frame format bit set */
            can_filter.filter_list_low = (id << 3) | (1 << 2);
            /* configure EFID[28:13] mask */
            can_filter.filter_mask_high = mask >> 13;
            /* configure EFID[12:0] and frame format bit mask */
            /* both data and remote frames can be received */
            can_filter.filter_mask_low = (mask << 3) | (1 << 2);

            break;

        case CANAny:
            error("CANAny mode is not supported \r\n");
            return 0;

        default:
            error("parameter is not supported \r\n");
            return 0;
    }

    can_filter_init(&can_filter);
    can1_filter_start_bank(handle);

    return handle;
}

/** Reset CAN interface.
 *
 * To use after error overflow.
 */
void can_reset(can_t *obj)
{
    can_deinit(obj->can);
}

/**  Detects read errors - Used to detect read overflow errors.
 *
 *  @returns number of read errors
 */
unsigned char can_rderror(can_t *obj)
{
    return can_receive_error_number_get(obj->can);
}

/** Detects write errors - Used to detect write overflow errors.
 *
 *  @returns number of write errors
 */
unsigned char can_tderror(can_t *obj)
{
    return can_transmit_error_number_get(obj->can);
}

/** Puts or removes the CAN interface into silent monitoring mode.
 *
 *  @param silent boolean indicating whether to go into silent mode or not.
 */
void can_monitor(can_t *obj, int silent)
{
    if (silent) {
        dev_can_mode_config(obj->can, CAN_SILENT_MODE);
    } else {
        dev_can_mode_config(obj->can, CAN_NORMAL_MODE);
    }
}

#endif