forked from Emandhal/MCP251XFD
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MCP251XFD.c
/
MCP251XFD.c
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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*!*****************************************************************************
* @file MCP251XFD.c
* @author Fabien 'Emandhal' MAILLY
* @version 1.0.6
* @date 16/04/2023
* @brief MCP251XFD driver
* @details
* The MCP251XFD component is a CAN-bus controller supporting CAN2.0A, CAN2.0B
* and CAN-FD with SPI interface
* Follow datasheet MCP2517FD Rev.B (July 2019)
* MCP2518FD Rev.B (Dec 2020)
* MCP251863 Rev.A (Sept 2022) [Have a MCP2518FD inside]
* Follow MCP25XXFD Family Reference Manual (DS20005678D)
******************************************************************************/
//-----------------------------------------------------------------------------
#include "MCP251XFD.h"
//-----------------------------------------------------------------------------
#ifdef __cplusplus
# include <cstdint>
extern "C" {
#endif
//-----------------------------------------------------------------------------
//=============================================================================
// Prototypes for private functions
//=============================================================================
// Test all RAM address of the MCP251XFD for the driver flag DRIVER_INIT_CHECK_RAM
static eERRORRESULT __MCP251XFD_TestRAM(MCP251XFD *pComp);
//-----------------------------------------------------------------------------
#define MCP251XFD_USE_SID11 ( (pComp->InternalConfig & (MCP251XFD_CANFD_USE_RRS_BIT_AS_SID11 | MCP251XFD_CANFD_ENABLED)) == (MCP251XFD_CANFD_USE_RRS_BIT_AS_SID11 | MCP251XFD_CANFD_ENABLED) )
#define MCP251XFD_TIME_DIFF(begin,end) ( ((end) >= (begin)) ? ((end) - (begin)) : (UINT32_MAX - ((begin) - (end) - 1)) ) // Works only if time difference is strictly inferior to (UINT32_MAX/2) and call often
//-----------------------------------------------------------------------------
//**********************************************************************************************************************************************************
//=============================================================================
// MCP251XFD device initialization
//=============================================================================
eERRORRESULT Init_MCP251XFD(MCP251XFD *pComp, const MCP251XFD_Config *pConf)
{
#ifdef CHECK_NULL_PARAM
if ((pComp == NULL) || (pConf == NULL)) return ERR__PARAMETER_ERROR;
if (pComp->fnSPI_Init == NULL) return ERR__PARAMETER_ERROR;
if (pComp->fnGetCurrentms == NULL) return ERR__PARAMETER_ERROR;
#endif
eERRORRESULT Error;
uint32_t Result = 0;
pComp->InternalConfig = 0;
//--- Check configuration ---------------------------------
if ((pConf->XtalFreq != 0) && (pConf->XtalFreq < MCP251XFD_XTALFREQ_MIN)) return ERR__FREQUENCY_ERROR; // The device crystal should not be < 4MHz
if ((pConf->XtalFreq != 0) && (pConf->XtalFreq > MCP251XFD_XTALFREQ_MAX)) return ERR__FREQUENCY_ERROR; // The device crystal should not be > 40MHz
if ((pConf->OscFreq != 0) && (pConf->OscFreq < MCP251XFD_OSCFREQ_MIN )) return ERR__FREQUENCY_ERROR; // The device oscillator should not be < 2MHz
if ((pConf->OscFreq != 0) && (pConf->OscFreq > MCP251XFD_OSCFREQ_MAX )) return ERR__FREQUENCY_ERROR; // The device oscillator should not be > 40MHz
uint32_t CompFreq = 0;
if (pConf->XtalFreq != 0) CompFreq = pConf->XtalFreq; else CompFreq = pConf->OscFreq; // Select the component frequency
if (CompFreq == 0) return ERR__CONFIGURATION; // Both XtalFreq and OscFreq are configured to 0
//--- Configure SPI Interface -----------------------------
if (pComp->SPIClockSpeed > MCP251XFD_SPICLOCK_MAX) return ERR__SPI_FREQUENCY_ERROR; // The SPI clock should not be superior to the SPI clock max
if ((pComp->DriverConfig & MCP251XFD_DRIVER_SAFE_RESET) == 0)
{
Error = pComp->fnSPI_Init(pComp->InterfaceDevice, pComp->SPI_ChipSelect, pComp->SPIClockSpeed); // Initialize the SPI interface only in case of no safe reset (the interface will be initialized in the reset at a safe speed)
if (Error != ERR_OK) return Error; // If there is an error while calling fnSPI_Init() then return the error
}
//--- Reset -----------------------------------------------
Error = MCP251XFD_ResetDevice(pComp); // Reset the device
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ResetDevice() then return the error
pComp->InternalConfig = MCP251XFD_DEV_PS_SET(MCP251XFD_DEVICE_SLEEP_NOT_CONFIGURED); // Device is in normal power state, sleep is not yet configured
//--- Test SPI connection ---------------------------------
Error = MCP251XFD_WriteRAM32(pComp, (MCP251XFD_RAM_ADDR + MCP251XFD_RAM_SIZE - 4), 0xAA55AA55); // Write 0xAA55AA55 at address
if (Error != ERR_OK) return Error; // If there is an error while writing the RAM address then return the error
Error = MCP251XFD_ReadRAM32(pComp, (MCP251XFD_RAM_ADDR + MCP251XFD_RAM_SIZE - 4), &Result); // Read again the data
if ((Error == ERR__CRC_ERROR) || (Result != 0xAA55AA55)) return ERR__NO_DEVICE_DETECTED; // If CRC mismatch or data read is not 0xAA55AA55 then no device is detected
if (Error != ERR_OK) return Error; // If there is an error while reading the RAM address then return the error
//--- Configure component clock ---------------------------
uint8_t Config = MCP251XFD_SFR_OSC_WAKEUP | MCP251XFD_SFR_OSC8_SCLKDIV_SET(MCP251XFD_SCLK_DivBy1) | MCP251XFD_SFR_OSC8_PLLDIS;
if ((pConf->SysclkConfig == MCP251XFD_SYSCLK_IS_CLKIN_MUL_5) || (pConf->SysclkConfig == MCP251XFD_SYSCLK_IS_CLKIN_MUL_10))
{ Config |= MCP251XFD_SFR_OSC8_PLLEN; CompFreq *= 10; } // Activate the 10x PLL from Xtal/Resonator frequency @4MHz or from oscillator frequency @2MHz
if (CompFreq > MCP251XFD_CLKINPLL_MAX) return ERR__FREQUENCY_ERROR; // If component clock (CLKIN+PLL) is too high then return an error
if ((pConf->SysclkConfig == MCP251XFD_SYSCLK_IS_CLKIN_DIV_2) || (pConf->SysclkConfig == MCP251XFD_SYSCLK_IS_CLKIN_MUL_5))
{ Config |= MCP251XFD_SFR_OSC8_SCLKDIV_SET(MCP251XFD_SCLK_DivBy2); CompFreq /= 2; } // Configure CLKIN+PLL divisor from Xtal/Resonator and PLL or oscillator frequency
if (pConf->SYSCLK_Result != NULL) *pConf->SYSCLK_Result = CompFreq; // Save the internal SYSCLK if needed
if (CompFreq > MCP251XFD_SYSCLK_MAX) return ERR__FREQUENCY_ERROR; // If component clock (SYSCLK) is too high then return an error
if (pConf->ClkoPinConfig != MCP251XFD_CLKO_SOF) Config |= MCP251XFD_SFR_OSC8_CLKODIV_SET(pConf->ClkoPinConfig); // Configure the CLKO pin (CLKIN+PLL div by 1, 2, 4, 10 or Start Of Frame)
Config |= MCP251XFD_SFR_OSC8_LPMEN; // Set now the Low Power Mode for further check of which module MCP251XFD it is
Error = MCP251XFD_WriteSFR8(pComp, RegMCP251XFD_OSC_CONFIG, Config); // Write the Oscillator Register configuration
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_WriteSFR8() then return the error
//--- Check clocks stabilization --------------------------
uint8_t CheckVal = ((uint8_t)(Config) & MCP251XFD_SFR_OSC8_CHECKFLAGS) | MCP251XFD_SFR_OSC8_OSCRDY; // Check if PLL Locked (if enabled), OSC clock is running and stable, and SCLKDIV is synchronized (if divided by 2)
uint32_t StartTime = pComp->fnGetCurrentms(); // Start the timeout
while (true)
{
Error = MCP251XFD_ReadSFR8(pComp, RegMCP251XFD_OSC_CHECK, &Config); // Read current OSC register mode with
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ReadSFR8() then return the error
if ((Config & MCP251XFD_SFR_OSC8_CHECKFLAGS) == CheckVal) break; // Check if the controller's clocks are ready
if (MCP251XFD_TIME_DIFF(StartTime, pComp->fnGetCurrentms()) > 4) return ERR__DEVICE_TIMEOUT; // Wait at least 3ms (see TOSCSTAB in Table 7-3 from datasheet Electrical Specifications) + 1ms because GetCurrentms can be 1 cycle before the new ms. If timeout then return the error
}
//--- Set desired SPI clock speed -------------------------
if (pComp->SPIClockSpeed > (((CompFreq >> 1) * 85) / 100)) return ERR__SPI_FREQUENCY_ERROR; // Ensure that FSCK is less than or equal to 0.85*(FSYSCLK/2). Follows datasheets errata for: The SPI can write corrupted data to the RAM at fast SPI speeds
if ((pComp->DriverConfig & MCP251XFD_DRIVER_SAFE_RESET) > 0)
{
Error = pComp->fnSPI_Init(pComp->InterfaceDevice, pComp->SPI_ChipSelect, pComp->SPIClockSpeed); // Set the SPI speed clock to desired clock speed
if (Error != ERR_OK) return Error; // If there is an error while changing SPI interface speed then return the error
}
//--- Configure CRC Interrupts ----------------------------
if ((pComp->DriverConfig & MCP251XFD_DRIVER_USE_READ_WRITE_CRC) > 0) // If there is a DRIVER_USE_READ_WRITE_CRC flag then
{
Error = MCP251XFD_ConfigureCRC(pComp, MCP251XFD_CRC_ALL_EVENTS); // Configure the CRC and all interrupts related to CRC
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ConfigureCRC() then return the error
}
//--- Check which MCP251XFD it is ------------------------- // Since the DEVID register return the same value for MCP2517FD and MCP2518FD, this driver use the OSC.LPMEN to check which one it is
Error = MCP251XFD_ReadSFR8(pComp, RegMCP251XFD_OSC_CONFIG, &Config); // Read current OSC config mode
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ReadSFR8() then return the error
pComp->InternalConfig |= MCP251XFD_DEV_ID_SET((Config & MCP251XFD_SFR_OSC8_LPMEN) > 0 ? MCP2518FD : MCP2517FD); // Set which one it is to the internal config of the driver
Config &= ~MCP251XFD_SFR_OSC8_LPMEN;
Error = MCP251XFD_WriteSFR8(pComp, RegMCP251XFD_OSC_CONFIG, Config); // Write the OSC config mode with the LPM bit cleared
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_WriteSFR8() then return the error
//--- Test SPI connection and RAM Test --------------------
if ((pComp->DriverConfig & MCP251XFD_DRIVER_INIT_CHECK_RAM) > 0) // If there is a DRIVER_INIT_CHECK_RAM flag then
{
Error = __MCP251XFD_TestRAM(pComp); // Check the all the RAM only of the device
if (Error != ERR_OK) return Error; // If there is an error while calling __MCP251XFD_TestRAM() then return the error
}
else // Else check only SPI interface
{
Error = MCP251XFD_WriteRAM32(pComp, (MCP251XFD_RAM_ADDR + MCP251XFD_RAM_SIZE - 4), 0xAA55AA55); // Write 0xAA55AA55 at address
if (Error != ERR_OK) return Error; // If there is an error while writing the RAM address then return the error
Error = MCP251XFD_ReadRAM32(pComp, (MCP251XFD_RAM_ADDR + MCP251XFD_RAM_SIZE - 4), &Result); // Read again the data
if (Error != ERR_OK) return Error; // If there is an error while reading the RAM address then return the error
if (Result != 0xAA55AA55) return ERR__RAM_TEST_FAIL; // If data read is not 0xAA55AA55 then return an error
}
//--- Configure RAM ECC -----------------------------------
if ((pComp->DriverConfig & MCP251XFD_DRIVER_ENABLE_ECC) > 0) // If there is a DRIVER_ENABLE_ECC flag then
{
Error = MCP251XFD_ConfigureECC(pComp, true, MCP251XFD_ECC_ALL_EVENTS, 0x55); // Configure the ECC and enable all interrupts related to ECC
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ConfigureECC() then return the error
}
//--- Initialize RAM if configured ------------------------
if ((pComp->DriverConfig & MCP251XFD_DRIVER_INIT_SET_RAM_AT_0) > 0) // If there is a DRIVER_INIT_SET_RAM_AT_0 flag then
{
Error = MCP251XFD_InitRAM(pComp); // Initialize all RAM addresses with 0x00000000
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_InitRAM() then return the error
}
//--- Initialize Int pins or GPIOs ------------------------
Error = MCP251XFD_SetGPIOPinsOutputLevel(pComp, pComp->GPIOsOutState, MCP251XFD_GPIO0_Mask | MCP251XFD_GPIO1_Mask); // Set GPIO pins output level before change to mode GPIO. This is to get directly the good output level when (if) pins will be in output mode
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ConfigurePins() then return the error
Error = MCP251XFD_ConfigurePins(pComp, pConf->GPIO0PinMode, pConf->GPIO1PinMode, pConf->INTsOutMode, pConf->TXCANOutMode, (pConf->ClkoPinConfig == MCP251XFD_CLKO_SOF)); // Configure pins
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ConfigurePins() then return the error
//--- Set Nominal and Data bitrate ------------------------
MCP251XFD_BitTimeConfig ConfBitTime;
ConfBitTime.Stats = pConf->BitTimeStats;
Error = MCP251XFD_CalculateBitTimeConfiguration(CompFreq, pConf->NominalBitrate, pConf->DataBitrate, &ConfBitTime); // Calculate Bit Time
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_CalculateBitTimeConfiguration() then return the error
Error = MCP251XFD_SetBitTimeConfiguration(pComp, &ConfBitTime, pConf->DataBitrate == MCP251XFD_NO_CANFD); // Set Bit Time configuration to registers
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_SetBitTimeConfiguration() then return the error
//--- CAN configuration -----------------------------------
Error = MCP251XFD_WriteSFR8(pComp, RegMCP251XFD_CiCON+2, 0x00); // Disable TEF and TXQ configuration in the RegMCP251XFD_CiCON register (Third byte only)
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_WriteSFR8() then return the error
Error = MCP251XFD_ConfigureCANController(pComp, pConf->ControlFlags, pConf->Bandwidth); // Configure the CAN control
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ConfigureCANControl() then return the error
//--- System interrupt enable -----------------------------
Error = MCP251XFD_ConfigureInterrupt(pComp, (setMCP251XFD_InterruptEvents)pConf->SysInterruptFlags); // Configure interrupts to enable
return Error;
}
//=============================================================================
// [STATIC] RAM initialization of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_InitRAM(MCP251XFD *pComp)
{
eERRORRESULT Error;
for (uint16_t Address = MCP251XFD_RAM_ADDR; Address < (MCP251XFD_RAM_ADDR + MCP251XFD_RAM_SIZE); Address += 4)
{
Error = MCP251XFD_WriteRAM32(pComp, Address, 0x00000000); // Write 0x00000000 at address
if (Error != ERR_OK) return Error; // If there is an error while writing the RAM address then return the error
}
return ERR_OK;
}
//**********************************************************************************************************************************************************
//=============================================================================
// Get actual device of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_GetDeviceID(MCP251XFD *pComp, eMCP251XFD_Devices* device, uint8_t* deviceId, uint8_t* deviceRev)
{
#ifdef CHECK_NULL_PARAM
if ((pComp == NULL) || (device == NULL)) return ERR__PARAMETER_ERROR;
#endif
*device = (eMCP251XFD_Devices)MCP251XFD_DEV_ID_GET(pComp->InternalConfig); // Get device found when initializing the module
eERRORRESULT Error;
if ((deviceId != NULL) || (deviceRev != NULL))
{
uint8_t Value;
Error = MCP251XFD_ReadSFR8(pComp, RegMCP251XFD_DEVID, &Value); // Read value of the DEVID register (First byte only)
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ReadSFR8() then return the error
if (deviceId != NULL) *deviceId = MCP251XFD_SFR_DEVID8_ID_GET(Value); // Get Device ID
if (deviceRev != NULL) *deviceRev = MCP251XFD_SFR_DEVID8_REV_GET(Value); // Get Device Revision
}
return ERR_OK;
}
//**********************************************************************************************************************************************************
//=============================================================================
// Read data from the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_ReadData(MCP251XFD *pComp, uint16_t address, uint8_t* data, uint16_t size)
{
#ifdef CHECK_NULL_PARAM
if ((pComp == NULL) || (data == NULL)) return ERR__PARAMETER_ERROR;
if (pComp->fnSPI_Transfer == NULL) return ERR__PARAMETER_ERROR;
#endif
const bool UseCRC = ((pComp->DriverConfig & MCP251XFD_DRIVER_USE_READ_WRITE_CRC) > 0);
const bool InRAM = (address >= MCP251XFD_RAM_ADDR) && (address < (MCP251XFD_RAM_ADDR + MCP251XFD_RAM_SIZE)); // True if address is in the RAM region ; False if address is in Controller SFR or CAN SFR region
if (address > MCP251XFD_END_ADDR) return ERR__PARAMETER_ERROR;
uint8_t Buffer[MCP251XFD_TRANS_BUF_SIZE];
eERRORRESULT Error;
//--- Define the Increment value ---
uint16_t Increment = MCP251XFD_TRANS_BUF_SIZE - (UseCRC ? 5 : 2); // If use CRC for read, Buffer size minus 2 for Command, minus 1 for length, minus 2 for CRC, else just 2 for command
if (UseCRC && InRAM) // In RAM region and use CRC for read?
{
if ((size & 0b11) != 0) return ERR__DATA_MODULO; // size should be a multiple of 4 in case of Write CRC or Safe Write
Increment &= 0xFFFC; // If in RAM region then the increment should be the nearest less than or equal value multiple by 4
} // Here Increment cannot be 0 because MCP251XFD_TRANS_BUF_SIZE is compiler protected to be not less than 9
//--- Create all clusters of data and send them ---
uint8_t* pBuf;
size_t BufRemain = 0;
size_t ByteCount;
while (size > 0)
{
const uint16_t Addr = MCP251XFD_SPI_16BITS_WORD((UseCRC ? MCP251XFD_SPI_INSTRUCTION_READ_CRC : MCP251XFD_SPI_INSTRUCTION_READ), address);
//--- Compose SPI command ---
pBuf = &Buffer[0];
*pBuf = ((Addr >> 8) & 0xFF); // Set first byte of SPI command
++pBuf;
*pBuf = Addr & 0xFF; // Set next byte of SPI command
++pBuf;
ByteCount = (size > Increment ? Increment : size); // Define byte count to send
//--- If needed, set 0x00 byte while reading data on SPI interface else send garbage data ---
if ((pComp->DriverConfig & MCP251XFD_DRIVER_CLEAR_BUFFER_BEFORE_READ) > 0)
{
const size_t BuffUsedSize = ByteCount + (UseCRC ? 2 + 1 - 2 : 2); // Here 2 for Command + 1 for Length - 2 for CRC (CRC on the SPI send is not checked by the controller thus will be set to 0 too), else just 2 for command
for (size_t z = 2; z < BuffUsedSize; ++z) Buffer[z] = 0x00; // Set to 0
}
// Set length of data
uint8_t LenData = 0;
if (UseCRC && InRAM) LenData = (ByteCount >> 2) & 0xFF; // If use CRC for read and in RAM, set how many data word that is requested
else LenData = ByteCount & 0xFF; // Set how many data byte that is requested
if (UseCRC) { *pBuf = LenData; ++pBuf; } // Add Len in the frame if use CRC
//--- Now send the data through SPI interface ---
const size_t ByteToReadCount = ByteCount + (UseCRC ? (2 + 1 + 2) : 2); // In case of use CRC for read, here are 2 bytes for Command + 1 for Length + 2 for CRC, else just 2 for command
Error = pComp->fnSPI_Transfer(pComp->InterfaceDevice, pComp->SPI_ChipSelect, &Buffer[0], &Buffer[0], ByteToReadCount); // Transfer the data in the buffer
if (Error != ERR_OK) return Error; // If there is an error while transferring data then return the error
//--- Copy buffer to data ---
BufRemain = ByteCount; // Set how many data that will fit in the buffer
while ((BufRemain > 0) && (size > 0))
{
*data = *pBuf; // Copy data
++pBuf;
++data;
--BufRemain;
--size;
++address;
}
//--- Check CRC ---
if (UseCRC)
{
// Replace Head data of the Tx buffer into the Rx buffer
Buffer[0] = (uint8_t)((Addr >> 8) & 0xFF);
Buffer[1] = (uint8_t)((Addr >> 0) & 0xFF);
Buffer[2] = LenData;
// Compute CRC and compare to the one in the buffer
#ifdef CHECK_NULL_PARAM
if (pComp->fnComputeCRC16 == NULL) return ERR__PARAMETER_ERROR; // If the CRC function is not present, raise an error
#endif
uint16_t CRC = pComp->fnComputeCRC16(&Buffer[0], (ByteCount + 2 + 1)); // Compute CRC on the Buffer data (2 for Command + 1 for Length)
uint16_t BufCRC = *pBuf << 8; // Get CRC MSB on the next buffer byte
++pBuf;
BufCRC |= *pBuf; // Get CRC LSB on the next buffer byte
if (CRC != BufCRC) return ERR__CRC_ERROR; // If CRC mismatch, then raise an error
}
}
return ERR_OK;
}
//=============================================================================
// Write data to the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_WriteData(MCP251XFD *pComp, uint16_t address, const uint8_t* data, uint16_t size)
{
#ifdef CHECK_NULL_PARAM
if ((pComp == NULL) || (data == NULL)) return ERR__PARAMETER_ERROR;
if (pComp->fnSPI_Transfer == NULL) return ERR__PARAMETER_ERROR;
#endif
const bool UseCRC = ((pComp->DriverConfig & (MCP251XFD_DRIVER_USE_READ_WRITE_CRC | MCP251XFD_DRIVER_USE_SAFE_WRITE)) > 0);
const bool UseSafe = ((pComp->DriverConfig & MCP251XFD_DRIVER_USE_SAFE_WRITE) > 0);
const bool InRAM = (address >= MCP251XFD_RAM_ADDR) && (address < (MCP251XFD_RAM_ADDR + MCP251XFD_RAM_SIZE)); // True if address is in the RAM region ; False if address is in Controller SFR or CAN SFR region
if (address > MCP251XFD_END_ADDR) return ERR__PARAMETER_ERROR;
uint8_t Buffer[MCP251XFD_TRANS_BUF_SIZE];
uint32_t Increment;
eERRORRESULT Error;
//--- Define the Increment value ---
uint32_t Instruction = (UseCRC ? MCP251XFD_SPI_INSTRUCTION_WRITE_CRC : MCP251XFD_SPI_INSTRUCTION_WRITE);
if (UseSafe == false)
{
Increment = MCP251XFD_TRANS_BUF_SIZE - (UseCRC ? 5 : 2); // If use CRC for writeBuffer size minus 2 for Command, minus 1 for length, minus 2 for CRC, else just 2 for command
if (UseCRC && InRAM) // In RAM region and use CRC for write
{
if ((size & 0b11) != 0) return ERR__DATA_MODULO; // size should be a multiple of 4 in case of Write CRC or Safe Write
Increment &= 0xFFFC; // If in RAM region then the increment should be the nearest less than or equal value multiple by 4
} // Here Increment cannot be 0 because MCP251XFD_TRANS_BUF_SIZE is compiler protected to be not less than 9
}
else
{
Instruction = MCP251XFD_SPI_INSTRUCTION_SAFE_WRITE; // Set safe write instruction
if (InRAM)
{
if ((size & 0b11) != 0) return ERR__DATA_MODULO; // size should be a multiple of 4 in case of Write CRC or Safe Write
Increment = 4; // If use safe write and in RAM then the increment is 4 bytes
}
else Increment = 1; // If use safe write and in SFR then the increment is 1 byte
}
//--- Create all clusters of data and send them ---
uint8_t* pBuf;
size_t BufRemain = 0;
size_t ByteCount = 0;
while (size > 0)
{
const uint16_t Addr = MCP251XFD_SPI_16BITS_WORD(Instruction , address);
//--- Compose SPI command ---
pBuf = &Buffer[0];
*pBuf = ((Addr >> 8) & 0xFF); // Set first byte of SPI command
++pBuf;
*pBuf = Addr & 0xFF; // Set next byte of SPI command
++pBuf;
//--- Set length of data ---
ByteCount = (size > Increment ? Increment : size); // Define byte count to send
if (UseCRC && (UseSafe == false)) // Add Len in the frame if use CRC but not use safe
{
if (InRAM) *pBuf = (ByteCount >> 2) & 0xFF; // If use CRC for write and in RAM, set how many data word that is requested
else *pBuf = ByteCount & 0xFF; // Set how many data byte that is requested
++pBuf;
}
//--- Copy data to buffer ---
BufRemain = Increment; // Set how many data that will fit in the buffer
while ((BufRemain > 0) && (size > 0))
{
*pBuf = *data; // Copy data
++pBuf;
++data;
--BufRemain;
--size;
++address;
}
//--- Compute CRC and add to the buffer ---
if (UseCRC)
{
#ifdef CHECK_NULL_PARAM
if (pComp->fnComputeCRC16 == NULL) return ERR__PARAMETER_ERROR; // If the CRC function is not present, raise an error
#endif
const size_t ByteToComputeCount = ByteCount + (UseSafe ? 2 : (2 + 1));
uint16_t CRC = pComp->fnComputeCRC16(&Buffer[0], ByteToComputeCount); // Compute CRC on the Buffer data (2 for Command + 1 for Length for not safe)
*pBuf = (CRC >> 8) & 0xFF; // Put CRC MSB on the next buffer byte
++pBuf;
*pBuf = CRC & 0xFF; // Put CRC LSB on the next buffer byte
}
//--- Now send the data through SPI interface ---
const size_t ByteToWriteCount = ByteCount + (UseSafe ? (2 + 2) : (UseCRC ? (2 + 1 + 2) : 2)); // In case of use CRC for write here are 2 bytes for Command + 1 for Length for not safe + 2 for CRC, else just 2 for command
Error = pComp->fnSPI_Transfer(pComp->InterfaceDevice, pComp->SPI_ChipSelect, &Buffer[0], NULL, ByteToWriteCount); // Transfer the data in the buffer (2 for Command + 1 for Length + 2 for CRC)
if (Error != ERR_OK) return Error; // If there is an error while transferring data then return the error
}
return ERR_OK;
}
//**********************************************************************************************************************************************************
//=============================================================================
// Transmit a message object (with data) to the FIFO of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_TransmitMessageObjectToFIFO(MCP251XFD *pComp, uint8_t* messageObjectToSend, uint8_t objectSize, eMCP251XFD_FIFO toFIFO, bool andFlush)
{
#ifdef CHECK_NULL_PARAM
if ((pComp == NULL) || (messageObjectToSend == NULL)) return ERR__PARAMETER_ERROR;
#endif
if (toFIFO == MCP251XFD_TEF) return ERR__PARAMETER_ERROR;
if ((objectSize & 0x3) > 0) return ERR__BYTE_COUNT_MODULO_4;
eERRORRESULT Error;
//--- Get address where to write the frame ---
uint32_t NextAddress = 0;
Error = MCP251XFD_GetNextMessageAddressFIFO(pComp, toFIFO, &NextAddress, NULL); // Get next message address
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_GetNextMessageAddressFIFO() then return the error
NextAddress += MCP251XFD_RAM_ADDR;
//--- Write data to RAM ---
Error = MCP251XFD_WriteData(pComp, NextAddress, &messageObjectToSend[0], objectSize); // Write data to RAM address
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_WriteData() then return the error
//--- Update FIFO and flush if asked ---
return MCP251XFD_UpdateFIFO(pComp, toFIFO, andFlush);
}
//=============================================================================
// Transmit a message to a FIFO of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_TransmitMessageToFIFO(MCP251XFD *pComp, MCP251XFD_CANMessage* messageToSend, eMCP251XFD_FIFO toFIFO, bool andFlush)
{
#ifdef CHECK_NULL_PARAM
if ((pComp == NULL) || (messageToSend == NULL)) return ERR__PARAMETER_ERROR;
#endif
if (toFIFO == MCP251XFD_TEF) return ERR__PARAMETER_ERROR;
uint8_t Buffer[MCP251XFD_CAN_TX_MESSAGE_SIZE_MAX];
MCP251XFD_CAN_TX_Message* Message = (MCP251XFD_CAN_TX_Message*)Buffer; // The first 8 bytes represent the MCP251XFD_CAN_TX_Message struct
//--- Fill message ID (T0) ---
bool Extended = ((messageToSend->ControlFlags & MCP251XFD_EXTENDED_MESSAGE_ID) > 0);
bool CANFDframe = ((messageToSend->ControlFlags & MCP251XFD_CANFD_FRAME ) > 0);
Message->T0.T0 = MCP251XFD_MessageIDtoObjectMessageIdentifier(messageToSend->MessageID, Extended, MCP251XFD_USE_SID11 && CANFDframe);
//--- Fill message controls (T1) ---
Message->T1.T1 = 0; // Initialize message Controls to 0
Message->T1.SEQ = messageToSend->MessageSEQ; // Set message Sequence
if (CANFDframe ) Message->T1.FDF = 1;
if ((messageToSend->ControlFlags & MCP251XFD_SWITCH_BITRATE ) > 0) Message->T1.BRS = 1;
if ((messageToSend->ControlFlags & MCP251XFD_REMOTE_TRANSMISSION_REQUEST) > 0) Message->T1.RTR = 1;
if ((messageToSend->ControlFlags & MCP251XFD_EXTENDED_MESSAGE_ID ) > 0) Message->T1.IDE = 1;
if ((messageToSend->ControlFlags & MCP251XFD_TRANSMIT_ERROR_PASSIVE ) > 0) Message->T1.ESI = 1;
Message->T1.DLC = (uint8_t)messageToSend->DLC;
//--- Fill payload data ---
if ((messageToSend->DLC != MCP251XFD_DLC_0BYTE) && (messageToSend->PayloadData == NULL)) return ERR__NO_DATA_AVAILABLE;
uint8_t BytesDLC = MCP251XFD_DLCToByte(messageToSend->DLC, CANFDframe);
if (messageToSend->PayloadData != NULL)
{
uint8_t* pBuff = &Buffer[sizeof(MCP251XFD_CAN_TX_Message)]; // Next bytes of the Buffer is for payload
uint8_t* pData = &messageToSend->PayloadData[0]; // Select the first byte of payload data
uint8_t BytesToCopy = BytesDLC; // Get how many byte in the payload data will be copied
while (BytesToCopy-- > 0) *pBuff++ = *pData++; // Copy data
if ((BytesDLC & 0x3) > 0) // Not modulo 4?
for (uint8_t z = 0; z < (4 - (BytesDLC & 0x3)); z++) *pBuff++ = 0; // Fill with 0
}
//--- Send data ---
uint8_t BytesToSend = (sizeof(MCP251XFD_CAN_TX_Message) + BytesDLC);
if ((BytesToSend & 0x3) != 0) BytesToSend = (BytesToSend & 0xFC) + 4; // Adjust to the upper modulo 4 bytes (mandatory for RAM access)
return MCP251XFD_TransmitMessageObjectToFIFO(pComp, &Buffer[0], BytesToSend, toFIFO, andFlush);
}
//=============================================================================
// Receive a message object (with data) to the FIFO of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_ReceiveMessageObjectFromFIFO(MCP251XFD *pComp, uint8_t* messageObjectGet, uint8_t objectSize, eMCP251XFD_FIFO fromFIFO)
{
#ifdef CHECK_NULL_PARAM
if ((pComp == NULL) || (messageObjectGet == NULL)) return ERR__PARAMETER_ERROR;
#endif
if (fromFIFO == MCP251XFD_TXQ) return ERR__PARAMETER_ERROR;
if ((objectSize & 0x3) > 0) return ERR__BYTE_COUNT_MODULO_4;
eERRORRESULT Error;
//--- Get address where to write the frame ---
uint32_t NextAddress = 0;
Error = MCP251XFD_GetNextMessageAddressFIFO(pComp, fromFIFO, &NextAddress, NULL); // Get next message address
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_GetNextMessageAddressFIFO() then return the error
NextAddress += MCP251XFD_RAM_ADDR; // Add RAM offset address
//--- Write data to RAM ---
Error = MCP251XFD_ReadData(pComp, NextAddress, &messageObjectGet[0], objectSize); // Read data to RAM address
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ReadData() then return the error
//--- Update FIFO ---
return MCP251XFD_UpdateFIFO(pComp, fromFIFO, false); // Can't flush a receive FIFO
}
//=============================================================================
// Receive a message from a FIFO of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_ReceiveMessageFromFIFO(MCP251XFD *pComp, MCP251XFD_CANMessage* messageGet, eMCP251XFD_PayloadSize payloadSize, uint32_t* timeStamp, eMCP251XFD_FIFO fromFIFO)
{
#ifdef CHECK_NULL_PARAM
if ((pComp == NULL) || (messageGet == NULL)) return ERR__PARAMETER_ERROR;
#endif
if (fromFIFO == MCP251XFD_TXQ) return ERR__PARAMETER_ERROR;
uint8_t Buffer[MCP251XFD_CAN_RX_MESSAGE_SIZE_MAX];
MCP251XFD_CAN_RX_Message* Message = (MCP251XFD_CAN_RX_Message*)Buffer; // The first 8 bytes represent the MCP251XFD_CAN_RX_Message struct
eERRORRESULT Error;
//--- Get data ---
uint8_t BytesPayload = MCP251XFD_PayloadToByte(payloadSize);
uint8_t BytesToGet = (sizeof(MCP251XFD_CAN_RX_Message) + BytesPayload);
if (fromFIFO == MCP251XFD_TEF) BytesToGet = sizeof(MCP251XFD_CAN_TX_EventObject); // In case of TEF, there is not data
if (timeStamp == NULL) BytesToGet -= sizeof(uint32_t); // Time Stamp not needed = 4 bytes less
if ((BytesToGet & 0x3) != 0) BytesToGet = (BytesToGet & 0xFC) + 4; // Adjust to the upper modulo 4 bytes (mandatory for RAM access)
Error = MCP251XFD_ReceiveMessageObjectFromFIFO(pComp, &Buffer[0], BytesToGet, fromFIFO); // Read bytes from RAM
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ReceiveMessageObjectFromFIFO() then return the error
//--- Extract message ID (R0) ---
bool Extended = (Message->R1.IDE == 1);
bool CANFDframe = (Message->R1.FDF == 1);
messageGet->MessageID = MCP251XFD_ObjectMessageIdentifierToMessageID(Message->R0.R0, Extended, MCP251XFD_USE_SID11 && CANFDframe); // Extract SID/EID from R0
//--- Extract message controls (R1) ---
messageGet->ControlFlags = MCP251XFD_NO_MESSAGE_CTRL_FLAGS;
messageGet->MessageSEQ = 0u;
if (fromFIFO == MCP251XFD_TEF) messageGet->MessageSEQ = ((MCP251XFD_CAN_TX_EventObject*)Buffer)->TE1.SEQ; // If it is a TEF, extract the Sequence by casting Buffer into a TEF object and get SEQ in TE1
if (CANFDframe ) messageGet->ControlFlags = (setMCP251XFD_MessageCtrlFlags)(messageGet->ControlFlags + MCP251XFD_CANFD_FRAME );
if (Message->R1.BRS == 1) messageGet->ControlFlags = (setMCP251XFD_MessageCtrlFlags)(messageGet->ControlFlags + MCP251XFD_SWITCH_BITRATE );
if (Message->R1.RTR == 1) messageGet->ControlFlags = (setMCP251XFD_MessageCtrlFlags)(messageGet->ControlFlags + MCP251XFD_REMOTE_TRANSMISSION_REQUEST);
if (Extended ) messageGet->ControlFlags = (setMCP251XFD_MessageCtrlFlags)(messageGet->ControlFlags + MCP251XFD_EXTENDED_MESSAGE_ID );
if (Message->R1.ESI == 1) messageGet->ControlFlags = (setMCP251XFD_MessageCtrlFlags)(messageGet->ControlFlags + MCP251XFD_TRANSMIT_ERROR_PASSIVE );
messageGet->DLC = (eMCP251XFD_DataLength)Message->R1.DLC;
//--- Extract TimeStamp ---
uint8_t* pBuff = &Buffer[sizeof(MCP251XFD_CAN_RX_Message_Identifier) + sizeof(MCP251XFD_CAN_RX_Message_Control)]; // Next bytes of the Buffer is for timestamp and/or payload
if (timeStamp != NULL)
{
*timeStamp = Message->TimeStamp;
pBuff += sizeof(uint32_t); // If TimeStamp extracted, update the pBuff
}
//--- Extract payload data ---
if (fromFIFO != MCP251XFD_TEF)
{
if ((messageGet->DLC != MCP251XFD_DLC_0BYTE) && (messageGet->PayloadData == NULL)) return ERR__NO_DATA_AVAILABLE;
if (messageGet->PayloadData != NULL)
{
uint8_t* pData = &messageGet->PayloadData[0]; // Select the first byte of payload data
uint8_t BytesDLC = MCP251XFD_DLCToByte(messageGet->DLC, CANFDframe); // Get how many byte need to be extract from the message to correspond to its DLC
if (BytesPayload < BytesDLC) BytesDLC = BytesPayload; // Get the least between BytesPayload and BytesDLC
while (BytesDLC-- > 0) *pData++ = *pBuff++; // Copy data
}
}
return ERR_OK;
}
//**********************************************************************************************************************************************************
//=============================================================================
// CRC Configuration of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_ConfigureCRC(MCP251XFD *pComp, setMCP251XFD_CRCEvents interrupts)
{
return MCP251XFD_WriteSFR8(pComp, RegMCP251XFD_CRC_CONFIG, interrupts); // Write configuration to the CRC register (last byte only)
}
//=============================================================================
// Get CRC Status of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_GetCRCEvents(MCP251XFD *pComp, setMCP251XFD_CRCEvents* events, uint16_t* lastCRCMismatch)
{
#ifdef CHECK_NULL_PARAM
if (events == NULL) return ERR__PARAMETER_ERROR;
#endif
eERRORRESULT Error;
Error = MCP251XFD_ReadSFR8(pComp, RegMCP251XFD_CRC_FLAGS, (uint8_t*)events); // Read status of the CRC register (third byte only)
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ReadSFR8() then return the error
*events = (setMCP251XFD_CRCEvents)(*events & MCP251XFD_CRC_EVENTS_MASK); // Get CRC error interrupt flag status and CRC command format error interrupt flag status
if (lastCRCMismatch != NULL)
{
Error = MCP251XFD_ReadSFR16(pComp, RegMCP251XFD_CRC_CRC, lastCRCMismatch); // Read Address where last CRC error occurred (first 2 bytes only)
}
return Error;
}
//**********************************************************************************************************************************************************
//=============================================================================
// ECC Configuration of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_ConfigureECC(MCP251XFD *pComp, bool enableECC, setMCP251XFD_ECCEvents interrupts, uint8_t fixedParityValue)
{
uint8_t Config[2] = {MCP251XFD_SFR_ECCCON8_ECCDIS | MCP251XFD_SFR_ECCCON8_SECID | MCP251XFD_SFR_ECCCON8_DEDID, 0}; // By default, disable all controls
Config[0] |= (uint8_t)interrupts; // Single error correction and double error detection interrupts enable
if (enableECC) Config[0] |= MCP251XFD_SFR_ECCCON8_ECCEN; // If ECC enable then add the ECC enable flag
Config[1] = MCP251XFD_SFR_ECCCON8_PARITY_SET(fixedParityValue); // Add the fixed parity used during write to RAM when ECC is disabled
return MCP251XFD_WriteData(pComp, RegMCP251XFD_ECCCON, &Config[0], 2); // Write configuration to the ECC register (first 2 bytes only)
}
//=============================================================================
// Get ECC Status of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_GetECCEvents(MCP251XFD *pComp, setMCP251XFD_ECCEvents* events, uint16_t* lastErrorAddress)
{
#ifdef CHECK_NULL_PARAM
if (events == NULL) return ERR__PARAMETER_ERROR;
#endif
eERRORRESULT Error;
Error = MCP251XFD_ReadSFR8(pComp, RegMCP251XFD_ECCSTAT_FLAGS, (uint8_t*)events); // Read status of the ECC register (first bytes only)
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ReadSFR8() then return the error
if (lastErrorAddress != NULL)
{
Error = MCP251XFD_ReadSFR16(pComp, RegMCP251XFD_ECCSTAT_ERRADDR, lastErrorAddress); // Read Address where last ECC error occurred (last 2 bytes only)
}
return Error;
}
//**********************************************************************************************************************************************************
//=============================================================================
// Configure pins of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_ConfigurePins(MCP251XFD *pComp, eMCP251XFD_GPIO0Mode GPIO0PinMode, eMCP251XFD_GPIO1Mode GPIO1PinMode, eMCP251XFD_OutMode INTOutMode, eMCP251XFD_OutMode TXCANOutMode, bool CLKOasSOF)
{
eERRORRESULT Error;
uint8_t Config = MCP251XFD_SFR_IOCON8_GPIO0_INT0 | MCP251XFD_SFR_IOCON8_GPIO1_INT1; // By default, disable all controls
if ((GPIO0PinMode != MCP251XFD_PIN_AS_INT0_TX) && (GPIO0PinMode != MCP251XFD_PIN_AS_XSTBY)) Config |= MCP251XFD_SFR_IOCON8_GPIO0_MODE; // If the pin INT0/GPIO0/XSTBY is in GPIO mode then set GPIO mode
if ((GPIO1PinMode != MCP251XFD_PIN_AS_INT1_RX )) Config |= MCP251XFD_SFR_IOCON8_GPIO1_MODE; // If the pin INT1/GPIO1 is in GPIO mode then set GPIO mode
if ( TXCANOutMode == MCP251XFD_PINS_OPENDRAIN_OUT ) Config |= MCP251XFD_SFR_IOCON8_TXCANOD; // If the pin TXCAN mode is open drain mode then set open drain output
if ( CLKOasSOF ) Config |= MCP251XFD_SFR_IOCON8_SOF; // If the pin CLKO/SOF is in SOF mode then set SOF signal on CLKO pin
if ( INTOutMode == MCP251XFD_PINS_OPENDRAIN_OUT ) Config |= MCP251XFD_SFR_IOCON8_INTOD; // If all interrupt pins mode are open drain mode then set open drain output
Error = MCP251XFD_WriteSFR8(pComp, RegMCP251XFD_IOCON_PINMODE, Config); // Write configuration to the IOCON register (last byte only)
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_WriteSFR8() then return the error
Config = MCP251XFD_SFR_IOCON8_XSTBYDIS | MCP251XFD_SFR_IOCON8_GPIO0_OUTPUT | MCP251XFD_SFR_IOCON8_GPIO1_OUTPUT; // By default, disable all controls
if (GPIO0PinMode == MCP251XFD_PIN_AS_XSTBY ) Config |= MCP251XFD_SFR_IOCON8_XSTBYEN; // If the pin INT0/GPIO0/XSTBY is in XSTBY mode then enable XSTBY mode
if (GPIO0PinMode == MCP251XFD_PIN_AS_GPIO0_IN) Config |= MCP251XFD_SFR_IOCON8_GPIO0_INPUT; // If the pin INT0/GPIO0/XSTBY is in GPIO input mode then set GPIO input mode
if (GPIO1PinMode == MCP251XFD_PIN_AS_GPIO1_IN) Config |= MCP251XFD_SFR_IOCON8_GPIO1_INPUT; // If the pin INT1/GPIO1 is in GPIO input mode then set GPIO input mode
return MCP251XFD_WriteSFR8(pComp, RegMCP251XFD_IOCON_DIRECTION, Config); // Write configuration to the IOCON register (first byte only)
}
//=============================================================================
// Set GPIO pins direction of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_SetGPIOPinsDirection(MCP251XFD *pComp, uint8_t pinsDirection, uint8_t pinsChangeMask)
{
eERRORRESULT Error;
uint8_t Config;
pinsChangeMask &= 0x3;
Error = MCP251XFD_ReadSFR8(pComp, RegMCP251XFD_IOCON_DIRECTION, &Config); // Read actual configuration of the IOCON register (first byte only)
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_ReadSFR8() then return the error
Config &= ~pinsChangeMask; // Force change bits to 0
Config |= (pinsDirection & pinsChangeMask); // Apply new direction only on changed pins
return MCP251XFD_WriteSFR8(pComp, RegMCP251XFD_IOCON_DIRECTION, Config); // Write new configuration to the IOCON register (first byte only)
}
//=============================================================================
// Get GPIO pins input level of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_GetGPIOPinsInputLevel(MCP251XFD *pComp, uint8_t *pinsState)
{
return MCP251XFD_ReadSFR8(pComp, RegMCP251XFD_IOCON_INLEVEL, pinsState); // Read actual state of the input pins in the IOCON register (third byte only)
}
//=============================================================================
// Set GPIO pins output level of the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_SetGPIOPinsOutputLevel(MCP251XFD *pComp, uint8_t pinsLevel, uint8_t pinsChangeMask)
{
#ifdef CHECK_NULL_PARAM
if (pComp == NULL) return ERR__PARAMETER_ERROR;
#endif
pinsChangeMask &= 0x3;
pComp->GPIOsOutState &= ~pinsChangeMask; // Force change bits to 0
pComp->GPIOsOutState |= (pinsLevel & pinsChangeMask); // Apply new output level only on changed pins
return MCP251XFD_WriteSFR8(pComp, RegMCP251XFD_IOCON_OUTLEVEL, pComp->GPIOsOutState); // Write new configuration to the IOCON register (Second byte only)
}
//**********************************************************************************************************************************************************
//=============================================================================
// Calculate Bit Time for CAN2.0 or CAN-FD Configuration for the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_CalculateBitTimeConfiguration(const uint32_t fsysclk, const uint32_t desiredNominalBitrate, const uint32_t desiredDataBitrate, MCP251XFD_BitTimeConfig *pConf)
{
#ifdef CHECK_NULL_PARAM
if (pConf == NULL) return ERR__PARAMETER_ERROR;
#endif
//--- Check values ----------------------------------------
if (fsysclk < MCP251XFD_SYSCLK_MIN ) return ERR__PARAMETER_ERROR;
if (fsysclk > MCP251XFD_SYSCLK_MAX ) return ERR__PARAMETER_ERROR;
if (desiredNominalBitrate < MCP251XFD_NOMBITRATE_MIN ) return ERR__BAUDRATE_ERROR;
if (desiredNominalBitrate > MCP251XFD_NOMBITRATE_MAX ) return ERR__BAUDRATE_ERROR;
if (desiredDataBitrate != MCP251XFD_NO_CANFD)
if (desiredDataBitrate < MCP251XFD_DATABITRATE_MIN) return ERR__BAUDRATE_ERROR;
if (desiredDataBitrate > MCP251XFD_DATABITRATE_MAX) return ERR__BAUDRATE_ERROR;
//--- Declaration -----------------------------------------
uint32_t ErrorTQ, ErrorNTQ, ErrorDTQ, DTQbits = 0;
uint32_t BestBRP = MCP251XFD_NBRP_MAX, BestNTQbits = MCP251XFD_NTQBIT_MAX, BestDTQbits = MCP251XFD_DTQBIT_MAX;
//--- Calculate Nominal & Data Bit Time parameter ---------
uint32_t MinErrorBR = UINT32_MAX;
uint32_t BRP = MCP251XFD_NBRP_MAX; // Select the worst BRP value. Here all value from max to min will be tested to get the best tuple of NBRP and DBRP, identical TQ in both phases prevents quantization errors during bit rate switching
while (--BRP >= MCP251XFD_NBRP_MIN)
{
uint32_t NTQbits = fsysclk / desiredNominalBitrate / BRP; // Calculate the NTQbits according to BRP and the desired Nominal Bitrate
if ((NTQbits < MCP251XFD_NTQBIT_MIN) || (NTQbits > MCP251XFD_NTQBIT_MAX)) continue; // This TQbits count is not possible with this BRP, then do the next BRP value
if (desiredDataBitrate != MCP251XFD_NO_CANFD)
{
DTQbits = fsysclk / desiredDataBitrate / BRP; // Calculate the DTQbits according to BRP and the desired Data Bitrate
if ((DTQbits < MCP251XFD_DTQBIT_MIN) || (DTQbits > MCP251XFD_DTQBIT_MAX)) continue; // This TQbits count is not possible with this BRP, then do the next BRP value
}
// NTQ & DTQ bits count
ErrorNTQ = (fsysclk - (desiredNominalBitrate * NTQbits * BRP)); // Calculate NTQ error
if (desiredDataBitrate != MCP251XFD_NO_CANFD)
{
if (ErrorNTQ == 0) ErrorNTQ = 1; // Adjust NTQ error
ErrorDTQ = (fsysclk - (desiredDataBitrate * DTQbits * BRP)); if (ErrorDTQ == 0) ErrorDTQ = 1; // Calculate DTQ error
ErrorTQ = (ErrorNTQ * ErrorDTQ);
}
else ErrorTQ = ErrorNTQ;
if (ErrorTQ <= MinErrorBR) // If better error then
{ MinErrorBR = ErrorTQ; BestBRP = BRP; BestNTQbits = NTQbits; BestDTQbits = DTQbits; } // Save best parameters
// NTQ+1 & DTQ bits count
if (NTQbits < MCP251XFD_NTQBIT_MAX)
{
ErrorNTQ = ((desiredNominalBitrate * (NTQbits+1) * BRP) - fsysclk); // Calculate NTQ error with NTQbits+1
if (desiredDataBitrate != MCP251XFD_NO_CANFD)
{
if (ErrorNTQ == 0) ErrorNTQ = 1; // Adjust NTQ error
ErrorDTQ = (fsysclk - (desiredDataBitrate * DTQbits * BRP)); if (ErrorDTQ == 0) ErrorDTQ = 1; // Calculate DTQ error
ErrorTQ = (ErrorNTQ * ErrorDTQ);
}
else ErrorTQ = ErrorNTQ;
if (ErrorTQ <= MinErrorBR) // If better error then
{ MinErrorBR = ErrorTQ; BestBRP = BRP; BestNTQbits = NTQbits+1; BestDTQbits = DTQbits; } // Save best parameters
}
// NTQ+1 & DTQ or DTQ+1 bits count
if (desiredDataBitrate != MCP251XFD_NO_CANFD)
{
if (DTQbits < MCP251XFD_DTQBIT_MAX)
{
ErrorNTQ = (fsysclk - (desiredNominalBitrate * NTQbits * BRP)); if (ErrorNTQ == 0) ErrorNTQ = 1; // Calculate NTQ error
ErrorDTQ = ((desiredDataBitrate * (DTQbits+1) * BRP) - fsysclk); if (ErrorDTQ == 0) ErrorDTQ = 1; // Calculate DTQ error with DTQbits+1
ErrorTQ = (ErrorNTQ * ErrorDTQ);
if (ErrorTQ <= MinErrorBR) // If better error then
{ MinErrorBR = ErrorTQ; BestBRP = BRP; BestNTQbits = NTQbits; BestDTQbits = DTQbits+1; } // Save best parameters
}
if ((NTQbits < MCP251XFD_NTQBIT_MAX) && (DTQbits < MCP251XFD_DTQBIT_MAX))
{
ErrorNTQ = ((desiredNominalBitrate * (NTQbits+1) * BRP) - fsysclk); if (ErrorNTQ == 0) ErrorNTQ = 1; // Calculate NTQ error with NTQbits+1
ErrorDTQ = ((desiredDataBitrate * (DTQbits+1) * BRP) - fsysclk); if (ErrorDTQ == 0) ErrorDTQ = 1; // Calculate DTQ error with DTQbits+1
ErrorTQ = (ErrorNTQ * ErrorDTQ);
if (ErrorTQ <= MinErrorBR) // If better error then
{ MinErrorBR = ErrorTQ; BestBRP = BRP; BestNTQbits = NTQbits+1; BestDTQbits = DTQbits+1; } // Save best parameters
}
}
}
if (MinErrorBR == UINT32_MAX) return ERR__BITTIME_ERROR; // Impossible to find a good BRP
//--- Calculate Nominal segments --------------------------
pConf->NBRP = BestBRP - 1; // ** Save the best NBRP in the configuration **
uint32_t NTSEG2 = BestNTQbits / 5; // The Nominal Sample Point must be close to 80% (5x20%) of NTQ per bits so NTSEG2 should be 20% of NTQbits
if ((BestNTQbits % 5) > 2) NTSEG2++; // To be as close as possible to 80%
if (NTSEG2 < MCP251XFD_NTSEG2_MIN) NTSEG2 = MCP251XFD_NTSEG2_MIN; // Correct NTSEG2 if < 1
if (NTSEG2 > MCP251XFD_NTSEG2_MAX) NTSEG2 = MCP251XFD_NTSEG2_MAX; // Correct NTSEG2 if > 128
pConf->NTSEG2 = NTSEG2 - 1; // ** Save the NTSEG2 in the configuration **
uint32_t NTSEG1 = BestNTQbits - NTSEG2 - MCP251XFD_NSYNC; // NTSEG1 = NTQbits - NTSEG2 - 1 (NSYNC)
if (NTSEG1 < MCP251XFD_NTSEG1_MIN) NTSEG1 = MCP251XFD_NTSEG1_MIN; // Correct NTSEG1 if < 1
if (NTSEG1 > MCP251XFD_NTSEG1_MAX) NTSEG1 = MCP251XFD_NTSEG1_MAX; // Correct NTSEG1 if > 256
pConf->NTSEG1 = NTSEG1 - 1; // ** Save the NTSEG1 in the configuration **
uint32_t NSJW = NTSEG2; // Normally NSJW = NTSEG2, maximizing NSJW lessens the requirement for the oscillator tolerance
if (NTSEG1 < NTSEG2) NSJW = NTSEG1; // But NSJW = min(NPHSEG1, NPHSEG2)
if (NSJW < MCP251XFD_NSJW_MIN) NSJW = MCP251XFD_NSJW_MIN; // Correct NSJW if < 1
if (NSJW > MCP251XFD_NSJW_MAX) NSJW = MCP251XFD_NSJW_MAX; // Correct NSJW if > 128
pConf->NSJW = NSJW - 1; // ** Save the NSJW in the configuration **
//--- Calculate Data segments -----------------------------
if (desiredDataBitrate != MCP251XFD_NO_CANFD)
{
pConf->DBRP = BestBRP - 1; // ** Save the best DBRP in the configuration **
uint32_t DTSEG2 = BestDTQbits / 5; // The Data Sample Point must be close to 80% (5x20%) of DTQ per bits so DTSEG2 should be 20% of DTQbits
if ((BestDTQbits % 5) > 2) DTSEG2++; // To be as close as possible to 80%
if (DTSEG2 < MCP251XFD_NTSEG2_MIN) DTSEG2 = MCP251XFD_NTSEG2_MIN; // Correct DTSEG2 if < 1
if (DTSEG2 > MCP251XFD_NTSEG2_MAX) DTSEG2 = MCP251XFD_NTSEG2_MAX; // Correct DTSEG2 if > 16
pConf->DTSEG2 = DTSEG2 - 1; // ** Save the DTSEG2 in the configuration **
uint32_t DTSEG1 = BestDTQbits - DTSEG2 - MCP251XFD_DSYNC; // DTSEG1 = DTQbits - DTSEG2 - 1 (DSYNC)
if (DTSEG1 < MCP251XFD_NTSEG1_MIN) DTSEG1 = MCP251XFD_NTSEG1_MIN; // Correct DTSEG1 if < 1
if (DTSEG1 > MCP251XFD_NTSEG1_MAX) DTSEG1 = MCP251XFD_NTSEG1_MAX; // Correct DTSEG1 if > 32
pConf->DTSEG1 = DTSEG1 - 1; // ** Save the DTSEG1 in the configuration **
uint32_t DSJW = DTSEG2; // Normally DSJW = DTSEG2, maximizing DSJW lessens the requirement for the oscillator tolerance
if (DTSEG1 < DTSEG2) DSJW = DTSEG1; // But DSJW = min(DPHSEG1, DPHSEG2)
if (DSJW < MCP251XFD_DSJW_MIN) DSJW = MCP251XFD_DSJW_MIN; // Correct DSJW if < 1
if (DSJW > MCP251XFD_DSJW_MAX) DSJW = MCP251XFD_DSJW_MAX; // Correct DSJW if > 128
pConf->DSJW = DSJW - 1; // ** Save the DSJW in the configuration **
//--- Calculate Transmitter Delay Compensation ----------
if (desiredDataBitrate >= 1000000) // Enable Automatic TDC for DBR of 1Mbps and Higher
pConf->TDCMOD = MCP251XFD_AUTO_MODE; // ** Set Automatic TDC measurement compensations for transmitter delay variations
else pConf->TDCMOD = MCP251XFD_MANUAL_MODE; // ** Set Manual; Don�t measure, use TDCV + TDCO from register
const uint32_t SSP = BestBRP * DTSEG1; // In order to set the SSP to 80%, SSP = TDCO + TDCV (Equation 3-10 of MCP25XXFD Family Reference Manual). SSP is set to DBRP * (DPRSEG + DPHSEG1) = DBRP * DTSEG1
uint32_t TDCO = SSP;
if (TDCO > MCP251XFD_TDCO_MAX) TDCO = MCP251XFD_TDCO_MAX; // Correct TDCO if > 63
pConf->TDCO = TDCO; // ** Save the TDCO in the configuration **
uint32_t TDCV = SSP - TDCO; // TDCV is the remaining of SSP: TDCV = SSP - TDCO (Equation 3-10 of MCP25XXFD Family Reference Manual)
if (TDCV > MCP251XFD_TDCV_MAX) TDCV = MCP251XFD_TDCV_MAX; // Correct TDCV if > 63
pConf->TDCV = TDCV; // ** Save the TDCV in the configuration **
pConf->EDGE_FILTER = true; // ** Edge Filtering enabled, according to ISO 11898-1:2015 **
}
else
{
pConf->DBRP = 0x0; // ** Set the DBRP in the configuration **
pConf->DTSEG2 = 0x3; // ** Set the DTSEG2 in the configuration **
pConf->DTSEG1 = 0x0E; // ** Set the DTSEG1 in the configuration **
pConf->DSJW = 0x3; // ** Set the DSJW in the configuration **
pConf->TDCMOD = MCP251XFD_AUTO_MODE; // ** Set Automatic TDC measurement compensations for transmitter delay variations
pConf->TDCO = 0x10; // ** Set the TDCO in the configuration **
pConf->TDCV = 0x00; // ** Set the TDCV in the configuration **
}
eERRORRESULT Error = ERR_OK;
if (pConf->Stats != NULL)
Error = MCP251XFD_CalculateBitrateStatistics(fsysclk, pConf, desiredDataBitrate == MCP251XFD_NO_CANFD); // If statistics are necessary, then calculate them
return Error; // If there is an error while calling MCP251XFD_CalculateBitrateStatistics() then return the error
}
//=============================================================================
// Calculate Bitrate Statistics of a Bit Time configuration
//=============================================================================
eERRORRESULT MCP251XFD_CalculateBitrateStatistics(const uint32_t fsysclk, MCP251XFD_BitTimeConfig *pConf, bool can20only)
{
#ifdef CHECK_NULL_PARAM
if (pConf == NULL) return ERR__PARAMETER_ERROR;
if (pConf->Stats == NULL) return ERR__PARAMETER_ERROR;
#endif
//--- Check values ----------------------------------------
if (fsysclk < MCP251XFD_SYSCLK_MIN) return ERR__PARAMETER_ERROR;
if (fsysclk > MCP251XFD_SYSCLK_MAX) return ERR__PARAMETER_ERROR;
//--- Declaration -----------------------------------------
uint32_t DTQbits = 0;
//--- Calculate bus length & Nominal Sample Point ---------
const uint32_t NTQ = (((pConf->NBRP+1) * 1000000) / (fsysclk / 1000)); // Nominal Time Quanta = 1/FSYSCLK multiply by 1000000000 to get ns (Equation 3-3 of MCP25XXFD Family Reference Manual)
const uint32_t NPRSEG = (pConf->NTSEG1+1) - (pConf->NTSEG2+1); // Here PHSEG2 (NTSEG2) should be equal to PHSEG1 so NPRSEG = NTSEG1 - NTSEG2 (Figure 3-2 of MCP25XXFD Family Reference Manual)
pConf->Stats->MaxBusLength = (uint32_t)(((NTQ * NPRSEG) - (2 * MCP251XFD_tTXDtRXD_MAX)) / (2 * MCP251XFD_tBUS_CONV)); // Formula is (2x(tTXD�RXD + (5*BusLen))/NTQ = NPRSEG => BusLen = ((NTQ*NPRESG)-(2*tTXD))/(2*5) in meter (Equation 3-9 of MCP25XXFD Family Reference Manual)
const uint32_t NTQbits = (MCP251XFD_NSYNC + (pConf->NTSEG1+1) + (pConf->NTSEG2+1)); // NTQ per bits = NSYNC + NTSEG1 + NTSEG2 (Equation 3-5 of MCP25XXFD Family Reference Manual)
uint32_t SamplePoint = ((MCP251XFD_NSYNC + (pConf->NTSEG1+1)) * 100) / NTQbits; // Calculate actual nominal sample point
pConf->Stats->NSamplePoint = (uint32_t)(SamplePoint * 100); // ** Save actual Nominal sample point with 2 digits after the decimal point (divide by 100 to get percentage)
pConf->Stats->NominalBitrate = (fsysclk / (pConf->NBRP+1) / NTQbits); // ** Save actual Nominal Bitrate
//--- Calculate Data Sample Point -------------------------
if (can20only == false)
{
DTQbits = (MCP251XFD_DSYNC + (pConf->DTSEG1+1) + (pConf->DTSEG2+1)); // DTQ per bits = DSYNC + DTSEG1 + DTSEG2 (Equation 3-6 of MCP25XXFD Family Reference Manual)
SamplePoint = ((MCP251XFD_DSYNC + (pConf->DTSEG1+1)) * 100) / DTQbits; // Calculate actual data sample point
pConf->Stats->DSamplePoint = (uint32_t)(SamplePoint * 100.0f); // ** Save actual Data sample point with 2 digits after the decimal point (divide by 100 to get percentage)
pConf->Stats->DataBitrate = (fsysclk / (pConf->DBRP+1) / DTQbits); // ** Save actual Data Bitrate
}
else
{
pConf->Stats->DSamplePoint = 0; // ** Set actual Data sample point
pConf->Stats->DataBitrate = 0; // ** Set actual Data Bitrate
}
//--- Calculate oscillator tolerance ----------------------
const uint32_t NPHSEG1 = (pConf->NTSEG1+1) - NPRSEG; // Get NPHSEG1
const uint32_t MinNPHSEG = (NPHSEG1 <= (pConf->NTSEG2+1) ? NPHSEG1 : (pConf->NTSEG2+1)); // Get min(NPHSEG1, NPHSEG2)
pConf->Stats->OscTolC1 = (((pConf->NSJW+1) * 10000) / (2 * 10 * NTQbits)); // Condition 1 for the maximum tolerance of the oscillator with 2 digits after the decimal point (Equation 3-12 of MCP25XXFD Family Reference Manual)
pConf->Stats->OscTolerance = pConf->Stats->OscTolC1;
pConf->Stats->OscTolC2 = ((MinNPHSEG * 10000) / (2 * (13 * NTQbits - (pConf->NTSEG2+1)))); // Condition 2 for the maximum tolerance of the oscillator with 2 digits after the decimal point (Equation 3-13 of MCP25XXFD Family Reference Manual)
pConf->Stats->OscTolerance = (pConf->Stats->OscTolC2 < pConf->Stats->OscTolerance ? pConf->Stats->OscTolC2 : pConf->Stats->OscTolerance); // Oscillator Tolerance, minimum of conditions 1-5 (Equation 3-11 of MCP25XXFD Family Reference Manual)
if (can20only)
{
pConf->Stats->OscTolC3 = 0;
pConf->Stats->OscTolC4 = 0;
pConf->Stats->OscTolC5 = 0;
}
else
{
pConf->Stats->OscTolC3 = (((pConf->DSJW+1) * 10000) / (2 * 10 * DTQbits)); // Condition 3 for the maximum tolerance of the oscillator with 2 digits after the decimal point (Equation 3-14 of MCP25XXFD Family Reference Manual)
pConf->Stats->OscTolerance = (pConf->Stats->OscTolC3 < pConf->Stats->OscTolerance ? pConf->Stats->OscTolC3 : pConf->Stats->OscTolerance); // Oscillator Tolerance, minimum of conditions 1-5 (Equation 3-11 of MCP25XXFD Family Reference Manual)
const uint32_t NBRP = (pConf->NBRP + 1), DBRP = (pConf->DBRP + 1);
pConf->Stats->OscTolC4 = ((MinNPHSEG * 10000) / (2 * ((((6 * DTQbits - (pConf->DTSEG2+1)) * DBRP) / NBRP) + (7 * NTQbits)))); // Condition 4 for the maximum tolerance of the oscillator with 2 digits after the decimal point (Equation 3-15 of MCP25XXFD Family Reference Manual)
pConf->Stats->OscTolerance = (pConf->Stats->OscTolC4 < pConf->Stats->OscTolerance ? pConf->Stats->OscTolC4 : pConf->Stats->OscTolerance); // Oscillator Tolerance, minimum of conditions 1-5 (Equation 3-11 of MCP25XXFD Family Reference Manual)
const int32_t NBRP_DBRP = ((NBRP * 10000) / DBRP), MaxBRP = ((NBRP_DBRP - 10000) > 0 ? (NBRP_DBRP - 10000) : 0); // NBRP/DBRP and max(0,(NBRP/DBRP-1)). The use of 10000 is to set 2 digits on the C5 result
pConf->Stats->OscTolC5 = ((((pConf->DSJW+1) * 10000) - MaxBRP) / (2 * (((2 * NTQbits - (pConf->NTSEG2+1)) * NBRP) / DBRP + (pConf->DTSEG2+1) + 4 * DTQbits))); // Condition 5 for the maximum tolerance of the oscillator with 2 digits after the decimal point (Equation 3-16 of MCP25XXFD Family Reference Manual) [WARNING: An error seems to be present in the original formula]
pConf->Stats->OscTolerance = (pConf->Stats->OscTolC5 < pConf->Stats->OscTolerance ? pConf->Stats->OscTolC5 : pConf->Stats->OscTolerance); // Oscillator Tolerance, minimum of conditions 1-5 (Equation 3-11 of MCP25XXFD Family Reference Manual)
}
return ERR_OK;
}
//=============================================================================
// Set Bit Time Configuration to the MCP251XFD device
//=============================================================================
eERRORRESULT MCP251XFD_SetBitTimeConfiguration(MCP251XFD *pComp, MCP251XFD_BitTimeConfig *pConf, bool can20only)
{
#ifdef CHECK_NULL_PARAM
if (pConf == NULL) return ERR__PARAMETER_ERROR;
#endif
eERRORRESULT Error;
//--- Write Nominal Bit Time configuration ----------------
MCP251XFD_CiNBTCFG_Register NConfig;
NConfig.CiNBTCFG = MCP251XFD_CAN_CiNBTCFG_BRP_SET(pConf->NBRP) | MCP251XFD_CAN_CiNBTCFG_TSEG1_SET(pConf->NTSEG1) // Set Nominal Bit Time configuration
| MCP251XFD_CAN_CiNBTCFG_TSEG2_SET(pConf->NTSEG2) | MCP251XFD_CAN_CiNBTCFG_SJW_SET(pConf->NSJW);
Error = MCP251XFD_WriteData(pComp, RegMCP251XFD_CiNBTCFG, &NConfig.Bytes[0], sizeof(MCP251XFD_CiNBTCFG_Register)); // Write configuration to the CiNBTCFG register
if (Error != ERR_OK) return Error; // If there is an error while calling MCP251XFD_WriteData() then return the error
if (!can20only)
{
//--- Write Data Bit Time configuration -----------------