mbed-os/targets/TARGET_GigaDevice/TARGET_GD32E10X/can_api.c

644 lines
17 KiB
C

/* 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 = 0;
/* 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);
}
}
const PinMap *can_rd_pinmap()
{
return PinMap_CAN_TD;
}
const PinMap *can_td_pinmap()
{
return PinMap_CAN_RD;
}
#endif