Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CAN to SiLabs targets #10468

Merged
merged 3 commits into from
May 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions targets/TARGET_Silicon_Labs/TARGET_EFM32/PeripheralNames.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,17 @@ typedef enum {
} UARTName;
#endif

#if DEVICE_CAN
typedef enum {
#ifdef CAN0_BASE
CAN_0 = (int)CAN0_BASE,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please guard each enum entry with the respective base address define, in line with the other definitions in this file

#endif
#ifdef CAN1_BASE
CAN_1 = (int)CAN1_BASE,
#endif
} CANName;
#endif

#if DEVICE_QSPI
typedef enum {
#ifdef QSPI0_BASE
Expand Down
331 changes: 331 additions & 0 deletions targets/TARGET_Silicon_Labs/TARGET_EFM32/can_api.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
/* mbed Microcontroller Library
* Copyright (c) 2019 ToolSense
*
* 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 "clocking.h"


#if DEVICE_CAN

#include "cmsis.h"
#include "pinmap.h"
#include "pinmap_function.h"
#include "PeripheralPins.h"
#include "mbed_assert.h"
#include "em_cmu.h"
#include "em_can.h"

static uint32_t can_irq_ids[CAN_COUNT] = {0};
static can_irq_handler irq_handler;

// CAN bus interfaces
#define CAN_TX_IF 0
#define CAN_RX_IF 1

void can_init(can_t *obj, PinName rd, PinName td)
{
can_init_freq(obj, rd, td, 100000);
}

void can_init_freq(can_t *obj, PinName rd, PinName td, int hz)
{
CANName can_rd = (CANName)pinmap_peripheral(rd, PinMap_CAN_RX);
CANName can_td = (CANName)pinmap_peripheral(td, PinMap_CAN_TX);
obj->instance = (CAN_TypeDef *)pinmap_merge(can_rd, can_td);

CMU_Clock_TypeDef cmuClock_number;
switch ((CANName)obj->instance) {
#ifdef CAN0
case CAN_0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be wrapped based on presence of base peripheral

cmuClock_number = cmuClock_CAN0;
break;
#endif
#ifdef CAN1
case CAN_1:
cmuClock_number = cmuClock_CAN1;
break;
#endif
}

MBED_ASSERT((unsigned int)rd != NC);
MBED_ASSERT((unsigned int)td != NC);


// Configure CAN pins
pinmap_pinout(rd, PinMap_CAN_RX);
pinmap_pinout(td, PinMap_CAN_TX);


CMU_ClockEnable(cmuClock_number, true);

CAN_Init_TypeDef CanInit = CAN_INIT_DEFAULT;
CanInit.bitrate=hz;
CAN_Init(obj->instance, &CanInit);
CAN_SetMode(obj->instance, canModeNormal);

uint32_t loc_rd = pin_location(rd, PinMap_CAN_RX);
uint32_t loc_td = pin_location(td, PinMap_CAN_TX);

CAN_SetRoute(obj->instance, true, loc_rd, loc_td);


// Add pull-ups
if (rd != NC) {
pin_mode(rd, gpioModeInput);
}
if (td != NC) {
pin_mode(td, gpioModePushPull);
}

CAN_ConfigureMessageObject(obj->instance, CAN_TX_IF, 1, true, true, false, true, true);

CAN_ConfigureMessageObject(obj->instance, CAN_RX_IF, 2, true, false, false, true, true);

CAN_MessageObject_TypeDef receiver;

receiver.msgNum = 2;
receiver.id = 0;
receiver.mask = 0;
receiver.extended = false;
receiver.extendedMask = false;

CAN_SetIdAndFilter(obj->instance, CAN_RX_IF, true, &receiver, true);
}

void can_irq_init(can_t *obj, can_irq_handler handler, uint32_t id)
{
int index = 0;

switch ((CANName)obj->instance) {
#ifdef CAN0
case CAN_0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, please wrap in presence of base peripheral

index = 0;
break;
#endif
#ifdef CAN1
case CAN_1:
index = 1;
break;
#endif
}

irq_handler = handler;
can_irq_ids[index] = id;
}

void can_irq_free(can_t *obj)
{
CAN_MessageIntDisable(obj->instance, _CAN_IF0IEN_MESSAGE_MASK);
CAN_MessageIntClear(obj->instance, 0xFFFFFFFF);

switch ((CANName)obj->instance) {
#ifdef CAN0
case CAN_0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, please wrap in presence of the base peripheral

NVIC_DisableIRQ(CAN0_IRQn);
break;
#endif
#ifdef CAN1
case CAN_1:
NVIC_DisableIRQ(CAN1_IRQn);
break;
#endif
}
}

void can_free(can_t *obj)
{
CAN_Enable(obj->instance, false);
}

int can_frequency(can_t *obj, int f)
{
CAN_Init_TypeDef CanInit = CAN_INIT_DEFAULT;
CanInit.bitrate=f;

CAN_SetBitTiming(obj->instance,
CanInit.bitrate,
CanInit.propagationTimeSegment,
CanInit.phaseBufferSegment1,
CanInit.phaseBufferSegment2,
CanInit.synchronisationJumpWidth);
return 0;
}

int can_write(can_t *obj, CAN_Message msg, int cc)
{
CAN_MessageObject_TypeDef message;

message.id = msg.id;
message.msgNum = 1;

if (msg.format == 0) message.extended = false;
else message.extended = true;

message.dlc = msg.len;

for (int i = 0; i < message.dlc; ++i) {
message.data[i] = (uint32_t)msg.data[i];
}

CAN_SendMessage(obj->instance, CAN_TX_IF, &message, true);

return 1;
}

int can_read(can_t *obj, CAN_Message *msg, int handle)
{
CAN_MessageObject_TypeDef receiver;

if (CAN_HasNewdata(obj->instance)) {

receiver.msgNum = 2;
receiver.extended = false;
receiver.extendedMask = false;

CAN_ReadMessage(obj->instance, CAN_RX_IF, &receiver);

msg->id = receiver.id;
msg->len = receiver.dlc;

for (int i = 0; i < receiver.dlc; ++i) {
msg->data[i] = (unsigned char)receiver.data[i];
}

return 1;
}

return 0;
}

void can_reset(can_t *obj)
{
CAN_Reset(obj->instance);
}

unsigned char can_rderror(can_t *obj)
{
return ((obj->instance->ERRCNT>>_CAN_ERRCNT_REC_SHIFT)&0xFF);
}

unsigned char can_tderror(can_t *obj)
{
return ((obj->instance->ERRCNT)&0xFF);
}

void can_monitor(can_t *obj, int silent)
{
CanMode mode = MODE_NORMAL;

if (silent) {
mode = MODE_SILENT;
}

CAN_SetMode(obj->instance, mode);
}

int can_mode(can_t *obj, CanMode mode)
{
CAN_SetMode(obj->instance, mode);
return 1;
}

int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t handle)
{
CAN_MessageObject_TypeDef message;

message.msgNum = 2;
message.id = id;
message.mask = mask;

if (format == CANStandard) {
message.extended = false;
message.extendedMask = false;
} else {
message.extended = true;
message.extendedMask = true;
}

CAN_SetIdAndFilter(obj->instance, CAN_RX_IF, true, &message, true);

return handle;
}

void can_irq_set(can_t *obj, CanIrqType type, uint32_t enable)
{
uint32_t ier = _CAN_IF0IEN_MESSAGE_MASK;

if (enable) {
CAN_MessageIntEnable(obj->instance, ier);
} else {
CAN_MessageIntDisable(obj->instance, ier);
}

switch ((CANName)obj->instance) {
#ifdef CAN0
case CAN_0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usage of enum values should be wrapped and compiled in/out based on whether the base peripheral is present or not.

NVIC_SetVector(CAN0_IRQn, (uint32_t)CAN0_IRQHandler);
NVIC_EnableIRQ(CAN0_IRQn);
break;
#endif
#ifdef CAN1
case CAN_1:
NVIC_SetVector(CAN1_IRQn, (uint32_t)CAN1_IRQHandler);
NVIC_EnableIRQ(CAN1_IRQn);
break;
#endif
}
}

static void can_irq(CANName name, int id)
{
CAN_TypeDef *can;
can = (CAN_TypeDef *)name;

if (can->STATUS & CAN_STATUS_RXOK) {
irq_handler(can_irq_ids[id], IRQ_RX);
CAN_MessageIntClear(can, 0xFFFFFFFF);
}

if (can->STATUS & CAN_STATUS_TXOK) {
irq_handler(can_irq_ids[id], IRQ_TX);
CAN_MessageIntClear(can, 0xFFFFFFFF);
}
}

#ifdef CAN0
void CAN0_IRQHandler(void)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap in respective peripheral base definition, as this shouldn't be there if there is no (for example) CAN1.

{
can_irq(CAN_0, 0);
}
#endif

#ifdef CAN1
void CAN1_IRQHandler(void)
{
can_irq(CAN_1, 1);
}
#endif

const PinMap *can_rd_pinmap()
{
return PinMap_CAN_TX;
}

const PinMap *can_td_pinmap()
{
return PinMap_CAN_RX;
}

#endif //DEVICE_CAN
Loading