Dual Can PICO V1.0

Raspberry PICO is an interesting extension of Raspberry family. It is based on RP2040 microcontroller, a dual-core Arm Cortex-M0+ processor, flexible clock running up to 133 MHz 264kB on-chip SRAM 2MB on-board QSPI flash, 26 GPIO pins, including 3 analogue inputs.

There are the following peripherals:

  • 2 × UART
  • 2 × SPI controllers
  • 2 × I2C controllers
  • 16 × PWM channels
  • 1 × USB 1.1 controller and PHY, with host and device support
  • 8 × PIO state machines

Dual Can Bus PICO is an adapter to use our Can Bus Board with PICO. The idea is to connect all the 40 pins connector of Raspberry to the new board.

Buy on our shop

Hardware Files

All our projects are open hardware, these are the production files:

Software Configuration

An embedded system is very interesting for open hardware community only if it is well supported with free IDE and library. There are two IDE software to develop firmware: Arduino IDE and Visual Studio Code.

For stability and simplicity we prefer to use Arduino IDE. The RP2040 core used in this example is developed by Earle Philhower. Use the following steps to prepare the IDE:

  • Open IDE
  • Click on File
  • Click on Preference
  • Click on Additional Board Manager URLs and add this link https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
  • Click OK
  • Click on Tools
  • Click on Board Manager
  • Install RP2040 core
  • Install ACAN2515 of Pierre Molinaro
  • Click on Sketch
  • Include Library
  • Manage Libraries
  • Select Raspberry Pi PICO

Dual Can Bus Sketch

Download PicoDualCan_V1.0.ino

There are two kind of RTC for our boards, the DS3231 and PCF85063. Verify the installed RTC on your board and enabled the correct #define.

//——————————————————————————————————————————————————————————————————————————————
//  SG Electronic Systems srls 
//  This Example is derived by
//  ACAN2515 Demo in loopback mode, for the Raspberry Pi Pico
//  Thanks to Duncan Greenwood for providing this sample sketch
//——————————————————————————————————————————————————————————————————————————————
// Pico Adapter Pinout
// SPI0_SCK         2     
// SPI0_MISO        0     
// SPI0_MOSI        3     
// SPI0_CS0         1     CAN0
// SPI0_CS1         9     CAN1      
// MCP2515_INT1     4     CAN0
// MCP2515_INT2     12    CAN1 
// SPI1_SCK         10    
// SPI1_MISO        8     
// SPI1_MOSI        11    
// SPI1_CS0         6     
// SPI1_CS1         13    
// I2C0_SDA         20    RTC
// I2C0_SCL         21    RTC
// I2C1_SDA         14    
// I2C1_SCL         15    
// LED1             18
// LED2             19
// UART0_RX         17
// UART0_TX         16
#ifndef ARDUINO_ARCH_RP2040
#error "Select a Raspberry Pi Pico board"
#endif
#define LED1  18
#define LED2  19
#define LED
#define CAN0
#define CAN1
#define RTC
#define RTC_DS3231
//#define RTC_PCF85063
//——————————————————————————————————————————————————————————————————————————————
#include <Wire.h>
#ifdef RTC_DS3231
#include "DS3231.h"
#endif
#ifdef RTC_PCF85063
#include "PCF85063TP.h"
#endif
#include <ACAN2515.h>
//——————————————————————————————————————————————————————————————————————————————
// The Pico has two SPI peripherals, SPI and SPI1. Either (or both) can be used.
// The are no default pin assignments so they must be set explicitly.
// Testing was done with Earle Philhower's arduino-pico core:
// https://github.com/earlephilhower/arduino-pico
//——————————————————————————————————————————————————————————————————————————————
static const byte SPI0_SCK  = 2 ; // SCK input of MCP2515
static const byte SPI0_MOSI = 3 ; // SDI input of MCP2515
static const byte SPI0_MISO = 0 ; // SDO output of MCP2515
static const byte SPI0_CS0  = 1 ;  // CS input of MCP2515 1
static const byte SPI0_CS1  = 9 ;  // CS input of MCP2515 (2
static const byte I2C0_SDA  = 20 ; 
static const byte I2C0_SCL  = 21 ; 
#ifdef CAN0
static const byte MCP2515_INT0 = 4 ;  // INT output of MCP2515 (adapt to your design)
ACAN2515 can0 (SPI0_CS0, SPI, MCP2515_INT0) ;
#endif
#ifdef CAN1
static const byte MCP2515_INT1 = 12 ;  // INT output of MCP2515 (adapt to your design)
ACAN2515 can1 (SPI0_CS1, SPI, MCP2515_INT1) ;
#endif
#ifdef RTC_DS3231
#define DS3231_I2C_ADDRESS 0x68
#endif
#ifdef RTC_PCF85063
PCD85063TP clock;//define a object of PCD85063TP class
#endif
static const uint32_t QUARTZ_FREQUENCY = 16UL * 1000UL * 1000UL ; // 16 MHz
static uint32_t gBlinkLedDate0 = 0 ;
static uint32_t gReceivedFrameCount0 = 0 ;
static uint32_t gSentFrameCount0 = 0 ;
static uint32_t gBlinkLedDate1 = 0 ;
static uint32_t gReceivedFrameCount1 = 0 ;
static uint32_t gSentFrameCount1 = 0 ;
//——————————————————————————————————————————————————————————————————————————————
//   SETUP
//——————————————————————————————————————————————————————————————————————————————
void setup () {
  #ifdef LED
  pinMode (LED1, OUTPUT) ;  // For CAN0
  digitalWrite (LED1, HIGH) ;
  pinMode (LED2, OUTPUT) ;  // For CAN1
  digitalWrite (LED2, HIGH) ;
  #else   //--- Switch on builtin led
  pinMode (LED_BUILTIN, OUTPUT) ;
  digitalWrite (LED_BUILTIN, HIGH) ;
  #endif
  //--- Start serial
  Serial.begin (115200) ;
  
  //--- Wait for serial (blink led at 10 Hz during waiting)
  while (!Serial) {
    delay (50) ;
    #ifdef LED
    digitalWrite (LED1, !digitalRead (LED1)) ;
    delay (50) ;
    digitalWrite (LED2, !digitalRead (LED2)) ;
    #else
    digitalWrite (LED_BUILTIN, !digitalRead (LED_BUILTIN)) ;
    #endif
  }
  //--- There are no default SPI pins so they must be explicitly assigned
  SPI.setSCK(SPI0_SCK);
  SPI.setTX(SPI0_MOSI);
  SPI.setRX(SPI0_MISO);
  //--- Begin SPI
  SPI.begin () ;
  #ifdef CAN0
  //--- Configure ACAN2515
  Serial.println ("Configure ACAN2515 CAN0") ;
  ACAN2515Settings settings0 (QUARTZ_FREQUENCY, 125UL * 1000UL) ; // CAN bit rate 125 kb/s
  settings0.mRequestedMode = ACAN2515Settings::NormalMode  ; // Select NormalMode  mode
  const uint16_t errorCode0 = can0.begin (settings0, [] { can0.isr () ; }) ;
  if (errorCode0 == 0) {
    Serial.print ("Bit Rate prescaler: ") ;
    Serial.println (settings0.mBitRatePrescaler) ;
    Serial.print ("Propagation Segment: ") ;
    Serial.println (settings0.mPropagationSegment) ;
    Serial.print ("Phase segment 1: ") ;
    Serial.println (settings0.mPhaseSegment1) ;
    Serial.print ("Phase segment 2: ") ;
    Serial.println (settings0.mPhaseSegment2) ;
    Serial.print ("SJW: ") ;
    Serial.println (settings0.mSJW) ;
    Serial.print ("Triple Sampling: ") ;
    Serial.println (settings0.mTripleSampling ? "yes" : "no") ;
    Serial.print ("Actual bit rate: ") ;
    Serial.print (settings0.actualBitRate ()) ;
    Serial.println (" bit/s") ;
    Serial.print ("Exact bit rate ? ") ;
    Serial.println (settings0.exactBitRate () ? "yes" : "no") ;
    Serial.print ("Sample point: ") ;
    Serial.print (settings0.samplePointFromBitStart ()) ;
    Serial.println ("%") ;
  } else {
    Serial.print ("Configuration 0 error 0x") ;
    Serial.println (errorCode0, HEX) ;
  }
  #endif
  #ifdef CAN1
    //--- There are no default SPI pins so they must be explicitly assigned
  //--- Configure ACAN2515 CAN1
  Serial.println ("Configure ACAN2515 CAN1") ;
  ACAN2515Settings settings1 (QUARTZ_FREQUENCY, 125UL * 1000UL) ; // can1 bit rate 125 kb/s
  settings1.mRequestedMode = ACAN2515Settings::NormalMode  ; // Select NormalMode  mode
  const uint16_t errorCode1 = can1.begin (settings1, [] { can1.isr () ; }) ;
  if (errorCode1 == 0) {
    Serial.print ("Bit Rate prescaler: ") ;
    Serial.println (settings1.mBitRatePrescaler) ;
    Serial.print ("Propagation Segment: ") ;
    Serial.println (settings1.mPropagationSegment) ;
    Serial.print ("Phase segment 1: ") ;
    Serial.println (settings1.mPhaseSegment1) ;
    Serial.print ("Phase segment 2: ") ;
    Serial.println (settings1.mPhaseSegment2) ;
    Serial.print ("SJW: ") ;
    Serial.println (settings1.mSJW) ;
    Serial.print ("Triple Sampling: ") ;
    Serial.println (settings1.mTripleSampling ? "yes" : "no") ;
    Serial.print ("Actual bit rate: ") ;
    Serial.print (settings1.actualBitRate ()) ;
    Serial.println (" bit/s") ;
    Serial.print ("Exact bit rate ? ") ;
    Serial.println (settings1.exactBitRate () ? "yes" : "no") ;
    Serial.print ("Sample point: ") ;
    Serial.print (settings1.samplePointFromBitStart ()) ;
    Serial.println ("%") ;
  }else{
    Serial.print ("Configuration 1 error 0x") ;
    Serial.println (errorCode1, HEX) ;
  }  
  #endif
  #ifdef RTC
  Wire.setSDA(I2C0_SDA);
  Wire.setSCL(I2C0_SCL);
  Wire.begin();
  #endif
  #ifdef RTC_PCF85063 
  clock.begin();
  //clock.setcalibration(1, 32767.2);  // Setting offset by clock frequency
  uint8_t ret = clock.calibratBySeconds(0, -0.000041);
  Serial.print("offset value: ");
  Serial.print("0x");
  Serial.println(ret, HEX);
  #endif
}
#ifdef RTC_PCF85063 
void displayTime()
{
  char minute, hour, dayOfWeek, dayOfMonth, month, year, second;
  clock.getTime();
  second = clock.second;
  minute = clock.minute;
  hour = clock.hour;
  dayOfWeek = clock.dayOfWeek;
  dayOfMonth = clock.dayOfMonth;
  month = clock.month;
  year = clock.year+2000;
  // send it to the serial monitor
  Serial.print(hour, DEC);
  // convert the byte variable to a decimal number when displayed
  Serial.print(":");
  if (minute<10)
  {
    Serial.print("0");
  }
  Serial.print(minute, DEC);
  Serial.print(":");
  if (second<10)
  {
    Serial.print("0");
  }
  Serial.print(second, DEC);
  Serial.print(" ");
  Serial.print(dayOfMonth, DEC);
  Serial.print("/");
  Serial.print(month, DEC);
  Serial.print("/");
  Serial.print(year, DEC);
  Serial.print(" Day of week: ");
  switch(dayOfWeek){
  case 1:{
    Serial.println("Sunday");
    }
    break;
  case 2:{
    Serial.println("Monday");
  }
    break;
  case 3:{
    Serial.println("Tuesday");
  }
    break;
  case 4:{
    Serial.println("Wednesday");
  }
    break;
  case 5:{
    Serial.println("Thursday");
    }
    break;
  case 6:{
    Serial.println("Friday");
  }
    break;
  case 7:{
    Serial.println("Saturday");
  }
    break;
    default:
  Serial.println();
  break;
  } 
}
#endif
#ifdef RTC_DS3231
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return( (val/10*16) + (val%10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return( (val/16*10) + (val%16) );
}
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte
dayOfMonth, byte month, byte year)
{
  // sets time and date data to DS3231
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set next input to start at the seconds register
  Wire.write(decToBcd(second)); // set seconds
  Wire.write(decToBcd(minute)); // set minutes
  Wire.write(decToBcd(hour)); // set hours
  Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
  Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
  Wire.write(decToBcd(month)); // set month
  Wire.write(decToBcd(year)); // set year (0 to 99)
  Wire.endTransmission();
}
void readDS3231time(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  // request seven bytes of data from DS3231 starting from register 00h
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}
void displayTime()
{
  byte  minute, hour, dayOfWeek, dayOfMonth, month, year, second;
  // retrieve data from DS3231
  readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
  &year); 
  // send it to the serial monitor
  Serial.print(hour, DEC);
  // convert the byte variable to a decimal number when displayed
  Serial.print(":");
  if (minute<10)
  {
    Serial.print("0");
  }
  Serial.print(minute, DEC);
  Serial.print(":");
  if (second<10)
  {
    Serial.print("0");
  }
  Serial.print(second, DEC);
  Serial.print(" ");
  Serial.print(dayOfMonth, DEC);
  Serial.print("/");
  Serial.print(month, DEC);
  Serial.print("/");
  Serial.print(year, DEC);
  Serial.print(" Day of week: ");
  switch(dayOfWeek){
  case 1:{
    Serial.println("Sunday");
    }
    break;
  case 2:{
    Serial.println("Monday");
  }
    break;
  case 3:{
    Serial.println("Tuesday");
  }
    break;
  case 4:{
    Serial.println("Wednesday");
  }
    break;
  case 5:{
    Serial.println("Thursday");
    }
    break;
  case 6:{
    Serial.println("Friday");
  }
    break;
  case 7:{
    Serial.println("Saturday");
  }
    break;
  }
  
}
#endif
void loop () {
  CANMessage frame0 ;
  CANMessage frame1 ;
  CANMessage frame_read ;
  can0.poll () ;
  can1.poll () ;
  frame0.id =  0x010 ;
  frame0.len =  8 ;
  frame0.data[0] = 10;
  frame0.data[1] = 2;
  frame0.data[2] = 3;
  frame0.data[3] = 4;
  frame0.data[4] = 5;
  frame0.data[5] = 6;
  frame0.data[6] = 7;
  frame0.data[7] = 8;
  frame1.id =  0x002 ;
  frame1.len =  8 ;
  frame1.data[0] = 11;
  frame1.data[1] = 12;
  frame1.data[2] = 13;
  frame1.data[3] = 14;
  frame1.data[4] = 15;
  frame1.data[5] = 16;
  frame1.data[6] = 17;
  frame1.data[7] = 18;
  int len = 0;
  #ifdef CAN0
  // CAN0 loop
  if (gBlinkLedDate0 < millis ()) {
    gBlinkLedDate0 += 2000 ;
    digitalWrite (LED1, !digitalRead (LED1)) ;
    const bool ok0 = can0.tryToSend (frame0) ;
    if (ok0) {
      gSentFrameCount0 += 1 ;
      Serial.print ("Sent 0: ") ;
      Serial.println (gSentFrameCount0) ;
    } else {
      Serial.println ("Send failure 0") ;
    }
  }
  if (can0.available ()) {
    can0.receive (frame_read) ;
    gReceivedFrameCount0 ++ ;
    Serial.print ("CAN0 Received : ") ;
    Serial.print (gReceivedFrameCount0) ;
	Serial.print (" Id: ") ;
	len = frame_read.len;
    Serial.print (frame_read.id) ;
	Serial.print (" Len: ") ;
    Serial.print (len) ;
	Serial.print (" Data: ") ;
	for(int i = 0; i<len; i++)    // print the data
	{
		Serial.print(frame_read.data[i]);
		Serial.print(" ");
	}
	Serial.println();
  }
  #endif
  #ifdef CAN1
  // CAN1 loop
  if (gBlinkLedDate1 < millis ()) {
    gBlinkLedDate1 += 1000 ;
    digitalWrite (LED2, !digitalRead (LED2)) ;
    const bool ok1 = can1.tryToSend (frame1) ;
    if (ok1) {
      gSentFrameCount1 += 1 ;
      Serial.print ("Sent 1: ") ;
      Serial.println (gSentFrameCount1) ;
    } else {
      Serial.println ("Send failure 1") ;
    }
  }
  if (can1.available ()) {
    can1.receive (frame_read) ;
    gReceivedFrameCount1 ++ ;
    Serial.print ("CAN 1 Received: ") ;
    Serial.print (gReceivedFrameCount1) ;
  	Serial.print (" Id: ") ;
  	len = frame_read.len;
    Serial.print (frame_read.id) ;
	  Serial.print (" Len: ") ;
    Serial.print (len) ;
  	Serial.print (" Data: ") ;
  	for(int i = 0; i<len; i++)    // print the data
  	{
  		Serial.print(frame_read.data[i]);
  		Serial.print(" ");
  	}
	  Serial.println();
  }
  #endif
  #ifdef RTC
   delay(500);
   displayTime();
  #endif
}
//——————————————————————————————————————————————————————————————————————————————

Certification Disclaimer

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.