336 lines
7.8 KiB
C++
336 lines
7.8 KiB
C++
/*
|
|
* Can.cpp
|
|
*
|
|
* Created: 03.11.2013 22:09:17
|
|
* Author: netz
|
|
*/
|
|
|
|
#include "Can.h"
|
|
|
|
#define CANDDR_SPI DDRB
|
|
#define CANPORT_SPI PORTB
|
|
#define CANP_MISO PINB6
|
|
#define CANP_MOSI PINB5
|
|
#define CANP_SCK PINB7
|
|
|
|
#define CANDDR_CS DDRB
|
|
#define CANPORT_CS PORTB
|
|
#define CANP_CS PINB4
|
|
|
|
Can::Can() {
|
|
init_pin();
|
|
init_spi();
|
|
init_can();
|
|
}
|
|
|
|
void Can::init_pin() {
|
|
//SPI Pins
|
|
CANDDR_SPI |= (1<<CANP_SCK) | (1<<CANP_MOSI);
|
|
CANPORT_SPI &= ~((1<<CANP_SCK) | (1<<CANP_MOSI) | (1<<CANP_MISO));
|
|
|
|
//CS Pins
|
|
CANDDR_CS |= (1<<CANP_CS);
|
|
CANPORT_CS |= (1<<CANP_CS);
|
|
}
|
|
|
|
void Can::init_spi() {
|
|
SPCR = (1<<SPE) | (1<<MSTR);
|
|
SPSR = (1<<SPI2X);
|
|
}
|
|
|
|
void Can::init_can() {
|
|
|
|
// MCP2515 per Software Reset zuruecksetzten,
|
|
// danach ist der MCP2515 im Configuration Mode
|
|
CANPORT_CS &= ~(1<<CANP_CS);
|
|
spi_putc( SPI_RESET );
|
|
_delay_ms(1);
|
|
CANPORT_CS |= (1<<CANP_CS);
|
|
|
|
// etwas warten bis sich der MCP2515 zurueckgesetzt hat
|
|
_delay_ms(10);
|
|
|
|
/*
|
|
* Einstellen des Bit Timings
|
|
*
|
|
* Fosc = 16MHz
|
|
* BRP = 7 (teilen durch 8)
|
|
* TQ = 2 * (BRP + 1) / Fosc (=> 1 uS)
|
|
*
|
|
* Sync Seg = 1TQ
|
|
* Prop Seg = (PRSEG + 1) * TQ = 1 TQ
|
|
* Phase Seg1 = (PHSEG1 + 1) * TQ = 3 TQ
|
|
* Phase Seg2 = (PHSEG2 + 1) * TQ = 3 TQ
|
|
*
|
|
* Bus speed = 1 / (Total # of TQ) * TQ
|
|
* = 1 / 8 * TQ = 125 kHz
|
|
*/
|
|
|
|
// BRP = 7
|
|
mcp2515_write_register( CNF1, (1<<BRP0)|(1<<BRP1)|(1<<BRP2) );
|
|
|
|
// Prop Seg und Phase Seg1 einstellen
|
|
mcp2515_write_register( CNF2, (1<<BTLMODE)|(1<<PHSEG11) );
|
|
|
|
// Wake-up Filter deaktivieren, Phase Seg2 einstellen
|
|
mcp2515_write_register( CNF3, (1<<PHSEG21) );
|
|
|
|
// Aktivieren der Rx Buffer Interrupts
|
|
mcp2515_write_register( CANINTE, (1<<RX1IE)|(1<<RX0IE) );
|
|
|
|
/*
|
|
* Einstellen der Filter
|
|
*/
|
|
|
|
// Buffer 0 : Empfangen aller Nachrichten
|
|
mcp2515_write_register( RXB0CTRL, (1<<RXM1)|(1<<RXM0) );
|
|
|
|
// Buffer 1 : Empfangen aller Nachrichten
|
|
mcp2515_write_register( RXB1CTRL, (1<<RXM1)|(1<<RXM0) );
|
|
|
|
// Alle Bits der Empfangsmaske loeschen,
|
|
// damit werden alle Nachrichten empfangen
|
|
mcp2515_write_register( RXM0SIDH, 0 );
|
|
mcp2515_write_register( RXM0SIDL, 0 );
|
|
mcp2515_write_register( RXM0EID8, 0 );
|
|
mcp2515_write_register( RXM0EID0, 0 );
|
|
|
|
mcp2515_write_register( RXM1SIDH, 0 );
|
|
mcp2515_write_register( RXM1SIDL, 0 );
|
|
mcp2515_write_register( RXM1EID8, 0 );
|
|
mcp2515_write_register( RXM1EID0, 0 );
|
|
|
|
/*
|
|
* Einstellen der Pin Funktionen
|
|
*/
|
|
|
|
// Deaktivieren der Pins RXnBF Pins (High Impedance State)
|
|
mcp2515_write_register( BFPCTRL, 0 );
|
|
|
|
// TXnRTS Bits als Inputs schalten
|
|
mcp2515_write_register( TXRTSCTRL, 0 );
|
|
|
|
// Device zurueck in den normalen Modus versetzten
|
|
mcp2515_bit_modify( CANCTRL, 0xE0, 0);
|
|
}
|
|
|
|
uint8_t Can::spi_putc( uint8_t data ) {
|
|
// Sendet ein Byte
|
|
SPDR = data;
|
|
|
|
// Wartet bis Byte gesendet wurde
|
|
while( !( SPSR & (1<<SPIF) ) );
|
|
|
|
return SPDR;
|
|
}
|
|
|
|
void Can::mcp2515_write_register( uint8_t adress, uint8_t data )
|
|
{
|
|
// /CS des MCP2515 auf Low ziehen
|
|
CANPORT_CS &= ~(1<<CANP_CS);
|
|
|
|
spi_putc(SPI_WRITE);
|
|
spi_putc(adress);
|
|
spi_putc(data);
|
|
|
|
// /CS Leitung wieder freigeben
|
|
CANPORT_CS |= (1<<CANP_CS);
|
|
}
|
|
|
|
uint8_t Can::mcp2515_read_register(uint8_t adress)
|
|
{
|
|
uint8_t data;
|
|
|
|
// /CS des MCP2515 auf Low ziehen
|
|
CANPORT_CS &= ~(1<<CANP_CS);
|
|
|
|
spi_putc(SPI_READ);
|
|
spi_putc(adress);
|
|
|
|
data = spi_putc(0xff);
|
|
|
|
// /CS Leitung wieder freigeben
|
|
CANPORT_CS |= (1<<CANP_CS);
|
|
|
|
return data;
|
|
}
|
|
|
|
void Can::mcp2515_bit_modify(uint8_t adress, uint8_t mask, uint8_t data)
|
|
{
|
|
// /CS des MCP2515 auf Low ziehen
|
|
CANPORT_CS &= ~(1<<CANP_CS);
|
|
|
|
spi_putc(SPI_BIT_MODIFY);
|
|
spi_putc(adress);
|
|
spi_putc(mask);
|
|
spi_putc(data);
|
|
|
|
// /CS Leitung wieder freigeben
|
|
CANPORT_CS |= (1<<CANP_CS);
|
|
}
|
|
|
|
uint8_t Can::can_send_message(CANMessage *p_message)
|
|
{
|
|
uint8_t status, address;
|
|
|
|
// Status des MCP2515 auslesen
|
|
CANPORT_CS &= ~(1<<CANP_CS);
|
|
spi_putc(SPI_READ_STATUS);
|
|
status = spi_putc(0xff);
|
|
spi_putc(0xff);
|
|
CANPORT_CS |= (1<<CANP_CS);
|
|
|
|
/* Statusbyte:
|
|
*
|
|
* Bit Funktion
|
|
* 2 TXB0CNTRL.TXREQ
|
|
* 4 TXB1CNTRL.TXREQ
|
|
* 6 TXB2CNTRL.TXREQ
|
|
*/
|
|
|
|
if (bit_is_clear(status, 2)) {
|
|
address = 0x00;
|
|
}
|
|
else if (bit_is_clear(status, 4)) {
|
|
address = 0x02;
|
|
}
|
|
else if (bit_is_clear(status, 6)) {
|
|
address = 0x04;
|
|
}
|
|
else {
|
|
/* Alle Puffer sind belegt,
|
|
Nachricht kann nicht verschickt werden */
|
|
return 0;
|
|
}
|
|
|
|
CANPORT_CS &= ~(1<<CANP_CS); // CS Low
|
|
spi_putc(SPI_WRITE_TX | address);
|
|
|
|
// Standard ID einstellen
|
|
spi_putc((uint8_t) (p_message->id>>3));
|
|
spi_putc((uint8_t) (p_message->id<<5));
|
|
|
|
// Extended ID
|
|
spi_putc(0x00);
|
|
spi_putc(0x00);
|
|
|
|
uint8_t length = p_message->length;
|
|
|
|
if (length > 8) {
|
|
length = 8;
|
|
}
|
|
|
|
// Ist die Nachricht ein "Remote Transmit Request" ?
|
|
if (p_message->rtr)
|
|
{
|
|
/* Ein RTR hat zwar eine Laenge,
|
|
aber enthaelt keine Daten */
|
|
|
|
// Nachrichten Laenge + RTR einstellen
|
|
spi_putc((1<<RTR) | length);
|
|
}
|
|
else
|
|
{
|
|
// Nachrichten Laenge einstellen
|
|
spi_putc(length);
|
|
|
|
// Daten
|
|
for (uint8_t i=0;i<length;i++) {
|
|
spi_putc(p_message->data[i]);
|
|
}
|
|
}
|
|
CANPORT_CS |= (1<<CANP_CS); // CS auf High
|
|
|
|
asm volatile ("nop");
|
|
|
|
/* CAN Nachricht verschicken
|
|
die letzten drei Bit im RTS Kommando geben an welcher
|
|
Puffer gesendet werden soll */
|
|
CANPORT_CS &= ~(1<<CANP_CS); // CS wieder Low
|
|
if (address == 0x00) {
|
|
spi_putc(SPI_RTS | 0x01);
|
|
} else {
|
|
spi_putc(SPI_RTS | address);
|
|
}
|
|
CANPORT_CS |= (1<<CANP_CS); // CS auf High
|
|
|
|
return 1;
|
|
}
|
|
|
|
uint8_t Can::can_get_message(CANMessage *p_message)
|
|
{
|
|
// Status auslesen
|
|
uint8_t status = mcp2515_read_rx_status();
|
|
|
|
if (bit_is_set(status,6))
|
|
{
|
|
// Nachricht in Puffer 0
|
|
|
|
CANPORT_CS &= ~(1<<CANP_CS); // CS Low
|
|
spi_putc(SPI_READ_RX);
|
|
}
|
|
else if (bit_is_set(status,7))
|
|
{
|
|
// Nachricht in Puffer 1
|
|
|
|
CANPORT_CS &= ~(1<<CANP_CS); // CS Low
|
|
spi_putc(SPI_READ_RX | 0x04);
|
|
}
|
|
else {
|
|
/* Fehler: Keine neue Nachricht vorhanden */
|
|
return 0xff;
|
|
}
|
|
|
|
// Standard ID auslesen
|
|
p_message->id = (uint16_t) spi_putc(0xff) << 3;
|
|
p_message->id |= (uint16_t) spi_putc(0xff) >> 5;
|
|
|
|
spi_putc(0xff);
|
|
spi_putc(0xff);
|
|
|
|
// Laenge auslesen
|
|
uint8_t length = spi_putc(0xff) & 0x0f;
|
|
p_message->length = length;
|
|
|
|
// Daten auslesen
|
|
for (uint8_t i=0;i<length;i++) {
|
|
p_message->data[i] = spi_putc(0xff);
|
|
}
|
|
|
|
CANPORT_CS |= (1<<CANP_CS);
|
|
|
|
if (bit_is_set(status,3)) {
|
|
p_message->rtr = 1;
|
|
} else {
|
|
p_message->rtr = 0;
|
|
}
|
|
|
|
// Interrupt Flag loeschen
|
|
if (bit_is_set(status,6)) {
|
|
mcp2515_bit_modify(CANINTF, (1<<RX0IF), 0);
|
|
} else {
|
|
mcp2515_bit_modify(CANINTF, (1<<RX1IF), 0);
|
|
}
|
|
|
|
return (status & 0x07);
|
|
}
|
|
|
|
uint8_t Can::mcp2515_read_rx_status(void)
|
|
{
|
|
uint8_t data;
|
|
|
|
// /CS des MCP2515 auf Low ziehen
|
|
CANPORT_CS &= ~(1<<CANP_CS);
|
|
|
|
spi_putc(SPI_RX_STATUS);
|
|
data = spi_putc(0xff);
|
|
|
|
// Die Daten werden noch einmal wiederholt gesendet,
|
|
// man braucht also nur eins der beiden Bytes auswerten.
|
|
spi_putc(0xff);
|
|
|
|
// /CS Leitung wieder freigeben
|
|
CANPORT_CS |= (1<<CANP_CS);
|
|
|
|
return data;
|
|
} |