Initial commit
This commit is contained in:
288
libs/Can.h
Normal file
288
libs/Can.h
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Can.h
|
||||
*
|
||||
* Created on: 01 июня 2015 г.
|
||||
* Author: esaulenko
|
||||
*/
|
||||
|
||||
#ifndef DRIVERS_CAN_CAN_H_
|
||||
#define DRIVERS_CAN_CAN_H_
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "CommonTypes.h"
|
||||
#ifndef CAN_FIRMWARE
|
||||
#include "SysTimer.h"
|
||||
#include "Buffer.h"
|
||||
#include "scmRTOS.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
struct TCanPkt
|
||||
{
|
||||
uint32_t id;
|
||||
uint8_t data[8];
|
||||
uint8_t data_len;
|
||||
|
||||
TCanPkt() { }
|
||||
|
||||
TCanPkt (uint32_t pkt_id):
|
||||
id(pkt_id) { }
|
||||
|
||||
// принимает строку, заполняет данные пакета. Неуказанные данные заполняются нулями
|
||||
template <int N>
|
||||
inline void SetData (const char (&data_str)[N])
|
||||
{
|
||||
int data_size = (N <= 8) ? (N - 1) : 8;
|
||||
for (int i = 0; i < data_size; i++)
|
||||
data[i] = data_str[i];
|
||||
for (int i = data_size; i < 8; i++)
|
||||
data[i] = 0x00;
|
||||
}
|
||||
|
||||
inline void SetData (uint8_t A)
|
||||
{ data_len = 1;
|
||||
data[0] = A; data[1] = 0; data[2] = 0; data[3] = 0;
|
||||
data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0;
|
||||
}
|
||||
inline void SetData (uint8_t A, uint8_t B)
|
||||
{ data_len = 2;
|
||||
data[0] = A; data[1] = B; data[2] = 0; data[3] = 0;
|
||||
data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0;
|
||||
}
|
||||
inline void SetData (uint8_t A, uint8_t B, uint8_t C)
|
||||
{ data_len = 3;
|
||||
data[0] = A; data[1] = B; data[2] = C; data[3] = 0;
|
||||
data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0;
|
||||
}
|
||||
inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D)
|
||||
{ data_len = 4;
|
||||
data[0] = A; data[1] = B; data[2] = C; data[3] = D;
|
||||
data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0;
|
||||
}
|
||||
inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D, uint8_t E)
|
||||
{ data_len = 5;
|
||||
data[0] = A; data[1] = B; data[2] = C; data[3] = D;
|
||||
data[4] = E; data[5] = 0; data[6] = 0; data[7] = 0;
|
||||
}
|
||||
inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D, uint8_t E, uint8_t F)
|
||||
{ data_len = 6;
|
||||
data[0] = A; data[1] = B; data[2] = C; data[3] = D;
|
||||
data[4] = E; data[5] = F; data[6] = 0; data[7] = 0;
|
||||
}
|
||||
inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D, uint8_t E, uint8_t F, uint8_t G)
|
||||
{ data_len = 7;
|
||||
data[0] = A; data[1] = B; data[2] = C; data[3] = D;
|
||||
data[4] = E; data[5] = F; data[6] = G; data[7] = 0;
|
||||
}
|
||||
inline void SetData (uint8_t A, uint8_t B, uint8_t C, uint8_t D, uint8_t E, uint8_t F, uint8_t G, uint8_t H)
|
||||
{ data_len = 8;
|
||||
data[0] = A; data[1] = B; data[2] = C; data[3] = D;
|
||||
data[4] = E; data[5] = F; data[6] = G; data[7] = H;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct TCanInit
|
||||
{
|
||||
uint32_t baudrate; // "сырое" значение регистра BTR, см. TCanBaudrate
|
||||
uint8_t mode; // в наст. момент не используется. TODO Должно быть RO / RW
|
||||
uint64_t filters[27]; // массив со значениями для пар регистров; заполняется Filters::xxx
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CCan
|
||||
{
|
||||
public:
|
||||
|
||||
enum TCanState : uint8_t
|
||||
{
|
||||
CanActive,
|
||||
CanRxOnly, // внешний драйвер выключен
|
||||
CanSleep, // внешний драйвер выключен, модуль в спящем режиме
|
||||
|
||||
CanInitError = 0xFE,
|
||||
CanBusOff = 0xFF,
|
||||
};
|
||||
|
||||
/* TIME QUANTUM FREQUENCY:
|
||||
Ftq = Ntq * Fbaud
|
||||
where 8 <= Ntq <= 25, and Ntq - is integer!
|
||||
|
||||
BAUD RATE PRESCALER:
|
||||
BRP = (Fcan/Ftq)–1
|
||||
|
||||
INDIVIDUAL BIT TIME SEGMENTS:
|
||||
(Bit Time) = (Sync Segment) + (Time Segment 1) + (Time Segment 2)
|
||||
where
|
||||
(Sync Segment) = 1*Tq (constant)
|
||||
(Time Segment 2) ~= 30% of Nominal Bit Time = Ntq/3
|
||||
(Time Segment 2) > 1*Tq
|
||||
(Time Segment 1) > 3*Tq
|
||||
(Time Segment 1) > (Time Segment 2)
|
||||
(Time Segment 1) <= (Time Segment 2)+8
|
||||
(Synchronous Jump Width) <= (Time Segment 2)
|
||||
|
||||
Table of Parameters:
|
||||
--------+-----------------------------------------------------+
|
||||
BRP@Fcan| Ntq |
|
||||
9MHz |25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8|
|
||||
--------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
500.000 | | | | | | | | 0| | | | | | | | | 1| |
|
||||
250.000 | | | | | | | | 1| | | | | | 2| | | 3| |
|
||||
125.000 | | 2| | | | | | 3| | | | | | 5| | | 7| 8|
|
||||
100.000 | | | | | | | | 4| | | 5| | | | | 8| 9| |
|
||||
83.333 | | | | | | | | 5| | | | | | 8| | |11| |
|
||||
50.000 | | | | | | 8| | 9| | |11| | |14| |17|19| |
|
||||
33.333 | | | | | | | |14| | |17| | | | |26|29| |
|
||||
20.000 |17| | | | | | |24| | |29| | | | |44|49| |
|
||||
15.000 |23|24| | | |29| | | | |39| | |49| |59| |74|
|
||||
10.000 |35| | | | |44| |49| | |59| | |74| |89|99| |
|
||||
--------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
*/
|
||||
#define CAN_BTR(BRP,TSEG1,TSEG2,SJW) (((((((SJW)&0x03)<<4)|((TSEG2)&0x07))<<4)|((TSEG1)&0x0F))<<16)|((BRP)&0x3FF)
|
||||
|
||||
// значения регистра BTR
|
||||
enum TCanBaudrate {
|
||||
CanBaudrate500 = CAN_BTR(0,10,5,3),
|
||||
|
||||
CanBaudrate250 = CAN_BTR(1,10,5,3),
|
||||
|
||||
// CanBaudrate125 = CAN_BTR(2,14,7,3),
|
||||
CanBaudrate125 = CAN_BTR(3,10,5,3),
|
||||
|
||||
CanBaudrate100 = CAN_BTR(4,10,5,3),
|
||||
|
||||
CanBaudrate83 = CAN_BTR(5,10,5,3),
|
||||
|
||||
// CanBaudrate50 = CAN_BTR(8,11,6,3),
|
||||
CanBaudrate50 = CAN_BTR(9,10,5,3),
|
||||
|
||||
CanBaudrate33 = CAN_BTR(14,10,5,3),
|
||||
|
||||
CanBaudrate20 = CAN_BTR(17,15,7,3),
|
||||
// CanBaudrate20 = CAN_BTR(24,10,5,3),
|
||||
|
||||
CanBaudrate15 = CAN_BTR(23,15,7,3),
|
||||
// CanBaudrate15 = CAN_BTR(24,14,7,3),
|
||||
// CanBaudrate15 = CAN_BTR(29,11,6,3),
|
||||
|
||||
CanBaudrate10 = CAN_BTR(35,15,7,3),
|
||||
// CanBaudrate10 = CAN_BTR(44,11,6,3),
|
||||
// CanBaudrate10 = CAN_BTR(49,10,5,3),
|
||||
|
||||
};
|
||||
|
||||
#ifndef CAN_FIRMWARE
|
||||
CCan ();
|
||||
|
||||
uint32_t Init (TCanChannel aCh, const TCanInit * apInit);
|
||||
|
||||
uint32_t Send (TCanChannel aCh, TCanPkt * apPkt, uint32_t aTimeout);
|
||||
|
||||
void ReceivedIsr (TCanChannel aCh, TCanPkt * apPkt);
|
||||
|
||||
uint32_t ProcessQueue ();
|
||||
|
||||
uint32_t GetRcv (TCanChannel aCh, TCanPkt * apPkt);
|
||||
|
||||
uint32_t GetRcvTimeout (TCanChannel aCh)
|
||||
{ return _rx_tmr[aCh].Value(); }
|
||||
|
||||
#endif // CAN_FIRMWARE
|
||||
|
||||
// объявление фильтров при инициализации
|
||||
struct Filter {
|
||||
// добавить пару идентификаторов для точного соответствия
|
||||
static constexpr uint64_t List11 (uint16_t id1, uint16_t id2)
|
||||
{
|
||||
return ((uint64_t) (IdStd (id1) | 0x01) << 32) |
|
||||
(uint64_t) (IdStd (id2) | 0x01);
|
||||
}
|
||||
static constexpr uint64_t List29 (uint32_t id1, uint32_t id2)
|
||||
{
|
||||
return ((uint64_t) (IdExt (id1) | 0x01) << 32) |
|
||||
(uint64_t) (IdExt (id2) | 0x01);
|
||||
}
|
||||
|
||||
// добавить один идентификатор
|
||||
static constexpr uint64_t List11 (uint16_t id1)
|
||||
{
|
||||
return ((uint64_t) (IdStd (id1) | 0x01) << 32) |
|
||||
(uint64_t) (IdStd (id1) | 0x01);
|
||||
}
|
||||
static constexpr uint64_t List29 (uint32_t id1)
|
||||
{
|
||||
return ((uint64_t) (IdExt (id1) | 0x01) << 32) |
|
||||
(uint64_t) (IdExt (id1) | 0x01);
|
||||
}
|
||||
|
||||
|
||||
// добавить один идентификатор с маской
|
||||
static constexpr uint64_t Mask11 (uint16_t id, uint16_t mask)
|
||||
{
|
||||
return ((uint64_t) (IdStd (id)) << 32) |
|
||||
(uint64_t) (IdStd (mask) | 0x01);
|
||||
}
|
||||
static constexpr uint64_t Mask29 (uint32_t id, uint32_t mask)
|
||||
{
|
||||
return ((uint64_t) (IdExt (id)) << 32) |
|
||||
(uint64_t) (IdExt (mask) | 0x01);
|
||||
}
|
||||
|
||||
private:
|
||||
// формирование ID для фильтров
|
||||
static constexpr uint32_t IdStd (uint16_t id)
|
||||
{ return (id & 0x7FFul) << 21; }
|
||||
static constexpr uint32_t IdExt (uint32_t id)
|
||||
{ return ((id & 0x1FFFFFFFul) << 3) | 0x04; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifndef CAN_FIRMWARE
|
||||
private:
|
||||
|
||||
uint8_t sleep_en;
|
||||
bool IsSleepEn (TCanChannel aCh)
|
||||
{ return sleep_en & aCh; }
|
||||
|
||||
|
||||
// очередь пакетов на отправку - тот же TCanPkt, но с таймером
|
||||
struct TCanPktTxInt: public TCanPkt {
|
||||
uint32_t timeout;
|
||||
TimerMs tmr;
|
||||
};
|
||||
|
||||
typedef CircularBuffer<TCanPkt, 16> TRxBuf;
|
||||
typedef CircularBuffer<TCanPktTxInt, 32> TTxBuf;
|
||||
|
||||
TRxBuf _rx_buf[2];
|
||||
TTxBuf _tx_buf[2];
|
||||
|
||||
uint32_t TxByTimeout (TCanChannel aCh);
|
||||
|
||||
bool IsTxQueueEmpty (TCanChannel aCh);
|
||||
|
||||
TCanState _state[2];
|
||||
|
||||
// Send() и обработка очереди вызываются из разных потоков, нужна защита
|
||||
OS::TMutex can_mutex;
|
||||
|
||||
// сбрасывается по приёму любого пакета
|
||||
TimerMs _rx_tmr[2];
|
||||
|
||||
#endif // CAN_FIRMWARE
|
||||
};
|
||||
|
||||
|
||||
|
||||
extern CCan gCan;
|
||||
|
||||
|
||||
|
||||
#endif /* DRIVERS_CAN_CAN_H_ */
|
||||
Reference in New Issue
Block a user