Skip to content

Commit

Permalink
Merge SLCAN support to master
Browse files Browse the repository at this point in the history
CAN is part of the default build for the KITCHEN42 and BRAINV3.3 targets
The SLCAN feature is gated behind CAN_RX_AVAILABLE && VCDC_AVAILABLE
It can be enabled on other STM32F042 based targets with some care:
  Only the CANRX pin is accessible on most chips
  On the TSSOP-20 package, the DFU bootloader functionality on the BOOT0
  pin is shared with CANRX and must be disabled by tweaking the option
  bytes
  • Loading branch information
devanlai committed Aug 25, 2017
2 parents ef99b35 + 6311fd2 commit fb18de2
Show file tree
Hide file tree
Showing 20 changed files with 941 additions and 15 deletions.
2 changes: 1 addition & 1 deletion libopencm3
Submodule libopencm3 updated 278 files
237 changes: 237 additions & 0 deletions src/CAN/can.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/*
* Copyright (c) 2015, Devan Lai
*
* Permission to use, copy, modify, and/or distribute this software
* for any purpose with or without fee is hereby granted, provided
* that the above copyright notice and this permission notice
* appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <libopencm3/stm32/can.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/cm3/nvic.h>

#include <string.h>

#include "config.h"
#include "can.h"

#if CAN_RX_AVAILABLE

#define IS_POW_OF_TWO(X) (((X) & ((X)-1)) == 0)
_Static_assert(IS_POW_OF_TWO(CAN_RX_BUFFER_SIZE),
"Unmasked circular buffer size must be a power of two");
_Static_assert(CAN_RX_BUFFER_SIZE <= UINT8_MAX/2,
"Buffer size too big for unmasked circular buffer");

static volatile CAN_Message can_rx_buffer[CAN_RX_BUFFER_SIZE];
static volatile uint8_t can_rx_head = 0;
static volatile uint8_t can_rx_tail = 0;

bool can_rx_buffer_empty(void) {
return can_rx_head == can_rx_tail;
}

bool can_rx_buffer_full(void) {
return (uint8_t)(can_rx_tail - can_rx_head) == CAN_RX_BUFFER_SIZE;
}

CAN_Message* can_rx_buffer_peek(void) {
if (!can_rx_buffer_empty()) {
return (CAN_Message*)(&can_rx_buffer[(can_rx_head % CAN_RX_BUFFER_SIZE)]);
} else {
return NULL;
}
}

void can_rx_buffer_pop(void) {
can_rx_head++;

// Re-enable the ISR since we made space
nvic_enable_irq(CAN_NVIC_LINE);
}

static CAN_Message* can_rx_buffer_tail(void) {
return (CAN_Message*)&can_rx_buffer[(can_rx_tail % CAN_RX_BUFFER_SIZE)];
}

static void can_rx_buffer_extend(void) {
can_rx_tail++;
}

void can_rx_buffer_put(const CAN_Message* msg) {
memcpy((void*)&can_rx_buffer[(can_rx_tail % CAN_RX_BUFFER_SIZE)], (const void*)msg, sizeof(CAN_Message));
can_rx_tail++;
}

void can_rx_buffer_get(CAN_Message* msg) {
memcpy((void*)msg, (const void*)&can_rx_buffer[(can_rx_head % CAN_RX_BUFFER_SIZE)], sizeof(CAN_Message));
can_rx_head++;

// Re-enable the ISR since we made space
nvic_enable_irq(CAN_NVIC_LINE);
}

bool can_reconfigure(uint32_t baudrate, CanMode mode) {
nvic_disable_irq(CAN_NVIC_LINE);
can_disable_irq(CAN1, CAN_IER_FMPIE0);
can_reset(CAN1);

if (mode == MODE_RESET) {
// Just stop after resetting the CAN controller.
return true;
}

/* Set appropriate bit timing */
uint32_t sjw = CAN_BTR_SJW_1TQ;
uint32_t ts1;
uint32_t ts2;
uint32_t brp;

if (baudrate == 1000000) {
brp = 3;
ts1 = CAN_BTR_TS1_11TQ;
ts2 = CAN_BTR_TS2_4TQ;
} else if (baudrate == 500000) {
brp = 6;
ts1 = CAN_BTR_TS1_11TQ;
ts2 = CAN_BTR_TS2_4TQ;
} else if (baudrate == 250000) {
brp = 12;
ts1 = CAN_BTR_TS1_11TQ;
ts2 = CAN_BTR_TS2_4TQ;
} else if (baudrate == 125000) {
brp = 24;
ts1 = CAN_BTR_TS1_11TQ;
ts2 = CAN_BTR_TS2_4TQ;
} else if (baudrate == 100000) {
brp = 30;
ts1 = CAN_BTR_TS1_11TQ;
ts2 = CAN_BTR_TS2_4TQ;
} else {
return false;
}

bool loopback = (mode == MODE_TEST_LOCAL || mode == MODE_TEST_SILENT);
bool silent = (mode == MODE_SILENT || mode == MODE_TEST_SILENT);

bool TTCM = false; /* TTCM: Time triggered comm mode? */
bool ABOM = true; /* ABOM: Automatic bus-off management? */
bool AWUM = false; /* AWUM: Automatic wakeup mode? */
bool NART = false; /* NART: No automatic retransmission? */
bool RFLM = true; /* RFLM: Receive FIFO locked mode? */
bool TXFP = false; /* TXFP: Transmit FIFO priority? */

/* CAN cell init. */
if (can_init(CAN1, TTCM, ABOM, AWUM, NART, RFLM, TXFP,
sjw, ts1, ts2,
brp, /* BRP+1: Baud rate prescaler */
loopback,
silent) != 0) {
return false;
} else {
can_filter_id_mask_32bit_init(CAN1,
0, /* Filter ID */
0, /* CAN ID */
0, /* CAN ID mask */
0, /* FIFO assignment (here: FIFO0) */
true); /* Enable the filter. */
}

can_enable_irq(CAN1, CAN_IER_FMPIE0);
nvic_enable_irq(CAN_NVIC_LINE);
return true;
}

bool can_setup(uint32_t baudrate, CanMode mode) {
/* Enable CAN clock */
rcc_periph_clock_enable(RCC_CAN);

/* Setup CANRX */
gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO8);
gpio_set_af(GPIOB, GPIO_AF4, GPIO8);

#if CAN_TX_AVAILABLE
gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9);
gpio_set_af(GPIOB, GPIO_AF4, GPIO9);
#endif

return can_reconfigure(baudrate, mode);
}

static uint8_t can_fifo_depth(void) {
uint8_t fifo_depth = (CAN_RF0R(CAN1) & CAN_RF0R_FMP0_MASK);
// Account for one fifo entry possibly going away
if (CAN_RF0R(CAN1) & CAN_RF0R_RFOM0) {
fifo_depth = fifo_depth > 0 ? (fifo_depth - 1) : 0;
}

return fifo_depth;
}

bool can_read(CAN_Message* msg) {
bool success = false;

if (can_fifo_depth() > 0) {
// Wait for the previous message to be released
while (CAN_RF0R(CAN1) & CAN_RF0R_RFOM0);

uint8_t fmi;
bool ext, rtr;
can_receive(CAN1, 0, true, &msg->id, &ext, &rtr, &fmi, &msg->len, msg->data, NULL);
msg->format = ext ? CANExtended : CANStandard;
msg->type = rtr ? CANRemote : CANData;
success = true;
}

return success;
}

bool can_read_buffer(CAN_Message* msg) {
bool success = false;
if (!can_rx_buffer_empty()) {
can_rx_buffer_get(msg);
success = true;
}

return success;
}

bool can_write(CAN_Message* msg) {
bool ext = msg->format == CANExtended;
bool rtr = msg->type == CANRemote;
return (can_transmit(CAN1, msg->id, ext, rtr, msg->len, msg->data) != -1);
}

void cec_can_isr(void) {
uint8_t messages_queued = 0;
uint8_t fifo_depth = can_fifo_depth();
while (!can_rx_buffer_full() && messages_queued < fifo_depth) {
CAN_Message* msg = can_rx_buffer_tail();
if (can_read(msg)) {
can_rx_buffer_extend();
messages_queued++;
} else {
break;
}
}

// If the software buffer is full, disable the ISR so that
// the main loop can drain the buffer over USB.
if (messages_queued == 0) {
nvic_disable_irq(CAN_NVIC_LINE);
}
}


#endif
40 changes: 40 additions & 0 deletions src/CAN/can.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2015, Devan Lai
*
* Permission to use, copy, modify, and/or distribute this software
* for any purpose with or without fee is hereby granted, provided
* that the above copyright notice and this permission notice
* appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef CAN_H
#define CAN_H

#include "can_helper.h"

#define CAN_RX_BUFFER_SIZE 16

extern bool can_setup(uint32_t baudrate, CanMode mode);
extern bool can_reconfigure(uint32_t baudrate, CanMode mode);
extern bool can_read(CAN_Message* msg);
extern bool can_read_buffer(CAN_Message* msg);

extern bool can_write(CAN_Message* msg);

extern bool can_rx_buffer_empty(void);
extern bool can_rx_buffer_full(void);
extern void can_rx_buffer_put(const CAN_Message* msg);
extern void can_rx_buffer_get(CAN_Message* msg);
extern CAN_Message* can_rx_buffer_peek(void);
extern void can_rx_buffer_pop(void);

#endif
59 changes: 59 additions & 0 deletions src/CAN/can_helper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* 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.
*/
#ifndef MBED_CAN_HELPER_H
#define MBED_CAN_HELPER_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

enum CANFormat {
CANStandard = 0,
CANExtended = 1,
CANAny = 2
};
typedef enum CANFormat CANFormat;

enum CANType {
CANData = 0,
CANRemote = 1
};
typedef enum CANType CANType;

struct CAN_Message {
uint32_t id; // 29 bit identifier
uint8_t data[8]; // Data field
uint8_t len; // Length of data field in bytes
CANFormat format; // 0 - STANDARD, 1- EXTENDED IDENTIFIER
CANType type; // 0 - DATA FRAME, 1 - REMOTE FRAME
};
typedef struct CAN_Message CAN_Message;

typedef enum {
MODE_RESET,
MODE_NORMAL,
MODE_SILENT,
MODE_TEST_LOCAL,
MODE_TEST_SILENT
} CanMode;

#ifdef __cplusplus
};
#endif

#endif // MBED_CAN_HELPER_H
Loading

0 comments on commit fb18de2

Please sign in to comment.