Merge pull request #12651 from mbezuidenhout/feature/can-bus

Feature/can bus
This commit is contained in:
Theo Arends 2021-07-17 14:52:12 +02:00 committed by GitHub
commit de2e86f022
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1947 additions and 1 deletions

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>arduino-mcp2515</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2013 Seeed Technology Inc.
Copyright (c) 2016 Dmitry
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,240 @@
Arduino MCP2515 CAN interface library
---------------------------------------------------------
<br>
CAN-BUS is a common industrial bus because of its long travel distance, medium communication speed and high reliability. It is commonly found on modern machine tools and as an automotive diagnostic bus. This CAN-BUS Shield adopts MCP2515 CAN Bus controller with SPI interface and MCP2551 CAN transceiver to give your Arduino/Seeeduino CAN-BUS capibility. With an OBD-II converter cable added on and the OBD-II library imported, you are ready to build an onboard diagnostic device or data logger.
- Implements CAN V2.0B at up to 1 Mb/s
- SPI Interface up to 10 MHz
- Standard (11 bit) and extended (29 bit) data and remote frames
- Two receive buffers with prioritized message storage
## Library Installation
1. Download the ZIP file from https://github.com/autowp/arduino-mcp2515/archive/master.zip
2. From the Arduino IDE: Sketch -> Include Library... -> Add .ZIP Library...
3. Restart the Arduino IDE to see the new "mcp2515" library with examples
<br>
# Usage:
## 1. Initialization
To create connection with MCP2515 provide pin number where SPI CS is connected (10 by default), baudrate and mode
The available modes are listed as follows:
```C++
mcp2515.setNormalMode();
mcp2515.setLoopbackMode();
mcp2515.setListenOnlyMode();
```
The available baudrates are listed as follows:
```C++
enum CAN_SPEED {
CAN_5KBPS,
CAN_10KBPS,
CAN_20KBPS,
CAN_31K25BPS,
CAN_33KBPS,
CAN_40KBPS,
CAN_50KBPS,
CAN_80KBPS,
CAN_83K3BPS,
CAN_95KBPS,
CAN_100KBPS,
CAN_125KBPS,
CAN_200KBPS,
CAN_250KBPS,
CAN_500KBPS,
CAN_1000KBPS
};
```
<br>
Example of initialization
```C++
MCP2515 mcp2515(10);
mcp2515.reset();
mcp2515.setBitrate(CAN_125KBPS);
mcp2515.setLoopbackMode();
```
<br>
<br>
You can also set oscillator frequency for module when setting bitrate:
```C++
mcp2515.setBitrate(CAN_125KBPS, MCP_8MHZ);
```
<br>
The available clock speeds are listed as follows:
```C++
enum CAN_CLOCK {
MCP_20MHZ,
MCP_16MHZ,
MCP_8MHZ
};
```
Default value is MCP_16MHZ
<br>
Note: To transfer data on high speed of CAN interface via UART dont forget to update UART baudrate as necessary.
##2. Frame data format
Library uses Linux-like structure to store can frames;
```C++
struct can_frame {
uint32_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
uint8_t can_dlc;
uint8_t data[8];
};
```
For additional information see [SocketCAN](https://www.kernel.org/doc/Documentation/networking/can.txt)
## 3. Send Data
```C++
MCP2515::ERROR sendMessage(const MCP2515::TXBn txbn, const struct can_frame *frame);
MCP2515::ERROR sendMessage(const struct can_frame *frame);
```
This is a function to send data onto the bus.
For example, In the 'send' example, we have:
```C++
struct can_frame frame;
frame.can_id = 0x000;
frame.can_dlc = 4;
frame.data[0] = 0xFF;
frame.data[1] = 0xFF;
frame.data[2] = 0xFF;
frame.data[3] = 0xFF;
/* send out the message to the bus and
tell other devices this is a standard frame from 0x00. */
mcp2515.sendMessage(&frame);
```
```C++
struct can_frame frame;
frame.can_id = 0x12345678 | CAN_EFF_MASK;
frame.can_dlc = 2;
frame.data[0] = 0xFF;
frame.data[1] = 0xFF;
/* send out the message to the bus using second TX buffer and
tell other devices this is a extended frame from 0x12345678. */
mcp2515.sendMessage(MCP2515::TXB1, &frame);
```
<br>
## 4. Receive Data
The following function is used to receive data on the 'receive' node:
```C++
MCP2515::ERROR readMessage(const MCP2515::RXBn rxbn, struct can_frame *frame);
MCP2515::ERROR readMessage(struct can_frame *frame);
```
In conditions that masks and filters have been set. This function can only get frames that meet the requirements of masks and filters.
You can choise one of two method to receive: interrup-based and polling
Example of poll read
```C++
struct can_frame frame;
void loop() {
if (mcp2515.readMessage(&frame) == MCP2515::ERROR_OK) {
// frame contains received message
}
}
```
Example of interrupt based read
```C++
bool interrupt = false;
struct can_frame frame;
void irqHandler() {
interrupt = true;
}
void setup() {
...
attachInterrupt(0, irqHandler, FALLING);
}
void loop() {
if (interrupt) {
interrupt = false;
uint8_t irq = mcp2515.getInterrupts();
if (irq & MCP2515::CANINTF_RX0IF) {
if (mcp2515.readMessage(MCP2515::RXB0, &frame) == MCP2515::ERROR_OK) {
// frame contains received from RXB0 message
}
}
if (irq & MCP2515::CANINTF_RX1IF) {
if (mcp2515.readMessage(MCP2515::RXB1, &frame) == MCP2515::ERROR_OK) {
// frame contains received from RXB1 message
}
}
}
}
```
<br>
##5. Set Receive Mask and Filter
There are 2 receive mask registers and 5 filter registers on the controller chip that guarantee you get data from the target device. They are useful especially in a large network consisting of numerous nodes.
We provide two functions for you to utilize these mask and filter registers. They are:
```C++
MCP2515::ERROR setFilterMask(const MASK mask, const bool ext, const uint32_t ulData)
MCP2515::ERROR setFilter(const RXF num, const bool ext, const uint32_t ulData)
```
**MASK mask** represents one of two mask **MCP2515::MASK0** or **MCP2515::MASK1**
**RXF num** represents one of six acceptance filters registers from **MCP2515::RXF0** to **MCP2515::RXF5**
**ext** represents the status of the frame. **false** means it's a mask or filter for a standard frame. **true** means it's for a extended frame.
**ulData** represents the content of the mask of filter.
<br>
## 6. Examples
Example implementation of CanHacker (lawicel) protocol based device: [https://github.com/autowp/can-usb](https://github.com/autowp/can-usb)
<br>
For more information, please refer to [wiki page](http://www.seeedstudio.com/wiki/CAN-BUS_Shield).
----
This software is written by loovee ([luweicong@seeed.cc](luweicong@seeed.cc "luweicong@seeed.cc")) for seeed studio,<br>
Updated by Dmitry ([https://github.com/autowp](https://github.com/autowp "https://github.com/autowp"))<br>
and is licensed under [The MIT License](http://opensource.org/licenses/mit-license.php). Check [LICENSE.md](LICENSE.md) for more information.<br>
Contributing to this software is warmly welcomed. You can do this basically by<br>
[forking](https://help.github.com/articles/fork-a-repo), committing modifications and then [pulling requests](https://help.github.com/articles/using-pull-requests) (follow the links above<br>
for operating guide). Adding change log and your contact into file header is encouraged.<br>
Thanks for your contribution.
Seeed Studio is an open hardware facilitation company based in Shenzhen, China. <br>
Benefiting from local manufacture power and convenient global logistic system, <br>
we integrate resources to serve new era of innovation. Seeed also works with <br>
global distributors and partners to push open hardware movement.<br>

View File

@ -0,0 +1,45 @@
#ifndef CAN_H_
#define CAN_H_
#include <stdint.h>
typedef unsigned char __u8;
typedef unsigned short __u16;
typedef unsigned long __u32;
/* special address description flags for the CAN_ID */
#define CAN_EFF_FLAG 0x80000000UL /* EFF/SFF is set in the MSB */
#define CAN_RTR_FLAG 0x40000000UL /* remote transmission request */
#define CAN_ERR_FLAG 0x20000000UL /* error message frame */
/* valid bits in CAN ID for frame formats */
#define CAN_SFF_MASK 0x000007FFUL /* standard frame format (SFF) */
#define CAN_EFF_MASK 0x1FFFFFFFUL /* extended frame format (EFF) */
#define CAN_ERR_MASK 0x1FFFFFFFUL /* omit EFF, RTR, ERR flags */
/*
* Controller Area Network Identifier structure
*
* bit 0-28 : CAN identifier (11/29 bit)
* bit 29 : error message frame flag (0 = data frame, 1 = error message)
* bit 30 : remote transmission request flag (1 = rtr frame)
* bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
*/
typedef __u32 canid_t;
#define CAN_SFF_ID_BITS 11
#define CAN_EFF_ID_BITS 29
/* CAN payload length and DLC definitions according to ISO 11898-1 */
#define CAN_MAX_DLC 8
#define CAN_MAX_DLEN 8
struct can_frame {
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
__u8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
__u8 data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};
#endif /* CAN_H_ */

View File

@ -0,0 +1,39 @@
#include <SPI.h>
#include <mcp2515.h>
struct can_frame canMsg;
MCP2515 mcp2515(10);
void setup() {
Serial.begin(115200);
SPI.begin();
mcp2515.reset();
mcp2515.setBitrate(CAN_125KBPS);
mcp2515.setNormalMode();
Serial.println("------- CAN Read ----------");
Serial.println("ID DLC DATA");
}
void loop() {
if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) {
Serial.print(canMsg.can_id, HEX); // print ID
Serial.print(" ");
Serial.print(canMsg.can_dlc, HEX); // print DLC
Serial.print(" ");
for (int i = 0; i<canMsg.can_dlc; i++) { // print the data
Serial.print(canMsg.data[i],HEX);
Serial.print(" ");
}
Serial.println();
}
}

View File

@ -0,0 +1,53 @@
#include <SPI.h>
#include <mcp2515.h>
struct can_frame canMsg1;
struct can_frame canMsg2;
MCP2515 mcp2515(10);
void setup() {
canMsg1.can_id = 0x0F6;
canMsg1.can_dlc = 8;
canMsg1.data[0] = 0x8E;
canMsg1.data[1] = 0x87;
canMsg1.data[2] = 0x32;
canMsg1.data[3] = 0xFA;
canMsg1.data[4] = 0x26;
canMsg1.data[5] = 0x8E;
canMsg1.data[6] = 0xBE;
canMsg1.data[7] = 0x86;
canMsg2.can_id = 0x036;
canMsg2.can_dlc = 8;
canMsg2.data[0] = 0x0E;
canMsg2.data[1] = 0x00;
canMsg2.data[2] = 0x00;
canMsg2.data[3] = 0x08;
canMsg2.data[4] = 0x01;
canMsg2.data[5] = 0x00;
canMsg2.data[6] = 0x00;
canMsg2.data[7] = 0xA0;
while (!Serial);
Serial.begin(115200);
SPI.begin();
mcp2515.reset();
mcp2515.setBitrate(CAN_125KBPS);
mcp2515.setNormalMode();
Serial.println("Example: Write to CAN");
}
void loop() {
mcp2515.sendMessage(&canMsg1);
mcp2515.sendMessage(&canMsg2);
Serial.println("Messages sent");
delay(100);
}

View File

@ -0,0 +1,14 @@
MCP2515 KEYWORD1
CAN_5KBPS LITERAL1
CAN_10KBPS LITERAL1
CAN_20KBPS LITERAL1
CAN_40KBPS LITERAL1
CAN_50KBPS LITERAL1
CAN_80KBPS LITERAL1
CAN_100KBPS LITERAL1
CAN_125KBPS LITERAL1
CAN_200KBPS LITERAL1
CAN_250KBPS LITERAL1
CAN_500KBPS LITERAL1
CAN_1000KBPS LITERAL1

View File

@ -0,0 +1,693 @@
#include "mcp2515.h"
const struct MCP2515::TXBn_REGS MCP2515::TXB[MCP2515::N_TXBUFFERS] = {
{MCP_TXB0CTRL, MCP_TXB0SIDH, MCP_TXB0DATA},
{MCP_TXB1CTRL, MCP_TXB1SIDH, MCP_TXB1DATA},
{MCP_TXB2CTRL, MCP_TXB2SIDH, MCP_TXB2DATA}
};
const struct MCP2515::RXBn_REGS MCP2515::RXB[N_RXBUFFERS] = {
{MCP_RXB0CTRL, MCP_RXB0SIDH, MCP_RXB0DATA, CANINTF_RX0IF},
{MCP_RXB1CTRL, MCP_RXB1SIDH, MCP_RXB1DATA, CANINTF_RX1IF}
};
MCP2515::MCP2515(const uint8_t _CS)
{
SPI.begin();
SPICS = _CS;
pinMode(SPICS, OUTPUT);
endSPI();
}
void MCP2515::startSPI() {
SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
digitalWrite(SPICS, LOW);
}
void MCP2515::endSPI() {
digitalWrite(SPICS, HIGH);
SPI.endTransaction();
}
MCP2515::ERROR MCP2515::reset(void)
{
startSPI();
SPI.transfer(INSTRUCTION_RESET);
endSPI();
delay(10);
uint8_t zeros[14];
memset(zeros, 0, sizeof(zeros));
setRegisters(MCP_TXB0CTRL, zeros, 14);
setRegisters(MCP_TXB1CTRL, zeros, 14);
setRegisters(MCP_TXB2CTRL, zeros, 14);
setRegister(MCP_RXB0CTRL, 0);
setRegister(MCP_RXB1CTRL, 0);
setRegister(MCP_CANINTE, CANINTF_RX0IF | CANINTF_RX1IF | CANINTF_ERRIF | CANINTF_MERRF);
modifyRegister(MCP_RXB0CTRL,
RXBnCTRL_RXM_MASK | RXB0CTRL_BUKT,
RXBnCTRL_RXM_STDEXT | RXB0CTRL_BUKT);
modifyRegister(MCP_RXB1CTRL, RXBnCTRL_RXM_MASK, RXBnCTRL_RXM_STDEXT);
// clear filters and masks
/*RXF filters[] = {RXF0, RXF1, RXF2, RXF3, RXF4, RXF5};
for (int i=0; i<6; i++) {
ERROR result = setFilter(filters[i], true, 0);
if (result != ERROR_OK) {
return result;
}
}
MASK masks[] = {MASK0, MASK1};
for (int i=0; i<2; i++) {
ERROR result = setFilterMask(masks[i], true, 0);
if (result != ERROR_OK) {
return result;
}
}*/
return ERROR_OK;
}
uint8_t MCP2515::readRegister(const REGISTER reg)
{
startSPI();
SPI.transfer(INSTRUCTION_READ);
SPI.transfer(reg);
uint8_t ret = SPI.transfer(0x00);
endSPI();
return ret;
}
void MCP2515::readRegisters(const REGISTER reg, uint8_t values[], const uint8_t n)
{
startSPI();
SPI.transfer(INSTRUCTION_READ);
SPI.transfer(reg);
// mcp2515 has auto-increment of address-pointer
for (uint8_t i=0; i<n; i++) {
values[i] = SPI.transfer(0x00);
}
endSPI();
}
void MCP2515::setRegister(const REGISTER reg, const uint8_t value)
{
startSPI();
SPI.transfer(INSTRUCTION_WRITE);
SPI.transfer(reg);
SPI.transfer(value);
endSPI();
}
void MCP2515::setRegisters(const REGISTER reg, const uint8_t values[], const uint8_t n)
{
startSPI();
SPI.transfer(INSTRUCTION_WRITE);
SPI.transfer(reg);
for (uint8_t i=0; i<n; i++) {
SPI.transfer(values[i]);
}
endSPI();
}
void MCP2515::modifyRegister(const REGISTER reg, const uint8_t mask, const uint8_t data)
{
startSPI();
SPI.transfer(INSTRUCTION_BITMOD);
SPI.transfer(reg);
SPI.transfer(mask);
SPI.transfer(data);
endSPI();
}
uint8_t MCP2515::getStatus(void)
{
startSPI();
SPI.transfer(INSTRUCTION_READ_STATUS);
uint8_t i = SPI.transfer(0x00);
endSPI();
return i;
}
MCP2515::ERROR MCP2515::setConfigMode()
{
return setMode(CANCTRL_REQOP_CONFIG);
}
MCP2515::ERROR MCP2515::setListenOnlyMode()
{
return setMode(CANCTRL_REQOP_LISTENONLY);
}
MCP2515::ERROR MCP2515::setSleepMode()
{
return setMode(CANCTRL_REQOP_SLEEP);
}
MCP2515::ERROR MCP2515::setLoopbackMode()
{
return setMode(CANCTRL_REQOP_LOOPBACK);
}
MCP2515::ERROR MCP2515::setNormalMode()
{
return setMode(CANCTRL_REQOP_NORMAL);
}
MCP2515::ERROR MCP2515::setMode(const CANCTRL_REQOP_MODE mode)
{
modifyRegister(MCP_CANCTRL, CANCTRL_REQOP, mode);
unsigned long endTime = millis() + 10;
bool modeMatch = false;
while (millis() < endTime) {
uint8_t newmode = readRegister(MCP_CANSTAT);
newmode &= CANSTAT_OPMOD;
modeMatch = newmode == mode;
if (modeMatch) {
break;
}
}
return modeMatch ? ERROR_OK : ERROR_FAIL;
}
MCP2515::ERROR MCP2515::setBitrate(const CAN_SPEED canSpeed)
{
return setBitrate(canSpeed, MCP_16MHZ);
}
MCP2515::ERROR MCP2515::setBitrate(const CAN_SPEED canSpeed, CAN_CLOCK canClock)
{
ERROR error = setConfigMode();
if (error != ERROR_OK) {
return error;
}
uint8_t set, cfg1, cfg2, cfg3;
set = 1;
switch (canClock)
{
case (MCP_8MHZ):
switch (canSpeed)
{
case (CAN_5KBPS): // 5KBPS
cfg1 = MCP_8MHz_5kBPS_CFG1;
cfg2 = MCP_8MHz_5kBPS_CFG2;
cfg3 = MCP_8MHz_5kBPS_CFG3;
break;
case (CAN_10KBPS): // 10KBPS
cfg1 = MCP_8MHz_10kBPS_CFG1;
cfg2 = MCP_8MHz_10kBPS_CFG2;
cfg3 = MCP_8MHz_10kBPS_CFG3;
break;
case (CAN_20KBPS): // 20KBPS
cfg1 = MCP_8MHz_20kBPS_CFG1;
cfg2 = MCP_8MHz_20kBPS_CFG2;
cfg3 = MCP_8MHz_20kBPS_CFG3;
break;
case (CAN_31K25BPS): // 31.25KBPS
cfg1 = MCP_8MHz_31k25BPS_CFG1;
cfg2 = MCP_8MHz_31k25BPS_CFG2;
cfg3 = MCP_8MHz_31k25BPS_CFG3;
break;
case (CAN_33KBPS): // 33.33KBPS
cfg1 = MCP_8MHz_33k3BPS_CFG1;
cfg2 = MCP_8MHz_33k3BPS_CFG2;
cfg3 = MCP_8MHz_33k3BPS_CFG3;
break;
case (CAN_40KBPS): // 40Kbps
cfg1 = MCP_8MHz_40kBPS_CFG1;
cfg2 = MCP_8MHz_40kBPS_CFG2;
cfg3 = MCP_8MHz_40kBPS_CFG3;
break;
case (CAN_50KBPS): // 50Kbps
cfg1 = MCP_8MHz_50kBPS_CFG1;
cfg2 = MCP_8MHz_50kBPS_CFG2;
cfg3 = MCP_8MHz_50kBPS_CFG3;
break;
case (CAN_80KBPS): // 80Kbps
cfg1 = MCP_8MHz_80kBPS_CFG1;
cfg2 = MCP_8MHz_80kBPS_CFG2;
cfg3 = MCP_8MHz_80kBPS_CFG3;
break;
case (CAN_100KBPS): // 100Kbps
cfg1 = MCP_8MHz_100kBPS_CFG1;
cfg2 = MCP_8MHz_100kBPS_CFG2;
cfg3 = MCP_8MHz_100kBPS_CFG3;
break;
case (CAN_125KBPS): // 125Kbps
cfg1 = MCP_8MHz_125kBPS_CFG1;
cfg2 = MCP_8MHz_125kBPS_CFG2;
cfg3 = MCP_8MHz_125kBPS_CFG3;
break;
case (CAN_200KBPS): // 200Kbps
cfg1 = MCP_8MHz_200kBPS_CFG1;
cfg2 = MCP_8MHz_200kBPS_CFG2;
cfg3 = MCP_8MHz_200kBPS_CFG3;
break;
case (CAN_250KBPS): // 250Kbps
cfg1 = MCP_8MHz_250kBPS_CFG1;
cfg2 = MCP_8MHz_250kBPS_CFG2;
cfg3 = MCP_8MHz_250kBPS_CFG3;
break;
case (CAN_500KBPS): // 500Kbps
cfg1 = MCP_8MHz_500kBPS_CFG1;
cfg2 = MCP_8MHz_500kBPS_CFG2;
cfg3 = MCP_8MHz_500kBPS_CFG3;
break;
case (CAN_1000KBPS): // 1Mbps
cfg1 = MCP_8MHz_1000kBPS_CFG1;
cfg2 = MCP_8MHz_1000kBPS_CFG2;
cfg3 = MCP_8MHz_1000kBPS_CFG3;
break;
default:
set = 0;
break;
}
break;
case (MCP_16MHZ):
switch (canSpeed)
{
case (CAN_5KBPS): // 5Kbps
cfg1 = MCP_16MHz_5kBPS_CFG1;
cfg2 = MCP_16MHz_5kBPS_CFG2;
cfg3 = MCP_16MHz_5kBPS_CFG3;
break;
case (CAN_10KBPS): // 10Kbps
cfg1 = MCP_16MHz_10kBPS_CFG1;
cfg2 = MCP_16MHz_10kBPS_CFG2;
cfg3 = MCP_16MHz_10kBPS_CFG3;
break;
case (CAN_20KBPS): // 20Kbps
cfg1 = MCP_16MHz_20kBPS_CFG1;
cfg2 = MCP_16MHz_20kBPS_CFG2;
cfg3 = MCP_16MHz_20kBPS_CFG3;
break;
case (CAN_33KBPS): // 20Kbps
cfg1 = MCP_16MHz_33k3BPS_CFG1;
cfg2 = MCP_16MHz_33k3BPS_CFG2;
cfg3 = MCP_16MHz_33k3BPS_CFG3;
break;
case (CAN_40KBPS): // 40Kbps
cfg1 = MCP_16MHz_40kBPS_CFG1;
cfg2 = MCP_16MHz_40kBPS_CFG2;
cfg3 = MCP_16MHz_40kBPS_CFG3;
break;
case (CAN_50KBPS): // 50Kbps
cfg2 = MCP_16MHz_50kBPS_CFG2;
cfg3 = MCP_16MHz_50kBPS_CFG3;
break;
case (CAN_80KBPS): // 80Kbps
cfg1 = MCP_16MHz_80kBPS_CFG1;
cfg2 = MCP_16MHz_80kBPS_CFG2;
cfg3 = MCP_16MHz_80kBPS_CFG3;
break;
case (CAN_100KBPS): // 100Kbps
cfg1 = MCP_16MHz_100kBPS_CFG1;
cfg2 = MCP_16MHz_100kBPS_CFG2;
cfg3 = MCP_16MHz_100kBPS_CFG3;
break;
case (CAN_125KBPS): // 125Kbps
cfg1 = MCP_16MHz_125kBPS_CFG1;
cfg2 = MCP_16MHz_125kBPS_CFG2;
cfg3 = MCP_16MHz_125kBPS_CFG3;
break;
case (CAN_200KBPS): // 200Kbps
cfg1 = MCP_16MHz_200kBPS_CFG1;
cfg2 = MCP_16MHz_200kBPS_CFG2;
cfg3 = MCP_16MHz_200kBPS_CFG3;
break;
case (CAN_250KBPS): // 250Kbps
cfg1 = MCP_16MHz_250kBPS_CFG1;
cfg2 = MCP_16MHz_250kBPS_CFG2;
cfg3 = MCP_16MHz_250kBPS_CFG3;
break;
case (CAN_500KBPS): // 500Kbps
cfg1 = MCP_16MHz_500kBPS_CFG1;
cfg2 = MCP_16MHz_500kBPS_CFG2;
cfg3 = MCP_16MHz_500kBPS_CFG3;
break;
case (CAN_1000KBPS): // 1Mbps
cfg1 = MCP_16MHz_1000kBPS_CFG1;
cfg2 = MCP_16MHz_1000kBPS_CFG2;
cfg3 = MCP_16MHz_1000kBPS_CFG3;
break;
default:
set = 0;
break;
}
break;
case (MCP_20MHZ):
switch (canSpeed)
{
case (CAN_40KBPS): // 40Kbps
cfg1 = MCP_20MHz_40kBPS_CFG1;
cfg2 = MCP_20MHz_40kBPS_CFG2;
cfg3 = MCP_20MHz_40kBPS_CFG3;
break;
case (CAN_50KBPS): // 50Kbps
cfg1 = MCP_20MHz_50kBPS_CFG1;
cfg2 = MCP_20MHz_50kBPS_CFG2;
cfg3 = MCP_20MHz_50kBPS_CFG3;
break;
case (CAN_80KBPS): // 80Kbps
cfg1 = MCP_20MHz_80kBPS_CFG1;
cfg2 = MCP_20MHz_80kBPS_CFG2;
cfg3 = MCP_20MHz_80kBPS_CFG3;
break;
case (CAN_100KBPS): // 100Kbps
cfg1 = MCP_20MHz_100kBPS_CFG1;
cfg2 = MCP_20MHz_100kBPS_CFG2;
cfg3 = MCP_20MHz_100kBPS_CFG3;
break;
case (CAN_125KBPS): // 125Kbps
cfg1 = MCP_20MHz_125kBPS_CFG1;
cfg2 = MCP_20MHz_125kBPS_CFG2;
cfg3 = MCP_20MHz_125kBPS_CFG3;
break;
case (CAN_200KBPS): // 200Kbps
cfg1 = MCP_20MHz_200kBPS_CFG1;
cfg2 = MCP_20MHz_200kBPS_CFG2;
cfg3 = MCP_20MHz_200kBPS_CFG3;
break;
case (CAN_250KBPS): // 250Kbps
cfg1 = MCP_20MHz_250kBPS_CFG1;
cfg2 = MCP_20MHz_250kBPS_CFG2;
cfg3 = MCP_20MHz_250kBPS_CFG3;
break;
case (CAN_500KBPS): // 500Kbps
cfg1 = MCP_20MHz_500kBPS_CFG1;
cfg2 = MCP_20MHz_500kBPS_CFG2;
cfg3 = MCP_20MHz_500kBPS_CFG3;
break;
case (CAN_1000KBPS): // 1Mbps
cfg1 = MCP_20MHz_1000kBPS_CFG1;
cfg2 = MCP_20MHz_1000kBPS_CFG2;
cfg3 = MCP_20MHz_1000kBPS_CFG3;
break;
default:
set = 0;
break;
}
break;
default:
set = 0;
break;
}
if (set) {
setRegister(MCP_CNF1, cfg1);
setRegister(MCP_CNF2, cfg2);
setRegister(MCP_CNF3, cfg3);
return ERROR_OK;
}
else {
return ERROR_FAIL;
}
}
void MCP2515::prepareId(uint8_t *buffer, const bool ext, const uint32_t id)
{
uint16_t canid = (uint16_t)(id & 0x0FFFF);
if (ext) {
buffer[MCP_EID0] = (uint8_t) (canid & 0xFF);
buffer[MCP_EID8] = (uint8_t) (canid >> 8);
canid = (uint16_t)(id >> 16);
buffer[MCP_SIDL] = (uint8_t) (canid & 0x03);
buffer[MCP_SIDL] += (uint8_t) ((canid & 0x1C) << 3);
buffer[MCP_SIDL] |= TXB_EXIDE_MASK;
buffer[MCP_SIDH] = (uint8_t) (canid >> 5);
} else {
buffer[MCP_SIDH] = (uint8_t) (canid >> 3);
buffer[MCP_SIDL] = (uint8_t) ((canid & 0x07 ) << 5);
buffer[MCP_EID0] = 0;
buffer[MCP_EID8] = 0;
}
}
MCP2515::ERROR MCP2515::setFilterMask(const MASK mask, const bool ext, const uint32_t ulData)
{
ERROR res = setConfigMode();
if (res != ERROR_OK) {
return res;
}
uint8_t tbufdata[4];
prepareId(tbufdata, ext, ulData);
REGISTER reg;
switch (mask) {
case MASK0: reg = MCP_RXM0SIDH; break;
case MASK1: reg = MCP_RXM1SIDH; break;
default:
return ERROR_FAIL;
}
setRegisters(reg, tbufdata, 4);
return ERROR_OK;
}
MCP2515::ERROR MCP2515::setFilter(const RXF num, const bool ext, const uint32_t ulData)
{
ERROR res = setConfigMode();
if (res != ERROR_OK) {
return res;
}
REGISTER reg;
switch (num) {
case RXF0: reg = MCP_RXF0SIDH; break;
case RXF1: reg = MCP_RXF1SIDH; break;
case RXF2: reg = MCP_RXF2SIDH; break;
case RXF3: reg = MCP_RXF3SIDH; break;
case RXF4: reg = MCP_RXF4SIDH; break;
case RXF5: reg = MCP_RXF5SIDH; break;
default:
return ERROR_FAIL;
}
uint8_t tbufdata[4];
prepareId(tbufdata, ext, ulData);
setRegisters(reg, tbufdata, 4);
return ERROR_OK;
}
MCP2515::ERROR MCP2515::sendMessage(const TXBn txbn, const struct can_frame *frame)
{
const struct TXBn_REGS *txbuf = &TXB[txbn];
uint8_t data[13];
bool ext = (frame->can_id & CAN_EFF_FLAG);
bool rtr = (frame->can_id & CAN_RTR_FLAG);
uint32_t id = (frame->can_id & (ext ? CAN_EFF_MASK : CAN_SFF_MASK));
prepareId(data, ext, id);
data[MCP_DLC] = rtr ? (frame->can_dlc | RTR_MASK) : frame->can_dlc;
memcpy(&data[MCP_DATA], frame->data, frame->can_dlc);
setRegisters(txbuf->SIDH, data, 5 + frame->can_dlc);
modifyRegister(txbuf->CTRL, TXB_TXREQ, TXB_TXREQ);
return ERROR_OK;
}
MCP2515::ERROR MCP2515::sendMessage(const struct can_frame *frame)
{
if (frame->can_dlc > CAN_MAX_DLEN) {
return ERROR_FAILTX;
}
TXBn txBuffers[N_TXBUFFERS] = {TXB0, TXB1, TXB2};
for (int i=0; i<N_TXBUFFERS; i++) {
const struct TXBn_REGS *txbuf = &TXB[txBuffers[i]];
uint8_t ctrlval = readRegister(txbuf->CTRL);
if ( (ctrlval & TXB_TXREQ) == 0 ) {
return sendMessage(txBuffers[i], frame);
}
}
return ERROR_FAILTX;
}
MCP2515::ERROR MCP2515::readMessage(const RXBn rxbn, struct can_frame *frame)
{
const struct RXBn_REGS *rxb = &RXB[rxbn];
uint8_t tbufdata[5];
readRegisters(rxb->SIDH, tbufdata, 5);
uint32_t id = (tbufdata[MCP_SIDH]<<3) + (tbufdata[MCP_SIDL]>>5);
if ( (tbufdata[MCP_SIDL] & TXB_EXIDE_MASK) == TXB_EXIDE_MASK ) {
id = (id<<2) + (tbufdata[MCP_SIDL] & 0x03);
id = (id<<8) + tbufdata[MCP_EID8];
id = (id<<8) + tbufdata[MCP_EID0];
id |= CAN_EFF_FLAG;
}
uint8_t dlc = (tbufdata[MCP_DLC] & DLC_MASK);
if (dlc > CAN_MAX_DLEN) {
return ERROR_FAIL;
}
uint8_t ctrl = readRegister(rxb->CTRL);
if (ctrl & RXBnCTRL_RTR) {
id |= CAN_RTR_FLAG;
}
frame->can_id = id;
frame->can_dlc = dlc;
readRegisters(rxb->DATA, frame->data, dlc);
modifyRegister(MCP_CANINTF, rxb->CANINTF_RXnIF, 0);
return ERROR_OK;
}
MCP2515::ERROR MCP2515::readMessage(struct can_frame *frame)
{
ERROR rc;
uint8_t stat = getStatus();
if ( stat & STAT_RX0IF ) {
rc = readMessage(RXB0, frame);
} else if ( stat & STAT_RX1IF ) {
rc = readMessage(RXB1, frame);
} else {
rc = ERROR_NOMSG;
}
return rc;
}
bool MCP2515::checkReceive(void)
{
uint8_t res = getStatus();
if ( res & STAT_RXIF_MASK ) {
return true;
} else {
return false;
}
}
bool MCP2515::checkError(void)
{
uint8_t eflg = getErrorFlags();
if ( eflg & EFLG_ERRORMASK ) {
return true;
} else {
return false;
}
}
uint8_t MCP2515::getErrorFlags(void)
{
return readRegister(MCP_EFLG);
}
void MCP2515::clearRXnOVRFlags(void)
{
modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0);
}
uint8_t MCP2515::getInterrupts(void)
{
return readRegister(MCP_CANINTF);
}
void MCP2515::clearInterrupts(void)
{
setRegister(MCP_CANINTF, 0);
}
uint8_t MCP2515::getInterruptMask(void)
{
return readRegister(MCP_CANINTE);
}
void MCP2515::clearTXInterrupts(void)
{
modifyRegister(MCP_CANINTF, (CANINTF_TX0IF | CANINTF_TX1IF | CANINTF_TX2IF), 0);
}
void MCP2515::clearRXnOVR(void)
{
uint8_t eflg = getErrorFlags();
if (eflg != 0) {
clearRXnOVRFlags();
clearInterrupts();
//modifyRegister(MCP_CANINTF, CANINTF_ERRIF, 0);
}
}
void MCP2515::clearMERR()
{
//modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0);
//clearInterrupts();
modifyRegister(MCP_CANINTF, CANINTF_MERRF, 0);
}

View File

@ -0,0 +1,466 @@
#ifndef _MCP2515_H_
#define _MCP2515_H_
#include <Arduino.h>
#include <SPI.h>
#include "can.h"
/*
* Speed 8M
*/
#define MCP_8MHz_1000kBPS_CFG1 (0x00)
#define MCP_8MHz_1000kBPS_CFG2 (0x80)
#define MCP_8MHz_1000kBPS_CFG3 (0x80)
#define MCP_8MHz_500kBPS_CFG1 (0x00)
#define MCP_8MHz_500kBPS_CFG2 (0x90)
#define MCP_8MHz_500kBPS_CFG3 (0x82)
#define MCP_8MHz_250kBPS_CFG1 (0x00)
#define MCP_8MHz_250kBPS_CFG2 (0xB1)
#define MCP_8MHz_250kBPS_CFG3 (0x85)
#define MCP_8MHz_200kBPS_CFG1 (0x00)
#define MCP_8MHz_200kBPS_CFG2 (0xB4)
#define MCP_8MHz_200kBPS_CFG3 (0x86)
#define MCP_8MHz_125kBPS_CFG1 (0x01)
#define MCP_8MHz_125kBPS_CFG2 (0xB1)
#define MCP_8MHz_125kBPS_CFG3 (0x85)
#define MCP_8MHz_100kBPS_CFG1 (0x01)
#define MCP_8MHz_100kBPS_CFG2 (0xB4)
#define MCP_8MHz_100kBPS_CFG3 (0x86)
#define MCP_8MHz_80kBPS_CFG1 (0x01)
#define MCP_8MHz_80kBPS_CFG2 (0xBF)
#define MCP_8MHz_80kBPS_CFG3 (0x87)
#define MCP_8MHz_50kBPS_CFG1 (0x03)
#define MCP_8MHz_50kBPS_CFG2 (0xB4)
#define MCP_8MHz_50kBPS_CFG3 (0x86)
#define MCP_8MHz_40kBPS_CFG1 (0x03)
#define MCP_8MHz_40kBPS_CFG2 (0xBF)
#define MCP_8MHz_40kBPS_CFG3 (0x87)
#define MCP_8MHz_33k3BPS_CFG1 (0x47)
#define MCP_8MHz_33k3BPS_CFG2 (0xE2)
#define MCP_8MHz_33k3BPS_CFG3 (0x85)
#define MCP_8MHz_31k25BPS_CFG1 (0x07)
#define MCP_8MHz_31k25BPS_CFG2 (0xA4)
#define MCP_8MHz_31k25BPS_CFG3 (0x84)
#define MCP_8MHz_20kBPS_CFG1 (0x07)
#define MCP_8MHz_20kBPS_CFG2 (0xBF)
#define MCP_8MHz_20kBPS_CFG3 (0x87)
#define MCP_8MHz_10kBPS_CFG1 (0x0F)
#define MCP_8MHz_10kBPS_CFG2 (0xBF)
#define MCP_8MHz_10kBPS_CFG3 (0x87)
#define MCP_8MHz_5kBPS_CFG1 (0x1F)
#define MCP_8MHz_5kBPS_CFG2 (0xBF)
#define MCP_8MHz_5kBPS_CFG3 (0x87)
/*
* speed 16M
*/
#define MCP_16MHz_1000kBPS_CFG1 (0x00)
#define MCP_16MHz_1000kBPS_CFG2 (0xD0)
#define MCP_16MHz_1000kBPS_CFG3 (0x82)
#define MCP_16MHz_500kBPS_CFG1 (0x00)
#define MCP_16MHz_500kBPS_CFG2 (0xF0)
#define MCP_16MHz_500kBPS_CFG3 (0x86)
#define MCP_16MHz_250kBPS_CFG1 (0x41)
#define MCP_16MHz_250kBPS_CFG2 (0xF1)
#define MCP_16MHz_250kBPS_CFG3 (0x85)
#define MCP_16MHz_200kBPS_CFG1 (0x01)
#define MCP_16MHz_200kBPS_CFG2 (0xFA)
#define MCP_16MHz_200kBPS_CFG3 (0x87)
#define MCP_16MHz_125kBPS_CFG1 (0x03)
#define MCP_16MHz_125kBPS_CFG2 (0xF0)
#define MCP_16MHz_125kBPS_CFG3 (0x86)
#define MCP_16MHz_100kBPS_CFG1 (0x03)
#define MCP_16MHz_100kBPS_CFG2 (0xFA)
#define MCP_16MHz_100kBPS_CFG3 (0x87)
#define MCP_16MHz_80kBPS_CFG1 (0x03)
#define MCP_16MHz_80kBPS_CFG2 (0xFF)
#define MCP_16MHz_80kBPS_CFG3 (0x87)
#define MCP_16MHz_50kBPS_CFG1 (0x07)
#define MCP_16MHz_50kBPS_CFG2 (0xFA)
#define MCP_16MHz_50kBPS_CFG3 (0x87)
#define MCP_16MHz_40kBPS_CFG1 (0x07)
#define MCP_16MHz_40kBPS_CFG2 (0xFF)
#define MCP_16MHz_40kBPS_CFG3 (0x87)
#define MCP_16MHz_33k3BPS_CFG1 (0x4E)
#define MCP_16MHz_33k3BPS_CFG2 (0xF1)
#define MCP_16MHz_33k3BPS_CFG3 (0x85)
#define MCP_16MHz_20kBPS_CFG1 (0x0F)
#define MCP_16MHz_20kBPS_CFG2 (0xFF)
#define MCP_16MHz_20kBPS_CFG3 (0x87)
#define MCP_16MHz_10kBPS_CFG1 (0x1F)
#define MCP_16MHz_10kBPS_CFG2 (0xFF)
#define MCP_16MHz_10kBPS_CFG3 (0x87)
#define MCP_16MHz_5kBPS_CFG1 (0x3F)
#define MCP_16MHz_5kBPS_CFG2 (0xFF)
#define MCP_16MHz_5kBPS_CFG3 (0x87)
/*
* speed 20M
*/
#define MCP_20MHz_1000kBPS_CFG1 (0x00)
#define MCP_20MHz_1000kBPS_CFG2 (0xD9)
#define MCP_20MHz_1000kBPS_CFG3 (0x82)
#define MCP_20MHz_500kBPS_CFG1 (0x00)
#define MCP_20MHz_500kBPS_CFG2 (0xFA)
#define MCP_20MHz_500kBPS_CFG3 (0x87)
#define MCP_20MHz_250kBPS_CFG1 (0x41)
#define MCP_20MHz_250kBPS_CFG2 (0xFB)
#define MCP_20MHz_250kBPS_CFG3 (0x86)
#define MCP_20MHz_200kBPS_CFG1 (0x01)
#define MCP_20MHz_200kBPS_CFG2 (0xFF)
#define MCP_20MHz_200kBPS_CFG3 (0x87)
#define MCP_20MHz_125kBPS_CFG1 (0x03)
#define MCP_20MHz_125kBPS_CFG2 (0xFA)
#define MCP_20MHz_125kBPS_CFG3 (0x87)
#define MCP_20MHz_100kBPS_CFG1 (0x04)
#define MCP_20MHz_100kBPS_CFG2 (0xFA)
#define MCP_20MHz_100kBPS_CFG3 (0x87)
#define MCP_20MHz_80kBPS_CFG1 (0x04)
#define MCP_20MHz_80kBPS_CFG2 (0xFF)
#define MCP_20MHz_80kBPS_CFG3 (0x87)
#define MCP_20MHz_50kBPS_CFG1 (0x09)
#define MCP_20MHz_50kBPS_CFG2 (0xFA)
#define MCP_20MHz_50kBPS_CFG3 (0x87)
#define MCP_20MHz_40kBPS_CFG1 (0x09)
#define MCP_20MHz_40kBPS_CFG2 (0xFF)
#define MCP_20MHz_40kBPS_CFG3 (0x87)
enum CAN_CLOCK {
MCP_20MHZ,
MCP_16MHZ,
MCP_8MHZ
};
enum CAN_SPEED {
CAN_5KBPS,
CAN_10KBPS,
CAN_20KBPS,
CAN_31K25BPS,
CAN_33KBPS,
CAN_40KBPS,
CAN_50KBPS,
CAN_80KBPS,
CAN_83K3BPS,
CAN_95KBPS,
CAN_100KBPS,
CAN_125KBPS,
CAN_200KBPS,
CAN_250KBPS,
CAN_500KBPS,
CAN_1000KBPS
};
class MCP2515
{
public:
enum ERROR {
ERROR_OK = 0,
ERROR_FAIL = 1,
ERROR_ALLTXBUSY = 2,
ERROR_FAILINIT = 3,
ERROR_FAILTX = 4,
ERROR_NOMSG = 5
};
enum MASK {
MASK0,
MASK1
};
enum RXF {
RXF0 = 0,
RXF1 = 1,
RXF2 = 2,
RXF3 = 3,
RXF4 = 4,
RXF5 = 5
};
enum RXBn {
RXB0 = 0,
RXB1 = 1
};
enum TXBn {
TXB0 = 0,
TXB1 = 1,
TXB2 = 2
};
enum /*class*/ CANINTF : uint8_t {
CANINTF_RX0IF = 0x01,
CANINTF_RX1IF = 0x02,
CANINTF_TX0IF = 0x04,
CANINTF_TX1IF = 0x08,
CANINTF_TX2IF = 0x10,
CANINTF_ERRIF = 0x20,
CANINTF_WAKIF = 0x40,
CANINTF_MERRF = 0x80
};
private:
static const uint8_t CANCTRL_REQOP = 0xE0;
static const uint8_t CANCTRL_ABAT = 0x10;
static const uint8_t CANCTRL_OSM = 0x08;
static const uint8_t CANCTRL_CLKEN = 0x04;
static const uint8_t CANCTRL_CLKPRE = 0x03;
enum /*class*/ CANCTRL_REQOP_MODE : uint8_t {
CANCTRL_REQOP_NORMAL = 0x00,
CANCTRL_REQOP_SLEEP = 0x20,
CANCTRL_REQOP_LOOPBACK = 0x40,
CANCTRL_REQOP_LISTENONLY = 0x60,
CANCTRL_REQOP_CONFIG = 0x80,
CANCTRL_REQOP_POWERUP = 0xE0
};
static const uint8_t CANSTAT_OPMOD = 0xE0;
static const uint8_t CANSTAT_ICOD = 0x0E;
static const uint8_t TXB_EXIDE_MASK = 0x08;
static const uint8_t DLC_MASK = 0x0F;
static const uint8_t RTR_MASK = 0x40;
static const uint8_t RXBnCTRL_RXM_STD = 0x20;
static const uint8_t RXBnCTRL_RXM_EXT = 0x40;
static const uint8_t RXBnCTRL_RXM_STDEXT = 0x00;
static const uint8_t RXBnCTRL_RXM_MASK = 0x60;
static const uint8_t RXBnCTRL_RTR = 0x08;
static const uint8_t RXB0CTRL_BUKT = 0x04;
static const uint8_t MCP_SIDH = 0;
static const uint8_t MCP_SIDL = 1;
static const uint8_t MCP_EID8 = 2;
static const uint8_t MCP_EID0 = 3;
static const uint8_t MCP_DLC = 4;
static const uint8_t MCP_DATA = 5;
enum /*class*/ STAT : uint8_t {
STAT_RX0IF = (1<<0),
STAT_RX1IF = (1<<1)
};
static const uint8_t STAT_RXIF_MASK = STAT_RX0IF | STAT_RX1IF;
enum /*class*/ TXBnCTRL : uint8_t {
TXB_ABTF = 0x40,
TXB_MLOA = 0x20,
TXB_TXERR = 0x10,
TXB_TXREQ = 0x08,
TXB_TXIE = 0x04,
TXB_TXP = 0x03
};
enum /*class*/ EFLG : uint8_t {
EFLG_RX1OVR = (1<<7),
EFLG_RX0OVR = (1<<6),
EFLG_TXBO = (1<<5),
EFLG_TXEP = (1<<4),
EFLG_RXEP = (1<<3),
EFLG_TXWAR = (1<<2),
EFLG_RXWAR = (1<<1),
EFLG_EWARN = (1<<0)
};
static const uint8_t EFLG_ERRORMASK = EFLG_RX1OVR
| EFLG_RX0OVR
| EFLG_TXBO
| EFLG_TXEP
| EFLG_RXEP;
enum /*class*/ INSTRUCTION : uint8_t {
INSTRUCTION_WRITE = 0x02,
INSTRUCTION_READ = 0x03,
INSTRUCTION_BITMOD = 0x05,
INSTRUCTION_LOAD_TX0 = 0x40,
INSTRUCTION_LOAD_TX1 = 0x42,
INSTRUCTION_LOAD_TX2 = 0x44,
INSTRUCTION_RTS_TX0 = 0x81,
INSTRUCTION_RTS_TX1 = 0x82,
INSTRUCTION_RTS_TX2 = 0x84,
INSTRUCTION_RTS_ALL = 0x87,
INSTRUCTION_READ_RX0 = 0x90,
INSTRUCTION_READ_RX1 = 0x94,
INSTRUCTION_READ_STATUS = 0xA0,
INSTRUCTION_RX_STATUS = 0xB0,
INSTRUCTION_RESET = 0xC0
};
enum /*class*/ REGISTER : uint8_t {
MCP_RXF0SIDH = 0x00,
MCP_RXF0SIDL = 0x01,
MCP_RXF0EID8 = 0x02,
MCP_RXF0EID0 = 0x03,
MCP_RXF1SIDH = 0x04,
MCP_RXF1SIDL = 0x05,
MCP_RXF1EID8 = 0x06,
MCP_RXF1EID0 = 0x07,
MCP_RXF2SIDH = 0x08,
MCP_RXF2SIDL = 0x09,
MCP_RXF2EID8 = 0x0A,
MCP_RXF2EID0 = 0x0B,
MCP_CANSTAT = 0x0E,
MCP_CANCTRL = 0x0F,
MCP_RXF3SIDH = 0x10,
MCP_RXF3SIDL = 0x11,
MCP_RXF3EID8 = 0x12,
MCP_RXF3EID0 = 0x13,
MCP_RXF4SIDH = 0x14,
MCP_RXF4SIDL = 0x15,
MCP_RXF4EID8 = 0x16,
MCP_RXF4EID0 = 0x17,
MCP_RXF5SIDH = 0x18,
MCP_RXF5SIDL = 0x19,
MCP_RXF5EID8 = 0x1A,
MCP_RXF5EID0 = 0x1B,
MCP_TEC = 0x1C,
MCP_REC = 0x1D,
MCP_RXM0SIDH = 0x20,
MCP_RXM0SIDL = 0x21,
MCP_RXM0EID8 = 0x22,
MCP_RXM0EID0 = 0x23,
MCP_RXM1SIDH = 0x24,
MCP_RXM1SIDL = 0x25,
MCP_RXM1EID8 = 0x26,
MCP_RXM1EID0 = 0x27,
MCP_CNF3 = 0x28,
MCP_CNF2 = 0x29,
MCP_CNF1 = 0x2A,
MCP_CANINTE = 0x2B,
MCP_CANINTF = 0x2C,
MCP_EFLG = 0x2D,
MCP_TXB0CTRL = 0x30,
MCP_TXB0SIDH = 0x31,
MCP_TXB0SIDL = 0x32,
MCP_TXB0EID8 = 0x33,
MCP_TXB0EID0 = 0x34,
MCP_TXB0DLC = 0x35,
MCP_TXB0DATA = 0x36,
MCP_TXB1CTRL = 0x40,
MCP_TXB1SIDH = 0x41,
MCP_TXB1SIDL = 0x42,
MCP_TXB1EID8 = 0x43,
MCP_TXB1EID0 = 0x44,
MCP_TXB1DLC = 0x45,
MCP_TXB1DATA = 0x46,
MCP_TXB2CTRL = 0x50,
MCP_TXB2SIDH = 0x51,
MCP_TXB2SIDL = 0x52,
MCP_TXB2EID8 = 0x53,
MCP_TXB2EID0 = 0x54,
MCP_TXB2DLC = 0x55,
MCP_TXB2DATA = 0x56,
MCP_RXB0CTRL = 0x60,
MCP_RXB0SIDH = 0x61,
MCP_RXB0SIDL = 0x62,
MCP_RXB0EID8 = 0x63,
MCP_RXB0EID0 = 0x64,
MCP_RXB0DLC = 0x65,
MCP_RXB0DATA = 0x66,
MCP_RXB1CTRL = 0x70,
MCP_RXB1SIDH = 0x71,
MCP_RXB1SIDL = 0x72,
MCP_RXB1EID8 = 0x73,
MCP_RXB1EID0 = 0x74,
MCP_RXB1DLC = 0x75,
MCP_RXB1DATA = 0x76
};
static const uint32_t SPI_CLOCK = 10000000; // 10MHz
static const int N_TXBUFFERS = 3;
static const int N_RXBUFFERS = 2;
static const struct TXBn_REGS {
REGISTER CTRL;
REGISTER SIDH;
REGISTER DATA;
} TXB[N_TXBUFFERS];
static const struct RXBn_REGS {
REGISTER CTRL;
REGISTER SIDH;
REGISTER DATA;
CANINTF CANINTF_RXnIF;
} RXB[N_RXBUFFERS];
uint8_t SPICS;
private:
void startSPI();
void endSPI();
ERROR setMode(const CANCTRL_REQOP_MODE mode);
uint8_t readRegister(const REGISTER reg);
void readRegisters(const REGISTER reg, uint8_t values[], const uint8_t n);
void setRegister(const REGISTER reg, const uint8_t value);
void setRegisters(const REGISTER reg, const uint8_t values[], const uint8_t n);
void modifyRegister(const REGISTER reg, const uint8_t mask, const uint8_t data);
void prepareId(uint8_t *buffer, const bool ext, const uint32_t id);
public:
MCP2515(const uint8_t _CS);
ERROR reset(void);
ERROR setConfigMode();
ERROR setListenOnlyMode();
ERROR setSleepMode();
ERROR setLoopbackMode();
ERROR setNormalMode();
ERROR setBitrate(const CAN_SPEED canSpeed);
ERROR setBitrate(const CAN_SPEED canSpeed, const CAN_CLOCK canClock);
ERROR setFilterMask(const MASK num, const bool ext, const uint32_t ulData);
ERROR setFilter(const RXF num, const bool ext, const uint32_t ulData);
ERROR sendMessage(const TXBn txbn, const struct can_frame *frame);
ERROR sendMessage(const struct can_frame *frame);
ERROR readMessage(const RXBn rxbn, struct can_frame *frame);
ERROR readMessage(struct can_frame *frame);
bool checkReceive(void);
bool checkError(void);
uint8_t getErrorFlags(void);
void clearRXnOVRFlags(void);
uint8_t getInterrupts(void);
uint8_t getInterruptMask(void);
void clearInterrupts(void);
void clearTXInterrupts(void);
uint8_t getStatus(void);
void clearRXnOVR(void);
void clearMERR();
};
#endif

View File

@ -860,6 +860,8 @@ const char HTTP_SNS_COLOR_GREEN[] PROGMEM = "{s}%s " D_COLOR_GREEN "{
const char HTTP_SNS_COLOR_BLUE[] PROGMEM = "{s}%s " D_COLOR_BLUE "{m}%u " "{e}";
const char HTTP_SNS_MILLILITERS[] PROGMEM = "{s}%s " D_VOLUME "{m}%s " D_UNIT_MILLILITERS "{e}";
const char HTTP_SNS_GAS[] PROGMEM = "{s}%s " D_GAS "{m}%d " D_UNIT_PERCENT "LEL{e}";
const char HTTP_SNS_SOC[] PROGMEM = "{s}%s " D_SOC "{m}%d " D_UNIT_PERCENT "{e}";
const char HTTP_SNS_SOH[] PROGMEM = "{s}%s " D_SOH "{m}%d " D_UNIT_PERCENT "{e}";
#endif // USE_WEBSERVER
#endif // _I18N_H_

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Gewig"
#define D_WARMLIGHT "Warm"
#define D_WEB_SERVER "Webbediener"
#define D_SOC "Laai kondisie"
#define D_SOH "Laai vermoeë"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "WAARSKUWING Hierdie weergawe ondersteun nie aanhoudende instellings nie"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Тегло"
#define D_WARMLIGHT "Топла"
#define D_WEB_SERVER "Уеб сървър"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "ПРЕДУПРЕЖДЕНИЕ Тази версия не поддържа постоянни настройки"
@ -830,6 +832,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Hmotnost"
#define D_WARMLIGHT "Teplé světlo"
#define D_WEB_SERVER "Web Server"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "UPOZORNĚNÍ Tato verze nepodporuje trvalé nastavení"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Gewicht"
#define D_WARMLIGHT "warm"
#define D_WEB_SERVER "Web-Server"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "ACHTUNG: Diese Version unterstützt keine persistenten Einstellungen"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Puls"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Βάρος"
#define D_WARMLIGHT "Θερμό"
#define D_WEB_SERVER "Διακομιστής Web"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "ΠΡΟΕΙΔΟΠΟΙΗΣΗ Αυτή η έκδοση δεν αποθηκεύει τις ρυθμίσεις"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Weight"
#define D_WARMLIGHT "Warm"
#define D_WEB_SERVER "Web Server"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "WARNING This version does not support persistent settings"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Peso"
#define D_WARMLIGHT "Cálida"
#define D_WEB_SERVER "Servidor Web"
#define D_SOC "Estado de Carga"
#define D_SOH "Estado de Salud"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "Cuidado, esta versión no guarda los cambios"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "Cruce por cero"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Poids"
#define D_WARMLIGHT "Chaud"
#define D_WEB_SERVER "Serveur web"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "ATTENTION Cette version ne supporte pas les réglages persistants"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Gewicht"
#define D_WARMLIGHT "Waarm"
#define D_WEB_SERVER "Webserver"
#define D_SOC "State of Charge"
#define D_SOH "State of Charge"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "WARSKOGING Dizze ferzje bewarret gjin ynstellings"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "משקל"
#define D_WARMLIGHT "חום"
#define D_WEB_SERVER "Web שרת"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "אזהרה גרסה זו אינה תומכת בהגדרות קבועות"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Tömeg"
#define D_WARMLIGHT "Meleg fény"
#define D_WEB_SERVER "Webszerver"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "VIGYÁZZ! Ez a verzió nem támogat tartós beállításokat"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Peso"
#define D_WARMLIGHT "Calda"
#define D_WEB_SERVER "Server web"
#define D_SOC "Stato di Carica"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "ATTENZIONE Questa versione non supporta il salvataggio delle impostazioni"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "Impulsi ZC"
#define D_SENSOR_HALLEFFECT "Effetto hall"
#define D_SENSOR_EPD_DATA "EPD - Dati"
#define D_SENSOR_MCP2515_CS "MCP2515 - CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "무게"
#define D_WARMLIGHT "따뜻하게"
#define D_WEB_SERVER "웹 서버"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "경고: 이 버전은 영구 설정을 지원하지 않습니다"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Gewicht"
#define D_WARMLIGHT "Warm"
#define D_WEB_SERVER "Webserver"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "WAARSCHUWING Deze versie bewaart geen instellingen"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Waga"
#define D_WARMLIGHT "Temperatura światła"
#define D_WEB_SERVER "Serwer Web"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "UWAGA Ta wersja nie obsługuje zapisu ustawień"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "Efekt Halla"
#define D_SENSOR_EPD_DATA "EPD Dane"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Peso"
#define D_WARMLIGHT "Luz quente"
#define D_WEB_SERVER "Servidor WEB"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "AVISO: esta versão não supporta configurações persistentes"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "Efeito Hall"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Peso"
#define D_WARMLIGHT "Luz Quente"
#define D_WEB_SERVER "Servidor WEB"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "AVISO esta versão não supporta configurações persistentes"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "Efeito Hall"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Greutate"
#define D_WARMLIGHT "Cald"
#define D_WEB_SERVER "Server Web"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "ATENȚIE Această versiune nu suportă setări permanente"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Weight"
#define D_WARMLIGHT "Тепло"
#define D_WEB_SERVER "Web сервер"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "ПРЕДУПРЕЖДЕНИЕ Эта версия не поддерживает персистентные настройки"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Hmotnosť"
#define D_WARMLIGHT "Teplé svetlo"
#define D_WEB_SERVER "Web Server"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "UPOZORNENIE Táto verzia nepodporuje trvalé nastavenia"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Vikt"
#define D_WARMLIGHT "Varm"
#define D_WEB_SERVER "Webbserver"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "VARNING Denna version supporterar inte beständiga inställningar"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Weight"
#define D_WARMLIGHT "Sıcak"
#define D_WEB_SERVER "Web Sunucusu"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "UYARI Bu versiyon ayarların kalıcı olarak kaydedilmesine olanak sağlamıyor"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Вага"
#define D_WARMLIGHT "Тепло"
#define D_WEB_SERVER "Web сервер"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "ПОПЕРЕДЖЕННЯ! Ця версія не підтримує збереження налаштувань"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "Cân nặng"
#define D_WARMLIGHT "Ấm"
#define D_WEB_SERVER "Máy chủ Web"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "Cảnh báo phiên bản này không hỗ trợ các cài đặt vĩnh viễn"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "重量"
#define D_WARMLIGHT "暖"
#define D_WEB_SERVER "Web Server"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "警告:精简固件不支持持久保存设置"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -198,6 +198,8 @@
#define D_WEIGHT "重量"
#define D_WARMLIGHT "暖光"
#define D_WEB_SERVER "網頁伺服器"
#define D_SOC "State of Charge"
#define D_SOH "State of Health"
// tasmota.ino
#define D_WARNING_MINIMAL_VERSION "警告,這個版本並不支援將設定永久的儲存!"
@ -831,6 +833,7 @@
#define D_SENSOR_ZEROCROSS "ZC Pulse"
#define D_SENSOR_HALLEFFECT "HallEffect"
#define D_SENSOR_EPD_DATA "EPD Data"
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
// Units
#define D_UNIT_AMPERE "安培"

View File

@ -1795,7 +1795,8 @@ void GpioInit(void)
ValidSpiPinUsed(GPIO_ST7789_DC) || // ST7789 CS may be omitted so chk DC too
ValidSpiPinUsed(GPIO_ST7789_CS) ||
(ValidSpiPinUsed(GPIO_SSD1331_CS) && ValidSpiPinUsed(GPIO_SSD1331_DC)) ||
ValidSpiPinUsed(GPIO_SDCARD_CS)
ValidSpiPinUsed(GPIO_SDCARD_CS) ||
ValidSpiPinUsed(GPIO_MCP2515_CS)
);
// If SPI_CS and/or SPI_DC is used they must be valid
TasmotaGlobal.spi_enabled = (valid_cs) ? SPI_MOSI_MISO : SPI_NONE;

View File

@ -169,6 +169,7 @@ enum UserSelectablePins {
GPIO_I2S_OUT_DATA, GPIO_I2S_OUT_CLK, GPIO_I2S_OUT_SLCT,
GPIO_I2S_IN_DATA, GPIO_I2S_IN_CLK, GPIO_I2S_IN_SLCT,
GPIO_INTERRUPT,
GPIO_MCP2515_CS, // MCP2515 Chip Select
GPIO_SENSOR_END };
enum ProgramSelectablePins {
@ -358,6 +359,7 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_I2S_OUT_DATA "|" D_SENSOR_I2S_OUT_CLK "|" D_SENSOR_I2S_OUT_SLCT "|"
D_SENSOR_I2S_IN_DATA "|" D_SENSOR_I2S_IN_CLK "|" D_SENSOR_I2S_IN_SLCT "|"
D_SENSOR_INTERRUPT "|"
D_SENSOR_MCP2515_CS "|"
;
const char kSensorNamesFixed[] PROGMEM =
@ -459,6 +461,9 @@ const uint16_t kGpioNiceList[] PROGMEM = {
#ifdef USE_SDCARD
AGPIO(GPIO_SDCARD_CS),
#endif // USE_SDCARD
#ifdef USE_MCP2515
AGPIO(GPIO_MCP2515_CS),
#endif // USE_MCP2515
#endif // USE_SPI
AGPIO(GPIO_SSPI_MISO), // Software SPI Master Input Client Output

275
tasmota/xsns_90_mcp2515.ino Normal file
View File

@ -0,0 +1,275 @@
/*
xsns_89_mcp2515.ino - MCP2515 CAN bus support for Tasmota
Copyright (C) 2021 Marius Bezuidenhout
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_SPI
#ifdef USE_MCP2515
/*********************************************************************************************\
* MCP2515 - Microchip CAN controller
*
* Connections:
* MCP2515 ESP8266 Tasmota
* ------- -------------- ----------
* INT not used
* SCK GPIO14 SPI CLK
* SI GPIO13 SPI MOSI
* SO GPIO12 SPI MISO
* CS GPIO0..5,15,16 MCP2515
* Gnd Gnd
* VCC Vin/5V
\*********************************************************************************************/
#define XSNS_90 90
#include "mcp2515.h"
// set defaults if not defined
#ifndef MCP2515_BITRATE
#define MCP2515_BITRATE CAN_500KBPS
#endif
#ifndef MCP2515_CLOCK
#define MCP2515_CLOCK MCP_8MHZ
#endif
#ifndef MCP2515_MAX_FRAMES
#define MCP2515_MAX_FRAMES 14
#endif
#ifndef CAN_KEEP_ALIVE_SECS
#define CAN_KEEP_ALIVE_SECS 300
#endif
#ifndef MCP2515_TIMEOUT
#define MCP2515_TIMEOUT 10
#endif
#ifndef MCP2515_BMS_CLIENT
#define MCP2515_BMS_CLIENT
// Look for Freedom Won BMS data in CAN message
#ifndef MCP2515_BMS_FREEDWON
#define MCP2515_BMS_FREEDWON
#endif // MCP2515_BMS_FREEDWON
#endif // MCP2515_BMS_CLIENT
#ifdef MCP2515_BMS_CLIENT
struct BMS_Struct {
uint16_t stateOfCharge;
uint16_t stateOfHealth;
uint16_t battVoltage; // Div 100
int16_t battAmp; // Div 10
int16_t battTemp; // Div 10
char name[17];
} bms;
#endif
int8_t mcp2515_init_status = 1;
uint32_t lastFrameRecv = 0;
struct can_frame canFrame;
MCP2515 *mcp2515 = nullptr;
char c2h(char c)
{
return "0123456789ABCDEF"[0x0F & (unsigned char)c];
}
void MCP2515_Init(void) {
mcp2515 = new MCP2515(5);
if (MCP2515::ERROR_OK != mcp2515->reset()) {
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to reset module"));
mcp2515_init_status = 0;
}
if (MCP2515::ERROR_OK != mcp2515->setBitrate(MCP2515_BITRATE, MCP2515_CLOCK)) {
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to set module bitrate"));
mcp2515_init_status = 0;
}
if (mcp2515_init_status && MCP2515::ERROR_OK != mcp2515->setNormalMode()) {
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to set normal mode"));
mcp2515_init_status = 0;
}
#ifdef MCP2515_BMS_FREEDWON
// TODO: Filter CAN bus messages
//mcp2515->setFilterMask();
//mcp2515->setFilter();
#endif
}
void MCP2515_Read() {
uint8_t nCounter = 0;
bool checkRcv;
if (mcp2515_init_status) {
checkRcv = mcp2515->checkReceive();
while (checkRcv && nCounter <= MCP2515_MAX_FRAMES) {
mcp2515->checkReceive();
nCounter++;
if (mcp2515->readMessage(&canFrame) == MCP2515::ERROR_OK) {
lastFrameRecv = TasmotaGlobal.uptime;
#ifdef MCP2515_BMS_CLIENT
#ifdef MCP2515_BMS_FREEDWON
switch (canFrame.can_id) {
// Charge/Discharge parameters
case 0x351:
break;
// State of Charge/Health
case 0x355:
if (6 >= canFrame.can_dlc) {
bms.stateOfCharge = (canFrame.data[1] << 8) | canFrame.data[0];
bms.stateOfHealth = (canFrame.data[3] << 8) | canFrame.data[2];
} else {
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Unexpected length (%d) for ID 0x355"), canFrame.can_dlc);
}
break;
// Voltage/Current/Temperature
case 0x356:
if (6 >= canFrame.can_dlc) {
bms.battVoltage = (canFrame.data[1] << 8) | canFrame.data[0];
bms.battAmp = (canFrame.data[3] << 8) | canFrame.data[2];
bms.battTemp = (canFrame.data[5] << 8) | canFrame.data[4]; // Convert to fahrenheit if SetOpion8 is set
} else {
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Unexpected length (%d) for ID 0x356"), canFrame.can_dlc);
}
break;
// Manufacturer name
case 0x35E:
// Battery Model / Firmware version
case 0x35F:
break;
// Battery / BMS name
case 0x370:
case 0x371:
for(int i = 0; i < canFrame.can_dlc; i++) {
uint8_t nameStrPos = i + ((canFrame.can_id & 0x1) * 8); // If can_id is 0x371 then fill from byte 8 onwards
bms.name[nameStrPos] = canFrame.data[i];
}
bms.name[16] = 0; // Ensure that name is null terminated
break;
// Modules status
case 0x372:
// Min/Max cell voltage/temperature
case 0x373:
// Min. cell voltage id string
case 0x374:
// Max. cell voltage id string
case 0x375:
// Min. cell temperature id string
case 0x376:
// Max. cell temperature id string
case 0x377:
// Installed capacity
case 0x379:
// Serial number
case 0x380:
case 0x381:
break;
default:
char canMsg[17];
canMsg[0] = 0;
for(int i = 0; i < canFrame.can_dlc; i++) {
canMsg[i*2] = c2h(canFrame.data[i]>>4);
canMsg[i*2+1] = c2h(canFrame.data[i]);
}
if (canFrame.can_dlc > 0) {
canMsg[(canFrame.can_dlc - 1) * 2 + 2] = 0;
}
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Received message 0x%s from ID 0x%X"), canMsg, (uint32_t)canFrame.can_id);
break;
}
#endif // MCP2515_BMS_FREEDWON
#endif // MCP2515_BMS_CLIENT
} else if (mcp2515->checkError()) {
uint8_t errFlags = mcp2515->getErrorFlags();
mcp2515->clearRXnOVRFlags();
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Received error %d"), errFlags);
break;
}
}
#ifdef MCP2515_BMS_FREEDWON
if (!(TasmotaGlobal.uptime%CAN_KEEP_ALIVE_SECS) && TasmotaGlobal.uptime>60) {
canFrame.can_id = 0x305;
canFrame.can_dlc = 0;
if (MCP2515::ERROR_OK != mcp2515->sendMessage(&canFrame)) {
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to send keep alive frame"));
}
}
#endif
}
}
void MCP2515_Show(bool Json) {
if (Json) {
if (lastFrameRecv > 0 && TasmotaGlobal.uptime - lastFrameRecv <= MCP2515_TIMEOUT ) {
#ifdef MCP2515_BMS_CLIENT
ResponseAppend_P(PSTR(",\"MCP2515\":{\"SOC\":%d,\"SOH\":%d}"), \
bms.stateOfCharge,
bms.stateOfHealth
);
#endif // MCP2515_BMS_CLIENT
}
#ifdef USE_WEBSERVER
} else {
#ifdef MCP2515_BMS_CLIENT
char ampStr[6];
dtostrf((float(bms.battAmp) / 10), 5, 1, ampStr);
WSContentSend_PD(HTTP_SNS_SOC, bms.name, bms.stateOfCharge);
WSContentSend_PD(HTTP_SNS_SOH, bms.name, bms.stateOfHealth);
WSContentSend_Voltage(bms.name, (float(bms.battVoltage) / 100));
WSContentSend_PD(HTTP_SNS_CURRENT, ampStr);
WSContentSend_Temp(bms.name, ConvertTemp(float(bms.battTemp) / 10));
#endif // MCP2515_BMS_CLIENT
#endif // USE_WEBSERVER
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xsns90(uint8_t function)
{
bool result = false;
if (PinUsed(GPIO_MCP2515_CS, GPIO_ANY) && TasmotaGlobal.spi_enabled) {
switch (function) {
case FUNC_INIT:
MCP2515_Init();
break;
case FUNC_EVERY_50_MSECOND:
MCP2515_Read();
break;
case FUNC_JSON_APPEND:
MCP2515_Show(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
MCP2515_Show(0);
break;
#endif // USE_WEBSERVER
}
}
return result;
}
#endif // USE_MCP2515
#endif // USE_SPI