-
Notifications
You must be signed in to change notification settings - Fork 754
/
llfdcan.h
269 lines (226 loc) · 9.4 KB
/
llfdcan.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// SAE J2284-4 document specifies a bus-line network running at 2 Mbit/s
// SAE J2284-5 document specifies a point-to-point communication running at 5 Mbit/s
#define CAN_PCLK 80000U // KHz, sourced from PLL1Q
#define BITRATE_PRESCALER 2U // Valid from 250Kbps to 5Mbps with 80Mhz clock
#define CAN_SP_NOMINAL 80U // 80% for both SAE J2284-4 and SAE J2284-5
#define CAN_SP_DATA_2M 80U // 80% for SAE J2284-4
#define CAN_SP_DATA_5M 75U // 75% for SAE J2284-5
#define CAN_QUANTA(speed, prescaler) (CAN_PCLK / ((speed) / 10U * (prescaler)))
#define CAN_SEG1(tq, sp) (((tq) * (sp) / 100U)- 1U)
#define CAN_SEG2(tq, sp) ((tq) * (100U - (sp)) / 100U)
// FDCAN core settings
#define FDCAN_MESSAGE_RAM_SIZE 0x2800UL
#define FDCAN_START_ADDRESS 0x4000AC00UL
#define FDCAN_OFFSET 3384UL // bytes for each FDCAN module, equally
#define FDCAN_OFFSET_W 846UL // words for each FDCAN module, equally
#define FDCAN_END_ADDRESS 0x4000D3FCUL // Message RAM has a width of 4 bytes
// FDCAN_RX_FIFO_0_EL_CNT + FDCAN_TX_FIFO_EL_CNT can't exceed 47 elements (47 * 72 bytes = 3,384 bytes) per FDCAN module
// RX FIFO 0
#define FDCAN_RX_FIFO_0_EL_CNT 46UL
#define FDCAN_RX_FIFO_0_HEAD_SIZE 8UL // bytes
#define FDCAN_RX_FIFO_0_DATA_SIZE 64UL // bytes
#define FDCAN_RX_FIFO_0_EL_SIZE (FDCAN_RX_FIFO_0_HEAD_SIZE + FDCAN_RX_FIFO_0_DATA_SIZE)
#define FDCAN_RX_FIFO_0_EL_W_SIZE (FDCAN_RX_FIFO_0_EL_SIZE / 4UL)
#define FDCAN_RX_FIFO_0_OFFSET 0UL
// TX FIFO
#define FDCAN_TX_FIFO_EL_CNT 1UL
#define FDCAN_TX_FIFO_HEAD_SIZE 8UL // bytes
#define FDCAN_TX_FIFO_DATA_SIZE 64UL // bytes
#define FDCAN_TX_FIFO_EL_SIZE (FDCAN_TX_FIFO_HEAD_SIZE + FDCAN_TX_FIFO_DATA_SIZE)
#define FDCAN_TX_FIFO_EL_W_SIZE (FDCAN_TX_FIFO_EL_SIZE / 4UL)
#define FDCAN_TX_FIFO_OFFSET (FDCAN_RX_FIFO_0_OFFSET + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_W_SIZE))
#define CAN_NAME_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? "FDCAN1" : (((CAN_DEV) == FDCAN2) ? "FDCAN2" : "FDCAN3"))
#define CAN_NUM_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? 0UL : (((CAN_DEV) == FDCAN2) ? 1UL : 2UL))
void print(const char *a);
// kbps multiplied by 10
const uint32_t speeds[] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U};
const uint32_t data_speeds[] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U, 20000U, 50000U};
bool fdcan_request_init(FDCAN_GlobalTypeDef *FDCANx) {
bool ret = true;
// Exit from sleep mode
FDCANx->CCCR &= ~(FDCAN_CCCR_CSR);
while ((FDCANx->CCCR & FDCAN_CCCR_CSA) == FDCAN_CCCR_CSA);
// Request init
uint32_t timeout_counter = 0U;
FDCANx->CCCR |= FDCAN_CCCR_INIT;
while ((FDCANx->CCCR & FDCAN_CCCR_INIT) == 0U) {
// Delay for about 1ms
delay(10000);
timeout_counter++;
if (timeout_counter >= CAN_INIT_TIMEOUT_MS){
ret = false;
break;
}
}
return ret;
}
bool fdcan_exit_init(FDCAN_GlobalTypeDef *FDCANx) {
bool ret = true;
FDCANx->CCCR &= ~(FDCAN_CCCR_INIT);
uint32_t timeout_counter = 0U;
while ((FDCANx->CCCR & FDCAN_CCCR_INIT) != 0U) {
// Delay for about 1ms
delay(10000);
timeout_counter++;
if (timeout_counter >= CAN_INIT_TIMEOUT_MS) {
ret = false;
break;
}
}
return ret;
}
bool llcan_set_speed(FDCAN_GlobalTypeDef *FDCANx, uint32_t speed, uint32_t data_speed, bool non_iso, bool loopback, bool silent) {
UNUSED(speed);
bool ret = fdcan_request_init(FDCANx);
if (ret) {
// Enable config change
FDCANx->CCCR |= FDCAN_CCCR_CCE;
//Reset operation mode to Normal
FDCANx->CCCR &= ~(FDCAN_CCCR_TEST);
FDCANx->TEST &= ~(FDCAN_TEST_LBCK);
FDCANx->CCCR &= ~(FDCAN_CCCR_MON);
FDCANx->CCCR &= ~(FDCAN_CCCR_ASM);
FDCANx->CCCR &= ~(FDCAN_CCCR_NISO);
// TODO: add as a separate safety mode
// Enable ASM restricted operation(for debug or automatic bitrate switching)
//FDCANx->CCCR |= FDCAN_CCCR_ASM;
uint8_t prescaler = BITRATE_PRESCALER;
if (speed < 2500U) {
// The only way to support speeds lower than 250Kbit/s (down to 10Kbit/s)
prescaler = BITRATE_PRESCALER * 16U;
}
// Set the nominal bit timing values
uint32_t tq = CAN_QUANTA(speed, prescaler);
uint32_t sp = CAN_SP_NOMINAL;
uint32_t seg1 = CAN_SEG1(tq, sp);
uint32_t seg2 = CAN_SEG2(tq, sp);
uint8_t sjw = MIN(127U, seg2);
FDCANx->NBTP = (((sjw & 0x7FUL)-1U)<<FDCAN_NBTP_NSJW_Pos) | (((seg1 & 0xFFU)-1U)<<FDCAN_NBTP_NTSEG1_Pos) | (((seg2 & 0x7FU)-1U)<<FDCAN_NBTP_NTSEG2_Pos) | (((prescaler & 0x1FFUL)-1U)<<FDCAN_NBTP_NBRP_Pos);
// Set the data bit timing values
if (data_speed == 50000U) {
sp = CAN_SP_DATA_5M;
} else {
sp = CAN_SP_DATA_2M;
}
tq = CAN_QUANTA(data_speed, prescaler);
seg1 = CAN_SEG1(tq, sp);
seg2 = CAN_SEG2(tq, sp);
sjw = MIN(15U, seg2);
FDCANx->DBTP = (((sjw & 0xFUL)-1U)<<FDCAN_DBTP_DSJW_Pos) | (((seg1 & 0x1FU)-1U)<<FDCAN_DBTP_DTSEG1_Pos) | (((seg2 & 0xFU)-1U)<<FDCAN_DBTP_DTSEG2_Pos) | (((prescaler & 0x1FUL)-1U)<<FDCAN_DBTP_DBRP_Pos);
if (non_iso) {
// FD non-ISO mode
FDCANx->CCCR |= FDCAN_CCCR_NISO;
}
// Silent loopback is known as internal loopback in the docs
if (loopback) {
FDCANx->CCCR |= FDCAN_CCCR_TEST;
FDCANx->TEST |= FDCAN_TEST_LBCK;
FDCANx->CCCR |= FDCAN_CCCR_MON;
}
// Silent is known as bus monitoring in the docs
if (silent) {
FDCANx->CCCR |= FDCAN_CCCR_MON;
}
ret = fdcan_exit_init(FDCANx);
if (!ret) {
print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (2)\n");
}
} else {
print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (1)\n");
}
return ret;
}
void llcan_irq_disable(const FDCAN_GlobalTypeDef *FDCANx) {
if (FDCANx == FDCAN1) {
NVIC_DisableIRQ(FDCAN1_IT0_IRQn);
NVIC_DisableIRQ(FDCAN1_IT1_IRQn);
} else if (FDCANx == FDCAN2) {
NVIC_DisableIRQ(FDCAN2_IT0_IRQn);
NVIC_DisableIRQ(FDCAN2_IT1_IRQn);
} else if (FDCANx == FDCAN3) {
NVIC_DisableIRQ(FDCAN3_IT0_IRQn);
NVIC_DisableIRQ(FDCAN3_IT1_IRQn);
} else {
}
}
void llcan_irq_enable(const FDCAN_GlobalTypeDef *FDCANx) {
if (FDCANx == FDCAN1) {
NVIC_EnableIRQ(FDCAN1_IT0_IRQn);
NVIC_EnableIRQ(FDCAN1_IT1_IRQn);
} else if (FDCANx == FDCAN2) {
NVIC_EnableIRQ(FDCAN2_IT0_IRQn);
NVIC_EnableIRQ(FDCAN2_IT1_IRQn);
} else if (FDCANx == FDCAN3) {
NVIC_EnableIRQ(FDCAN3_IT0_IRQn);
NVIC_EnableIRQ(FDCAN3_IT1_IRQn);
} else {
}
}
bool llcan_init(FDCAN_GlobalTypeDef *FDCANx) {
uint32_t can_number = CAN_NUM_FROM_CANIF(FDCANx);
bool ret = fdcan_request_init(FDCANx);
if (ret) {
// Enable config change
FDCANx->CCCR |= FDCAN_CCCR_CCE;
// Enable automatic retransmission
FDCANx->CCCR &= ~(FDCAN_CCCR_DAR);
// Enable transmission pause feature
FDCANx->CCCR |= FDCAN_CCCR_TXP;
// Disable protocol exception handling
FDCANx->CCCR |= FDCAN_CCCR_PXHD;
// FD with BRS
FDCANx->CCCR |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE);
// Set TX mode to FIFO
FDCANx->TXBC &= ~(FDCAN_TXBC_TFQM);
// Configure TX element data size
FDCANx->TXESC |= 0x7U << FDCAN_TXESC_TBDS_Pos; // 64 bytes
//Configure RX FIFO0 element data size
FDCANx->RXESC |= 0x7U << FDCAN_RXESC_F0DS_Pos;
// Disable filtering, accept all valid frames received
FDCANx->XIDFC &= ~(FDCAN_XIDFC_LSE); // No extended filters
FDCANx->SIDFC &= ~(FDCAN_SIDFC_LSS); // No standard filters
FDCANx->GFC &= ~(FDCAN_GFC_RRFE); // Accept extended remote frames
FDCANx->GFC &= ~(FDCAN_GFC_RRFS); // Accept standard remote frames
FDCANx->GFC &= ~(FDCAN_GFC_ANFE); // Accept extended frames to FIFO 0
FDCANx->GFC &= ~(FDCAN_GFC_ANFS); // Accept standard frames to FIFO 0
uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET);
uint32_t TxFIFOSA = RxFIFO0SA + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE);
// RX FIFO 0
FDCANx->RXF0C |= (FDCAN_RX_FIFO_0_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_RXF0C_F0SA_Pos;
FDCANx->RXF0C |= FDCAN_RX_FIFO_0_EL_CNT << FDCAN_RXF0C_F0S_Pos;
// RX FIFO 0 switch to non-blocking (overwrite) mode
FDCANx->RXF0C |= FDCAN_RXF0C_F0OM;
// TX FIFO (mode set earlier)
FDCANx->TXBC |= (FDCAN_TX_FIFO_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_TXBC_TBSA_Pos;
FDCANx->TXBC |= FDCAN_TX_FIFO_EL_CNT << FDCAN_TXBC_TFQS_Pos;
// Flush allocated RAM
uint32_t EndAddress = TxFIFOSA + (FDCAN_TX_FIFO_EL_CNT * FDCAN_TX_FIFO_EL_SIZE);
for (uint32_t RAMcounter = RxFIFO0SA; RAMcounter < EndAddress; RAMcounter += 4U) {
*(uint32_t *)(RAMcounter) = 0x00000000;
}
// Enable both interrupts for each module
FDCANx->ILE = (FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1);
FDCANx->IE &= 0x0U; // Reset all interrupts
// Messages for INT0
FDCANx->IE |= FDCAN_IE_RF0NE; // Rx FIFO 0 new message
FDCANx->IE |= FDCAN_IE_PEDE | FDCAN_IE_PEAE | FDCAN_IE_BOE | FDCAN_IE_EPE | FDCAN_IE_RF0LE;
// Messages for INT1 (Only TFE works??)
FDCANx->ILS |= FDCAN_ILS_TFEL;
FDCANx->IE |= FDCAN_IE_TFEE; // Tx FIFO empty
ret = fdcan_exit_init(FDCANx);
if(!ret) {
print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (2)!\n");
}
llcan_irq_enable(FDCANx);
} else {
print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (1)!\n");
}
return ret;
}
void llcan_clear_send(FDCAN_GlobalTypeDef *FDCANx) {
// from datasheet: "Transmit cancellation is not intended for Tx FIFO operation."
// so we need to clear pending transmission manually by resetting FDCAN core
FDCANx->IR |= 0x3FCFFFFFU; // clear all interrupts
bool ret = llcan_init(FDCANx);
UNUSED(ret);
}