Merge branch 'master' into dev/realdash

This commit is contained in:
Josh Stewart 2019-04-03 08:45:08 +11:00 committed by GitHub
commit 3dee0df227
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 11338 additions and 60727 deletions

View File

@ -62,8 +62,8 @@ script:
- platformio update
# - platformio run -e megaatmega2560 -e teensy35 -e bluepill_f103c8 -e genericSTM32F103RB
- platformio run -e megaatmega2560 -e teensy35 -e genericSTM32F103RB
- curl -v --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pioenvs/megaatmega2560/firmware.hex" "https://speeduino.com:2078/bin/master.hex"
- curl -v --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./reference/speeduino.ini" "https://speeduino.com:2078/master.ini"
- curl --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pioenvs/megaatmega2560/firmware.hex" "https://speeduino.com:2078/bin/master.hex"
- curl --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./reference/speeduino.ini" "https://speeduino.com:2078/master.ini"
- cd ..
- chmod +x speeduino/misra/check_misra.sh
- speeduino/misra/check_misra.sh

View File

@ -32,10 +32,9 @@ lib_deps = EEPROM, FlexCAN
[env:genericSTM32F103RB]
platform = ststm32@~4.5.0
framework = arduino
; framework-arduinoststm32
board = genericSTM32F103RB
lib_deps = EEPROM
build_flags = -fpermissive -std=gnu++11
build_flags = -fpermissive -std=gnu++11 -DCORE_STM32_OFFICIAL
[env:genericSTM32F103RB_STM32GENERIC]
;platform = ststm32@~4.5.0
@ -46,14 +45,19 @@ board = genericSTM32F103RB
lib_deps = EEPROM, HardwareTimer, Flash_STM32
build_flags = -fpermissive -std=gnu++11 -UBOARD_NR_GPIO_PINS -DUSE_STM32GENERIC -DMENU_USB_SERIAL
;STM32 Official core
[env:black_F407VE]
;platform = ststm32@~4.5.0
platform = ststm32
framework = arduino
; framework-arduinoststm32
board = black_F407VE
;board = genericSTM32F407VET6
board = black_f407ve
lib_deps = EEPROM
build_flags = -fpermissive -std=gnu++11 -UBOARD_NR_GPIO_PINS -DUSE_STM32GENERIC -DMENU_USB_SERIAL
board_build.core = stm32
;build_flags = -fpermissive -std=gnu++11 -UBOARD_NR_GPIO_PINS -DCORE_STM32_OFFICIAL -DSRAM_AS_EEPROM
build_flags = -fpermissive -std=gnu++11 -UBOARD_NR_GPIO_PINS -DCORE_STM32_OFFICIAL -DSPI_AS_EEPROM
[env:bluepill_f103c8]
platform = ststm32
@ -62,7 +66,7 @@ framework = arduino
board = bluepill_f103c8
lib_deps = EEPROM
;build_flags = -fpermissive -std=gnu++11 -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,-Map,output.map
build_flags = -fpermissive -std=gnu++11 -Os -UBOARD_NR_GPIO_PINS
build_flags = -fpermissive -std=gnu++11 -Os -DCORE_STM32_OFFICIAL -UBOARD_NR_GPIO_PINS
;SAMD21
[env:samd21]

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

View File

@ -8,8 +8,8 @@ G04 CONTOUR ON CENTER OF CONTOUR VECTOR*
%MOIN*%
%OFA0B0*%
%SFA1.0B1.0*%
%ADD10C,0.039370*%
%ADD11C,0.075000*%
%ADD10C,0.075000*%
%ADD11C,0.039370*%
%ADD12C,0.079370*%
%ADD13C,0.078000*%
%ADD14R,0.079370X0.079370*%
@ -23,9 +23,11 @@ G04 CONTOUR ON CENTER OF CONTOUR VECTOR*
G90*
G70*
G54D10*
X708Y897D03*
G54D11*
X580Y301D03*
X767Y664D03*
G54D11*
G54D10*
X592Y814D03*
X992Y814D03*
G54D12*
@ -37,19 +39,17 @@ X417Y514D03*
X717Y514D03*
X417Y414D03*
X717Y414D03*
G54D11*
G54D10*
X992Y914D03*
X592Y914D03*
G54D10*
G54D11*
X767Y314D03*
G54D13*
X892Y151D03*
X792Y151D03*
G54D10*
G54D11*
X542Y189D03*
X355Y326D03*
G54D11*
X708Y897D03*
G54D14*
X417Y714D03*
G54D15*
@ -387,17 +387,16 @@ X743Y989D01*
D02*
X267Y988D02*
X266Y988D01*
G54D18*
D02*
X686Y514D02*
X579Y514D01*
D02*
X579Y514D02*
X580Y320D01*
G54D19*
D02*
X618Y314D02*
X618Y302D01*
G54D18*
D02*
X579Y514D02*
X580Y320D01*
D02*
X686Y514D02*
X579Y514D01*
G36*
X184Y976D02*
X184Y974D01*

View File

@ -8,13 +8,12 @@ T102C0.038000
T103C0.035000
%
T100
X005421Y001885
X007671Y006635
X007671Y003135
X003546Y003260
X005796Y003010
X005421Y001885
T101
X004171Y005135
X007171Y006135
X007171Y004135
X004171Y006135
@ -22,13 +21,14 @@ X004171Y004135
X007171Y007135
X007171Y005135
X004171Y007135
X004171Y005135
T102
X008921Y001510
X007921Y001510
T103
X009921Y008135
X005921Y008135
X009921Y009135
X005921Y009135
X009921Y008135
T00
M30

View File

@ -0,0 +1,80 @@
*Pick And Place List
*Company=
*Author=
*eMail=
*
*Project=VR Conditioner v3
*Date=13:46:42
*CreatedBy=Fritzing 0.9.3b.04.19.5c895d327c44a3114e5fcc9d8260daf0cbb52806
*
*
*Coordinates in mm, always center of component
*Origin 0/0=Lower left corner of PCB
*Rotation in degree (0-360, math. pos.)
*
*No;Value;Package;X;Y;Rotation;Side;Name
1;;;7.2644;-19.3733;0;Bottom;Copper Fill16
2;;;18.5928;-4.28574;0;Bottom;Copper Fill28
3;0.01µF;0805 [SMD, multilayer];22.0984;-15.5844;0;Top;C2
4;;;15.9623;-12.8549;0;Bottom;TXT6
5;;;19.7866;-18.2811;0;Bottom;Copper Fill18
6;;;20.2946;-7.13054;0;Bottom;Copper Fill27
7;;;19.4858;-7.96443;0;Bottom;Via14
8;;;12.9286;-17.3413;0;Bottom;Copper Fill4
9;;;16.9926;-11.4485;0;Bottom;Copper Fill8
10;;;4.572;-23.9707;0;Bottom;Copper Fill14
11;1µF;0805 [SMD, multilayer];22.0984;-18.1244;0;Top;C3
12;4.7k;THT;20.1209;-20.6644;0;Bottom;R12
13;;;8.5344;-8.33704;0;Bottom;Copper Fill10
14;;;23.0391;-15.6173;-90;Bottom;TXT3
15;;;14.7234;-7.64693;0;Bottom;Via16
16;;;24.4094;-6.57174;0;Bottom;Copper Fill26
17;10k;0805 [SMD];20.5109;-25.4269;0;Top;R7
18;0.1µF;0805 [SMD, multilayer];22.0984;-13.0444;0;Top;C1
19;;;7.47785;-6.63903;0;Bottom;TXT4
20;;;10.287;-19.0685;0;Bottom;Copper Fill17
21;;;8.9789;-7.89254;0;Bottom;Copper Fill9
22;;;12.7;-19.3733;0;Bottom;Copper Fill2
23;;;11.6586;-14.4711;0;Bottom;Copper Fill23
24;;;17.5514;-4.83184;0;Bottom;Copper Fill36
25;;;21.082;-12.2613;0;Bottom;Copper Fill5
26;10k;0805 [SMD];9.08086;-23.2044;0;Top;R9
27;;;14.6685;-7.28294;0;Bottom;Copper Fill12
28;;;11.4554;-6.76224;0;Bottom;Copper Fill32
29;;;17.1704;-14.2933;0;Bottom;Copper Fill7
30;;;17.9832;-16.4015;0;Bottom;Copper Fill3
31;1nF;0805 [SMD, multilayer];7.03084;-14.3144;180;Top;C5
32;;;8.5344;-8.33704;0;Bottom;Copper Fill30
33;;;9.9568;-13.6837;0;Bottom;Copper Fill24
34;;;9.00835;-8.28193;0;Bottom;Via19
35;;;13.7709;-4.78943;0;Bottom;Via15
36;;;16.1823;-10.5259;0;Bottom;TXT6
37;;QSOP16;14.5583;-5.41172;180;Top;MAX9926
38;;;9.398;-8.33704;0;Bottom;Copper Fill11
39;1k;0805 [SMD];23.6133;-9.79693;90;Top;R4
40;;;11.4554;-5.49224;0;Bottom;Copper Fill35
41;;;9.6012;-13.7091;0;Bottom;Copper Fill15
42;;;19.4858;-16.8544;0;Bottom;Via17
43;;;22.1742;-8.04494;0;Bottom;Copper Fill25
44;;;21.336;-16.8587;0;Bottom;Copper Fill20
45;4.7k;THT;20.1209;-23.2044;180;Bottom;R10
46;;;17.1704;-16.8333;0;Bottom;Copper Fill6
47;;;17.2085;-3.37134;0;Bottom;Copper Fill34
48;;;17.5514;-6.76224;0;Bottom;Copper Fill33
49;;;6.00746;-7.5823;0;Bottom;TXT5
50;;;15.9512;-14.2933;0;Bottom;Copper Fill29
51;;;24.2316;-15.3601;0;Bottom;Copper Fill19
52;;;14.574;-25.1077;0;Bottom;TXT1
53;1nF;0805 [SMD, multilayer];6.15085;-24.2294;-90;Top;C4
54;;;8.2804;-14.3187;0;Bottom;Copper Fill22
55;;DIP (Dual Inline) [THT];14.4059;-14.3144;0;Bottom;IC1
56;1k;0805 [SMD];21.0733;-9.79693;90;Top;R6
57;;;15.5948;-17.8602;0;Bottom;TXT6
58;10k;0805 [SMD];7.17586;-16.8544;0;Top;R13
59;;;13.9954;-14.4965;0;Bottom;Copper Fill1
60;;THT;21.3909;-3.83692;90;Bottom;J1
61;;;15.4313;-15.285;0;Bottom;TXT6
62;;;19.8374;-16.9222;0;Bottom;Copper Fill13
63;;;9.398;-8.33704;0;Bottom;Copper Fill31
64;10k;0805 [SMD];7.17586;-11.7744;0;Top;R11
65;;;20.701;-13.7599;0;Bottom;Copper Fill21

File diff suppressed because it is too large Load Diff

View File

@ -1,77 +0,0 @@
*Pick And Place List
*Company=
*Author=
*eMail=
*
*Project=VR Conditioner v3
*Date=22:29:16
*CreatedBy=Fritzing 0.9.3b.04.19.5c895d327c44a3114e5fcc9d8260daf0cbb52806
*
*
*Coordinates in mm, always center of component
*Origin 0/0=Lower left corner of PCB
*Rotation in degree (0-360, math. pos.)
*
*No;Value;Package;X;Y;Rotation;Side;Name
1;;;17.1704;-14.2933;0;Bottom;Copper Fill7
2;;;9.398;-8.33704;0;Bottom;Copper Fill11
3;;;21.082;-12.2613;0;Bottom;Copper Fill5
4;;;20.701;-13.7599;0;Bottom;Copper Fill21
5;;;24.4094;-6.57174;0;Bottom;Copper Fill26
6;1k;0805 [SMD];23.6133;-9.79693;90;Top;R4
7;;;17.1704;-16.8333;0;Bottom;Copper Fill6
8;;;8.2804;-14.3187;0;Bottom;Copper Fill22
9;10k;0805 [SMD];9.08086;-23.2044;0;Top;R9
10;4.7k;THT;20.1209;-20.6644;0;Bottom;R12
11;;;7.47785;-6.63903;0;Bottom;TXT4
12;0.1µF;0805 [SMD, multilayer];22.0984;-13.0444;0;Top;C1
13;;;8.5344;-8.33704;0;Bottom;Copper Fill30
14;;;8.5344;-8.33704;0;Bottom;Copper Fill10
15;;;13.9954;-14.4965;0;Bottom;Copper Fill1
16;;;13.7709;-4.78943;0;Bottom;Via15
17;;;14.7234;-7.64693;0;Bottom;Via16
18;;;16.9926;-11.4485;0;Bottom;Copper Fill8
19;;;10.287;-19.0685;0;Bottom;Copper Fill17
20;1nF;0805 [SMD, multilayer];7.03084;-14.3144;180;Top;C5
21;;QSOP16;14.5583;-5.41172;180;Top;MAX9926
22;10k;0805 [SMD];7.17586;-11.7744;0;Top;R11
23;4.7k;THT;20.1209;-23.2044;180;Bottom;R10
24;;;20.2946;-7.13054;0;Bottom;Copper Fill27
25;;;7.2644;-19.3733;0;Bottom;Copper Fill16
26;;;17.2085;-3.37134;0;Bottom;Copper Fill34
27;10k;0805 [SMD];7.17586;-16.8544;0;Top;R13
28;;;22.1568;-12.723;-90;Bottom;IMG1
29;;;9.9568;-13.6837;0;Bottom;Copper Fill24
30;10k;0805 [SMD];20.5109;-25.4269;0;Top;R7
31;1k;0805 [SMD];21.0733;-9.79693;90;Top;R6
32;;;12.9286;-17.3413;0;Bottom;Copper Fill4
33;;;25.6195;-12.6698;-90;Bottom;TXT3
34;;;18.5928;-4.28574;0;Bottom;Copper Fill28
35;1µF;0805 [SMD, multilayer];22.0984;-18.1244;0;Top;C3
36;;;4.572;-23.9707;0;Bottom;Copper Fill14
37;;;8.9789;-7.89254;0;Bottom;Copper Fill9
38;;;6.00745;-7.5823;0;Bottom;TXT5
39;;;17.5514;-6.76224;0;Bottom;Copper Fill33
40;;DIP (Dual Inline) [THT];14.4059;-14.3144;0;Bottom;IC1
41;;;14.2736;-25.0931;0;Bottom;TXT1
42;;;14.6685;-7.28294;0;Bottom;Copper Fill12
43;;;11.6586;-14.4711;0;Bottom;Copper Fill23
44;;;11.4554;-6.76224;0;Bottom;Copper Fill32
45;;;9.00836;-8.28192;0;Bottom;Via19
46;;;19.4858;-7.96443;0;Bottom;Via14
47;;;15.9512;-14.2933;0;Bottom;Copper Fill29
48;;;22.1742;-8.04494;0;Bottom;Copper Fill25
49;;;19.7866;-18.2811;0;Bottom;Copper Fill18
50;;;24.2316;-15.3601;0;Bottom;Copper Fill19
51;;;11.4554;-5.49224;0;Bottom;Copper Fill35
52;;;12.7;-19.3733;0;Bottom;Copper Fill2
53;;;9.398;-8.33704;0;Bottom;Copper Fill31
54;;;19.4858;-16.8544;0;Bottom;Via17
55;0.01µF;0805 [SMD, multilayer];22.0984;-15.5844;0;Top;C2
56;;;9.6012;-13.7091;0;Bottom;Copper Fill15
57;;;17.9832;-16.4015;0;Bottom;Copper Fill3
58;1nF;0805 [SMD, multilayer];6.15085;-24.2294;-90;Top;C4
59;;;17.5514;-4.83184;0;Bottom;Copper Fill36
60;;;21.336;-16.8587;0;Bottom;Copper Fill20
61;;THT;21.3909;-3.83692;90;Bottom;J1
62;;;19.8374;-16.9222;0;Bottom;Copper Fill13

View File

@ -6,7 +6,7 @@
MTversion = 2.25
queryCommand = "Q"
signature = "speeduino 201902"
signature = "speeduino 201903-dev"
versionInfo = "S" ;This info is what is displayed to user
[TunerStudio]
@ -166,6 +166,7 @@
pageReadCommand = "p%2i%2o%2c", "p%2i%2o%2c", "p%2i%2o%2c", "p%2i%2o%2c", "p%2i%2o%2c", "p%2i%2o%2c", "p%2i%2o%2c", "p%2i%2o%2c", "p%2i%2o%2c", "p%2i%2o%2c"
pageValueWrite = "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v"
pageChunkWrite = "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v"
crc32CheckCommand = "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i"
blockingFactor = 2048
tableBlockingFactor = 2048
@ -213,7 +214,7 @@ page = 1
pinLayout = bits, U08, 15, [0:7], "Speeduino v0.1", "Speeduino v0.2", "Speeduino v0.3", "Speeduino v0.4", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "NA6 MX5 PNP", "Turtana PCB", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "Plazomat I/O 0.1", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "Daz V6 Shield 0.1", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "NO2C", "UA4C", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "dvjcodec Teensy RevA", "dvjcodec Teensy RevB", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
tachoPin = bits, U08, 16, [0:5], "Board Default", "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", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID", "INVALID"
tachoDiv = bits, U08, 16, [6:7], "Normal", "Half", "INVALID", "INVALID"
unused2-17 = scalar, U08, 17, "ms", 0.1, 0.0, 0.0, 25.5, 1
tachoDuration = scalar, U08, 17, "ms", 1.0, 0.0, 1.0, 6.0, 0
unused2-18 = scalar, U08, 18, "ms", 0.1, 0.0, 0.0, 25.5, 1
tpsThresh = scalar, U08, 19, "%/s", 1.0, 0.0, 0.0, 255, 0
taeTime = scalar, U08, 20, "ms", 10, 0.0, 0.0, 2550, 0
@ -857,6 +858,7 @@ page = 9
;unused10_152 = scalar, U08, 152, "", 1, 0, 0, 255, 0
;unused10_153 = scalar, U08, 153, "", 1, 0, 0, 255, 0
iacStepperInv = bits, U08, 153, [0:0], "No", "Yes"
iacCoolTime = bits , U08, 153, [1:3], "0", "1", "2", "3", "4", "5", "6","INVALID"
unused10_154 = scalar, U08, 154, "", 1, 0, 0, 255, 0
unused10_155 = scalar, U08, 155, "", 1, 0, 0, 255, 0
@ -987,7 +989,7 @@ page = 10
knock_recoveryStepTime = scalar, U08, 120, "Sec", 0.1, 0.0, 0.0, 2.5, 1
knock_recoveryStep = scalar, U08, 121, "Deg", 1.0, 0.0, 0.0, 50, 0
unused11_122_191 = array, U08, 122,[69],"RPM", 100.0, 0.0, 100, 25500, 0
unused11_122_191 = array, U08, 122,[70],"RPM", 100.0, 0.0, 100, 25500, 0
;-------------------------------------------------------------------------------
@ -1130,6 +1132,7 @@ page = 10
defaultValue = boostPin, 0
defaultValue = fuelPumpPin, 0
defaultValue = tachoPin, 0
defaultValue = tachoDuration, 2
defaultValue = perToothIgn, 0
defaultValue = resetControlPin, 0
@ -1348,6 +1351,8 @@ menuDialog = main
iacChannels = "The number of output channels used for PWM valves. Select 1 for 2-wire valves or 2 for 3-wire valves."
iacStepTime = "Pause time between each step. Values that are too low can cause the motor to behave erratically or not at all"
iacCoolTime = "Cool time between each step. Set to zero if you don't want any cooling at all"
iacStepHome = "Homing steps to perform on startup. Must be greater than the fully open steps value"
iacStepHyster = "The minimum number of steps to move in any one go."
iacAlgorithm = "Selects method of idle control.\nNone = no idle control valve.\nOn/Off valve.\nPWM valve (2,3 wire).\nStepper Valve (4,6,8 wire)."
@ -1641,6 +1646,7 @@ menuDialog = main
dialog = tacho, "Tacho"
field = "Output pin", tachoPin
field = "Output speed", tachoDiv
field = "Pulse duration", tachoDuration
dialog = accelEnrichments_center, ""
field = "TPSdot Threshold", tpsThresh
@ -1719,6 +1725,7 @@ menuDialog = main
dialog = stepper_idle, "Stepper Idle"
field = "Step time (ms)", iacStepTime, { iacAlgorithm == 4 || iacAlgorithm == 5 }
field = "Cool time (ms)", iacCoolTime, { iacAlgorithm == 4 || iacAlgorithm == 5 }
field = "Home steps", iacStepHome, { iacAlgorithm == 4 || iacAlgorithm == 5 }
field = "Minimum Steps", iacStepHyster, { iacAlgorithm == 4 || iacAlgorithm == 5 }
field = "Stepper Inverted", iacStepperInv, { iacAlgorithm == 4 || iacAlgorithm == 5 }
@ -1815,7 +1822,7 @@ menuDialog = main
field = "#Note: During cranking the fixed/locked timing angle is overriden by the Cranking advance angle value above"
field = ""
field = "#The below option is currently in testing! If unsure what this is, please set to No"
field = "Use new ignition mode", perToothIgn, {TrigPattern == 0 || TrigPattern == 1 || TrigPattern == 2 || TrigPattern == 4 || TrigPattern == 12 || TrigPattern == 13 } ;Only works for missing tooth, distributor, dual wheel, 4g63, nissan 360, Subaru 6/7
field = "Use new ignition mode", perToothIgn, {TrigPattern == 0 || TrigPattern == 1 || TrigPattern == 2 || TrigPattern == 3 || TrigPattern == 4 || TrigPattern == 12 || TrigPattern == 13 } ;Only works for missing tooth, distributor, dual wheel, GM 7X, 4g63, nissan 360, Subaru 6/7
dialog = dwellSettings, "Dwell Settings", 4
topicHelp = "http://speeduino.com/wiki/index.php/Dwell"
@ -3131,7 +3138,7 @@ cmdtestspk450dc = "E\x03\x0C"
; you change it.
ochGetCommand = "r\$tsCanId\x30%2o%2c"
ochBlockSize = 90
ochBlockSize = 91
secl = scalar, U08, 0, "sec", 1.000, 0.000
status1 = scalar, U08, 1, "bits", 1.000, 0.000
@ -3152,10 +3159,10 @@ cmdtestspk450dc = "E\x03\x0C"
tpsaccden = bits, U08, 2, [5:5]
mapaccaen = bits, U08, 2, [6:6]
mapaccden = bits, U08, 2, [7:7]
dwell = scalar, U08, 3, "ms", 0.100, 0.000
syncLossCounter = scalar, U08, 3, "", 1.000, 0.000
map = scalar, U16, 4, "kpa", 1.000, 0.000
iatRaw = scalar, U08, 6, "°C", 1.000, 0.000
coolantRaw = scalar, U08, 7, "°C", 1.000, 0.000
iatRaw = scalar, U08, 6, "°C", 1.000, 0.000
coolantRaw = scalar, U08, 7, "°C", 1.000, 0.000
batCorrection = scalar, U08, 8, "%", 1.000, 0.000
batteryVoltage = scalar, U08, 9, "V", 0.100, 0.000
afr = scalar, U08, 10, "O2", 0.100, 0.000
@ -3229,7 +3236,8 @@ cmdtestspk450dc = "E\x03\x0C"
nFuelChannels = bits, U08, 84, [4:7]
fuelLoad = scalar, S16, 85, { bitStringValue( algorithmUnits , algorithm ) }, 1.000, 0.000
ignLoad = scalar, S16, 87, { bitStringValue( algorithmUnits , ignAlgorithm ) }, 1.000, 0.000
syncLossCounter = scalar, U08, 89, "", 1.000, 0.000
dwell = scalar, U16, 89, "ms", 0.001, 0.000
#if CELSIUS

View File

@ -25,17 +25,17 @@ void fanControl();
#define READ_N2O_ARM_PIN() ((*n2o_arming_pin_port & n2o_arming_pin_mask) ? true : false)
volatile PORT_TYPE *boost_pin_port;
volatile byte boost_pin_mask;
volatile PINMASK_TYPE boost_pin_mask;
volatile PORT_TYPE *vvt_pin_port;
volatile byte vvt_pin_mask;
volatile PINMASK_TYPE vvt_pin_mask;
volatile PORT_TYPE *fan_pin_port;
volatile byte fan_pin_mask;
volatile PINMASK_TYPE fan_pin_mask;
volatile PORT_TYPE *n2o_stage1_pin_port;
volatile byte n2o_stage1_pin_mask;
volatile PINMASK_TYPE n2o_stage1_pin_mask;
volatile PORT_TYPE *n2o_stage2_pin_port;
volatile byte n2o_stage2_pin_mask;
volatile PINMASK_TYPE n2o_stage2_pin_mask;
volatile PORT_TYPE *n2o_arming_pin_port;
volatile byte n2o_arming_pin_mask;
volatile PINMASK_TYPE n2o_arming_pin_mask;
volatile bool boost_pwm_state;
unsigned int boost_pwm_max_count; //Used for variable PWM frequency

View File

@ -10,6 +10,8 @@
* General
*/
#define PORT_TYPE uint8_t //Size of the port variables (Eg inj1_pin_port).
#define PINMASK_TYPE uint8_t
#define EEPROM_LIB_H <EEPROM.h>
void initBoard();
uint16_t freeRam();
@ -85,9 +87,14 @@
#define FUEL7_TIMER_DISABLE() TIMSK5 &= ~(1 << OCIE5C); //
#define FUEL8_TIMER_DISABLE() TIMSK5 &= ~(1 << OCIE5B); //
#define IGN1_TIMER_ENABLE() TIMSK5 |= (1 << OCIE5A) //Turn on the A compare unit (ie turn on the interrupt)
#define IGN2_TIMER_ENABLE() TIMSK5 |= (1 << OCIE5B) //Turn on the B compare unit (ie turn on the interrupt)
#define IGN3_TIMER_ENABLE() TIMSK5 |= (1 << OCIE5C) //Turn on the C compare unit (ie turn on the interrupt)
//These have the TIFR5 bits set to 1 to clear the interrupt flag. This prevents a false interrupt being called the first time the channel is enabled.
//I'm not sure why these are necessary as these should all be reset upon initialisation, but they do for the problem when added here
#define IGN1_TIMER_ENABLE() TIFR5 |= (1<<OCF5A); TIMSK5 |= (1 << OCIE5A) //Turn on the A compare unit (ie turn on the interrupt)
#define IGN2_TIMER_ENABLE() TIFR5 |= (1<<OCF5B); TIMSK5 |= (1 << OCIE5B) //Turn on the B compare unit (ie turn on the interrupt)
#define IGN3_TIMER_ENABLE() TIFR5 |= (1<<OCF5C); TIMSK5 |= (1 << OCIE5C) //Turn on the C compare unit (ie turn on the interrupt)
//#define IGN1_TIMER_ENABLE() TIMSK5 |= (1 << OCIE5A) //Turn on the B compare unit (ie turn on the interrupt)
//#define IGN2_TIMER_ENABLE() TIMSK5 |= (1 << OCIE5B) //Turn on the B compare unit (ie turn on the interrupt)
//#define IGN3_TIMER_ENABLE() TIMSK5 |= (1 << OCIE5C) //Turn on the C compare unit (ie turn on the interrupt)
#define IGN4_TIMER_ENABLE() TIMSK4 |= (1 << OCIE4A) //Turn on the A compare unit (ie turn on the interrupt)
#define IGN5_TIMER_ENABLE() TIMSK1 |= (1 << OCIE1C) //Turn on the A compare unit (ie turn on the interrupt)
#define IGN6_TIMER_ENABLE() TIMSK4 |= (1 << OCIE4B) //Replaces injector 4

View File

@ -16,12 +16,12 @@ void initBoard()
***********************************************************************************************************
* Auxilliaries
*/
//PWM used by the Boost and VVT outputs
//PWM used by the Boost and VVT outputs. C Channel is used by ign5
TCCR1B = 0x00; //Disbale Timer1 while we set it up
TCNT1 = 0; //Reset Timer Count
TIFR1 = 0x00; //Timer1 INT Flag Reg: Clear Timer Overflow Flag
TCCR1A = 0x00; //Timer1 Control Reg A: Wave Gen Mode normal (Simply counts up from 0 to 65535 (16-bit int)
TCCR1B = (1 << CS12); //Timer1 Control Reg B: Timer Prescaler set to 256. 1 tick = 16uS. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
TIFR1 = (1 << OCF1A) | (1<<OCF1B) | (1<<OCF1C) | (1<<TOV1) | (1<<ICF1); //Clear the compare flags, overflow flag and external input flag bits
boost_pwm_max_count = 1000000L / (16 * configPage6.boostFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. The x2 is there because the frequency is stored at half value (in a byte) to allow freqneucies up to 511Hz
vvt_pwm_max_count = 1000000L / (16 * configPage6.vvtFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle
@ -33,17 +33,48 @@ void initBoard()
//Configure Timer2 for our low-freq interrupt code.
TCCR2B = 0x00; //Disbale Timer2 while we set it up
TCNT2 = 131; //Preload timer2 with 131 cycles, leaving 125 till overflow. As the timer runs at 125Khz, this causes overflow to occur at 1Khz = 1ms
TIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
TIMSK2 = 0x01; //Timer2 Set Overflow Interrupt enabled.
TCCR2A = 0x00; //Timer2 Control Reg A: Wave Gen Mode normal
/* Now configure the prescaler to CPU clock divided by 128 = 125Khz */
TCCR2B |= (1<<CS22) | (1<<CS20); // Set bits
TCCR2B &= ~(1<<CS21); // Clear bit
TCCR2B &= ~(1<<CS21); // Clear bit. Shouldn't be needed as initial value is 0 anyway, but best to play it safe
TIFR2 = (1 << OCF2A) | (1<<OCF2B) | (1<<TOV2); //Clear the compare flag bits and overflow flag bit
//Enable the watchdog timer for 2 second resets (Good reference: https://tushev.org/articles/arduino/5/arduino-and-watchdog-timer)
//Boooooooooo WDT is currently broken on Mega 2560 bootloaders :(
//wdt_enable(WDTO_2S);
/*
***********************************************************************************************************
* Schedules
* */
//Much help in this from http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html
//Fuel Schedules, which uses timer 3
TCCR3B = 0x00; //Disable Timer3 while we set it up
TCNT3 = 0; //Reset Timer Count
TCCR3A = 0x00; //Timer3 Control Reg A: Wave Gen Mode normal
TCCR3B = (1 << CS12); //Same as: 0x03. Timer3 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
TIFR3 = (1 << OCF3A) | (1<<OCF3B) | (1<<OCF3C) | (1<<TOV3) | (1<<ICF3); //Clear the compare flags, overflow flag and external input flag bits
//Ignition Schedules, which uses timer 5. This is also used by the fast version of micros(). If the speed of this timer is changed from 4uS ticks, that MUST be changed as well. See globals.h and timers.ino
TCCR5B = 0x00; //Disable Timer5 while we set it up
TCNT5 = 0; //Reset Timer Count
TCCR5A = 0x00; //Timer5 Control Reg A: Wave Gen Mode normal
TCCR5B = (1 << CS11) | (1 << CS10); //Timer5 Control Reg B: Timer Prescaler set to 64. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
TIFR5 = (1 << OCF5A) | (1<<OCF5B) | (1<<OCF5C) | (1<<TOV5) | (1<<ICF5); //Clear the compare flags, overflow flag and external input flag bits
#if defined(TIMER5_MICROS)
TIMSK5 |= (1 << TOIE5); //Enable the timer5 overflow interrupt (See timers.ino for ISR)
TIMSK0 &= ~_BV(TOIE0); // disable timer0 overflow interrupt
#endif
//The remaining Schedules (Schedules 4 for fuel and ignition) use Timer4
TCCR4B = 0x00; //Disable Timer4 while we set it up
TCNT4 = 0; //Reset Timer Count
TCCR4A = 0x00; //Timer4 Control Reg A: Wave Gen Mode normal
TCCR4B = (1 << CS12); //Timer4 Control Reg B: aka Divisor = 256 = 122.5HzTimer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
TIFR4 = (1 << OCF4A) | (1<<OCF4B) | (1<<OCF4C) | (1<<TOV4) | (1<<ICF4); //Clear the compare flags, overflow flag and external input flag bits
}
uint16_t freeRam()
@ -76,6 +107,6 @@ static inline unsigned long micros_safe()
return newMicros;
}
#endif
#endif //TIMER5_MICROS
#endif
#endif //CORE_AVR

View File

@ -1,21 +1,30 @@
#ifndef STM32_H
#define STM32_H
#if defined(CORE_STM32)
#if defined(CORE_STM32_GENERIC)
/*
***********************************************************************************************************
* General
*/
#define PORT_TYPE uint8_t
#define PORT_TYPE uint32_t
#define PINMASK_TYPE uint32_t
#define micros_safe() micros() //timer5 method is not used on anything but AVR, the micros_safe() macro is simply an alias for the normal micros()
#ifndef USE_SERIAL3
#define USE_SERIAL3
#define EEPROM_LIB_H <Fram.h>
#endif
void initBoard();
uint16_t freeRam();
#if defined(USE_STM32GENERIC)
#ifndef Serial
#define Serial Serial1
#endif
#if defined(USE_FRAM)
#include <Fram.h>
FramClass EEPROM(PB0, PB3, PB4, PB5, 15000000);
#endif
//Much of the below is not correct, but included to allow compilation
//STM32F1/variants/.../board.cpp
#if defined (STM32F4)
@ -59,12 +68,26 @@
/*
***********************************************************************************************************
* Schedules
* Timers Table for STM32F1
* TIMER1 TIMER2 TIMER3 TIMER4
* 1 - 1 - INJ1 1 - IGN1 1 - oneMSInterval
* 2 - BOOST 2 - INJ2 2 - IGN2 2 -
* 3 - VVT 3 - INJ3 3 - IGN3 3 -
* 4 - IDLE 4 - INJ4 4 - IGN4 4 -
*
* Timers Table for STM32F4
* TIMER1 TIMER2 TIMER3 TIMER4 TIMER5 TIMER8
* 1 - 1 - INJ1 1 - IGN1 1 - IGN5 1 - INJ5 1 - oneMSInterval
* 2 - BOOST 2 - INJ2 2 - IGN2 2 - IGN6 2 - INJ6 2 -
* 3 - VVT 3 - INJ3 3 - IGN3 3 - IGN7 3 - INJ7 3 -
* 4 - IDLE 4 - INJ4 4 - IGN4 4 - IGN8 4 - INJ8 4 -
*
*/
#define MAX_TIMER_PERIOD 131070 //The longest period of time (in uS) that the timer can permit (IN this case it is 65535 * 2, as each timer tick is 2uS)
#define MAX_TIMER_PERIOD_SLOW 131070 //The longest period of time (in uS) that the timer can permit (IN this case it is 65535 * 2, as each timer tick is 2uS)
#define MAX_TIMER_PERIOD 65535*2 //The longest period of time (in uS) that the timer can permit (IN this case it is 65535 * 2, as each timer tick is 2uS)
#define MAX_TIMER_PERIOD_SLOW 65535*2 //The longest period of time (in uS) that the timer can permit (IN this case it is 65535 * 2, as each timer tick is 2uS)
#define uS_TO_TIMER_COMPARE(uS) (uS >> 1) //Converts a given number of uS into the required number of timer ticks until that time has passed.
#define uS_TO_TIMER_COMPARE_SLOW(uS) (uS >> 1) //Converts a given number of uS into the required number of timer ticks until that time has passed.
#if defined(ARDUINO_ARCH_STM32) // STM32GENERIC core
#if defined(ARDUINO_ARCH_STM32) && !defined(_VARIANT_ARDUINO_STM32_) // STM32GENERIC core
#define FUEL1_COUNTER (TIM2)->CNT
#define FUEL2_COUNTER (TIM2)->CNT
#define FUEL3_COUNTER (TIM2)->CNT
@ -238,7 +261,7 @@
***********************************************************************************************************
* Auxilliaries
*/
#if defined(ARDUINO_ARCH_STM32) // STM32GENERIC core
#if defined(ARDUINO_ARCH_STM32) && !defined(_VARIANT_ARDUINO_STM32_) // STM32GENERIC core
#define ENABLE_BOOST_TIMER() (TIM1)->CCER |= TIM_CCER_CC2E
#define DISABLE_BOOST_TIMER() (TIM1)->CCER &= ~TIM_CCER_CC2E
@ -266,7 +289,7 @@
***********************************************************************************************************
* Idle
*/
#if defined(ARDUINO_ARCH_STM32) // STM32GENERIC core
#if defined(ARDUINO_ARCH_STM32) && !defined(_VARIANT_ARDUINO_STM32_) // STM32GENERIC core
#define IDLE_COUNTER (TIM1)->CNT
#define IDLE_COMPARE (TIM1)->CCR4

View File

@ -1,12 +1,12 @@
#if defined(CORE_STM32)
#include "board_stm32.h"
#if defined(CORE_STM32_GENERIC) && !defined(ARDUINO_BLACK_F407VE)
#include "board_stm32_generic.h"
#include "globals.h"
#include "auxiliaries.h"
#include "idle.h"
#include "scheduler.h"
#include "HardwareTimer.h"
#if defined(STM32F4)
//These should really be in the stm32GENERIC libs, but for somereason they only have timers 1-4
#if defined(ARDUINO_ARCH_STM32) && defined(STM32_CORE_VERSION)
//These should really be in the stm32 libmaple libs, but for somereason they only have timers 1-4
#include <stm32_TIM_variant_11.h>
HardwareTimer Timer5(TIM5, chip_tim5, sizeof(chip_tim5) / sizeof(chip_tim5[0]));
HardwareTimer Timer8(TIM8, chip_tim8, sizeof(chip_tim8) / sizeof(chip_tim8[0]));
@ -18,9 +18,10 @@ void initBoard()
***********************************************************************************************************
* General
*/
#define FLASH_LENGTH 8192
#ifndef FLASH_LENGTH
#define FLASH_LENGTH 8192
#endif
delay(10);
/*
***********************************************************************************************************
* Idle
@ -33,8 +34,7 @@ void initBoard()
//This must happen at the end of the idle init
Timer1.setMode(4, TIMER_OUTPUT_COMPARE);
//timer_set_mode(TIMER1, 4, TIMER_OUTPUT_COMPARE;
if(idle_pwm_max_count > 0) { Timer1.attachInterrupt(4, idleInterrupt);} //on first flash the configPage4.iacAlgorithm is invalid
Timer1.resume();
if(idle_pwm_max_count > 0) { Timer1.attachInterrupt(4, idleInterrupt); } //on first flash the configPage4.iacAlgorithm is invalid
/*
@ -67,32 +67,23 @@ void initBoard()
Timer1.setMode(3, TIMER_OUTPUT_COMPARE);
if(boost_pwm_max_count > 0) { Timer1.attachInterrupt(2, boostInterrupt);}
if(vvt_pwm_max_count > 0) { Timer1.attachInterrupt(3, vvtInterrupt);}
Timer1.resume();
/*
***********************************************************************************************************
* Schedules
*/
#if defined(ARDUINO_ARCH_STM32) // STM32GENERIC core
//see https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/754bc2969921f1ef262bd69e7faca80b19db7524/STM32F1/system/libmaple/include/libmaple/timer.h#L444
Timer1.setPrescaleFactor((HAL_RCC_GetHCLKFreq() * 2U)-1); //2us resolution
Timer2.setPrescaleFactor((HAL_RCC_GetHCLKFreq() * 2U)-1); //2us resolution
Timer3.setPrescaleFactor((HAL_RCC_GetHCLKFreq() * 2U)-1); //2us resolution
#else //libmaple core aka STM32DUINO
//see https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/754bc2969921f1ef262bd69e7faca80b19db7524/STM32F1/system/libmaple/include/libmaple/timer.h#L444
#if defined (STM32F1) || defined(__STM32F1__)
//(CYCLES_PER_MICROSECOND == 72, APB2 at 72MHz, APB1 at 36MHz).
//Timer2 to 4 is on APB1, Timer1 on APB2. http://www.st.com/resource/en/datasheet/stm32f103cb.pdf sheet 12
Timer1.setPrescaleFactor((72 * 2U)-1); //2us resolution
Timer2.setPrescaleFactor((36 * 2U)-1); //2us resolution
Timer3.setPrescaleFactor((36 * 2U)-1); //2us resolution
#elif defined(STM32F4)
//(CYCLES_PER_MICROSECOND == 168, APB2 at 84MHz, APB1 at 42MHz).
//Timer2 to 14 is on APB1, Timers 1, 8, 9 and 10 on APB2. http://www.st.com/resource/en/datasheet/stm32f407vg.pdf sheet 120
Timer1.setPrescaleFactor((84 * 2U)-1); //2us resolution
Timer2.setPrescaleFactor((42 * 2U)-1); //2us resolution
Timer3.setPrescaleFactor((42 * 2U)-1); //2us resolution
#endif
#if defined (STM32F1) || defined(__STM32F1__)
//(CYCLES_PER_MICROSECOND == 72, APB2 at 72MHz, APB1 at 36MHz).
//Timer2 to 4 is on APB1, Timer1 on APB2. http://www.st.com/resource/en/datasheet/stm32f103cb.pdf sheet 12
Timer1.setPrescaleFactor((72 * 2)-1); //2us resolution
Timer2.setPrescaleFactor((36 * 2)-1); //2us resolution
Timer3.setPrescaleFactor((36 * 2)-1); //2us resolution
#elif defined(STM32F4)
//(CYCLES_PER_MICROSECOND == 168, APB2 at 84MHz, APB1 at 42MHz).
//Timer2 to 14 is on APB1, Timers 1, 8, 9 and 10 on APB2. http://www.st.com/resource/en/datasheet/stm32f407vg.pdf sheet 120
Timer1.setPrescaleFactor((168 * 2)-1); //2us resolution
Timer2.setPrescaleFactor((84 * 2)-1); //2us resolution
Timer3.setPrescaleFactor((84 * 2)-1); //2us resolution
#endif
Timer2.setMode(1, TIMER_OUTPUT_COMPARE);
Timer2.setMode(2, TIMER_OUTPUT_COMPARE);
@ -112,51 +103,56 @@ void initBoard()
Timer2.attachInterrupt(3, fuelSchedule3Interrupt);
Timer2.attachInterrupt(4, fuelSchedule4Interrupt);
#if (INJ_CHANNELS >= 5)
Timer5.setMode(1, TIMER_OUTPUT_COMPARE);
Timer5.attachInterrupt(1, fuelSchedule5Interrupt);
#endif
#if (INJ_CHANNELS >= 6)
Timer5.setMode(2, TIMER_OUTPUT_COMPARE);
Timer5.attachInterrupt(2, fuelSchedule6Interrupt);
#endif
#if (INJ_CHANNELS >= 7)
Timer5.setMode(3, TIMER_OUTPUT_COMPARE);
Timer5.attachInterrupt(3, fuelSchedule7Interrupt);
#endif
#if (INJ_CHANNELS >= 8)
Timer5.setMode(4, TIMER_OUTPUT_COMPARE);
Timer5.attachInterrupt(4, fuelSchedule8Interrupt);
#endif
//Ignition
#if (IGN_CHANNELS >= 1)
Timer3.attachInterrupt(1, ignitionSchedule1Interrupt);
#endif
#if (IGN_CHANNELS >= 2)
Timer3.attachInterrupt(2, ignitionSchedule2Interrupt);
#endif
#if (IGN_CHANNELS >= 3)
Timer3.attachInterrupt(3, ignitionSchedule3Interrupt);
#endif
#if (IGN_CHANNELS >= 4)
Timer3.attachInterrupt(4, ignitionSchedule4Interrupt);
#endif
#if (IGN_CHANNELS >= 5)
Timer4.setMode(1, TIMER_OUTPUT_COMPARE);
Timer4.attachInterrupt(1, ignitionSchedule5Interrupt);
#endif
#if (IGN_CHANNELS >= 6)
Timer4.setMode(2, TIMER_OUTPUT_COMPARE);
Timer4.attachInterrupt(2, ignitionSchedule6Interrupt);
#endif
#if (IGN_CHANNELS >= 7)
Timer4.setMode(3, TIMER_OUTPUT_COMPARE);
Timer4.attachInterrupt(3, ignitionSchedule7Interrupt);
#endif
#if (IGN_CHANNELS >= 8)
Timer4.setMode(4, TIMER_OUTPUT_COMPARE);
Timer4.attachInterrupt(4, ignitionSchedule8Interrupt);
#endif
Timer1.setOverflow(0xFFFF);
Timer1.resume();
Timer2.setOverflow(0xFFFF);
Timer2.resume();
Timer3.setOverflow(0xFFFF);
Timer3.resume();
#if (IGN_CHANNELS >= 5)
Timer4.setOverflow(0xFFFF);
Timer4.resume();
#endif
#if (INJ_CHANNELS >= 5)
Timer5.setOverflow(0xFFFF);
Timer5.resume();
#endif
}

View File

@ -0,0 +1,165 @@
#ifndef STM32F407VE_H
#define STM32F407VE_H
#if defined(CORE_STM32_OFFICIAL)
#include <Arduino.h>
#include <timer.h>
#include "stm32f4xx_ll_tim.h"
/*
***********************************************************************************************************
* General
*/
#define PORT_TYPE uint32_t
#define PINMASK_TYPE uint32_t
#define micros_safe() micros() //timer5 method is not used on anything but AVR, the micros_safe() macro is simply an alias for the normal micros()
#if defined(SRAM_AS_EEPROM)
#define EEPROM_LIB_H "src/BackupSram/BackupSramAsEEPROM.h"
#elif defined(SPI_AS_EEPROM)
#define EEPROM_LIB_H "src/SPIAsEEPROM/SPIAsEEPROM.h"
#else
#define EEPROM_LIB_H <EEPROM.h>
#endif
#ifndef LED_BUILTIN
#define LED_BUILTIN PA7
#endif
#define USE_SERIAL3
void initBoard();
uint16_t freeRam();
extern void oneMSIntervalIRQ(stimer_t *Timer);
extern void EmptyIRQCallback(stimer_t *Timer, uint32_t channel);
extern "C" char* sbrk(int incr);
/*
***********************************************************************************************************
* Schedules
*/
#define MAX_TIMER_PERIOD 65535*4 //The longest period of time (in uS) that the timer can permit (IN this case it is 65535 * 2, as each timer tick is 2uS)
#define MAX_TIMER_PERIOD_SLOW 65535*4//The longest period of time (in uS) that the timer can permit (IN this case it is 65535 * 2, as each timer tick is 2uS)
#define uS_TO_TIMER_COMPARE(uS) (uS>>2) //Converts a given number of uS into the required number of timer ticks until that time has passed.
#define uS_TO_TIMER_COMPARE_SLOW(uS) (uS>>2) //Converts a given number of uS into the required number of timer ticks until that time has passed.
#define FUEL1_COUNTER (TIM3)->CNT
#define FUEL2_COUNTER (TIM3)->CNT
#define FUEL3_COUNTER (TIM3)->CNT
#define FUEL4_COUNTER (TIM3)->CNT
#define FUEL1_COMPARE (TIM3)->CCR1
#define FUEL2_COMPARE (TIM3)->CCR2
#define FUEL3_COMPARE (TIM3)->CCR3
#define FUEL4_COMPARE (TIM3)->CCR4
#define IGN1_COUNTER (TIM2)->CNT
#define IGN2_COUNTER (TIM2)->CNT
#define IGN3_COUNTER (TIM2)->CNT
#define IGN4_COUNTER (TIM2)->CNT
#define IGN1_COMPARE (TIM2)->CCR1
#define IGN2_COMPARE (TIM2)->CCR2
#define IGN3_COMPARE (TIM2)->CCR3
#define IGN4_COMPARE (TIM2)->CCR4
#define FUEL5_COUNTER (TIM5)->CNT
#define FUEL6_COUNTER (TIM5)->CNT
#define FUEL7_COUNTER (TIM5)->CNT
#define FUEL8_COUNTER (TIM5)->CNT
#define FUEL5_COMPARE (TIM5)->CCR1
#define FUEL6_COMPARE (TIM5)->CCR2
#define FUEL7_COMPARE (TIM5)->CCR3
#define FUEL8_COMPARE (TIM5)->CCR4
#define IGN5_COUNTER (TIM4)->CNT
#define IGN6_COUNTER (TIM4)->CNT
#define IGN7_COUNTER (TIM4)->CNT
#define IGN8_COUNTER (TIM4)->CNT
#define IGN5_COMPARE (TIM4)->CCR1
#define IGN6_COMPARE (TIM4)->CCR2
#define IGN7_COMPARE (TIM4)->CCR3
#define IGN8_COMPARE (TIM4)->CCR4
#define FUEL1_TIMER_ENABLE() (TIM3)->CCER |= TIM_CCER_CC1E
#define FUEL2_TIMER_ENABLE() (TIM3)->CCER |= TIM_CCER_CC2E
#define FUEL3_TIMER_ENABLE() (TIM3)->CCER |= TIM_CCER_CC3E
#define FUEL4_TIMER_ENABLE() (TIM3)->CCER |= TIM_CCER_CC4E
#define FUEL1_TIMER_DISABLE() (TIM3)->CCER &= ~TIM_CCER_CC1E
#define FUEL2_TIMER_DISABLE() (TIM3)->CCER &= ~TIM_CCER_CC2E
#define FUEL3_TIMER_DISABLE() (TIM3)->CCER &= ~TIM_CCER_CC3E
#define FUEL4_TIMER_DISABLE() (TIM3)->CCER &= ~TIM_CCER_CC4E
#define IGN1_TIMER_ENABLE() (TIM2)->CCER |= TIM_CCER_CC1E
#define IGN2_TIMER_ENABLE() (TIM2)->CCER |= TIM_CCER_CC2E
#define IGN3_TIMER_ENABLE() (TIM2)->CCER |= TIM_CCER_CC3E
#define IGN4_TIMER_ENABLE() (TIM2)->CCER |= TIM_CCER_CC4E
#define IGN1_TIMER_DISABLE() (TIM2)->CCER &= ~TIM_CCER_CC1E
#define IGN2_TIMER_DISABLE() (TIM2)->CCER &= ~TIM_CCER_CC2E
#define IGN3_TIMER_DISABLE() (TIM2)->CCER &= ~TIM_CCER_CC3E
#define IGN4_TIMER_DISABLE() (TIM2)->CCER &= ~TIM_CCER_CC4E
#define FUEL5_TIMER_ENABLE() (TIM5)->CCER |= TIM_CCER_CC1E
#define FUEL6_TIMER_ENABLE() (TIM5)->CCER |= TIM_CCER_CC2E
#define FUEL7_TIMER_ENABLE() (TIM5)->CCER |= TIM_CCER_CC3E
#define FUEL8_TIMER_ENABLE() (TIM5)->CCER |= TIM_CCER_CC4E
#define FUEL5_TIMER_DISABLE() (TIM5)->CCER &= ~TIM_CCER_CC1E
#define FUEL6_TIMER_DISABLE() (TIM5)->CCER &= ~TIM_CCER_CC2E
#define FUEL7_TIMER_DISABLE() (TIM5)->CCER &= ~TIM_CCER_CC3E
#define FUEL8_TIMER_DISABLE() (TIM5)->CCER &= ~TIM_CCER_CC4E
#define IGN5_TIMER_ENABLE() (TIM4)->CCER |= TIM_CCER_CC1E
#define IGN6_TIMER_ENABLE() (TIM4)->CCER |= TIM_CCER_CC2E
#define IGN7_TIMER_ENABLE() (TIM4)->CCER |= TIM_CCER_CC3E
#define IGN8_TIMER_ENABLE() (TIM4)->CCER |= TIM_CCER_CC4E
#define IGN5_TIMER_DISABLE() (TIM4)->CCER &= ~TIM_CCER_CC1E
#define IGN6_TIMER_DISABLE() (TIM4)->CCER &= ~TIM_CCER_CC2E
#define IGN7_TIMER_DISABLE() (TIM4)->CCER &= ~TIM_CCER_CC3E
#define IGN8_TIMER_DISABLE() (TIM4)->CCER &= ~TIM_CCER_CC4E
/*
***********************************************************************************************************
* Auxilliaries
*/
#define ENABLE_BOOST_TIMER() (TIM1)->CCER |= TIM_CCER_CC2E
#define DISABLE_BOOST_TIMER() (TIM1)->CCER &= ~TIM_CCER_CC2E
#define ENABLE_VVT_TIMER() (TIM1)->CCER |= TIM_CCER_CC3E
#define DISABLE_VVT_TIMER() (TIM1)->CCER &= ~TIM_CCER_CC3E
#define BOOST_TIMER_COMPARE (TIM1)->CCR2
#define BOOST_TIMER_COUNTER (TIM1)->CNT
#define VVT_TIMER_COMPARE (TIM1)->CCR3
#define VVT_TIMER_COUNTER (TIM1)->CNT
/*
***********************************************************************************************************
* Idle
*/
#define IDLE_COUNTER (TIM1)->CNT
#define IDLE_COMPARE (TIM1)->CCR4
#define IDLE_TIMER_ENABLE() (TIM1)->CCER |= TIM_CCER_CC4E
#define IDLE_TIMER_DISABLE() (TIM1)->CCER &= ~TIM_CCER_CC4E
/*
***********************************************************************************************************
* Timers
*/
/*
***********************************************************************************************************
* CAN / Second serial
*/
HardwareSerial CANSerial(PD6,PD5);
#endif //CORE_STM32
#endif //STM32_H

View File

@ -0,0 +1,175 @@
#if defined(CORE_STM32_OFFICIAL)
#include "board_stm32_official.h"
#include "globals.h"
#include "auxiliaries.h"
#include "idle.h"
#include "scheduler.h"
#include <timer.h>
#if defined(STM32F4)
#define NR_OFF_TIMERS 9
//stimer_t HardwareTimers[NR_OFF_TIMERS + 1];
stimer_t HardwareTimers_1;
stimer_t HardwareTimers_2;
stimer_t HardwareTimers_3;
stimer_t HardwareTimers_4;
stimer_t HardwareTimers_5;
stimer_t HardwareTimers_8;
//These should really be in the stm32GENERIC libs, but for somereason they only have timers 1-4
// #include <stm32_TIM_variant_11.h>
// #include "src/HardwareTimers/HardwareTimer.h"
// HardwareTimer Timer5(TIM5, chip_tim5, sizeof(chip_tim5) / sizeof(chip_tim5[0]));
// HardwareTimer Timer8(TIM8, chip_tim8, sizeof(chip_tim8) / sizeof(chip_tim8[0]));
#else
#include "HardwareTimer.h"
#endif
extern void oneMSIntervalIRQ(stimer_t *Timer)
{
oneMSInterval();
}
extern void EmptyIRQCallback(stimer_t *Timer, uint32_t channel)
{
}
void initBoard()
{
/*
* Initialize timers
*/
HardwareTimers_1.timer = TIM1;
HardwareTimers_2.timer = TIM2;
HardwareTimers_3.timer = TIM3;
HardwareTimers_4.timer = TIM4;
HardwareTimers_5.timer = TIM5;
HardwareTimers_8.timer = TIM8;
/*
***********************************************************************************************************
* General
*/
#define FLASH_LENGTH 8192
/*
***********************************************************************************************************
* Idle
*/
if( (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_OL) || (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_CL) )
{
idle_pwm_max_count = 1000000L / (configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 5KHz
}
//This must happen at the end of the idle init
TimerPulseInit(&HardwareTimers_1, 0xFFFF, 0, EmptyIRQCallback);
//setTimerPrescalerRegister(&HardwareTimers_1, (uint32_t)(getTimerClkFreq(HardwareTimers_1.timer) / (500000)) - 1);
if(idle_pwm_max_count > 0) { attachIntHandleOC(&HardwareTimers_1, idleInterrupt, 4, 0);} //on first flash the configPage4.iacAlgorithm is invalid
//Timer1.setMode(4, TIMER_OUTPUT_COMPARE);
//timer_set_mode(TIMER1, 4, TIMER_OUTPUT_COMPARE;
//on first flash the configPage4.iacAlgorithm is invalid:
//if(idle_pwm_max_count > 0) { Timer1.attachInterrupt(4, idleInterrupt);}
//Timer1.resume();
/*
***********************************************************************************************************
* Timers
*/
#if defined(ARDUINO_BLACK_F407VE) || defined(STM32F4) || defined(_STM32F4_)
TimerHandleInit(&HardwareTimers_8, 1000, 168);
attachIntHandle(&HardwareTimers_8, oneMSIntervalIRQ);
#else
//Should do something here for other boards
#endif
pinMode(LED_BUILTIN, OUTPUT); //Visual WDT
/*
***********************************************************************************************************
* Auxilliaries
*/
//2uS resolution Min 8Hz, Max 5KHz
boost_pwm_max_count = 1000000L / (2 * configPage6.boostFreq * 2); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. The x2 is there because the frequency is stored at half value (in a byte) to allow freqneucies up to 511Hz
vvt_pwm_max_count = 1000000L / (2 * configPage6.vvtFreq * 2); //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle
//Need to be initialised last due to instant interrupt
// Timer1.setMode(2, TIMER_OUTPUT_COMPARE);
// Timer1.setMode(3, TIMER_OUTPUT_COMPARE);
// if(boost_pwm_max_count > 0) { Timer1.attachInterrupt(2, boostInterrupt);}
// if(vvt_pwm_max_count > 0) { Timer1.attachInterrupt(3, vvtInterrupt);}
if(idle_pwm_max_count > 0) { attachIntHandleOC(&HardwareTimers_1, boostInterrupt, 2, 0);}
if(vvt_pwm_max_count > 0) { attachIntHandleOC(&HardwareTimers_1, vvtInterrupt, 3, 0);}
// Timer1.resume();
TimerPulseInit(&HardwareTimers_3, 0xFFFF, 0, EmptyIRQCallback);
attachIntHandleOC(&HardwareTimers_3, fuelSchedule1Interrupt, 1, 0);
attachIntHandleOC(&HardwareTimers_3, fuelSchedule2Interrupt, 2, 0);
attachIntHandleOC(&HardwareTimers_3, fuelSchedule3Interrupt, 3, 0);
attachIntHandleOC(&HardwareTimers_3, fuelSchedule4Interrupt, 4, 0);
TimerPulseInit(&HardwareTimers_2, 0xFFFF, 0, EmptyIRQCallback);
attachIntHandleOC(&HardwareTimers_2, ignitionSchedule1Interrupt, 1, 0);
attachIntHandleOC(&HardwareTimers_2, ignitionSchedule2Interrupt, 2, 0);
attachIntHandleOC(&HardwareTimers_2, ignitionSchedule3Interrupt, 3, 0);
attachIntHandleOC(&HardwareTimers_2, ignitionSchedule4Interrupt, 4, 0);
//Attach interupt functions
//Injection
TimerPulseInit(&HardwareTimers_5, 0xFFFF, 0, EmptyIRQCallback);
//setTimerPrescalerRegister(&HardwareTimers_5, (uint32_t)(getTimerClkFreq(HardwareTimers_5.timer) / (1000000)) - 1);
#if (INJ_CHANNELS >= 5)
attachIntHandleOC(&HardwareTimers_5, fuelSchedule5Interrupt, 1, 0);
//Timer5.attachInterrupt(1, fuelSchedule5Interrupt);
#endif
#if (INJ_CHANNELS >= 6)
attachIntHandleOC(&HardwareTimers_5, fuelSchedule6Interrupt, 2, 0);
//Timer5.attachInterrupt(2, fuelSchedule6Interrupt);
#endif
#if (INJ_CHANNELS >= 7)
attachIntHandleOC(&HardwareTimers_5, fuelSchedule7Interrupt, 3, 0);
//Timer5.attachInterrupt(3, fuelSchedule7Interrupt);
#endif
#if (INJ_CHANNELS >= 8)
attachIntHandleOC(&HardwareTimers_5, fuelSchedule8Interrupt, 4, 0);
//Timer5.attachInterrupt(4, fuelSchedule8Interrupt);
#endif
TimerPulseInit(&HardwareTimers_4, 0xFFFF, 0, EmptyIRQCallback);
//setTimerPrescalerRegister(&HardwareTimers_4, (uint32_t)(getTimerClkFreq(HardwareTimers_4.timer) / (1000000)) - 1);
#if (IGN_CHANNELS >= 5)
attachIntHandleOC(&HardwareTimers_4, ignitionSchedule5Interrupt, 1, 0);
//Timer4.attachInterrupt(1, ignitionSchedule5Interrupt);
#endif
#if (IGN_CHANNELS >= 6)
attachIntHandleOC(&HardwareTimers_4, ignitionSchedule6Interrupt, 2, 0);
//Timer4.attachInterrupt(2, ignitionSchedule6Interrupt);
#endif
#if (IGN_CHANNELS >= 7)
attachIntHandleOC(&HardwareTimers_4, ignitionSchedule7Interrupt, 3, 0);
//Timer4.attachInterrupt(3, ignitionSchedule7Interrupt);
#endif
#if (IGN_CHANNELS >= 8)
attachIntHandleOC(&HardwareTimers_4, ignitionSchedule8Interrupt, 4, 0);
//Timer4.attachInterrupt(4, ignitionSchedule8Interrupt);
#endif
setTimerPrescalerRegister(&HardwareTimers_2, (uint32_t)(getTimerClkFreq(HardwareTimers_2.timer) / (250000)) - 1);
setTimerPrescalerRegister(&HardwareTimers_3, (uint32_t)(getTimerClkFreq(HardwareTimers_3.timer) / (250000)) - 1);
setTimerPrescalerRegister(&HardwareTimers_4, (uint32_t)(getTimerClkFreq(HardwareTimers_4.timer) / (250000)) - 1);
setTimerPrescalerRegister(&HardwareTimers_5, (uint32_t)(getTimerClkFreq(HardwareTimers_5.timer) / (250000)) - 1);
}
uint16_t freeRam()
{
char top = 't';
return &top - reinterpret_cast<char*>(sbrk(0));
}
//pinmapping the STM32F407 for different boards, at this moment no board is desgined.
//All boards are set to the default just to be sure.
#endif

View File

@ -9,9 +9,11 @@
void initBoard();
uint16_t freeRam();
#define PORT_TYPE uint8_t //Size of the port variables
#define PINMASK_TYPE uint8_t
#define BOARD_DIGITAL_GPIO_PINS 34
#define BOARD_NR_GPIO_PINS 34
#define USE_SERIAL3
#define EEPROM_LIB_H <EEPROM.h>
#define micros_safe() micros() //timer5 method is not used on anything but AVR, the micros_safe() macro is simply an alias for the normal micros()

View File

@ -260,4 +260,5 @@ uint16_t freeRam()
return (uint16_t)stackTop - heapTop;
}
#endif

View File

@ -7,8 +7,10 @@
* General
*/
#define PORT_TYPE uint32_t //Size of the port variables (Eg inj1_pin_port). Most systems use a byte, but SAMD21 and possibly others are a 32-bit unsigned int
#define PINMASK_TYPE uint32_t
#define BOARD_NR_GPIO_PINS 52 //Not sure this is correct
#define BOARD_DIGITAL_GPIO_PINS 52 //Pretty sure this isn't right
#define EEPROM_LIB_H <EEPROM.h> //The name of the file that provides the EEPROM class
#define micros_safe() micros() //timer5 method is not used on anything but AVR, the micros_safe() macro is simply an alias for the normal micros()
void initBoard();
uint16_t freeRam();

View File

@ -15,7 +15,7 @@ uint8_t Glow, Ghigh;
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
HardwareSerial &CANSerial = Serial3;
#elif defined(CORE_STM32)
#if defined(ARDUINO_ARCH_STM32) // STM32GENERIC core
#if defined(ARDUINO_ARCH_STM32) && !defined(_VARIANT_ARDUINO_STM32_) // STM32GENERIC core
SerialUART &CANSerial = Serial2;
#else //libmaple core aka STM32DUINO
HardwareSerial &CANSerial = Serial2;

View File

@ -12,7 +12,7 @@
#define canbusPage 9//Config Page 9
#define warmupPage 10 //Config Page 10
#define SERIAL_PACKET_SIZE 90 //Must match ochBlockSize in ini file
#define SERIAL_PACKET_SIZE 91 //Must match ochBlockSize in ini file
byte currentPage = 1;//Not the same as the speeduino config page numbers
bool isMap = true;
@ -45,12 +45,13 @@ const char pageTitles[] PROGMEM //This is being stored in the avr flash instead
void command();//This is the heart of the Command Line Interpeter. All that needed to be done was to make it human readable.
void sendValues(uint16_t, uint16_t,byte, byte);
void sendValuesLegacy();
void receiveValue(int, byte);
void receiveValue(uint16_t, byte);
void saveConfig();
void sendPage(bool);
void receiveCalibration(byte);
void sendToothLog(bool);
void testComm();
void commandButtons();
byte getPageValue(byte, uint16_t);
#endif // COMMS_H

View File

@ -11,6 +11,7 @@ A full copy of the license may be found in the projects root directory
#include "maths.h"
#include "utils.h"
#include "decoders.h"
#include "scheduledIO.h"
/*
Processes the data on the serial buffer.
@ -67,6 +68,24 @@ void command()
Serial.write(highByte(currentStatus.loopsPerSecond));
break;
case 'd': // Send a CRC32 value of a given page
cmdPending = true;
if (Serial.available() >= 2)
{
Serial.read(); //Ignore the first table value, it's always 0
uint32_t CRC32_val = calculateCRC32( Serial.read() );
//Split the 4 bytes of the CRC32 value into individual bytes and send
Serial.write( ((CRC32_val >> 24) & 255) );
Serial.write( ((CRC32_val >> 16) & 255) );
Serial.write( ((CRC32_val >> 8) & 255) );
Serial.write( (CRC32_val & 255) );
cmdPending = false;
}
break;
//The following can be used to show the amount of free memory
case 'E': // receive command button commands
@ -207,7 +226,7 @@ void command()
break;
case 'Q': // send code version
Serial.print(F("speeduino 201902"));
Serial.print(F("speeduino 201903-dev"));
break;
case 'r': //New format for the optimised OutputChannels
@ -237,7 +256,7 @@ void command()
break;
case 'S': // send code version
Serial.print(F("Speeduino 2019.02"));
Serial.print(F("Speeduino 2019.03-dev"));
currentStatus.secl = 0; //This is required in TS3 due to its stricter timings
break;
@ -471,7 +490,7 @@ void sendValues(uint16_t offset, uint16_t packetLength, byte cmd, byte portNum)
fullStatus[0] = currentStatus.secl; //secl is simply a counter that increments each second. Used to track unexpected resets (Which will reset this count to 0)
fullStatus[1] = currentStatus.status1; //status1 Bitfield
fullStatus[2] = currentStatus.engine; //Engine Status Bitfield
fullStatus[3] = (byte)(divu100(currentStatus.dwell)); //Dwell in ms * 10
fullStatus[3] = currentStatus.syncLossCounter;
fullStatus[4] = lowByte(currentStatus.MAP); //2 bytes for MAP
fullStatus[5] = highByte(currentStatus.MAP);
fullStatus[6] = (byte)(currentStatus.IAT + CALIBRATION_TEMPERATURE_OFFSET); //mat
@ -572,7 +591,8 @@ void sendValues(uint16_t offset, uint16_t packetLength, byte cmd, byte portNum)
fullStatus[86] = highByte(currentStatus.fuelLoad);
fullStatus[87] = lowByte(currentStatus.ignLoad);
fullStatus[88] = highByte(currentStatus.ignLoad);
fullStatus[89] = currentStatus.syncLossCounter;
fullStatus[89] = lowByte(currentStatus.dwell);
fullStatus[90] = highByte(currentStatus.dwell);
for(byte x=0; x<packetLength; x++)
{
@ -698,7 +718,7 @@ void sendValuesLegacy()
}
}
void receiveValue(int valueOffset, byte newValue)
void receiveValue(uint16_t valueOffset, byte newValue)
{
void* pnt_configPage;//This only stores the address of the value that it's pointing to and not the max size
@ -1642,14 +1662,14 @@ void commandButtons()
{
case 256: // cmd is stop
BIT_CLEAR(currentStatus.testOutputs, 1);
digitalWrite(pinInjector1, LOW);
digitalWrite(pinInjector2, LOW);
digitalWrite(pinInjector3, LOW);
digitalWrite(pinInjector4, LOW);
digitalWrite(pinCoil1, LOW);
digitalWrite(pinCoil2, LOW);
digitalWrite(pinCoil3, LOW);
digitalWrite(pinCoil4, LOW);
endCoil1Charge();
endCoil2Charge();
endCoil3Charge();
endCoil4Charge();
closeInjector1();
closeInjector2();
closeInjector3();
closeInjector4();
break;
case 257: // cmd is enable
@ -1657,10 +1677,10 @@ void commandButtons()
BIT_SET(currentStatus.testOutputs, 1);
break;
case 513: // cmd group is for injector1 on actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ digitalWrite(pinInjector1, HIGH); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ openInjector1(); }
break;
case 514: // cmd group is for injector1 off actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ){digitalWrite(pinInjector1, LOW);}
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ closeInjector1(); }
break;
case 515: // cmd group is for injector1 50% dc actions
//for (byte dcloop = 0; dcloop < 11; dcloop++)
@ -1672,64 +1692,64 @@ void commandButtons()
//}
break;
case 516: // cmd group is for injector2 on actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ digitalWrite(pinInjector2, HIGH); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ openInjector2(); }
break;
case 517: // cmd group is for injector2 off actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ digitalWrite(pinInjector2, LOW); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ closeInjector2(); }
break;
case 518: // cmd group is for injector2 50%dc actions
break;
case 519: // cmd group is for injector3 on actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinInjector3, HIGH); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ openInjector3(); }
break;
case 520: // cmd group is for injector3 off actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinInjector3, LOW); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ closeInjector3(); }
break;
case 521: // cmd group is for injector3 50%dc actions
break;
case 522: // cmd group is for injector4 on actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ digitalWrite(pinInjector4, HIGH); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ openInjector4(); }
break;
case 523: // cmd group is for injector4 off actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ digitalWrite(pinInjector4, LOW); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ){ closeInjector4(); }
break;
case 524: // cmd group is for injector4 50% dc actions
break;
case 769: // cmd group is for spark1 on actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil1, HIGH); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil1, coilHIGH); }
break;
case 770: // cmd group is for spark1 off actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil1, LOW); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil1, coilLOW); }
break;
case 771: // cmd group is for spark1 50%dc actions
break;
case 772: // cmd group is for spark2 on actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil2, HIGH); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil2, coilHIGH); }
break;
case 773: // cmd group is for spark2 off actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil2, LOW); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil2, coilLOW); }
break;
case 774: // cmd group is for spark2 50%dc actions
break;
case 775: // cmd group is for spark3 on actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil3, HIGH); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil3, coilHIGH); }
break;
case 776: // cmd group is for spark3 off actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil3, LOW); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil3, coilLOW); }
break;
case 777: // cmd group is for spark3 50%dc actions
break;
case 778: // cmd group is for spark4 on actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil4, HIGH); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil4, coilHIGH); }
break;
case 779: // cmd group is for spark4 off actions
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil4, LOW); }
if( BIT_CHECK(currentStatus.testOutputs, 1) ) { digitalWrite(pinCoil4, coilLOW); }
break;
case 780: // cmd group is for spark4 50%dc actions

View File

@ -206,33 +206,34 @@ static inline int crankingGetRPM(byte totalTeeth)
On decoders that are enabled for per tooth based timing adjustments, this function performs the timer compare changes on the schedules themselves
For each ignition channel, a check is made whether we're at the relevant tooth and whether that ignition schedule is currently running
Only if both these conditions are met will the schedule be updated with the latest timing information.
If it's the correct tooth, but
If it's the correct tooth, but the schedule is not yet started, calculate and an end compare value (This situation occurs when both the start and end of the ignition pulse happen after the end tooth, but before the next tooth)
*/
#define MIN_CYCLES_FOR_ENDCOMPARE 6
#define checkPerToothTiming(crankAngle, currentTooth) \
{ \
if (fixedCrankingOverride == 0) \
if ( (fixedCrankingOverride == 0) && (currentStatus.RPM > 0) ) \
{ \
if ( (currentTooth == ignition1EndTooth) ) \
{ \
if( (ignitionSchedule1.Status == RUNNING) ) { IGN1_COMPARE = IGN1_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition1EndAngle - crankAngle) ) ) ); } \
else { ignitionSchedule1.endCompare = IGN1_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition1EndAngle - crankAngle) ) ) ); ignitionSchedule1.endScheduleSetByDecoder = true; } \
else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule1.endCompare = IGN1_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition1EndAngle - crankAngle) ) ) ); ignitionSchedule1.endScheduleSetByDecoder = true; } \
} \
\
else if ( (currentTooth == ignition2EndTooth) ) \
{ \
if( (ignitionSchedule2.Status == RUNNING) ) { IGN2_COMPARE = IGN2_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition2EndAngle - crankAngle) ) ) ); } \
else { ignitionSchedule2.endCompare = IGN2_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition2EndAngle - crankAngle) ) ) ); ignitionSchedule2.endScheduleSetByDecoder = true; } \
else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule2.endCompare = IGN2_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition2EndAngle - crankAngle) ) ) ); ignitionSchedule2.endScheduleSetByDecoder = true; } \
} \
\
else if ( (currentTooth == ignition3EndTooth) ) \
{ \
if( (ignitionSchedule3.Status == RUNNING) ) { IGN3_COMPARE = IGN3_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition3EndAngle - crankAngle) ) ) ); } \
else { ignitionSchedule3.endCompare = IGN3_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition3EndAngle - crankAngle) ) ) ); ignitionSchedule3.endScheduleSetByDecoder = true; } \
else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule3.endCompare = IGN3_COUNTER + uS_TO_TIMER_COMPARE( fastDegreesToUS( ignitionLimits( (ignition3EndAngle - crankAngle) ) ) ); ignitionSchedule3.endScheduleSetByDecoder = true; } \
} \
else if ( (currentTooth == ignition4EndTooth) ) \
{ \
if( (ignitionSchedule4.Status == RUNNING) ) { IGN4_COMPARE = IGN4_COUNTER + uS_TO_TIMER_COMPARE_SLOW( fastDegreesToUS( ignitionLimits( (ignition4EndAngle - crankAngle) ) ) ); } \
else { ignitionSchedule4.endCompare = IGN4_COUNTER + uS_TO_TIMER_COMPARE_SLOW( fastDegreesToUS( ignitionLimits( (ignition4EndAngle - crankAngle) ) ) ); ignitionSchedule4.endScheduleSetByDecoder = true; } \
else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { ignitionSchedule4.endCompare = IGN4_COUNTER + uS_TO_TIMER_COMPARE_SLOW( fastDegreesToUS( ignitionLimits( (ignition4EndAngle - crankAngle) ) ) ); ignitionSchedule4.endScheduleSetByDecoder = true; } \
} \
} \
}
@ -276,6 +277,7 @@ void triggerPri_missingTooth()
validTrigger = true; //Flag this pulse as being a valid trigger (ie that it passed filters)
//if(toothCurrentCount > checkSyncToothCount || currentStatus.hasSync == false)
if( (toothLastToothTime > 0) && (toothLastMinusOneToothTime > 0) )
{
//Begin the missing tooth detection
//If the time between the current tooth and the last is greater than 1.5x the time between the last tooth and the tooth before that, we make the assertion that we must be at the first tooth after the gap
@ -287,7 +289,7 @@ void triggerPri_missingTooth()
if ( (curGap > targetGap) || (toothCurrentCount > triggerActualTeeth) )
{
//Missing tooth detected
if(toothCurrentCount < (triggerActualTeeth) && (currentStatus.hasSync == true) )
if( (toothCurrentCount < triggerActualTeeth) && (currentStatus.hasSync == true) )
{
//This occurs when we're at tooth #1, but haven't seen all the other teeth. This indicates a signal issue so we flag lost sync so this will attempt to resync on the next revolution.
currentStatus.hasSync = false;
@ -297,13 +299,19 @@ void triggerPri_missingTooth()
//else if (currentStatus.hasSync == false && toothCurrentCount < checkSyncToothCount ) { triggerFilterTime = 0; }
else
{
if(currentStatus.hasSync == true)
{
currentStatus.startRevolutions++; //Counter
if ( configPage4.TrigSpeed == CAM_SPEED ) { currentStatus.startRevolutions++; } //Add an extra revolution count if we're running at cam speed
}
else { currentStatus.startRevolutions = 0; }
toothCurrentCount = 1;
revolutionOne = !revolutionOne; //Flip sequential revolution tracker
toothOneMinusOneTime = toothOneTime;
toothOneTime = curTime;
currentStatus.hasSync = true;
currentStatus.startRevolutions++; //Counter
if ( configPage4.TrigSpeed == CAM_SPEED ) { currentStatus.startRevolutions++; } //Add an extra revolution count if we're running at cam speed
triggerFilterTime = 0; //This is used to prevent a condition where serious intermitent signals (Eg someone furiously plugging the sensor wire in and out) can leave the filter in an unrecoverable state
toothLastMinusOneToothTime = toothLastToothTime;
toothLastToothTime = curTime;
@ -320,6 +328,12 @@ void triggerPri_missingTooth()
}
}
else
{
toothLastMinusOneToothTime = toothLastToothTime;
toothLastToothTime = curTime;
}
//NEW IGNITION MODE
if( (configPage2.perToothIgn == true) && (!BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK)) )
@ -358,7 +372,7 @@ void triggerSec_missingTooth()
{
targetGap2 = (3 * (toothLastSecToothTime - toothLastMinusOneSecToothTime)) >> 1; //If the time between the current tooth and the last is greater than 1.5x the time between the last tooth and the tooth before that, we make the assertion that we must be at the first tooth after the gap
toothLastMinusOneSecToothTime = toothLastSecToothTime;
if ( ( curGap2 >= targetGap2 ) || ( secondaryToothCount > 3 ) )
if ( (curGap2 >= targetGap2) || (secondaryToothCount > 3) )
{
secondaryToothCount = 1;
revolutionOne = 1; //Sequential revolution reset
@ -383,7 +397,7 @@ void triggerSec_missingTooth()
uint16_t getRPM_missingTooth()
{
uint16_t tempRPM = 0;
if( (currentStatus.RPM < currentStatus.crankRPM) )
if( currentStatus.RPM < currentStatus.crankRPM )
{
if(toothCurrentCount != 1)
{
@ -434,22 +448,22 @@ void triggerSetEndTeeth_missingTooth()
byte toothAdder = 0;
if( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) && (configPage4.TrigSpeed == CRANK_SPEED) ) { toothAdder = configPage4.triggerTeeth; }
ignition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / triggerToothAngle ) - 1;
ignition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1;
if(ignition1EndTooth > (configPage4.triggerTeeth + toothAdder)) { ignition1EndTooth -= (configPage4.triggerTeeth + toothAdder); }
if(ignition1EndTooth <= 0) { ignition1EndTooth += (configPage4.triggerTeeth + toothAdder); }
if(ignition1EndTooth > (triggerActualTeeth + toothAdder)) { ignition1EndTooth = (triggerActualTeeth + toothAdder); }
ignition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / triggerToothAngle ) - 1;
ignition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1;
if(ignition2EndTooth > (configPage4.triggerTeeth + toothAdder)) { ignition2EndTooth -= (configPage4.triggerTeeth + toothAdder); }
if(ignition2EndTooth <= 0) { ignition2EndTooth += (configPage4.triggerTeeth + toothAdder); }
if(ignition2EndTooth > (triggerActualTeeth + toothAdder)) { ignition2EndTooth = (triggerActualTeeth + toothAdder); }
ignition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / triggerToothAngle ) - 1;
ignition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1;
if(ignition3EndTooth > (configPage4.triggerTeeth + toothAdder)) { ignition3EndTooth -= (configPage4.triggerTeeth + toothAdder); }
if(ignition3EndTooth <= 0) { ignition3EndTooth += (configPage4.triggerTeeth + toothAdder); }
if(ignition3EndTooth > (triggerActualTeeth + toothAdder)) { ignition3EndTooth = (triggerActualTeeth + toothAdder); }
ignition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / triggerToothAngle ) - 1;
ignition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1;
if(ignition4EndTooth > (configPage4.triggerTeeth + toothAdder)) { ignition4EndTooth -= (configPage4.triggerTeeth + toothAdder); }
if(ignition4EndTooth <= 0) { ignition4EndTooth += (configPage4.triggerTeeth + toothAdder); }
if(ignition4EndTooth > (triggerActualTeeth + toothAdder)) { ignition4EndTooth = (triggerActualTeeth + toothAdder); }
@ -592,22 +606,22 @@ void triggerSetEndTeeth_DualWheel()
byte toothAdder = 0;
if( (configPage4.sparkMode == IGN_MODE_SEQUENTIAL) && (configPage4.TrigSpeed == CRANK_SPEED) ) { toothAdder = configPage4.triggerTeeth; }
ignition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / triggerToothAngle ) - 1;
ignition1EndTooth = ( (ignition1EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1;
if(ignition1EndTooth > (configPage4.triggerTeeth + toothAdder)) { ignition1EndTooth -= (configPage4.triggerTeeth + toothAdder); }
if(ignition1EndTooth <= 0) { ignition1EndTooth += configPage4.triggerTeeth; }
if(ignition1EndTooth > (triggerActualTeeth + toothAdder)) { ignition1EndTooth = (triggerActualTeeth + toothAdder); }
ignition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / triggerToothAngle ) - 1;
ignition2EndTooth = ( (ignition2EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1;
if(ignition2EndTooth > (configPage4.triggerTeeth + toothAdder)) { ignition2EndTooth -= (configPage4.triggerTeeth + toothAdder); }
if(ignition2EndTooth <= 0) { ignition2EndTooth += configPage4.triggerTeeth; }
if(ignition1EndTooth > (triggerActualTeeth + toothAdder)) { ignition3EndTooth = (triggerActualTeeth + toothAdder); }
ignition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / triggerToothAngle ) - 1;
ignition3EndTooth = ( (ignition3EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1;
if(ignition3EndTooth > (configPage4.triggerTeeth + toothAdder)) { ignition3EndTooth -= (configPage4.triggerTeeth + toothAdder); }
if(ignition3EndTooth <= 0) { ignition3EndTooth += configPage4.triggerTeeth; }
if(ignition3EndTooth > (triggerActualTeeth + toothAdder)) { ignition3EndTooth = (triggerActualTeeth + toothAdder); }
ignition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / triggerToothAngle ) - 1;
ignition4EndTooth = ( (ignition4EndAngle - configPage4.triggerAngle) / (int16_t)(triggerToothAngle) ) - 1;
if(ignition4EndTooth > (configPage4.triggerTeeth + toothAdder)) { ignition4EndTooth -= (configPage4.triggerTeeth + toothAdder); }
if(ignition4EndTooth <= 0) { ignition4EndTooth += configPage4.triggerTeeth; }
if(ignition4EndTooth > (triggerActualTeeth + toothAdder)) { ignition4EndTooth = (triggerActualTeeth + toothAdder); }
@ -664,7 +678,7 @@ void triggerPri_BasicDistributor()
if( currentStatus.hasSync == true )
{
currentStatus.syncLossCounter++;
currentStatus.hasSync == false;
currentStatus.hasSync = false;
}
}
@ -765,6 +779,7 @@ void triggerSetEndTeeth_BasicDistributor()
Name: GM7X
Desc: GM 7X trigger wheel. It has six equally spaced teeth and a seventh tooth for cylinder identification.
Note: Within the code below, the sync tooth is referred to as tooth #3 rather than tooth #7. This makes for simpler angle calculations
https://speeduino.com/forum/download/file.php?id=4743
*/
void triggerSetup_GM7X()
{
@ -776,40 +791,62 @@ void triggerSetup_GM7X()
void triggerPri_GM7X()
{
lastGap = curGap;
curTime = micros();
curGap = curTime - toothLastToothTime;
toothCurrentCount++; //Increment the tooth counter
validTrigger = true; //Flag this pulse as being a valid trigger (ie that it passed filters)
lastGap = curGap;
curTime = micros();
curGap = curTime - toothLastToothTime;
toothCurrentCount++; //Increment the tooth counter
validTrigger = true; //Flag this pulse as being a valid trigger (ie that it passed filters)
//
if( toothCurrentCount > 7 )
{
toothCurrentCount = 1;
toothOneMinusOneTime = toothOneTime;
toothOneTime = curTime;
if( (toothLastToothTime > 0) && (toothLastMinusOneToothTime > 0) )
{
if( toothCurrentCount > 7 )
{
toothCurrentCount = 1;
toothOneMinusOneTime = toothOneTime;
toothOneTime = curTime;
toothLastMinusOneToothTime = toothLastToothTime;
toothLastToothTime = curTime;
triggerToothAngleIsCorrect = true;
}
else
{
targetGap = (lastGap) >> 1; //The target gap is set at half the last tooth gap
if ( curGap < targetGap) //If the gap between this tooth and the last one is less than half of the previous gap, then we are very likely at the magical 3rd tooth
triggerToothAngleIsCorrect = true;
}
else
{
targetGap = (lastGap) >> 1; //The target gap is set at half the last tooth gap
if ( curGap < targetGap ) //If the gap between this tooth and the last one is less than half of the previous gap, then we are very likely at the magical 3rd tooth
{
toothCurrentCount = 3;
currentStatus.hasSync = true;
triggerToothAngleIsCorrect = false;
currentStatus.startRevolutions++; //Counter
}
else
{
triggerToothAngleIsCorrect = true;
}
}
}
//New ignition mode!
if(configPage2.perToothIgn == true)
{
toothCurrentCount = 3;
currentStatus.hasSync = true;
triggerToothAngleIsCorrect = false;
currentStatus.startRevolutions++; //Counter
if(toothCurrentCount != 3) //Never do the check on the extra tooth. It's not needed anyway
{
uint16_t crankAngle;
if( toothCurrentCount < 3 )
{
crankAngle = ((toothCurrentCount - 1) * triggerToothAngle) + 42; //Number of teeth that have passed since tooth 1, multiplied by the angle each tooth represents, plus the angle that tooth 1 is ATDC. This gives accuracy only to the nearest tooth.
}
else
{
crankAngle = ((toothCurrentCount - 2) * triggerToothAngle) + 42; //Number of teeth that have passed since tooth 1, multiplied by the angle each tooth represents, plus the angle that tooth 1 is ATDC. This gives accuracy only to the nearest tooth.
}
checkPerToothTiming(crankAngle, toothCurrentCount);
}
}
else
{
toothLastMinusOneToothTime = toothLastToothTime;
toothLastToothTime = curTime;
triggerToothAngleIsCorrect = true;
}
}
toothLastMinusOneToothTime = toothLastToothTime;
toothLastToothTime = curTime;
}
void triggerSec_GM7X() { return; } //Not required
uint16_t getRPM_GM7X()
@ -858,6 +895,19 @@ void triggerSetEndTeeth_GM7X()
{
lastToothCalcAdvance = currentStatus.advance;
if(currentStatus.advance < 18 )
{
ignition1EndTooth = 7;
ignition2EndTooth = 2;
ignition3EndTooth = 5;
}
else
{
ignition1EndTooth = 6;
ignition2EndTooth = 1;
ignition3EndTooth = 4;
}
}
@ -1083,7 +1133,8 @@ void triggerPri_4G63()
}
//EXPERIMENTAL!
if(configPage2.perToothIgn == true)
//New ignition mode is ONLY available on 4g63 when the trigger angle is set to the stock value of 0.
if( (configPage2.perToothIgn == true) && (configPage4.triggerAngle == 0) )
{
if(configPage2.nCylinders == 4)
{

View File

@ -3,25 +3,27 @@
#include <Arduino.h>
#include "table.h"
//These are configuration options for changing around the outputs that are used. THese are just the defaults and may be changed in the sections below based on the hardware in use.
#define INJ_CHANNELS 4
#define IGN_CHANNELS 5
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
#define BOARD_DIGITAL_GPIO_PINS 54
#define BOARD_NR_GPIO_PINS 62
#define LED_BUILTIN 13
#define CORE_AVR
#define BOARD_H "board_avr2560.h"
#define INJ_CHANNELS 4
#define IGN_CHANNELS 5
//#define TIMER5_MICROS
#elif defined(CORE_TEENSY)
#define BOARD_H "board_teensy35.h"
#define INJ_CHANNELS 8
#define IGN_CHANNELS 8
#elif defined(STM32_MCU_SERIES) || defined(ARDUINO_ARCH_STM32) || defined(__STM32F1__) || defined(STM32F4) || defined(STM32)
#define CORE_STM32
#define BOARD_H "board_stm32.h"
//These should be updated to 8 later, but there's bits missing currently
#define INJ_CHANNELS 4
#define IGN_CHANNELS 5
#ifndef word
#define word(h, l) ((h << 8) | l) //word() function not defined for this platform in the main library
#endif
@ -34,13 +36,15 @@
#elif defined(ARDUINO_BLACK_F407VE) || defined(STM32F4)
#define BOARD_DIGITAL_GPIO_PINS 80
#define BOARD_NR_GPIO_PINS 80
#define LED_BUILTIN PA7
#endif
//These boards always make 8/8 channels available
#undef INJ_CHANNELS
#undef IGN_CHANNELS
#define INJ_CHANNELS 8
#define IGN_CHANNELS 8
#if defined(CORE_STM32_OFFICIAL)
//Need to identify the official core better
//#define CORE_STM32_OFFICIAL
#define BOARD_H "board_stm32_official.h"
#else
#define CORE_STM32_GENERIC
#define BOARD_H "board_stm32_generic.h"
#endif
//Specific mode for Bluepill due to its small flash size. This disables a number of strings from being compiled into the flash
@ -48,15 +52,17 @@
#define SMALL_FLASH_MODE
#endif
#if __GNUC__ < 7 //Already included on GCC 7
extern "C" char* sbrk(int incr); //Used to freeRam
#if defined(ARDUINO_ARCH_STM32) // STM32GENERIC core
#endif
#if !defined(_VARIANT_ARDUINO_STM32_) // STM32GENERIC core
inline unsigned char digitalPinToInterrupt(unsigned char Interrupt_pin) { return Interrupt_pin; } //This isn't included in the stm32duino libs (yet)
#define portOutputRegister(port) (volatile byte *)( &(port->ODR) )
#define portInputRegister(port) (volatile byte *)( &(port->IDR) )
#else //libmaple core aka STM32DUINO
//These are defined in STM32F1/variants/generic_stm32f103c/variant.h but return a non byte* value
#define portOutputRegister(port) (volatile byte *)( &(port->regs->ODR) )
#define portInputRegister(port) (volatile byte *)( &(port->regs->IDR) )
#ifndef portOutputRegister
#define portOutputRegister(port) (volatile byte *)( &(port->regs->ODR) )
#define portInputRegister(port) (volatile byte *)( &(port->regs->IDR) )
#endif
#endif
#elif defined(__SAMD21G18A__)
#define BOARD_H "board_samd21.h"
@ -222,7 +228,8 @@ const char TSfirmwareVersion[] PROGMEM = "Speeduino";
const byte data_structure_version = 2; //This identifies the data structure when reading / writing.
//const byte page_size = 64;
//const int16_t npage_size[11] PROGMEM = {0,288,128,288,128,288,128,240,192,192,192};
const int16_t npage_size[11] PROGMEM = {0,128,288,288,128,288,128,240,192,192,192};
#define NUM_PAGES 11
const uint16_t npage_size[NUM_PAGES] PROGMEM = {0,128,288,288,128,288,128,240,192,192,192};
//const byte page11_size = 128;
#define MAP_PAGE_SIZE 288
@ -252,48 +259,48 @@ struct table2D knockWindowDurationTable;
//These are for the direct port manipulation of the injectors, coils and aux outputs
volatile PORT_TYPE *inj1_pin_port;
volatile byte inj1_pin_mask;
volatile PINMASK_TYPE inj1_pin_mask;
volatile PORT_TYPE *inj2_pin_port;
volatile byte inj2_pin_mask;
volatile PINMASK_TYPE inj2_pin_mask;
volatile PORT_TYPE *inj3_pin_port;
volatile byte inj3_pin_mask;
volatile PINMASK_TYPE inj3_pin_mask;
volatile PORT_TYPE *inj4_pin_port;
volatile byte inj4_pin_mask;
volatile PINMASK_TYPE inj4_pin_mask;
volatile PORT_TYPE *inj5_pin_port;
volatile byte inj5_pin_mask;
volatile PINMASK_TYPE inj5_pin_mask;
volatile PORT_TYPE *inj6_pin_port;
volatile byte inj6_pin_mask;
volatile PINMASK_TYPE inj6_pin_mask;
volatile PORT_TYPE *inj7_pin_port;
volatile byte inj7_pin_mask;
volatile PINMASK_TYPE inj7_pin_mask;
volatile PORT_TYPE *inj8_pin_port;
volatile byte inj8_pin_mask;
volatile PINMASK_TYPE inj8_pin_mask;
volatile PORT_TYPE *ign1_pin_port;
volatile byte ign1_pin_mask;
volatile PINMASK_TYPE ign1_pin_mask;
volatile PORT_TYPE *ign2_pin_port;
volatile byte ign2_pin_mask;
volatile PINMASK_TYPE ign2_pin_mask;
volatile PORT_TYPE *ign3_pin_port;
volatile byte ign3_pin_mask;
volatile PINMASK_TYPE ign3_pin_mask;
volatile PORT_TYPE *ign4_pin_port;
volatile byte ign4_pin_mask;
volatile PINMASK_TYPE ign4_pin_mask;
volatile PORT_TYPE *ign5_pin_port;
volatile byte ign5_pin_mask;
volatile PINMASK_TYPE ign5_pin_mask;
volatile PORT_TYPE *ign6_pin_port;
volatile byte ign6_pin_mask;
volatile PINMASK_TYPE ign6_pin_mask;
volatile PORT_TYPE *ign7_pin_port;
volatile byte ign7_pin_mask;
volatile PINMASK_TYPE ign7_pin_mask;
volatile PORT_TYPE *ign8_pin_port;
volatile byte ign8_pin_mask;
volatile PINMASK_TYPE ign8_pin_mask;
volatile PORT_TYPE *tach_pin_port;
volatile byte tach_pin_mask;
volatile PINMASK_TYPE tach_pin_mask;
volatile PORT_TYPE *pump_pin_port;
volatile byte pump_pin_mask;
volatile PINMASK_TYPE pump_pin_mask;
volatile PORT_TYPE *triggerPri_pin_port;
volatile byte triggerPri_pin_mask;
volatile PINMASK_TYPE triggerPri_pin_mask;
volatile PORT_TYPE *triggerSec_pin_port;
volatile byte triggerSec_pin_mask;
volatile PINMASK_TYPE triggerSec_pin_mask;
//These need to be here as they are used in both speeduino.ino and scheduler.ino
bool channel1InjEnabled = true;
@ -313,6 +320,7 @@ int ignition5EndAngle = 0;
//These are variables used across multiple files
bool initialisationComplete = false; //Tracks whether the setup() function has run completely
byte fpPrimeTime = 0; //The time (in seconds, based on currentStatus.secl) that the fuel pump started priming
volatile uint16_t mainLoopCount;
unsigned long revolutionTime; //The time in uS that one revolution would take at current speed (The time tooth 1 was last seen, minus the time it was seen prior to that)
volatile unsigned long timer5_overflow_count = 0; //Increments every time counter 5 overflows. Used for the fast version of micros()
@ -325,8 +333,10 @@ volatile uint8_t compositeLogHistory[TOOTH_LOG_BUFFER];
volatile bool fpPrimed = false; //Tracks whether or not the fuel pump priming has been completed yet
volatile unsigned int toothHistoryIndex = 0;
volatile byte toothHistorySerialIndex = 0;
byte primaryTriggerEdge;
byte secondaryTriggerEdge;
byte primaryTriggerEdge;
byte secondaryTriggerEdge;
int CRANK_ANGLE_MAX = 720;
int CRANK_ANGLE_MAX_IGN = 360;
int CRANK_ANGLE_MAX_INJ = 360; //The number of crank degrees that the system track over. 360 for wasted / timed batch and 720 for sequential
@ -401,8 +411,8 @@ struct statuses {
volatile byte runSecs; //Counter of seconds since cranking commenced (overflows at 255 obviously)
volatile byte secl; //Continous
volatile unsigned int loopsPerSecond;
boolean launchingSoft; //True when in launch control soft limit mode
boolean launchingHard; //True when in launch control hard limit mode
bool launchingSoft; //True when in launch control soft limit mode
bool launchingHard; //True when in launch control hard limit mode
uint16_t freeRAM;
unsigned int clutchEngagedRPM;
bool flatShiftingHard;
@ -451,7 +461,7 @@ struct config2 {
byte pinMapping; // The board / ping mapping to be used
byte tachoPin : 6; //Custom pin setting for tacho output
byte tachoDiv : 2; //Whether to change the tacho speed
byte unused2_17;
byte tachoDuration; //The duration of the tacho pulse in mS
byte unused2_18;
byte tpsThresh;
byte taeTime;
@ -747,8 +757,8 @@ struct config9 {
uint8_t Auxinpinb[16]; // digital pin number when internal aux in use
byte iacStepperInv : 1; //stepper direction of travel to allow reversing. 0=normal, 1=inverted.
byte iacCoolTime : 3; // how long to wait for the stepper to cool between steps
byte unused10_153;
byte unused10_154;
byte unused10_155;
byte unused10_156;
@ -874,7 +884,7 @@ struct config10 {
byte knock_recoveryStepTime;
byte knock_recoveryStep;
byte unused11_122_191[69];
byte unused11_122_191[70];
#if defined(CORE_AVR)
};

View File

@ -39,12 +39,13 @@ struct StepperIdle idleStepper;
bool idleOn; //Simply tracks whether idle was on last time around
byte idleInitComplete = 99; //TRacks which idle method was initialised. 99 is a method that will never exist
unsigned int iacStepTime;
unsigned int iacCoolTime;
unsigned int completedHomeSteps;
volatile PORT_TYPE *idle_pin_port;
volatile byte idle_pin_mask;
volatile PINMASK_TYPE idle_pin_mask;
volatile PORT_TYPE *idle2_pin_port;
volatile byte idle2_pin_mask;
volatile PINMASK_TYPE idle2_pin_mask;
volatile bool idle_pwm_state;
unsigned int idle_pwm_max_count; //Used for variable PWM frequency

View File

@ -104,6 +104,7 @@ void initialiseIdle()
iacCrankStepsTable.values = configPage6.iacCrankSteps;
iacCrankStepsTable.axisX = configPage6.iacCrankBins;
iacStepTime = configPage6.iacStepTime * 1000;
iacCoolTime = configPage9.iacCoolTime * 1000;
completedHomeSteps = 0;
idleStepper.curIdleStep = 0;
@ -133,6 +134,7 @@ void initialiseIdle()
iacCrankStepsTable.values = configPage6.iacCrankSteps;
iacCrankStepsTable.axisX = configPage6.iacCrankBins;
iacStepTime = configPage6.iacStepTime * 1000;
iacCoolTime = configPage9.iacCoolTime * 1000;
completedHomeSteps = 0;
idleCounter = 0;
@ -250,12 +252,14 @@ void idleControl()
else if( (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) < iacStepTable.axisX[IDLE_TABLE_SIZE-1])
{
//Standard running
if ((mainLoopCount & 255) == 1)
//We must also have more than zero RPM for the running state
if (((mainLoopCount & 255) == 1) && (currentStatus.RPM > 0))
{
//Only do a lookup of the required value around 4 times per second. Any more than this can create too much jitter and require a hyster value that is too high
idleStepper.targetIdleStep = table2D_getValue(&iacStepTable, (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET)) * 3; //All temps are offset by 40 degrees. Step counts are divided by 3 in TS. Multiply back out here
if(currentStatus.idleUpActive == true) { idleStepper.targetIdleStep += configPage2.idleUpAdder; } //Add Idle Up amount if active
iacStepTime = configPage6.iacStepTime * 1000;
iacCoolTime = configPage9.iacCoolTime * 1000;
}
doStep();
}
@ -272,6 +276,7 @@ void idleControl()
//This only needs to be run very infrequently, once every 32 calls to idleControl(). This is approx. once per second
idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD);
iacStepTime = configPage6.iacStepTime * 1000;
iacCoolTime = configPage9.iacCoolTime * 1000;
}
idle_cl_target_rpm = table2D_getValue(&iacClosedLoopTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) * 10; //All temps are offset by 40 degrees
@ -323,16 +328,37 @@ False: If the motor is ready for another step
static inline byte checkForStepping()
{
bool isStepping = false;
unsigned int timeCheck;
if( (idleStepper.stepperStatus == STEPPING) || (idleStepper.stepperStatus == COOLING) )
{
if(micros_safe() > (idleStepper.stepStartTime + iacStepTime) )
if (idleStepper.stepperStatus == STEPPING)
{
timeCheck = iacStepTime;
}
else
{
timeCheck = iacCoolTime;
}
if(micros_safe() > (idleStepper.stepStartTime + timeCheck) )
{
if(idleStepper.stepperStatus == STEPPING)
{
//Means we're currently in a step, but it needs to be turned off
digitalWrite(pinStepperStep, LOW); //Turn off the step
idleStepper.stepStartTime = micros_safe();
idleStepper.stepperStatus = COOLING; //'Cooling' is the time the stepper needs to sit in LOW state before the next step can be made
// if there is no cool time we can miss that step out completely.
if (iacCoolTime > 0)
{
idleStepper.stepperStatus = COOLING; //'Cooling' is the time the stepper needs to sit in LOW state before the next step can be made
}
else
{
idleStepper.stepperStatus = SOFF;
}
isStepping = true;
}
else
@ -389,13 +415,17 @@ static inline void disableIdle()
IDLE_TIMER_DISABLE();
digitalWrite(pinIdle1, LOW);
}
else if ( (configPage6.iacAlgorithm == IAC_ALGORITHM_STEP_CL) || (configPage6.iacAlgorithm == IAC_ALGORITHM_STEP_OL) )
else if ((configPage6.iacAlgorithm == IAC_ALGORITHM_STEP_OL) )
{
//Only disable the stepper motor if homing is completed
if( (checkForStepping() == false) && (isStepperHomed() == true) )
{
digitalWrite(pinStepperEnable, HIGH); //Disable the DRV8825
idleStepper.targetIdleStep = idleStepper.curIdleStep; //Don't try to move anymore
/* for open loop stepper we should just move to the cranking position when
disabling idle, since the only time this function is called in this scenario
is if the engine stops.
*/
idleStepper.targetIdleStep = table2D_getValue(&iacCrankStepsTable, (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET)) * 3; //All temps are offset by 40 degrees. Step counts are divided by 3 in TS. Multiply back out here
if(currentStatus.idleUpActive == true) { idleStepper.targetIdleStep += configPage2.idleUpAdder; } //Add Idle Up amount if active?
}
}
BIT_CLEAR(currentStatus.spark, BIT_SPARK_IDLE); //Turn the idle control flag off

View File

@ -131,6 +131,15 @@ void initialiseAll()
endCoil3Charge();
endCoil4Charge();
endCoil5Charge();
#if (INJ_CHANNELS >= 6)
endCoil6Charge();
#endif
#if (INJ_CHANNELS >= 7)
endCoil7Charge();
#endif
#if (INJ_CHANNELS >= 8)
endCoil8Charge();
#endif
//Similar for injectors, make sure they're turned off
closeInjector1();
@ -138,6 +147,15 @@ void initialiseAll()
closeInjector3();
closeInjector4();
closeInjector5();
#if (IGN_CHANNELS >= 6)
closeInjector6();
#endif
#if (IGN_CHANNELS >= 7)
closeInjector7();
#endif
#if (IGN_CHANNELS >= 8)
closeInjector8();
#endif
//Set the tacho output default state
digitalWrite(pinTachOut, HIGH);
@ -225,6 +243,7 @@ void initialiseAll()
triggerFilterTime = 0; //Trigger filter time is the shortest possible time (in uS) that there can be between crank teeth (ie at max RPM). Any pulses that occur faster than this time will be disgarded as noise. This is simply a default value, the actual values are set in the setup() functinos of each decoder
dwellLimit_uS = (1000 * configPage4.dwellLimit);
currentStatus.nChannels = (INJ_CHANNELS << 4) + IGN_CHANNELS; //First 4 bits store the number of injection channels, 2nd 4 store the number of ignition channels
fpPrimeTime = 0;
noInterrupts();
initialiseTriggers();
@ -701,6 +720,7 @@ void initialiseAll()
case IGN_MODE_ROTARY:
if(configPage10.rotaryType == ROTARY_IGN_FC)
{
//Ignition channel 1 is a wasted spark signal for leading signal on both rotors
ign1StartFunction = beginCoil1Charge;
ign1EndFunction = endCoil1Charge;
ign2StartFunction = beginCoil1Charge;
@ -711,6 +731,41 @@ void initialiseAll()
ign4StartFunction = beginTrailingCoilCharge;
ign4EndFunction = endTrailingCoilCharge2;
}
else if(configPage10.rotaryType == ROTARY_IGN_FD)
{
//Ignition channel 1 is a wasted spark signal for leading signal on both rotors
ign1StartFunction = beginCoil1Charge;
ign1EndFunction = endCoil1Charge;
ign2StartFunction = beginCoil1Charge;
ign2EndFunction = endCoil1Charge;
//Trailing coils have their own channel each
//IGN2 = front rotor trailing spark
ign3StartFunction = beginCoil2Charge;
ign3EndFunction = endCoil2Charge;
//IGN3 = rear rotor trailing spark
ign4StartFunction = beginCoil3Charge;
ign4EndFunction = endCoil3Charge;
//IGN4 not used
}
else if(configPage10.rotaryType == ROTARY_IGN_RX8)
{
//RX8 outputs are simply 1 coil and 1 output per plug
//IGN1 is front rotor, leading spark
ign1StartFunction = beginCoil1Charge;
ign1EndFunction = endCoil1Charge;
//IGN2 is rear rotor, leading spark
ign2StartFunction = beginCoil2Charge;
ign2EndFunction = endCoil2Charge;
//IGN3 = front rotor trailing spark
ign3StartFunction = beginCoil3Charge;
ign3EndFunction = endCoil3Charge;
//IGN4 = rear rotor trailing spark
ign4StartFunction = beginCoil4Charge;
ign4EndFunction = endCoil4Charge;
}
break;
@ -927,6 +982,7 @@ void setPinMapping(byte boardID)
//PC8~PC12 SDio
//PA13~PA15 & PB4 SWD(debug) pins
//PB0 EEPROM CS pin
//PA9 & PD10 Serial1
//PD5 & PD6 Serial2
pinInjector1 = PE7; //Output pin injector 1 is on
pinInjector2 = PE8; //Output pin injector 2 is on
@ -934,18 +990,18 @@ void setPinMapping(byte boardID)
pinInjector4 = PE10; //Output pin injector 4 is on
pinInjector5 = PE11; //Output pin injector 5 is on
pinInjector6 = PE12; //Output pin injector 6 is on
pinCoil1 = PB5; //Pin for coil 1
pinCoil2 = PB6; //Pin for coil 2
pinCoil3 = PB7; //Pin for coil 3
pinCoil4 = PB8; //Pin for coil 4
pinCoil5 = PB9; //Pin for coil 5
pinCoil1 = PD0; //Pin for coil 1
pinCoil2 = PD1; //Pin for coil 2
pinCoil3 = PD2; //Pin for coil 3
pinCoil4 = PD3; //Pin for coil 4
pinCoil5 = PD4; //Pin for coil 5
pinTPS = A0; //TPS input pin
pinMAP = A1; //MAP sensor pin
pinIAT = A2; //IAT sensor pin
pinCLT = A3; //CLT sensor pin
pinO2 = A4; //O2 Sensor pin
pinBat = A5; //Battery reference voltage pin
pinBaro = A10;
pinBaro = A9;
pinIdle1 = PB8; //Single wire idle control
pinIdle2 = PB9; //2 wire idle control
pinBoost = PE0; //Boost control
@ -955,8 +1011,8 @@ void setPinMapping(byte boardID)
pinStepperEnable = PD9; //Enable pin for DRV8825
pinDisplayReset = PE1; // OLED reset pin
pinFan = PE2; //Pin for the fan output
pinFuelPump = PA6; //Fuel pump output
pinTachOut = PA7; //Tacho output pin
pinFuelPump = PC0; //Fuel pump output
pinTachOut = PC1; //Tacho output pin
//external interrupt enabled pins
//external interrupts could be enalbed in any pin, except same port numbers (PA4,PE4)
pinFlex = PE2; // Flex sensor (Must be external interrupt enabled)
@ -1087,7 +1143,7 @@ void setPinMapping(byte boardID)
break;
case 20:
#ifndef SMALL_FLASH_MODE //No support for bluepill here anyway
#if defined(CORE_AVR) && !defined(SMALL_FLASH_MODE) //No support for bluepill here anyway
//Pin mappings as per the Plazomat In/Out shields Rev 0.1
pinInjector1 = 8; //Output pin injector 1 is on
pinInjector2 = 9; //Output pin injector 2 is on
@ -1322,37 +1378,80 @@ void setPinMapping(byte boardID)
#endif
default:
#ifndef SMALL_FLASH_MODE //No support for bluepill here anyway
//Pin mappings as per the v0.2 shield
pinInjector1 = 8; //Output pin injector 1 is on
pinInjector2 = 9; //Output pin injector 2 is on
pinInjector3 = 10; //Output pin injector 3 is on
pinInjector4 = 11; //Output pin injector 4 is on
pinInjector5 = 12; //Output pin injector 5 is on
pinCoil1 = 28; //Pin for coil 1
pinCoil2 = 24; //Pin for coil 2
pinCoil3 = 40; //Pin for coil 3
pinCoil4 = 36; //Pin for coil 4
pinCoil5 = 34; //Pin for coil 5 PLACEHOLDER value for now
pinTrigger = 20; //The CAS pin
pinTrigger2 = 21; //The Cam Sensor pin
pinTPS = A2; //TPS input pin
pinMAP = A3; //MAP sensor pin
pinIAT = A0; //IAT sensor pin
pinCLT = A1; //CLS sensor pin
pinO2 = A8; //O2 Sensor pin
pinBat = A4; //Battery reference voltage pin
pinStepperDir = 16; //Direction pin for DRV8825 driver
pinStepperStep = 17; //Step pin for DRV8825 driver
pinDisplayReset = 48; // OLED reset pin
pinFan = 47; //Pin for the fan output
pinFuelPump = 4; //Fuel pump output
pinTachOut = 49; //Tacho output pin
pinFlex = 3; // Flex sensor (Must be external interrupt enabled)
pinBoost = 5;
pinIdle1 = 6;
pinResetControl = 43; //Reset control output
#endif
#if defined(STM32F4)
//Black F407VE http://wiki.stm32duino.com/index.php?title=STM32F407
//PC8~PC12 SDio
//PA13~PA15 & PB4 SWD(debug) pins
//PB0 EEPROM CS pin
//PA9 & PD10 Serial1
//PD5 & PD6 Serial2
pinInjector1 = PE7; //Output pin injector 1 is on
pinInjector2 = PE8; //Output pin injector 2 is on
pinInjector3 = PE9; //Output pin injector 3 is on
pinInjector4 = PE10; //Output pin injector 4 is on
pinInjector5 = PE11; //Output pin injector 5 is on
pinInjector6 = PE12; //Output pin injector 6 is on
pinCoil1 = PD0; //Pin for coil 1
pinCoil2 = PD1; //Pin for coil 2
pinCoil3 = PD2; //Pin for coil 3
pinCoil4 = PD3; //Pin for coil 4
pinCoil5 = PD4; //Pin for coil 5
pinTPS = A0; //TPS input pin
pinMAP = A1; //MAP sensor pin
pinIAT = A2; //IAT sensor pin
pinCLT = A3; //CLT sensor pin
pinO2 = A4; //O2 Sensor pin
pinBat = A5; //Battery reference voltage pin
pinBaro = A9;
pinIdle1 = PB8; //Single wire idle control
pinIdle2 = PB9; //2 wire idle control
pinBoost = PE0; //Boost control
pinVVT_1 = PE1; //Default VVT output
pinStepperDir = PD8; //Direction pin for DRV8825 driver
pinStepperStep = PB15; //Step pin for DRV8825 driver
pinStepperEnable = PD9; //Enable pin for DRV8825
pinDisplayReset = PE1; // OLED reset pin
pinFan = PE2; //Pin for the fan output
pinFuelPump = PC0; //Fuel pump output
pinTachOut = PC1; //Tacho output pin
//external interrupt enabled pins
//external interrupts could be enalbed in any pin, except same port numbers (PA4,PE4)
pinFlex = PE2; // Flex sensor (Must be external interrupt enabled)
pinTrigger = PE3; //The CAS pin
pinTrigger2 = PE4; //The Cam Sensor pin
#else
#ifndef SMALL_FLASH_MODE //No support for bluepill here anyway
//Pin mappings as per the v0.2 shield
pinInjector1 = 8; //Output pin injector 1 is on
pinInjector2 = 9; //Output pin injector 2 is on
pinInjector3 = 10; //Output pin injector 3 is on
pinInjector4 = 11; //Output pin injector 4 is on
pinInjector5 = 12; //Output pin injector 5 is on
pinCoil1 = 28; //Pin for coil 1
pinCoil2 = 24; //Pin for coil 2
pinCoil3 = 40; //Pin for coil 3
pinCoil4 = 36; //Pin for coil 4
pinCoil5 = 34; //Pin for coil 5 PLACEHOLDER value for now
pinTrigger = 20; //The CAS pin
pinTrigger2 = 21; //The Cam Sensor pin
pinTPS = A2; //TPS input pin
pinMAP = A3; //MAP sensor pin
pinIAT = A0; //IAT sensor pin
pinCLT = A1; //CLS sensor pin
pinO2 = A8; //O2 Sensor pin
pinBat = A4; //Battery reference voltage pin
pinStepperDir = 16; //Direction pin for DRV8825 driver
pinStepperStep = 17; //Step pin for DRV8825 driver
pinDisplayReset = 48; // OLED reset pin
pinFan = 47; //Pin for the fan output
pinFuelPump = 4; //Fuel pump output
pinTachOut = 49; //Tacho output pin
pinFlex = 3; // Flex sensor (Must be external interrupt enabled)
pinBoost = 5;
pinIdle1 = 6;
pinResetControl = 43; //Reset control output
#endif
#endif
break;
}

View File

@ -11,7 +11,7 @@ unsigned long divu100(unsigned long);
//This is a dedicated function that specifically handles the case of mapping 0-1023 values into a 0 to X range
//This is a common case because it means converting from a standard 10-bit analog input to a byte or 10-bit analog into 0-511 (Eg the temperature readings)
#if defined(_VARIANT_ARDUINO_STM32_) //libmaple
#if defined(_VARIANT_ARDUINO_STM32_) //libmaple //ST stm32duino core returns 0 - 1023 for analog read first use analogReadResolution(12); to make it 12 bit
#define fastMap1023toX(x, out_max) ( ((unsigned long)x * out_max) >> 12)
//This is a new version that allows for out_min
#define fastMap10Bit(x, out_min, out_max) ( ( ((unsigned long)x * (out_max-out_min)) >> 12 ) + out_min)

View File

@ -1,45 +1,41 @@
#include "scheduledIO.h"
#include "scheduler.h"
#include "globals.h"
#include "timers.h"
volatile bool tachoAlt = true;
#define TACH_PULSE_HIGH() *tach_pin_port |= (tach_pin_mask)
#define TACH_PULSE_LOW() if( (configPage2.tachoDiv == 0) || tachoAlt ) { *tach_pin_port &= ~(tach_pin_mask); tachoAlt = !tachoAlt; }
inline void beginCoil1Charge() { digitalWrite(pinCoil1, coilHIGH); tachoOutputFlag = READY; }
inline void endCoil1Charge() { digitalWrite(pinCoil1, coilLOW); }
inline void beginCoil2Charge() { digitalWrite(pinCoil2, coilHIGH); tachoOutputFlag = READY; }
inline void endCoil2Charge() { digitalWrite(pinCoil2, coilLOW); }
inline void beginCoil3Charge() { digitalWrite(pinCoil3, coilHIGH); tachoOutputFlag = READY; }
inline void endCoil3Charge() { digitalWrite(pinCoil3, coilLOW); }
inline void beginCoil1Charge() { digitalWrite(pinCoil1, coilHIGH); TACH_PULSE_LOW(); }
inline void endCoil1Charge() { digitalWrite(pinCoil1, coilLOW); TACH_PULSE_HIGH(); }
inline void beginCoil4Charge() { digitalWrite(pinCoil4, coilHIGH); tachoOutputFlag = READY; }
inline void endCoil4Charge() { digitalWrite(pinCoil4, coilLOW); }
inline void beginCoil2Charge() { digitalWrite(pinCoil2, coilHIGH); TACH_PULSE_LOW(); }
inline void endCoil2Charge() { digitalWrite(pinCoil2, coilLOW); TACH_PULSE_HIGH(); }
inline void beginCoil5Charge() { digitalWrite(pinCoil5, coilHIGH); tachoOutputFlag = READY; }
inline void endCoil5Charge() { digitalWrite(pinCoil5, coilLOW); }
inline void beginCoil3Charge() { digitalWrite(pinCoil3, coilHIGH); TACH_PULSE_LOW(); }
inline void endCoil3Charge() { digitalWrite(pinCoil3, coilLOW); TACH_PULSE_HIGH(); }
inline void beginCoil6Charge() { digitalWrite(pinCoil6, coilHIGH); tachoOutputFlag = READY; }
inline void endCoil6Charge() { digitalWrite(pinCoil6, coilLOW); }
inline void beginCoil4Charge() { digitalWrite(pinCoil4, coilHIGH); TACH_PULSE_LOW(); }
inline void endCoil4Charge() { digitalWrite(pinCoil4, coilLOW); TACH_PULSE_HIGH(); }
inline void beginCoil7Charge() { digitalWrite(pinCoil7, coilHIGH); tachoOutputFlag = READY; }
inline void endCoil7Charge() { digitalWrite(pinCoil7, coilLOW); }
inline void beginCoil5Charge() { digitalWrite(pinCoil5, coilHIGH); TACH_PULSE_LOW(); }
inline void endCoil5Charge() { digitalWrite(pinCoil5, coilLOW); TACH_PULSE_HIGH(); }
inline void beginCoil8Charge() { digitalWrite(pinCoil8, coilHIGH); tachoOutputFlag = READY; }
inline void endCoil8Charge() { digitalWrite(pinCoil8, coilLOW); }
inline void beginCoil6Charge() { digitalWrite(pinCoil6, coilHIGH); TACH_PULSE_LOW(); }
inline void endCoil6Charge() { digitalWrite(pinCoil6, coilLOW); TACH_PULSE_HIGH(); }
inline void beginCoil7Charge() { digitalWrite(pinCoil7, coilHIGH); TACH_PULSE_LOW(); }
inline void endCoil7Charge() { digitalWrite(pinCoil7, coilLOW); TACH_PULSE_HIGH(); }
inline void beginCoil8Charge() { digitalWrite(pinCoil8, coilHIGH); TACH_PULSE_LOW(); }
inline void endCoil8Charge() { digitalWrite(pinCoil8, coilLOW); TACH_PULSE_HIGH(); }
inline void beginTrailingCoilCharge() { digitalWrite(pinCoil2, coilHIGH); }
inline void endTrailingCoilCharge1() { digitalWrite(pinCoil2, coilLOW); *ign3_pin_port |= ign3_pin_mask; } //Sets ign3 (Trailing select) high
inline void endTrailingCoilCharge2() { digitalWrite(pinCoil2, coilLOW); *ign3_pin_port &= ~(ign3_pin_mask); } //sets ign3 (Trailing select) low
//The below 3 calls are all part of the rotary ignition mode
inline void beginTrailingCoilCharge() { digitalWrite(pinCoil2, coilHIGH); }
inline void endTrailingCoilCharge1() { digitalWrite(pinCoil2, coilLOW); *ign3_pin_port |= ign3_pin_mask; } //Sets ign3 (Trailing select) high
inline void endTrailingCoilCharge2() { digitalWrite(pinCoil2, coilLOW); *ign3_pin_port &= ~(ign3_pin_mask); } //sets ign3 (Trailing select) low
//As above but for ignition (Wasted COP mode)
void beginCoil1and3Charge() { digitalWrite(pinCoil1, coilHIGH); digitalWrite(pinCoil3, coilHIGH); TACH_PULSE_LOW(); }
void endCoil1and3Charge() { digitalWrite(pinCoil1, coilLOW); digitalWrite(pinCoil3, coilLOW); TACH_PULSE_HIGH(); }
void beginCoil2and4Charge() { digitalWrite(pinCoil2, coilHIGH); digitalWrite(pinCoil4, coilHIGH); TACH_PULSE_LOW(); }
void endCoil2and4Charge() { digitalWrite(pinCoil2, coilLOW); digitalWrite(pinCoil4, coilLOW); TACH_PULSE_HIGH(); }
void beginCoil1and3Charge() { digitalWrite(pinCoil1, coilHIGH); digitalWrite(pinCoil3, coilHIGH); tachoOutputFlag = READY; }
void endCoil1and3Charge() { digitalWrite(pinCoil1, coilLOW); digitalWrite(pinCoil3, coilLOW); }
void beginCoil2and4Charge() { digitalWrite(pinCoil2, coilHIGH); digitalWrite(pinCoil4, coilHIGH); tachoOutputFlag = READY; }
void endCoil2and4Charge() { digitalWrite(pinCoil2, coilLOW); digitalWrite(pinCoil4, coilLOW); }
void nullCallback() { return; }

View File

@ -50,7 +50,7 @@ void setIgnitionSchedule8(void (*startCallback)(), unsigned long timeout, unsign
static inline void refreshIgnitionSchedule1(unsigned long timeToEnd) __attribute__((always_inline));
//The ARM cores use seprate functions for their ISRs
#if defined(CORE_STM32) || defined(CORE_TEENSY)
#if defined(CORE_STM32_OFFICIAL) || defined(CORE_STM32_GENERIC) || defined(CORE_TEENSY)
static inline void fuelSchedule1Interrupt();
static inline void fuelSchedule2Interrupt();
static inline void fuelSchedule3Interrupt();

View File

@ -13,38 +13,6 @@ void initialiseSchedulers()
{
nullSchedule.Status = OFF;
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
//Much help in this from http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html
//Fuel Schedules, which uses timer 3
TCCR3B = 0x00; //Disable Timer3 while we set it up
TCNT3 = 0; //Reset Timer Count
TIFR3 = 0x00; //Timer3 INT Flag Reg: Clear Timer Overflow Flag
TCCR3A = 0x00; //Timer3 Control Reg A: Wave Gen Mode normal
TCCR3B = (1 << CS12); //Timer3 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
//TCCR3B = 0x03; //Timer3 Control Reg B: Timer Prescaler set to 64. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
//Ignition Schedules, which uses timer 5. This is also used by the fast version of micros(). If the speed of this timer is changed from 4uS ticks, that MUST be changed as well. See globals.h and timers.ino
TCCR5B = 0x00; //Disable Timer5 while we set it up
TCNT5 = 0; //Reset Timer Count
TIFR5 = 0x00; //Timer5 INT Flag Reg: Clear Timer Overflow Flag
TCCR5A = 0x00; //Timer5 Control Reg A: Wave Gen Mode normal
//TCCR5B = (1 << CS12); //Timer5 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
TCCR5B = 0x03; //aka Divisor = 64 = 490.1Hz
#if defined(TIMER5_MICROS)
TIMSK5 |= (1 << TOIE5); //Enable the timer5 overflow interrupt (See timers.ino for ISR)
TIMSK0 &= ~_BV(TOIE0); // disable timer0 overflow interrupt
#endif
//The remaining Schedules (Schedules 4 for fuel and ignition) use Timer4
TCCR4B = 0x00; //Disable Timer4 while we set it up
TCNT4 = 0; //Reset Timer Count
TIFR4 = 0x00; //Timer4 INT Flag Reg: Clear Timer Overflow Flag
TCCR4A = 0x00; //Timer4 Control Reg A: Wave Gen Mode normal
TCCR4B = (1 << CS12); //Timer4 Control Reg B: aka Divisor = 256 = 122.5HzTimer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
#endif
fuelSchedule1.Status = OFF;
fuelSchedule2.Status = OFF;
fuelSchedule3.Status = OFF;
@ -64,20 +32,28 @@ void initialiseSchedulers()
fuelSchedule8.schedulesSet = 0;
fuelSchedule1.counter = &FUEL1_COUNTER;
fuelSchedule1.compare = &FUEL1_COMPARE;
fuelSchedule2.counter = &FUEL2_COUNTER;
fuelSchedule2.compare = &FUEL2_COMPARE;
fuelSchedule3.counter = &FUEL3_COUNTER;
fuelSchedule3.compare = &FUEL3_COMPARE;
fuelSchedule4.counter = &FUEL4_COUNTER;
fuelSchedule4.compare = &FUEL4_COMPARE;
#if (INJ_CHANNELS >= 5)
fuelSchedule5.counter = &FUEL5_COUNTER;
fuelSchedule5.compare = &FUEL5_COMPARE;
#endif
#if (INJ_CHANNELS >= 6)
fuelSchedule5.counter = &FUEL6_COUNTER;
fuelSchedule6.counter = &FUEL6_COUNTER;
fuelSchedule6.compare = &FUEL6_COMPARE;
#endif
#if (INJ_CHANNELS >= 7)
fuelSchedule5.counter = &FUEL7_COUNTER;
fuelSchedule7.counter = &FUEL7_COUNTER;
fuelSchedule7.compare = &FUEL7_COMPARE;
#endif
#if (INJ_CHANNELS >= 8)
fuelSchedule5.counter = &FUEL8_COUNTER;
fuelSchedule8.counter = &FUEL8_COUNTER;
fuelSchedule8.compare = &FUEL8_COMPARE;
#endif
ignitionSchedule1.Status = OFF;
@ -89,6 +65,11 @@ void initialiseSchedulers()
ignitionSchedule7.Status = OFF;
ignitionSchedule8.Status = OFF;
IGN1_TIMER_ENABLE();
IGN2_TIMER_ENABLE();
IGN3_TIMER_ENABLE();
IGN4_TIMER_ENABLE();
ignitionSchedule1.schedulesSet = 0;
ignitionSchedule2.schedulesSet = 0;
ignitionSchedule3.schedulesSet = 0;
@ -98,6 +79,31 @@ void initialiseSchedulers()
ignitionSchedule7.schedulesSet = 0;
ignitionSchedule8.schedulesSet = 0;
ignitionSchedule1.counter = &IGN1_COUNTER;
ignitionSchedule1.compare = &IGN1_COMPARE;
ignitionSchedule2.counter = &IGN2_COUNTER;
ignitionSchedule2.compare = &IGN2_COMPARE;
ignitionSchedule3.counter = &IGN3_COUNTER;
ignitionSchedule3.compare = &IGN3_COMPARE;
ignitionSchedule4.counter = &IGN4_COUNTER;
ignitionSchedule4.compare = &IGN4_COMPARE;
#if (INJ_CHANNELS >= 5)
ignitionSchedule5.counter = &IGN5_COUNTER;
ignitionSchedule5.compare = &IGN5_COMPARE;
#endif
#if (INJ_CHANNELS >= 6)
ignitionSchedule6.counter = &IGN6_COUNTER;
ignitionSchedule6.compare = &IGN6_COMPARE;
#endif
#if (INJ_CHANNELS >= 7)
ignitionSchedule7.counter = &IGN7_COUNTER;
ignitionSchedule7.compare = &IGN7_COMPARE;
#endif
#if (INJ_CHANNELS >= 8)
ignitionSchedule8.counter = &IGN8_COUNTER;
ignitionSchedule8.compare = &IGN8_COMPARE;
#endif
}
/*
@ -1008,6 +1014,11 @@ static inline void ignitionSchedule1Interrupt() //Most ARM chips can simply call
ignitionCount += 1; //Increment the igintion counter
IGN1_TIMER_DISABLE();
}
else if (ignitionSchedule1.Status == OFF)
{
//Catch any spurious interrupts. This really shouldn't ever be called, but there as a safety
IGN1_TIMER_DISABLE();
}
}
#endif
@ -1035,6 +1046,11 @@ static inline void ignitionSchedule2Interrupt() //Most ARM chips can simply call
ignitionCount += 1; //Increment the igintion counter
IGN2_TIMER_DISABLE();
}
else if (ignitionSchedule2.Status == OFF)
{
//Catch any spurious interrupts. This really shouldn't ever be called, but there as a safety
IGN2_TIMER_DISABLE();
}
}
#endif
@ -1072,6 +1088,11 @@ static inline void ignitionSchedule3Interrupt() //Most ARM chips can simply call
}
else { IGN3_TIMER_DISABLE(); }
}
else if (ignitionSchedule3.Status == OFF)
{
//Catch any spurious interrupts. This really shouldn't ever be called, but there as a safety
IGN3_TIMER_DISABLE();
}
}
#endif
@ -1108,6 +1129,11 @@ static inline void ignitionSchedule4Interrupt() //Most ARM chips can simply call
}
else { IGN4_TIMER_DISABLE(); }
}
else if (ignitionSchedule4.Status == OFF)
{
//Catch any spurious interrupts. This really shouldn't ever be called, but there as a safety
IGN4_TIMER_DISABLE();
}
}
#endif
@ -1248,15 +1274,15 @@ void ftm3_isr(void)
#endif
#if (INJ_CHANNELS >= 6)
bool interrupt2 = (FTM3_C1SC & FTM_CSC_CHF);
else if(interrupt2) { FTM3_C1SC &= ~FTM_CSC_CHF; fuelSchedule6Interrupt(); }
if(interrupt2) { FTM3_C1SC &= ~FTM_CSC_CHF; fuelSchedule6Interrupt(); }
#endif
#if (INJ_CHANNELS >= 7)
bool interrupt3 = (FTM3_C2SC & FTM_CSC_CHF);
else if(interrupt3) { FTM3_C2SC &= ~FTM_CSC_CHF; fuelSchedule7Interrupt(); }
if(interrupt3) { FTM3_C2SC &= ~FTM_CSC_CHF; fuelSchedule7Interrupt(); }
#endif
#if (INJ_CHANNELS >= 8)
bool interrupt4 = (FTM3_C3SC & FTM_CSC_CHF);
else if(interrupt4) { FTM3_C3SC &= ~FTM_CSC_CHF; fuelSchedule8Interrupt(); }
if(interrupt4) { FTM3_C3SC &= ~FTM_CSC_CHF; fuelSchedule8Interrupt(); }
#endif
#if (IGN_CHANNELS >= 5)
bool interrupt5 = (FTM3_C4SC & FTM_CSC_CHF);
@ -1264,15 +1290,15 @@ void ftm3_isr(void)
#endif
#if (IGN_CHANNELS >= 6)
bool interrupt6 = (FTM3_C5SC & FTM_CSC_CHF);
else if(interrupt6) { FTM3_C5SC &= ~FTM_CSC_CHF; ignitionSchedule6Interrupt(); }
if(interrupt6) { FTM3_C5SC &= ~FTM_CSC_CHF; ignitionSchedule6Interrupt(); }
#endif
#if (IGN_CHANNELS >= 7)
bool interrupt7 = (FTM3_C6SC & FTM_CSC_CHF);
else if(interrupt7) { FTM3_C6SC &= ~FTM_CSC_CHF; ignitionSchedule7Interrupt(); }
if(interrupt7) { FTM3_C6SC &= ~FTM_CSC_CHF; ignitionSchedule7Interrupt(); }
#endif
#if (IGN_CHANNELS >= 8)
bool interrupt8 = (FTM3_C7SC & FTM_CSC_CHF);
else if(interrupt8) { FTM3_C7SC &= ~FTM_CSC_CHF; ignitionSchedule8Interrupt(); }
if(interrupt8) { FTM3_C7SC &= ~FTM_CSC_CHF; ignitionSchedule8Interrupt(); }
#endif
}

View File

@ -9,6 +9,7 @@ A full copy of the license may be found in the projects root directory
#include "maths.h"
#include "storage.h"
#include "comms.h"
#include "idle.h"
void initialiseADC()
{
@ -47,8 +48,8 @@ void initialiseADC()
BIT_CLEAR(ADCSRA,ADPS1);
BIT_CLEAR(ADCSRA,ADPS0);
#endif
#elif defined(ARDUINO_ARCH_STM32) //STM32GENERIC lib
analogReadResolution(10); //use 10bits for analog
#elif defined(ARDUINO_ARCH_STM32) //STM32GENERIC core and ST STM32duino core, change analog read to 12 bit
analogReadResolution(12); //use 12bits for analog reading on STM32 boards
#endif
MAPcurRev = 0;
MAPcount = 0;
@ -375,6 +376,24 @@ void readBat()
tempReading = analogRead(pinBat);
tempReading = fastMap1023toX(analogRead(pinBat), 245); //Get the current raw Battery value. Permissible values are from 0v to 24.5v (245)
#endif
//The following is a check for if the voltage has jumped up from under 5.5v to over 7v.
//If this occurs, it's very likely that the system has gone from being powered by USB to being powered from the 12v power source.
//Should that happen, we retrigger the fuel pump priming and idle homing (If using a stepper)
if( (currentStatus.battery10 < 55) && (tempReading > 70) && (currentStatus.RPM == 0) )
{
//Reprime the fuel pump
fpPrimeTime = currentStatus.secl;
fpPrimed = false;
FUEL_PUMP_ON();
//Redo the stepper homing
if( (configPage6.iacAlgorithm == IAC_ALGORITHM_STEP_CL) || (configPage6.iacAlgorithm == IAC_ALGORITHM_STEP_OL) )
{
initialiseIdle();
}
}
currentStatus.battery10 = ADC_FILTER(tempReading, configPage4.ADCFILTER_BAT, currentStatus.battery10);
}

View File

@ -338,30 +338,32 @@ void loop()
uint16_t injector2StartAngle = 0;
uint16_t injector3StartAngle = 0;
uint16_t injector4StartAngle = 0;
#if INJ_CHANNELS >= 5
uint16_t injector5StartAngle = 0; //For 5 cylinder testing
#if INJ_CHANNELS >= 6
#endif
#if INJ_CHANNELS >= 6
int injector6StartAngle = 0;
#endif
#if INJ_CHANNELS >= 7
#endif
#if INJ_CHANNELS >= 7
int injector7StartAngle = 0;
#endif
#if INJ_CHANNELS >= 8
#endif
#if INJ_CHANNELS >= 8
int injector8StartAngle = 0;
#endif
#endif
int ignition1StartAngle = 0;
int ignition2StartAngle = 0;
int ignition3StartAngle = 0;
int ignition4StartAngle = 0;
int ignition5StartAngle = 0;
#if IGN_CHANNELS >= 6
#if IGN_CHANNELS >= 6
int ignition6StartAngle = 0;
#endif
#if IGN_CHANNELS >= 7
#endif
#if IGN_CHANNELS >= 7
int ignition7StartAngle = 0;
#endif
#if IGN_CHANNELS >= 8
#endif
#if IGN_CHANNELS >= 8
int ignition8StartAngle = 0;
#endif
#endif
//These are used for comparisons on channels above 1 where the starting angle (for injectors or ignition) can be less than a single loop time
//(Don't ask why this is needed, it's just there)
int tempCrankAngle;
@ -506,23 +508,25 @@ void loop()
injector2StartAngle = calculateInjector2StartAngle(PWdivTimerPerDegree); //Note the shared timing with INJ2
injector3StartAngle = calculateInjector3StartAngle(PWdivTimerPerDegree);
injector4StartAngle = calculateInjector4StartAngle(PWdivTimerPerDegree);
injector5StartAngle = calculateInjector5StartAngle(PWdivTimerPerDegree);
#if INJ_CHANNELS >= 5
injector5StartAngle = calculateInjector5StartAngle(PWdivTimerPerDegree);
#endif
break;
//6 cylinders
case 6:
injector2StartAngle = calculateInjector2StartAngle(PWdivTimerPerDegree);
injector3StartAngle = calculateInjector3StartAngle(PWdivTimerPerDegree);
#if INJ_CHANNELS >= 6
if(configPage2.injLayout == INJ_SEQUENTIAL)
{
injector4StartAngle = (configPage2.inj1Ang + channel4InjDegrees - ( PWdivTimerPerDegree ));
if(injector4StartAngle > CRANK_ANGLE_MAX_INJ) {injector4StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector5StartAngle = (configPage2.inj2Ang + channel5InjDegrees - ( PWdivTimerPerDegree ));
if(injector5StartAngle > CRANK_ANGLE_MAX_INJ) {injector5StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector6StartAngle = (configPage2.inj3Ang + channel6InjDegrees - ( PWdivTimerPerDegree ));
if(injector6StartAngle > CRANK_ANGLE_MAX_INJ) {injector6StartAngle -= CRANK_ANGLE_MAX_INJ;}
}
#endif
#if INJ_CHANNELS >= 6
if(configPage2.injLayout == INJ_SEQUENTIAL)
{
injector4StartAngle = (configPage2.inj1Ang + channel4InjDegrees - ( PWdivTimerPerDegree ));
if(injector4StartAngle > CRANK_ANGLE_MAX_INJ) {injector4StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector5StartAngle = (configPage2.inj2Ang + channel5InjDegrees - ( PWdivTimerPerDegree ));
if(injector5StartAngle > CRANK_ANGLE_MAX_INJ) {injector5StartAngle -= CRANK_ANGLE_MAX_INJ;}
injector6StartAngle = (configPage2.inj3Ang + channel6InjDegrees - ( PWdivTimerPerDegree ));
if(injector6StartAngle > CRANK_ANGLE_MAX_INJ) {injector6StartAngle -= CRANK_ANGLE_MAX_INJ;}
}
#endif
break;
//8 cylinders
case 8:
@ -600,23 +604,20 @@ void loop()
}
else if(configPage4.sparkMode == IGN_MODE_ROTARY)
{
if(configPage10.rotaryType == ROTARY_IGN_FC)
{
byte splitDegrees = 0;
if (configPage2.fuelAlgorithm == LOAD_SOURCE_MAP) { splitDegrees = table2D_getValue(&rotarySplitTable, currentStatus.MAP/2); }
else { splitDegrees = table2D_getValue(&rotarySplitTable, currentStatus.TPS/2); }
byte splitDegrees = 0;
if (configPage2.fuelAlgorithm == LOAD_SOURCE_MAP) { splitDegrees = table2D_getValue(&rotarySplitTable, currentStatus.MAP/2); }
else { splitDegrees = table2D_getValue(&rotarySplitTable, currentStatus.TPS/2); }
//The trailing angles are set relative to the leading ones
ignition3EndAngle = ignition1EndAngle + splitDegrees;
ignition3StartAngle = ignition3EndAngle - dwellAngle;
if(ignition3StartAngle > CRANK_ANGLE_MAX_IGN) {ignition3StartAngle -= CRANK_ANGLE_MAX_IGN;}
if(ignition3StartAngle < 0) {ignition3StartAngle += CRANK_ANGLE_MAX_IGN;}
//The trailing angles are set relative to the leading ones
ignition3EndAngle = ignition1EndAngle + splitDegrees;
ignition3StartAngle = ignition3EndAngle - dwellAngle;
if(ignition3StartAngle > CRANK_ANGLE_MAX_IGN) {ignition3StartAngle -= CRANK_ANGLE_MAX_IGN;}
if(ignition3StartAngle < 0) {ignition3StartAngle += CRANK_ANGLE_MAX_IGN;}
ignition4EndAngle = ignition2EndAngle + splitDegrees;
ignition4StartAngle = ignition4EndAngle - dwellAngle;
if(ignition4StartAngle > CRANK_ANGLE_MAX_IGN) {ignition4StartAngle -= CRANK_ANGLE_MAX_IGN;}
if(ignition4StartAngle < 0) {ignition4StartAngle += CRANK_ANGLE_MAX_IGN;}
}
ignition4EndAngle = ignition2EndAngle + splitDegrees;
ignition4StartAngle = ignition4EndAngle - dwellAngle;
if(ignition4StartAngle > CRANK_ANGLE_MAX_IGN) {ignition4StartAngle -= CRANK_ANGLE_MAX_IGN;}
if(ignition4StartAngle < 0) {ignition4StartAngle += CRANK_ANGLE_MAX_IGN;}
}
break;
//5 cylinders
@ -679,6 +680,7 @@ void loop()
//If ignition timing is being tracked per tooth, perform the calcs to get the end teeth
//This only needs to be run if the advance figure has changed, otherwise the end teeth will still be the same
if( (configPage2.perToothIgn == true) && (lastToothCalcAdvance != currentStatus.advance) ) { triggerSetEndTeeth(); }
//if( (configPage2.perToothIgn == true) ) { triggerSetEndTeeth(); }
//***********************************************************************************************
//| BEGIN FUEL SCHEDULES

View File

@ -0,0 +1,77 @@
//#if defined(ARDUINO_BLACK_F407VE)
#if defined(CORE_STM32_OFFICIAL) && defined(SRAM_AS_EEPROM)
#include "BackupSramAsEEPROM.h"
BackupSramAsEEPROM::BackupSramAsEEPROM(){
//Enable the power interface clock
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
//Enable the backup SRAM clock by setting BKPSRAMEN bit i
RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN;
/** enable the backup regulator (used to maintain the backup SRAM content in
* standby and Vbat modes). NOTE : this bit is not reset when the device
* wakes up from standby, system reset or power reset. You can check that
* the backup regulator is ready on PWR->CSR.brr, see rm p144 */
//enable backup power regulator this makes sram backup posible. bit is not reset by software!
PWR->CSR |= PWR_CSR_BRE;
}
int8_t BackupSramAsEEPROM::write_byte( uint8_t *data, uint16_t bytes, uint16_t offset ) {
uint8_t* base_addr = (uint8_t *) BKPSRAM_BASE;
uint16_t i;
if( bytes + offset >= backup_size ) {
/* ERROR : the last byte is outside the backup SRAM region */
return -1;
}
/* disable backup domain write protection */
//Set the Disable Backup Domain write protection (DBP) bit in PWR power control register
PWR->CR |= PWR_CR_DBP;
for( i = 0; i < bytes; i++ ) {
*(base_addr + offset + i) = *(data + i);
}
//Enable write protection backup sram when finished
PWR->CR &= ~PWR_CR_DBP;
return 0;
}
int8_t BackupSramAsEEPROM::read_byte( uint8_t *data, uint16_t bytes, uint16_t offset ) {
uint8_t* base_addr = (uint8_t *) BKPSRAM_BASE;
uint16_t i;
if( bytes + offset >= backup_size ) {
/* ERROR : the last byte is outside the backup SRAM region */
return -1;
}
for( i = 0; i < bytes; i++ ) {
*(data + i) = *(base_addr + offset + i);
}
return 0;
}
uint8_t BackupSramAsEEPROM::read(uint16_t address) {
uint8_t val = 0;
read_byte(&val, 1, address);
return val;
}
int8_t BackupSramAsEEPROM::write(uint16_t address, uint8_t val) {
write_byte(&val, 1, address);
return 0;
}
int8_t BackupSramAsEEPROM::update(uint16_t address, uint8_t val) {
write_byte(&val, 1, address);
return 0;
}
BackupSramAsEEPROM EEPROM;
#endif

View File

@ -0,0 +1,29 @@
//Backup sram stores data in the battery backuped sram portion.
//The backup battery is available on the ebay stm32F407VET6 black boards.
#if defined(CORE_STM32_OFFICIAL) && defined(SRAM_AS_EEPROM)
//UGLY HACK TO PREVENT EEPROM LIBRARY BEING IMPORTED FIRST!!
//Use with black_F407VE board
#ifndef EEPROM_h
#define EEPROM_h
#include <stdint.h>
#include "stm32f407xx.h"
class BackupSramAsEEPROM {
private:
const uint16_t backup_size = 0x4000; //maximum of 4kb backuped sram available.
int8_t write_byte( uint8_t *data, uint16_t bytes, uint16_t offset );
int8_t read_byte( uint8_t *data, uint16_t bytes, uint16_t offset );
public:
BackupSramAsEEPROM();
uint8_t read(uint16_t address);
int8_t write(uint16_t address, uint8_t val);
int8_t update(uint16_t address, uint8_t val);
};
extern BackupSramAsEEPROM EEPROM;
#endif
#endif

View File

@ -1,217 +0,0 @@
/*
Optimized digital functions for AVR microcontrollers
by Watterott electronic (www.watterott.com)
based on http://code.google.com/p/digitalwritefast
*/
#ifndef __digitalWriteFast_h_
#define __digitalWriteFast_h_ 1
#define ERROR_SEQUENCE 0b10101010 //digitalReadFast will return this value if pin number is not constant
// general macros/defines
#ifndef BIT_READ
# define BIT_READ(value, bit) ((value) & (1UL << (bit)))
#endif
#ifndef BIT_SET
# define BIT_SET(value, bit) ((value) |= (1UL << (bit)))
#endif
#ifndef BIT_CLEAR
# define BIT_CLEAR(value, bit) ((value) &= ~(1UL << (bit)))
#endif
#ifndef BIT_WRITE
# define BIT_WRITE(value, bit, bitvalue) (bitvalue ? BIT_SET(value, bit) : BIT_CLEAR(value, bit))
#endif
#ifndef SWAP
#define SWAP(x,y) do{ (x)=(x)^(y); (y)=(x)^(y); (x)=(x)^(y); }while(0)
#endif
// workarounds for ARM microcontrollers
#if (!defined(__AVR__) || defined(ARDUINO_ARCH_SAM))
#ifndef PROGMEM
# define PROGMEM
#endif
#ifndef PGM_P
# define PGM_P const char *
#endif
#ifndef PSTR
# define PSTR(str) (str)
#endif
#ifndef memcpy_P
# define memcpy_P(dest, src, num) memcpy((dest), (src), (num))
#endif
#ifndef strcpy_P
# define strcpy_P(dst, src) strcpy((dst), (src))
#endif
#ifndef strcat_P
# define strcat_P(dst, src) strcat((dst), (src))
#endif
#ifndef strcmp_P
# define strcmp_P(a, b) strcmp((a), (b))
#endif
#ifndef strcasecmp_P
# define strcasecmp_P(a, b) strcasecmp((a), (b))
#endif
#ifndef strncmp_P
# define strncmp_P(a, b, n) strncmp((a), (b), (n))
#endif
#ifndef strncasecmp_P
# define strncasecmp_P(a, b, n) strncasecmp((a), (b), (n))
#endif
#ifndef strstr_P
# define strstr_P(a, b) strstr((a), (b))
#endif
#ifndef strlen_P
# define strlen_P(a) strlen((a))
#endif
#ifndef sprintf_P
# define sprintf_P(s, f, ...) sprintf((s), (f), __VA_ARGS__)
#endif
#ifndef pgm_read_byte
# define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#endif
#ifndef pgm_read_word
# define pgm_read_word(addr) (*(const unsigned short *)(addr))
#endif
#ifndef pgm_read_dword
# define pgm_read_dword(addr) (*(const unsigned long *)(addr))
#endif
#endif
// digital functions
// --- Arduino Mega ---
#if (defined(ARDUINO_AVR_MEGA) || \
defined(ARDUINO_AVR_MEGA1280) || \
defined(ARDUINO_AVR_MEGA2560) || \
defined(__AVR_ATmega1280__) || \
defined(__AVR_ATmega1281__) || \
defined(__AVR_ATmega2560__) || \
defined(__AVR_ATmega2561__))
#define UART_RX_PIN (0) //PE0
#define UART_TX_PIN (1) //PE1
#define I2C_SDA_PIN (20)
#define I2C_SCL_PIN (21)
#define SPI_HW_SS_PIN (53) //PB0
#define SPI_HW_MOSI_PIN (51) //PB2
#define SPI_HW_MISO_PIN (50) //PB3
#define SPI_HW_SCK_PIN (52) //PB1
#define __digitalPinToPortReg(P) \
(((P) >= 22 && (P) <= 29) ? &PORTA : \
((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PORTB : \
(((P) >= 30 && (P) <= 37) ? &PORTC : \
((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PORTD : \
((((P) >= 0 && (P) <= 3) || (P) == 5) ? &PORTE : \
(((P) >= 54 && (P) <= 61) ? &PORTF : \
((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PORTG : \
((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PORTH : \
(((P) == 14 || (P) == 15) ? &PORTJ : \
(((P) >= 62 && (P) <= 69) ? &PORTK : &PORTL))))))))))
#define __digitalPinToDDRReg(P) \
(((P) >= 22 && (P) <= 29) ? &DDRA : \
((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &DDRB : \
(((P) >= 30 && (P) <= 37) ? &DDRC : \
((((P) >= 18 && (P) <= 21) || (P) == 38) ? &DDRD : \
((((P) >= 0 && (P) <= 3) || (P) == 5) ? &DDRE : \
(((P) >= 54 && (P) <= 61) ? &DDRF : \
((((P) >= 39 && (P) <= 41) || (P) == 4) ? &DDRG : \
((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &DDRH : \
(((P) == 14 || (P) == 15) ? &DDRJ : \
(((P) >= 62 && (P) <= 69) ? &DDRK : &DDRL))))))))))
#define __digitalPinToPINReg(P) \
(((P) >= 22 && (P) <= 29) ? &PINA : \
((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PINB : \
(((P) >= 30 && (P) <= 37) ? &PINC : \
((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PIND : \
((((P) >= 0 && (P) <= 3) || (P) == 5) ? &PINE : \
(((P) >= 54 && (P) <= 61) ? &PINF : \
((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PING : \
((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PINH : \
(((P) == 14 || (P) == 15) ? &PINJ : \
(((P) >= 62 && (P) <= 69) ? &PINK : &PINL))))))))))
#define __digitalPinToBit(P) \
(((P) >= 7 && (P) <= 9) ? (P) - 3 : \
(((P) >= 10 && (P) <= 13) ? (P) - 6 : \
(((P) >= 22 && (P) <= 29) ? (P) - 22 : \
(((P) >= 30 && (P) <= 37) ? 37 - (P) : \
(((P) >= 39 && (P) <= 41) ? 41 - (P) : \
(((P) >= 42 && (P) <= 49) ? 49 - (P) : \
(((P) >= 50 && (P) <= 53) ? 53 - (P) : \
(((P) >= 54 && (P) <= 61) ? (P) - 54 : \
(((P) >= 62 && (P) <= 69) ? (P) - 62 : \
(((P) == 0 || (P) == 15 || (P) == 17 || (P) == 21) ? 0 : \
(((P) == 1 || (P) == 14 || (P) == 16 || (P) == 20) ? 1 : \
(((P) == 19) ? 2 : \
(((P) == 5 || (P) == 6 || (P) == 18) ? 3 : \
(((P) == 2) ? 4 : \
(((P) == 3 || (P) == 4) ? 5 : 7)))))))))))))))
// --- Other ---
#else
#define SPI_HW_SS_PIN SS
#define SPI_HW_MOSI_PIN MOSI
#define SPI_HW_MISO_PIN MISO
#define SPI_HW_SCK_PIN SCK
#endif
//#endif //#ifndef digitalPinToPortReg
//ref: http://forum.arduino.cc/index.php?topic=140409.msg1054868#msg1054868
//void OutputsErrorIfCalled( void ) __attribute__ (( error( "Line: "__line__ "Variable used for digitalWriteFast") ));
void NonConstantUsed( void ) __attribute__ (( error("") ));
#ifndef digitalWriteFast
#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR))
#define digitalWriteFast(P, V) \
if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
BIT_WRITE(*__digitalPinToPortReg(P), __digitalPinToBit(P), (V)); \
} else { \
NonConstantUsed(); \
}
#else
#define digitalWriteFast digitalWrite
#endif
#endif
#ifndef pinModeFast
#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR))
#define pinModeFast(P, V) \
if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
BIT_WRITE(*__digitalPinToDDRReg(P), __digitalPinToBit(P), (V)); \
} else { \
NonConstantUsed(); \
}
#else
#define pinModeFast pinMode
#endif
#endif
#ifndef digitalReadFast
#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR))
#define digitalReadFast(P) ( (byte) __digitalReadFast((P)) )
#define __digitalReadFast(P ) \
(__builtin_constant_p(P) ) ? ( \
( BIT_READ(*__digitalPinToPINReg(P), __digitalPinToBit(P))) ) : \
ERROR_SEQUENCE
#else
#define digitalReadFast digitalRead
#endif
#endif
#endif //__digitalWriteFast_h_

View File

@ -136,10 +136,10 @@ class FastCRC32
{
public:
FastCRC32();
uint32_t crc32(const uint8_t *data, const uint16_t datalen); // Alias CRC-32/ADCCP, PKZIP, Ethernet, 802.3
uint32_t crc32(const uint8_t *data, const uint16_t datalen, bool reflect=true); // Alias CRC-32/ADCCP, PKZIP, Ethernet, 802.3
uint32_t cksum(const uint8_t *data, const uint16_t datalen); // Alias CRC-32/POSIX
uint32_t crc32_upd(const uint8_t *data, uint16_t len); // Call for subsequent calculations with previous seed
uint32_t crc32_upd(const uint8_t *data, uint16_t len, bool reflect=true); // Call for subsequent calculations with previous seed
uint32_t cksum_upd(const uint8_t *data, uint16_t len); // Call for subsequent calculations with previous seed
#if !CRC_SW
uint32_t generic(const uint32_t polyom, const uint32_t seed, const uint32_t flags, const uint8_t *data, const uint16_t datalen); //Not available in non-hw-variant (not T3.x)

View File

@ -410,7 +410,7 @@ FastCRC32::FastCRC32(){}
#define CRC_TABLE_CRC32 crc_table_crc32
#endif
uint32_t FastCRC32::crc32_upd(const uint8_t *data, uint16_t len)
uint32_t FastCRC32::crc32_upd(const uint8_t *data, uint16_t len, bool reflect)
{
uint32_t crc = seed;
@ -440,17 +440,18 @@ uint32_t FastCRC32::crc32_upd(const uint8_t *data, uint16_t len)
crc = (crc >> 8) ^ pgm_read_dword(&CRC_TABLE_CRC32[(crc & 0xff) ^ *data++]);
}
crc = ~crc;
if(reflect) { crc = ~crc; }
seed = crc;
return crc;
}
uint32_t FastCRC32::crc32(const uint8_t *data, const uint16_t datalen)
uint32_t FastCRC32::crc32(const uint8_t *data, const uint16_t datalen, bool reflect)
{
// poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xcbf43926
seed = 0xffffffff;
return crc32_upd(data, datalen);
return crc32_upd(data, datalen, reflect);
}
/** CKSUM

View File

@ -0,0 +1,415 @@
/*
Copyright (c) 2017 Daniel Fekete
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.
*/
#include "HardwareTimer.h"
//#include CHIP_PERIPHERAL_INCLUDE
HardwareTimer *interruptTimers[18];
void (*pwm_callback_func)();
static void handleInterrupt(HardwareTimer *timer);
static const uint32_t OCMODE_NOT_USED = 0xFFFF;
HardwareTimer::HardwareTimer(TIM_TypeDef* timer) {
// this->tim_pin_list = pin_list;
// this->tim_pin_list_size = pin_list_size;
_timer.timer = timer;
//handle.Instance = timer;
// for(int i=0; i<4; i++) {
// channelOC[i].OCMode = OCMODE_NOT_USED;
// channelOC[i].OCPolarity = TIM_OCPOLARITY_HIGH;
// channelOC[i].OCNPolarity = TIM_OCNPOLARITY_HIGH;
// channelOC[i].OCFastMode = TIM_OCFAST_DISABLE;
// channelOC[i].OCIdleState = TIM_OCIDLESTATE_RESET;
// channelOC[i].OCNIdleState = TIM_OCNIDLESTATE_RESET;
// channelIC[i].ICPolarity = OCMODE_NOT_USED;
// channelIC[i].ICSelection = TIM_ICSELECTION_DIRECTTI;
// channelIC[i].ICPrescaler = TIM_ICPSC_DIV1;
// channelIC[i].ICFilter = 0;
// }
}
void HardwareTimer::pause() {
HAL_TIM_Base_Stop(&(_timer.handle));
}
void HardwareTimer::resume() {
HAL_TIM_Base_MspInit(_timer)
handle.Init.CounterMode = TIM_COUNTERMODE_UP;
handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
handle.Init.RepetitionCounter = 0;
HAL_TIM_Base_Init(&handle);
if (callbacks[0] != NULL) {
HAL_TIM_Base_Start_IT(&handle);
} else {
HAL_TIM_Base_Start(&handle);
}
resumeChannel(1, TIM_CHANNEL_1);
resumeChannel(2, TIM_CHANNEL_2);
resumeChannel(3, TIM_CHANNEL_3);
resumeChannel(4, TIM_CHANNEL_4);
}
void HardwareTimer::resumeChannel(int channel, int timChannel) {
if (channelOC[channel - 1].OCMode != OCMODE_NOT_USED) {
HAL_TIM_OC_ConfigChannel(&handle, &channelOC[channel - 1], timChannel);
if (callbacks[channel] != NULL) {
HAL_TIM_OC_Start_IT(&handle, timChannel);
} else {
HAL_TIM_OC_Start(&handle, timChannel);
}
}
if (channelIC[channel - 1].ICPolarity != OCMODE_NOT_USED) {
HAL_TIM_IC_ConfigChannel(&handle, &channelIC[channel - 1], timChannel);
if (callbacks[channel] != NULL) {
HAL_TIM_IC_Start_IT(&handle, timChannel);
} else {
HAL_TIM_IC_Start(&handle, timChannel);
}
}
}
uint32_t HardwareTimer::getPrescaleFactor() {
return handle.Init.Prescaler;
}
void HardwareTimer::setPrescaleFactor(uint32_t prescaler) {
handle.Init.Prescaler = prescaler;
}
uint32_t HardwareTimer::getOverflow() {
return handle.Init.Period;
}
void HardwareTimer::setOverflow(uint32_t overflow) {
handle.Init.Period = overflow;
}
uint32_t HardwareTimer::getCount(void) {
return __HAL_TIM_GET_COUNTER(&handle);
}
void HardwareTimer::setCount(uint32_t counter) {
__HAL_TIM_SET_COUNTER(&handle, counter);
}
#define MAX_RELOAD ((1 << 16) - 1) // Not always! 32 bit timers!
uint32_t HardwareTimer::getBaseFrequency() {
int freqMul = 2;
int freq2Mul = 2;
uint32_t pFreq = HAL_RCC_GetPCLK1Freq();
#ifdef STM32F1
freq2Mul = 1;
#endif
#ifdef TIM1
if (handle.Instance == TIM1) {
pFreq = HAL_RCC_GetPCLK2Freq();
freqMul = freq2Mul;
}
#endif
#ifdef TIM8
if (handle.Instance == TIM8) {
pFreq = HAL_RCC_GetPCLK2Freq();
freqMul = freq2Mul;
}
#endif
#ifdef TIM9
if (handle.Instance == TIM9) {
pFreq = HAL_RCC_GetPCLK2Freq();
freqMul = freq2Mul;
}
#endif
#ifdef TIM10
if (handle.Instance == TIM10) {
pFreq = HAL_RCC_GetPCLK2Freq();
freqMul = freq2Mul;
}
#endif
#ifdef TIM11
if (handle.Instance == TIM11) {
pFreq = HAL_RCC_GetPCLK2Freq();
freqMul = freq2Mul;
}
#endif
return pFreq * freqMul;
}
uint32_t HardwareTimer::setPeriod(uint32_t microseconds) {
if (!microseconds) {
this->setPrescaleFactor(1);
this->setOverflow(1);
return this->getOverflow();
}
uint32_t period_cyc = microseconds * (getBaseFrequency() / 1000000); //TODO!
uint32_t prescaler = (uint32_t)(period_cyc / MAX_RELOAD + 1);
uint32_t overflow = (uint32_t)((period_cyc + (prescaler / 2)) / prescaler);
this->setPrescaleFactor(prescaler);
this->setOverflow(overflow);
return overflow;
}
static bool isSameChannel(int channel, uint8_t signal) {
// switch(signal) {
// case TIM_CH1:
// case TIM_CH1N:
// return channel == 1;
// case TIM_CH2:
// case TIM_CH2N:
// return channel == 2;
// case TIM_CH3:
// case TIM_CH3N:
// return channel == 3;
// case TIM_CH4:
// case TIM_CH4N:
// return channel == 4;
// }
return false;
}
static const uint32_t PIN_NOT_USED = 0xFF;
void HardwareTimer::setMode(int channel, TIMER_MODES mode, uint8_t pin) {
int pinMode = PIN_NOT_USED;
int pull = GPIO_NOPULL;
switch(mode) {
case TIMER_PWM:
channelOC[channel - 1].OCMode = TIM_OCMODE_PWM1;
pinMode = GPIO_MODE_AF_PP;
break;
case TIMER_OUTPUT_COMPARE:
channelOC[channel - 1].OCMode = TIM_OCMODE_TIMING;
break;
case TIMER_OUTPUT_COMPARE_ACTIVE:
channelOC[channel - 1].OCMode = TIM_OCMODE_ACTIVE;
pinMode = GPIO_MODE_AF_PP;
break;
case TIMER_OUTPUT_COMPARE_INACTIVE:
channelOC[channel - 1].OCMode = TIM_OCMODE_ACTIVE;
pinMode = GPIO_MODE_AF_PP;
break;
case TIMER_OUTPUT_COMPARE_TOGGLE:
channelOC[channel - 1].OCMode = TIM_OCMODE_TOGGLE;
pinMode = GPIO_MODE_AF_PP;
break;
case TIMER_OUTPUT_COMPARE_PWM1:
channelOC[channel - 1].OCMode = TIM_OCMODE_PWM1;
pinMode = GPIO_MODE_AF_PP;
break;
case TIMER_OUTPUT_COMPARE_PWM2:
channelOC[channel - 1].OCMode = TIM_OCMODE_PWM2;
pinMode = GPIO_MODE_AF_PP;
break;
case TIMER_OUTPUT_COMPARE_FORCED_ACTIVE:
channelOC[channel - 1].OCMode = TIM_OCMODE_FORCED_ACTIVE;
pinMode = GPIO_MODE_AF_PP;
break;
case TIMER_OUTPUT_COMPARE_FORCED_INACTIVE:
channelOC[channel - 1].OCMode = TIM_OCMODE_FORCED_INACTIVE;
pinMode = GPIO_MODE_AF_PP;
break;
case TIMER_INPUT_CAPTURE_RISING:
channelIC[channel - 1].ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
pinMode = GPIO_MODE_AF_PP;
pull = GPIO_PULLDOWN;
break;
case TIMER_INPUT_CAPTURE_FALLING:
channelIC[channel - 1].ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
pinMode = GPIO_MODE_AF_PP;
pull = GPIO_PULLDOWN;
break;
}
if (pinMode != PIN_NOT_USED) {
//for(int i=0; i<tim_pin_list_size; i++) {
//if (isSameChannel(channel, tim_pin_list[i].signalType)) {
// if (pin == TIMER_DEFAULT_PIN ||
// (variant_pin_list[pin].port == tim_pin_list[i].port && variant_pin_list[pin].pin_mask == tim_pin_list[i].pinMask)) {
// stm32GpioClockEnable(tim_pin_list[i].port);
// GPIO_InitTypeDef GPIO_InitStruct;
// GPIO_InitStruct.Pin = tim_pin_list[i].pinMask;
// GPIO_InitStruct.Mode = pinMode;
// GPIO_InitStruct.Pull = pull;
// GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
// #ifdef STM32F1
// tim_pin_list[i].alternate();
// #else
// GPIO_InitStruct.Alternate = tim_pin_list[i].alternate;
// #endif
// HAL_GPIO_Init(tim_pin_list[i].port, &GPIO_InitStruct);
// return;
// }
//}
//}
}
}
void HardwareTimer::setCompare(int channel, uint32_t compare) {
channelOC[channel - 1].Pulse = compare;
}
uint32_t HardwareTimer::getCompare(int channel) {
if (channel == 1) return __HAL_TIM_GET_COMPARE(&handle, TIM_CHANNEL_1);
if (channel == 2) return __HAL_TIM_GET_COMPARE(&handle, TIM_CHANNEL_2);
if (channel == 3) return __HAL_TIM_GET_COMPARE(&handle, TIM_CHANNEL_3);
if (channel == 4) return __HAL_TIM_GET_COMPARE(&handle, TIM_CHANNEL_4);
return 0;
}
void HardwareTimer::attachInterrupt(void (*callback)(void)) {
callbacks[0] = callback;
}
void HardwareTimer::detachInterrupt() {
callbacks[0] = NULL;
}
void HardwareTimer::attachInterrupt(int channel, void (*callback)(void)) {
callbacks[channel] = callback;
}
void HardwareTimer::detachInterrupt(int channel) {
callbacks[channel] = NULL;
}
void HardwareTimer::refresh() {
HAL_TIM_GenerateEvent(&handle, TIM_EVENTSOURCE_UPDATE);
}
static void handleInterrupt(HardwareTimer *timer) {
if(__HAL_TIM_GET_FLAG(&timer->handle, TIM_FLAG_UPDATE) != RESET) {
__HAL_TIM_CLEAR_IT(&timer->handle, TIM_IT_UPDATE);
if (timer->callbacks[0] != NULL) timer->callbacks[0]();
}
if(__HAL_TIM_GET_FLAG(&timer->handle, TIM_FLAG_CC1) != RESET) {
__HAL_TIM_CLEAR_IT(&timer->handle, TIM_IT_CC1);
if (timer->callbacks[1] != NULL) timer->callbacks[1]();
}
if(__HAL_TIM_GET_FLAG(&timer->handle, TIM_FLAG_CC2) != RESET) {
__HAL_TIM_CLEAR_IT(&timer->handle, TIM_IT_CC2);
if (timer->callbacks[2] != NULL) timer->callbacks[2]();
}
if(__HAL_TIM_GET_FLAG(&timer->handle, TIM_FLAG_CC3) != RESET) {
__HAL_TIM_CLEAR_IT(&timer->handle, TIM_IT_CC3);
if (timer->callbacks[3] != NULL) timer->callbacks[3]();
}
if(__HAL_TIM_GET_FLAG(&timer->handle, TIM_FLAG_CC4) != RESET) {
__HAL_TIM_CLEAR_IT(&timer->handle, TIM_IT_CC4);
if (timer->callbacks[4] != NULL) timer->callbacks[4]();
}
}
#ifdef TIM1
HardwareTimer Timer1(TIM1);
#endif
#ifdef TIM2
HardwareTimer Timer2(TIM2);
#endif
#ifdef TIM3
HardwareTimer Timer3(TIM3);
#endif
#ifdef TIM4
HardwareTimer Timer4(TIM4);
#endif
#ifdef TIM5
HardwareTimer Timer5(TIM5);
#endif
#ifdef TIM8
HardwareTimer Timer8(TIM8);
#endif
//Timer interrupts:
// extern "C" void TIM1_CC_IRQHandler(void) {
// if (interruptTimers[0] != NULL) handleInterrupt(interruptTimers[0]);
// }
// extern "C" void TIM1_UP_IRQHandler(void) {
// if (interruptTimers[0] != NULL) handleInterrupt(interruptTimers[0]);
// }
// #ifndef TIM1_UP_TIM10_IRQHandler
// extern "C" void TIM1_UP_TIM10_IRQHandler(void) {
// if (interruptTimers[0] != NULL) handleInterrupt(interruptTimers[0]);
// if (interruptTimers[9] != NULL) handleInterrupt(interruptTimers[9]);
// }
// #endif
// in stm32_PWM.c
/*
extern "C" void TIM2_IRQHandler(void) {
handleInterrupt(interruptTimers[1]);
}*/
// extern "C" void TIM3_IRQHandler(void) {
// if (interruptTimers[2] != NULL) handleInterrupt(interruptTimers[2]);
// }
// extern "C" void TIM4_IRQHandler(void) {
// if (interruptTimers[3] != NULL) handleInterrupt(interruptTimers[3]);
// }

View File

@ -0,0 +1,123 @@
#ifndef HARDWARETIMER_H_
#define HARDWARETIMER_H_
#include "Arduino.h"
#include "timer.h"
//#include "stm32_gpio_af.h"
typedef enum {
//libmaple: // HAL compatible
TIMER_DISABLED,
TIMER_PWM, // == TIM_OCMODE_PWM1
TIMER_OUTPUT_COMPARE, // == TIM_OCMODE_TIMING no output, useful for only-interrupt
//other:
TIMER_OUTPUT_COMPARE_ACTIVE, // == TIM_OCMODE_ACTIVE pin is set high when counter == channel compare
TIMER_OUTPUT_COMPARE_INACTIVE, // == TIM_OCMODE_INACTIVE pin is set low when counter == channel compare
TIMER_OUTPUT_COMPARE_TOGGLE, // == TIM_OCMODE_TOGGLE pin toggles when counter == channel compare
TIMER_OUTPUT_COMPARE_PWM1, // == TIM_OCMODE_PWM1 pin high when counter < channel compare, low otherwise
TIMER_OUTPUT_COMPARE_PWM2, // == TIM_OCMODE_PWM2 pin low when counter < channel compare, high otherwise
TIMER_OUTPUT_COMPARE_FORCED_ACTIVE, // == TIM_OCMODE_FORCED_ACTIVE pin always high
TIMER_OUTPUT_COMPARE_FORCED_INACTIVE, // == TIM_OCMODE_FORCED_INACTIVE pin always low
//Input capture
TIMER_INPUT_CAPTURE_RISING, // == TIM_INPUTCHANNELPOLARITY_RISING
TIMER_INPUT_CAPTURE_FALLING, // == TIM_INPUTCHANNELPOLARITY_FALLING
//PWM input capture on channel 1 + channel 2
//TIMER_INPUT_CAPTURE_PWM, // == TIM_INPUTCHANNELPOLARITY_RISING (channel 1) + TIM_INPUTCHANNELPOLARITY_FALLING (channel 2)
//Encoder mode
//TIMER_ENCODER // == TIM_ENCODERMODE_TI1
} TIMER_MODES;
#define TIMER_DEFAULT_PIN 0xFF
class HardwareTimer {
public:
HardwareTimer(TIM_TypeDef* timer);
void pause(void);
void resume(void);
uint32_t getPrescaleFactor();
void setPrescaleFactor(uint32_t factor);
uint32_t getOverflow();
void setOverflow(uint32_t val);
uint32_t getCount(void);
void setCount(uint32_t val);
uint32_t setPeriod(uint32_t microseconds);
void setMode(int channel, TIMER_MODES mode, uint8_t pin = TIMER_DEFAULT_PIN);
uint32_t getCompare(int channel);
void setCompare(int channel, uint32_t compare);
//Add interrupt to period update
void attachInterrupt(void (*handler)(void));
void detachInterrupt();
//Add interrupt to channel
void attachInterrupt(int channel, void (*handler)(void));
void detachInterrupt(int channel);
void refresh(void);
uint32_t getBaseFrequency();
TIM_HandleTypeDef handle = {0};
TIM_OC_InitTypeDef channelOC[4];
TIM_IC_InitTypeDef channelIC[4];
//Callbacks: 0 for update, 1-4 for channels
void (*callbacks[5])(void);
stimer_t _timer;
// //const stm32_tim_pin_list_type *tim_pin_list;
// int tim_pin_list_size;
private:
void resumeChannel(int channel, int timChannel);
};
#ifdef TIM1
extern HardwareTimer Timer1;
#endif
#ifdef TIM2
extern HardwareTimer Timer2;
#endif
#ifdef TIM3
extern HardwareTimer Timer3;
#endif
#ifdef TIM4
extern HardwareTimer Timer4;
#endif
#ifdef TIM5
extern HardwareTimer Timer5;
#endif
#ifdef TIM8
extern HardwareTimer Timer8;
#endif
#endif

View File

@ -0,0 +1,304 @@
/* Arduino SPIMemory Library v.2.6.0
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 30/09/2016
* Modified by Prajwal Bhattaram - 14/04/2017
* Original code from @manitou48 <https://github.com/manitou48/DUEZoo/blob/master/dmaspi.ino>
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if defined (ARDUINO_ARCH_SAM)
#include "SPIMemory.h"
// Constructor
//DMASAM::DMASAM(){}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Private functions used by Arduino Due DMA operations //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Disable DMA Controller
void DMASAM::_dmac_disable() {
DMAC->DMAC_EN &= (~DMAC_EN_ENABLE);
}
// Enable DMA Controller.
void DMASAM::_dmac_enable() {
DMAC->DMAC_EN = DMAC_EN_ENABLE;
}
// Disable DMA Channel
void DMASAM::_dmac_channel_disable(uint32_t ul_num) {
DMAC->DMAC_CHDR = DMAC_CHDR_DIS0 << ul_num;
}
// Enable DMA Channel
void DMASAM::_dmac_channel_enable(uint32_t ul_num) {
DMAC->DMAC_CHER = DMAC_CHER_ENA0 << ul_num;
}
// Poll for transfer complete
bool DMASAM::_dmac_channel_transfer_done(uint32_t ul_num) {
return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true;
}
// start RX DMA
void DMASAM::SPIDmaRX(uint8_t* dst, uint16_t count) {
_dmac_channel_disable(SPI_DMAC_RX_CH);
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_SADDR = (uint32_t)&SPI0->SPI_RDR;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DADDR = (uint32_t)dst;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DSCR = 0;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLA = count |
DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR |
DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC |
DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CFG = DMAC_CFG_SRC_PER(SPI_RX_IDX) |
DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG;
_dmac_channel_enable(SPI_DMAC_RX_CH);
}
void DMASAM::SPIDmaRX(char* dst, uint16_t count) {
_dmac_channel_disable(SPI_DMAC_RX_CH);
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_SADDR = (uint32_t)&SPI0->SPI_RDR;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DADDR = (uint32_t)dst;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DSCR = 0;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLA = count |
DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR |
DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC |
DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING;
DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CFG = DMAC_CFG_SRC_PER(SPI_RX_IDX) |
DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG;
_dmac_channel_enable(SPI_DMAC_RX_CH);
}
// start TX DMA
void DMASAM::SPIDmaTX(const uint8_t* src, uint16_t count) {
static uint8_t ff = 0XFF;
uint32_t src_incr = DMAC_CTRLB_SRC_INCR_INCREMENTING;
if (!src) {
src = &ff;
src_incr = DMAC_CTRLB_SRC_INCR_FIXED;
}
_dmac_channel_disable(SPI_DMAC_TX_CH);
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_SADDR = (uint32_t)src;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DADDR = (uint32_t)&SPI0->SPI_TDR;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DSCR = 0;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLA = count |
DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR |
DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC |
src_incr | DMAC_CTRLB_DST_INCR_FIXED;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CFG = DMAC_CFG_DST_PER(SPI_TX_IDX) |
DMAC_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG;
_dmac_channel_enable(SPI_DMAC_TX_CH);
}
void DMASAM::SPIDmaCharTX(const char* src, uint16_t count) {
static char ff = 0XFF;
uint32_t src_incr = DMAC_CTRLB_SRC_INCR_INCREMENTING;
if (!src) {
src = &ff;
src_incr = DMAC_CTRLB_SRC_INCR_FIXED;
}
_dmac_channel_disable(SPI_DMAC_TX_CH);
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_SADDR = (uint32_t)src;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DADDR = (uint32_t)&SPI0->SPI_TDR;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DSCR = 0;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLA = count |
DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR |
DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC |
src_incr | DMAC_CTRLB_DST_INCR_FIXED;
DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CFG = DMAC_CFG_DST_PER(SPI_TX_IDX) |
DMAC_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG;
_dmac_channel_enable(SPI_DMAC_TX_CH);
}
void DMASAM::SPIBegin() {
PIO_Configure(
g_APinDescription[PIN_SPI_MOSI].pPort,
g_APinDescription[PIN_SPI_MOSI].ulPinType,
g_APinDescription[PIN_SPI_MOSI].ulPin,
g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration);
PIO_Configure(
g_APinDescription[PIN_SPI_MISO].pPort,
g_APinDescription[PIN_SPI_MISO].ulPinType,
g_APinDescription[PIN_SPI_MISO].ulPin,
g_APinDescription[PIN_SPI_MISO].ulPinConfiguration);
PIO_Configure(
g_APinDescription[PIN_SPI_SCK].pPort,
g_APinDescription[PIN_SPI_SCK].ulPinType,
g_APinDescription[PIN_SPI_SCK].ulPin,
g_APinDescription[PIN_SPI_SCK].ulPinConfiguration);
pmc_enable_periph_clk(ID_SPI0);
#if USE_SAM3X_DMAC
pmc_enable_periph_clk(ID_DMAC);
_dmac_disable();
DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED;
_dmac_enable();
#if USE_SAM3X_BUS_MATRIX_FIX
MATRIX->MATRIX_WPMR = 0x4d415400;
MATRIX->MATRIX_MCFG[1] = 1;
MATRIX->MATRIX_MCFG[2] = 1;
MATRIX->MATRIX_SCFG[0] = 0x01000010;
MATRIX->MATRIX_SCFG[1] = 0x01000010;
MATRIX->MATRIX_SCFG[7] = 0x01000010;
#endif // USE_SAM3X_BUS_MATRIX_FIX
#endif // USE_SAM3X_DMAC
}
// initialize SPI controller
void DMASAM::SPIInit(uint8_t dueSckDivisor) {
#if ENABLE_SPI_TRANSACTIONS
SPI.beginTransaction(SPISettings());
#endif // ENABLE_SPI_TRANSACTIONS
uint8_t scbr = dueSckDivisor;
Spi* pSpi = SPI0;
// disable SPI
pSpi->SPI_CR = SPI_CR_SPIDIS;
// reset SPI
pSpi->SPI_CR = SPI_CR_SWRST;
// no mode fault detection, set master mode
pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR;
// mode 0, 8-bit,
pSpi->SPI_CSR[SPI_CHIP_SEL] = SPI_CSR_SCBR(scbr) | SPI_CSR_NCPHA;
// enable SPI
pSpi->SPI_CR |= SPI_CR_SPIEN;
}
uint8_t DMASAM::SPITransfer(uint8_t b) {
Spi* pSpi = SPI0;
pSpi->SPI_TDR = b;
while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {}
b = pSpi->SPI_RDR;
return b;
}
// SPI receive a byte
uint8_t DMASAM::SPIRecByte() {
return SPITransfer(0XFF);
}
// SPI receive multiple bytes
uint8_t DMASAM::SPIRecByte(uint8_t* buf, size_t len) {
Spi* pSpi = SPI0;
int rtn = 0;
#if USE_SAM3X_DMAC
// clear overrun error
uint32_t s = pSpi->SPI_SR;
SPIDmaRX(buf, len);
SPIDmaTX(0, len);
uint32_t m = millis();
while (!_dmac_channel_transfer_done(SPI_DMAC_RX_CH)) {
if ((millis() - m) > SAM3X_DMA_TIMEOUT) {
_dmac_channel_disable(SPI_DMAC_RX_CH);
_dmac_channel_disable(SPI_DMAC_TX_CH);
rtn = 2;
break;
}
}
if (pSpi->SPI_SR & SPI_SR_OVRES) rtn |= 1;
#else // USE_SAM3X_DMAC
for (size_t i = 0; i < len; i++) {
pSpi->SPI_TDR = 0XFF;
while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {}
buf[i] = pSpi->SPI_RDR;
}
#endif // USE_SAM3X_DMAC
return rtn;
}
// SPI receive a char
int8_t DMASAM::SPIRecChar() {
return SPITransfer(0XFF);
}
// SPI receive multiple chars
int8_t DMASAM::SPIRecChar(char* buf, size_t len) {
Spi* pSpi = SPI0;
char rtn = 0;
#if USE_SAM3X_DMAC
// clear overrun error
uint32_t s = pSpi->SPI_SR;
SPIDmaRX(buf, len);
SPIDmaTX(0, len);
uint32_t m = millis();
while (!_dmac_channel_transfer_done(SPI_DMAC_RX_CH)) {
if ((millis() - m) > SAM3X_DMA_TIMEOUT) {
_dmac_channel_disable(SPI_DMAC_RX_CH);
_dmac_channel_disable(SPI_DMAC_TX_CH);
rtn = 2;
break;
}
}
if (pSpi->SPI_SR & SPI_SR_OVRES) rtn |= 1;
#else // USE_SAM3X_DMAC
for (size_t i = 0; i < len; i++) {
pSpi->SPI_TDR = 0XFF;
while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {}
buf[i] = pSpi->SPI_RDR;
}
#endif // USE_SAM3X_DMAC
return rtn;
}
// SPI send a byte
void DMASAM::SPISendByte(uint8_t b) {
SPITransfer(b);
}
void DMASAM::SPISendByte(const uint8_t* buf, size_t len) {
Spi* pSpi = SPI0;
#if USE_SAM3X_DMAC
SPIDmaTX(buf, len);
while (!_dmac_channel_transfer_done(SPI_DMAC_TX_CH)) {}
#else // #if USE_SAM3X_DMAC
while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {}
for (size_t i = 0; i < len; i++) {
pSpi->SPI_TDR = buf[i];
while ((pSpi->SPI_SR & SPI_SR_TDRE) == 0) {}
}
#endif // #if USE_SAM3X_DMAC
while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {}
// leave RDR empty
uint8_t b = pSpi->SPI_RDR;
}
// SPI send a char
void DMASAM::SPISendChar(char b) {
SPITransfer(b);
}
//SPI send multiple chars
void DMASAM::SPISendChar(const char* buf, size_t len) {
Spi* pSpi = SPI0;
#if USE_SAM3X_DMAC
SPIDmaCharTX(buf, len);
while (!_dmac_channel_transfer_done(SPI_DMAC_TX_CH)) {}
#else // #if USE_SAM3X_DMAC
while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {}
for (size_t i = 0; i < len; i++) {
pSpi->SPI_TDR = buf[i];
while ((pSpi->SPI_SR & SPI_SR_TDRE) == 0) {}
}
#endif // #if USE_SAM3X_DMAC
while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {}
// leave RDR empty
char b = pSpi->SPI_RDR;
}
DMASAM due; // default instantiation of DMASAM object
#endif

View File

@ -0,0 +1,59 @@
/* Arduino SPIMemory Library v.3.2.0
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 19/04/2018
* Modified by Prajwal Bhattaram - 20/04/2018
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef SAM_DMASPI_H
#define SAM_DMASPI_H
#include "SPIMemory.h"
class DMASAM {
public:
DMASAM(void){};
~DMASAM(void){};
void SPIDmaRX(uint8_t* dst, uint16_t count);
void SPIDmaRX(char* dst, uint16_t count);
void SPIDmaTX(const uint8_t* src, uint16_t count);
void SPIDmaCharTX(const char* src, uint16_t count);
void SPIBegin(void);
void SPIInit(uint8_t dueSckDivisor);
uint8_t SPITransfer(uint8_t b);
uint8_t SPIRecByte(void);
uint8_t SPIRecByte(uint8_t* buf, size_t len);
int8_t SPIRecChar(void);
int8_t SPIRecChar(char* buf, size_t len);
void SPISendByte(uint8_t b);
void SPISendByte(const uint8_t* buf, size_t len);
void SPISendChar(char b);
void SPISendChar(const char* buf, size_t len);
private:
void _dmac_disable(void);
void _dmac_enable(void);
void _dmac_channel_disable(uint32_t ul_num);
void _dmac_channel_enable(uint32_t ul_num);
bool _dmac_channel_transfer_done(uint32_t ul_num);
};
extern DMASAM due; ///< default DMASAM instance
#endif

View File

@ -0,0 +1,140 @@
// DMA memory to memory ZERO
// ch 18 beat burst block
// xdk sam0/drivers/dma/dma.c
// packages/arduino/tools/CMSIS/4.0.0-atmel/Device/ATMEL/samd21/include/component/dmac.h
// http://asf.atmel.com/docs/3.16.0/samd21/html/asfdoc_sam0_sercom_spi_dma_use_case.html
// assume normal SPI setup, then we take over with DMA
#ifdef ARDUINO_ARCH_SAMD
#include <SPI.h>
#define PRREG(x) Serial.print(#x" 0x"); Serial.println(x,HEX)
#define BYTES 1024
char txbuf[BYTES], rxbuf[BYTES];
/*void prmbs(char *lbl,unsigned long us,int bits) {
float mbs = (float)bits/us;
Serial.print(mbs,2); Serial.print(" mbs ");
Serial.print(us); Serial.print(" us ");
Serial.println(lbl);
}*/
// DMA 12 channels
typedef struct {
uint16_t btctrl;
uint16_t btcnt;
uint32_t srcaddr;
uint32_t dstaddr;
uint32_t descaddr;
} dmacdescriptor ;
volatile dmacdescriptor wrb[12] __attribute__ ((aligned (16)));
dmacdescriptor descriptor_section[12] __attribute__ ((aligned (16)));
dmacdescriptor descriptor __attribute__ ((aligned (16)));
static uint32_t chnltx = 0, chnlrx = 1; // DMA channels
enum XfrType { DoTX, DoRX, DoTXRX};
static XfrType xtype;
static uint8_t rxsink[1], txsrc[1] = {0xFF};
volatile uint32_t dmadone;
void DMAC_Handler() {
// interrupts DMAC_CHINTENCLR_TERR DMAC_CHINTENCLR_TCMPL DMAC_CHINTENCLR_SUSP
uint8_t active_channel;
// disable irqs ?
__disable_irq();
active_channel = DMAC->INTPEND.reg & DMAC_INTPEND_ID_Msk; // get channel number
DMAC->CHID.reg = DMAC_CHID_ID(active_channel);
dmadone = DMAC->CHINTFLAG.reg;
DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL; // clear
DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TERR;
DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_SUSP;
__enable_irq();
}
void dma_init() {
// probably on by default
PM->AHBMASK.reg |= PM_AHBMASK_DMAC ;
PM->APBBMASK.reg |= PM_APBBMASK_DMAC ;
NVIC_EnableIRQ( DMAC_IRQn ) ;
DMAC->BASEADDR.reg = (uint32_t)descriptor_section;
DMAC->WRBADDR.reg = (uint32_t)wrb;
DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);
}
Sercom *sercom = (Sercom *)SERCOM4; // SPI SERCOM
void spi_xfr(void *txdata, void *rxdata, size_t n) {
uint32_t temp_CHCTRLB_reg;
// set up transmit channel
DMAC->CHID.reg = DMAC_CHID_ID(chnltx);
DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << chnltx));
temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(0) |
DMAC_CHCTRLB_TRIGSRC(SERCOM4_DMAC_ID_TX) | DMAC_CHCTRLB_TRIGACT_BEAT;
DMAC->CHCTRLB.reg = temp_CHCTRLB_reg;
DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ; // enable all 3 interrupts
descriptor.descaddr = 0;
descriptor.dstaddr = (uint32_t) &sercom->SPI.DATA.reg;
descriptor.btcnt = n;
descriptor.srcaddr = (uint32_t)txdata;
descriptor.btctrl = DMAC_BTCTRL_VALID;
if (xtype != DoRX) {
descriptor.srcaddr += n;
descriptor.btctrl |= DMAC_BTCTRL_SRCINC;
}
memcpy(&descriptor_section[chnltx],&descriptor, sizeof(dmacdescriptor));
// rx channel enable interrupts
DMAC->CHID.reg = DMAC_CHID_ID(chnlrx);
DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << chnlrx));
temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(0) |
DMAC_CHCTRLB_TRIGSRC(SERCOM4_DMAC_ID_RX) | DMAC_CHCTRLB_TRIGACT_BEAT;
DMAC->CHCTRLB.reg = temp_CHCTRLB_reg;
DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ; // enable all 3 interrupts
dmadone = 0;
descriptor.descaddr = 0;
descriptor.srcaddr = (uint32_t) &sercom->SPI.DATA.reg;
descriptor.btcnt = n;
descriptor.dstaddr = (uint32_t)rxdata;
descriptor.btctrl = DMAC_BTCTRL_VALID;
if (xtype != DoTX) {
descriptor.dstaddr += n;
descriptor.btctrl |= DMAC_BTCTRL_DSTINC;
}
memcpy(&descriptor_section[chnlrx],&descriptor, sizeof(dmacdescriptor));
// start both channels ? order matter ?
DMAC->CHID.reg = DMAC_CHID_ID(chnltx);
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
DMAC->CHID.reg = DMAC_CHID_ID(chnlrx);
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
while(!dmadone); // await DMA done isr
DMAC->CHID.reg = DMAC_CHID_ID(chnltx); //disable DMA to allow lib SPI
DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
DMAC->CHID.reg = DMAC_CHID_ID(chnlrx);
DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
}
void spi_write(void *data, size_t n) {
xtype = DoTX;
spi_xfr(data,rxsink,n);
}
void spi_read(void *data, size_t n) {
xtype = DoRX;
spi_xfr(txsrc,data,n);
}
void spi_transfer(void *txdata, void *rxdata, size_t n) {
xtype = DoTXRX;
spi_xfr(txdata,rxdata,n);
}
#endif

View File

@ -0,0 +1,43 @@
/*
* This file implements a shim layer for the SPIMemory library (https://github.com/Marzogh/SPIMemory) to mimic a minimal
* subset of the standard Arduino EEPROM library
*/
#if defined(SPI_AS_EEPROM)
#include "SPIAsEEPROM.h"
#include "SPIMemory.h"
#include <SPI.h>
//SPIFlash flash;
SPIFlash flash(SS1, &SPI); //Use this constructor if using an SPI bus other than the default SPI. Only works with chips with more than one hardware SPI bus
SPIAsEEPROM::SPIAsEEPROM()
{
//Do some init stuff here
flash.begin();
//To use a custom flash memory size (if using memory from manufacturers not officially supported by the library) - declare a size variable according to the list in defines.h
//flash.begin(MB(1));
}
uint8_t SPIAsEEPROM::read(uint16_t address) {
uint8_t val = 0;
return val;
}
int8_t SPIAsEEPROM::write(uint16_t address, uint8_t val)
{
return 0;
}
int8_t SPIAsEEPROM::update(uint16_t address, uint8_t val)
{
return 0;
}
SPIAsEEPROM EEPROM;
#endif

View File

@ -0,0 +1,21 @@
#ifndef SPI_AS_EEPROM_H
#define SPI_AS_EEPROM_H
#if defined(SPI_AS_EEPROM)
#include <stdint.h>
class SPIAsEEPROM {
private:
public:
SPIAsEEPROM();
uint8_t read(uint16_t address);
int8_t write(uint16_t address, uint8_t val);
int8_t update(uint16_t address, uint8_t val);
};
extern SPIAsEEPROM EEPROM;
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,399 @@
/* Arduino SPIMemory Library v.3.2.1
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 19/05/2015
* Modified by @boseji <salearj@hotmail.com> - 02/03/2017
* Modified by Prajwal Bhattaram - 21/05/2018
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef SPIFLASH_H
#define SPIFLASH_H
#include "SPIMemory.h"
class SPIFlash {
public:
//------------------------------------ Constructor ------------------------------------//
//New Constructor to Accept the PinNames as a Chip select Parameter - @boseji <salearj@hotmail.com> 02.03.17
#if defined (ARDUINO_ARCH_SAMD) || defined(ARCH_STM32) || defined(CORE_STM32_OFFICIAL)
SPIFlash(uint8_t cs = CS, SPIClass *spiinterface=&SPI);
#elif defined (BOARD_RTL8195A)
SPIFlash(PinName cs = CS);
#else
SPIFlash(uint8_t cs = CS);
#endif
//----------------------------- Initial / Chip Functions ------------------------------//
bool begin(uint32_t flashChipSize = 0);
void setClock(uint32_t clockSpeed);
bool libver(uint8_t *b1, uint8_t *b2, uint8_t *b3);
bool sfdpPresent(void);
uint8_t error(bool verbosity = false);
uint16_t getManID(void);
uint32_t getJEDECID(void);
uint64_t getUniqueID(void);
uint32_t getAddress(uint16_t size);
uint16_t sizeofStr(String &inputStr);
uint32_t getCapacity(void);
uint32_t getMaxPage(void);
float functionRunTime(void);
//-------------------------------- Write / Read Bytes ---------------------------------//
bool writeByte(uint32_t _addr, uint8_t data, bool errorCheck = true);
uint8_t readByte(uint32_t _addr, bool fastRead = false);
//----------------------------- Write / Read Byte Arrays ------------------------------//
bool writeByteArray(uint32_t _addr, uint8_t *data_buffer, size_t bufferSize, bool errorCheck = true);
bool readByteArray(uint32_t _addr, uint8_t *data_buffer, size_t bufferSize, bool fastRead = false);
//-------------------------------- Write / Read Chars ---------------------------------//
bool writeChar(uint32_t _addr, int8_t data, bool errorCheck = true);
int8_t readChar(uint32_t _addr, bool fastRead = false);
//------------------------------ Write / Read Char Arrays -----------------------------//
bool writeCharArray(uint32_t _addr, char *data_buffer, size_t bufferSize, bool errorCheck = true);
bool readCharArray(uint32_t _addr, char *data_buffer, size_t buffer_size, bool fastRead = false);
//-------------------------------- Write / Read Shorts --------------------------------//
bool writeShort(uint32_t _addr, int16_t data, bool errorCheck = true);
int16_t readShort(uint32_t _addr, bool fastRead = false);
//-------------------------------- Write / Read Words ---------------------------------//
bool writeWord(uint32_t _addr, uint16_t data, bool errorCheck = true);
uint16_t readWord(uint32_t _addr, bool fastRead = false);
//-------------------------------- Write / Read Longs ---------------------------------//
bool writeLong(uint32_t _addr, int32_t data, bool errorCheck = true);
int32_t readLong(uint32_t _addr, bool fastRead = false);
//--------------------------- Write / Read Unsigned Longs -----------------------------//
bool writeULong(uint32_t _addr, uint32_t data, bool errorCheck = true);
uint32_t readULong(uint32_t _addr, bool fastRead = false);
//-------------------------------- Write / Read Floats --------------------------------//
bool writeFloat(uint32_t _addr, float data, bool errorCheck = true);
float readFloat(uint32_t _addr, bool fastRead = false);
//-------------------------------- Write / Read Strings -------------------------------//
bool writeStr(uint32_t _addr, String &data, bool errorCheck = true);
bool readStr(uint32_t _addr, String &data, bool fastRead = false);
//------------------------------- Write / Read Anything -------------------------------//
template <class T> bool writeAnything(uint32_t _addr, const T& data, bool errorCheck = true);
template <class T> bool readAnything(uint32_t _addr, T& data, bool fastRead = false);
//-------------------------------- Erase functions ------------------------------------//
bool eraseSection(uint32_t _addr, uint32_t _sz);
bool eraseSector(uint32_t _addr);
bool eraseBlock32K(uint32_t _addr);
bool eraseBlock64K(uint32_t _addr);
bool eraseChip(void);
//-------------------------------- Power functions ------------------------------------//
bool suspendProg(void);
bool resumeProg(void);
bool powerDown(void);
bool powerUp(void);
//-------------------------- Public Arduino Due Functions -----------------------------//
//#if defined (ARDUINO_ARCH_SAM)
//uint32_t freeRAM(void);
//#endif
//------------------------------- Public variables ------------------------------------//
private:
//------------------------------- Private functions -----------------------------------//
unsigned _createMask(unsigned a, unsigned b);
void _troubleshoot(uint8_t _code, bool printoverride = false);
void _endSPI(void);
bool _disableGlobalBlockProtect(void);
bool _isChipPoweredDown(void);
bool _prep(uint8_t opcode, uint32_t _addr, uint32_t size = 0);
bool _startSPIBus(void);
bool _beginSPI(uint8_t opcode);
bool _noSuspend(void);
bool _notBusy(uint32_t timeout = BUSY_TIMEOUT);
bool _notPrevWritten(uint32_t _addr, uint32_t size = 1);
bool _writeEnable(bool _troubleshootEnable = true);
bool _writeDisable(void);
bool _getJedecId(void);
bool _getManId(uint8_t *b1, uint8_t *b2);
bool _chipID(uint32_t flashChipSize = 0);
bool _transferAddress(void);
bool _addressCheck(uint32_t _addr, uint32_t size = 1);
bool _enable4ByteAddressing(void);
bool _disable4ByteAddressing(void);
uint8_t _nextByte(char IOType, uint8_t data = NULLBYTE);
uint16_t _nextInt(uint16_t = NULLINT);
void _nextBuf(uint8_t opcode, uint8_t *data_buffer, uint32_t size);
uint8_t _readStat1(void);
uint8_t _readStat2(void);
uint8_t _readStat3(void);
bool _getSFDPTable(uint32_t _tableAddress, uint8_t *data_buffer, uint8_t numberOfDWords);
bool _getSFDPData(uint32_t _address, uint8_t *data_buffer, uint8_t numberOfBytes);
uint32_t _getSFDPdword(uint32_t _tableAddress, uint8_t dWordNumber);
uint16_t _getSFDPint(uint32_t _tableAddress, uint8_t dWordNumber, uint8_t startByte);
uint8_t _getSFDPbyte(uint32_t _tableAddress, uint8_t dWordNumber, uint8_t byteNumber);
bool _getSFDPbit(uint32_t _tableAddress, uint8_t dWordNumber, uint8_t bitNumber);
uint32_t _getSFDPTableAddr(uint32_t paramHeaderNum);
uint32_t _calcSFDPEraseTimeUnits(uint8_t _unitBits);
bool _checkForSFDP(void);
void _getSFDPEraseParam(void);
void _getSFDPProgramTimeParam(void);
bool _getSFDPFlashParam(void);
template <class T> bool _write(uint32_t _addr, const T& value, uint32_t _sz, bool errorCheck, uint8_t _dataType);
template <class T> bool _read(uint32_t _addr, T& value, uint32_t _sz, bool fastRead = false, uint8_t _dataType = 0x00);
//template <class T> bool _writeErrorCheck(uint32_t _addr, const T& value);
template <class T> bool _writeErrorCheck(uint32_t _addr, const T& value, uint32_t _sz, uint8_t _dataType = 0x00);
//-------------------------------- Private variables ----------------------------------//
#ifdef SPI_HAS_TRANSACTION
SPISettings _settings;
#endif
//If multiple SPI ports are available this variable is used to choose between them (SPI, SPI1, SPI2 etc.)
SPIClass *_spi;
#if !defined (BOARD_RTL8195A)
uint8_t csPin;
#else
// Object declaration for the GPIO HAL type for csPin - @boseji <salearj@hotmail.com> 02.03.17
gpio_t csPin;
#endif
volatile uint8_t *cs_port;
bool pageOverflow, SPIBusState;
bool chipPoweredDown = false;
bool address4ByteEnabled = false;
bool _loopedOver = false;
uint8_t cs_mask, errorcode, stat1, stat2, stat3, _SPCR, _SPSR, _a0, _a1, _a2;
char READ = 'R';
char WRITE = 'W';
float _spifuncruntime = 0;
struct chipID {
bool supported;
bool supportedMan;
bool sfdpAvailable;
uint8_t manufacturerID;
uint8_t memoryTypeID;
uint8_t capacityID;
uint32_t capacity;
uint32_t eraseTime;
};
chipID _chip;
struct eraseParam{
bool supported;
uint8_t opcode;
uint32_t time;
} kb4Erase, kb32Erase, kb64Erase, kb256Erase, chipErase;
uint8_t _noOfParamHeaders, _noOfBasicParamDwords;
uint16_t _eraseTimeMultiplier, _prgmTimeMultiplier, _pageSize;
uint32_t currentAddress, _currentAddress = 0;
uint32_t _addressOverflow = false;
uint32_t _BasicParamTableAddr, _SectorMapParamTableAddr, _byteFirstPrgmTime, _byteAddnlPrgmTime, _pagePrgmTime;
uint8_t _uniqueID[8];
const uint8_t _capID[16] =
{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x43, 0x4B, 0x00, 0x01, 0x13, 0x37};
const uint32_t _memSize[16] =
{KB(64), KB(128), KB(256), KB(512), MB(1), MB(2), MB(4), MB(8), MB(16), MB(32), MB(8), MB(8), KB(256), KB(512), MB(4), KB(512)};
// To understand the _memSize definitions check defines.h
const uint8_t _supportedManID[7] = {WINBOND_MANID, MICROCHIP_MANID, CYPRESS_MANID, ADESTO_MANID, MICRON_MANID, ON_MANID, AMIC_MANID};
const uint8_t _altChipEraseReq[3] = {A25L512, M25P40, SST26};
};
//--------------------------------- Public Templates ------------------------------------//
// Writes any type of data to a specific location in the flash memory.
// Takes three arguments -
// 1. _addr --> Any address from 0 to maxAddress
// 2. T& value --> Variable to write
// 3. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
template <class T> bool SPIFlash::writeAnything(uint32_t _addr, const T& data, bool errorCheck) {
return _write(_addr, data, sizeof(data), errorCheck, _STRUCT_);
}
// Reads any type of data from a specific location in the flash memory.
// Takes three arguments -
// 1. _addr --> Any address from 0 to maxAddress
// 2. T& value --> Variable to return data into
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
template <class T> bool SPIFlash::readAnything(uint32_t _addr, T& data, bool fastRead) {
return _read(_addr, data, sizeof(data), fastRead);
}
//---------------------------------- Private Templates ----------------------------------//
template <class T> bool SPIFlash::_writeErrorCheck(uint32_t _addr, const T& value, uint32_t _sz, uint8_t _dataType) {
if (_isChipPoweredDown() || !_addressCheck(_addr, _sz) || !_notBusy()) {
return false;
}
const uint8_t* p = (const uint8_t*)(const void*)&value;
/*if (_dataType == _STRUCT_) {
uint8_t _inByte[_sz];
_beginSPI(READDATA);
_nextBuf(READDATA, &(*_inByte), _sz);
_endSPI();
for (uint16_t i = 0; i < _sz; i++) {
if (*p++ != _inByte[i]) {
_troubleshoot(0x0A); //0x0A is ERRORCHKFAIL
return false;
}
else {
return true;
}
}
}
else {*/
CHIP_SELECT
_nextByte(WRITE, READDATA);
_transferAddress();
for (uint16_t i = 0; i < _sz; i++) {
if (*p++ != _nextByte(READ)) {
_troubleshoot(0x0A); //0x0A is ERRORCHKFAIL
_endSPI();
return false;
}
}
_endSPI();
//}
return true;
}
// Writes any type of data to a specific location in the flash memory.
// Takes four arguments -
// 1. _addr --> Any address from 0 to maxAddress
// 2. T& value --> Variable to write
// 3. _sz --> Size of variable in bytes (1 byte = 8 bits)
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
template <class T> bool SPIFlash::_write(uint32_t _addr, const T& value, uint32_t _sz, bool errorCheck, uint8_t _dataType) {
bool _retVal;
#ifdef RUNDIAGNOSTIC
_spifuncruntime = micros();
#endif
uint32_t _addrIn = _addr;
if (!_prep(PAGEPROG, _addrIn, _sz)) {
return false;
}
_addrIn = _currentAddress;
//Serial.print("_addrIn: ");
//Serial.println(_addrIn, HEX);
const uint8_t* p = ((const uint8_t*)(const void*)&value);
//Serial.print(F("Address being written to: "));
//Serial.println(_addr);
uint32_t length = _sz;
uint16_t maxBytes = SPI_PAGESIZE-(_addrIn % SPI_PAGESIZE); // Force the first set of bytes to stay within the first page
if (!SPIBusState) {
_startSPIBus();
}
CHIP_SELECT
_nextByte(WRITE, PAGEPROG);
_transferAddress();
if (maxBytes > length) {
for (uint16_t i = 0; i < length; ++i) {
_nextByte(WRITE, *p++);
}
CHIP_DESELECT
}
else {
uint32_t writeBufSz;
uint16_t data_offset = 0;
do {
writeBufSz = (length<=maxBytes) ? length : maxBytes;
for (uint16_t i = 0; i < writeBufSz; ++i) {
_nextByte(WRITE, *p++);
}
CHIP_DESELECT
if (!_addressOverflow) {
_currentAddress += writeBufSz;
}
else {
if (data_offset >= _addressOverflow) {
_currentAddress = 0x00;
_addressOverflow = false;
}
}
data_offset += writeBufSz;
length -= writeBufSz;
maxBytes = SPI_PAGESIZE; // Now we can do up to 256 bytes per loop
if(!_notBusy() || !_writeEnable()) {
return false;
}
} while (length > 0);
}
if (!errorCheck) {
_endSPI();
return true;
}
else {
//Serial.print(F("Address sent to error check: "));
//Serial.println(_addr);
_retVal = _writeErrorCheck(_addr, value, _sz, _dataType);
}
#ifdef RUNDIAGNOSTIC
_spifuncruntime = micros() - _spifuncruntime;
#endif
return _retVal;
}
// Reads any type of data from a specific location in the flash memory.
// Takes four arguments -
// 1. _addr --> Any address from 0 to maxAddress
// 2. T& value --> Variable to return data into
// 3. _sz --> Size of the variable in bytes (1 byte = 8 bits)
// 4. fastRead --> defaults to false - executes _beginFastRead() if set to true
template <class T> bool SPIFlash::_read(uint32_t _addr, T& value, uint32_t _sz, bool fastRead, uint8_t _dataType) {
#ifdef RUNDIAGNOSTIC
_spifuncruntime = micros();
#endif
if (!_prep(READDATA, _addr, _sz)) {
return false;
}
else {
uint8_t* p = (uint8_t*)(void*)&value;
if (_dataType == _STRING_) {
char _inChar[_sz];
_beginSPI(READDATA);
_nextBuf(READDATA, (uint8_t*) &(*_inChar), _sz);
_endSPI();
for (uint16_t i = 0; i < _sz; i++) {
*p++ = _inChar[i];
}
}
else {
CHIP_SELECT
if (fastRead) {
_nextByte(WRITE, FASTREAD);
}
else {
_nextByte(WRITE, READDATA);
}
_transferAddress();
for (uint16_t i = 0; i < _sz; i++) {
*p++ =_nextByte(READ);
}
_endSPI();
}
}
#ifdef RUNDIAGNOSTIC
_spifuncruntime = micros() - _spifuncruntime;
#endif
return true;
}
#endif // _SPIFLASH_H_

View File

@ -0,0 +1,605 @@
/* Arduino SPIMemory Library v.3.2.1
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 19/05/2015
* Modified by @boseji <salearj@hotmail.com> - 02/03/2017
* Modified by Prajwal Bhattaram - 21/05/2018
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "SPIFlash.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Private functions used by read, write and erase operations //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Creates bit mask from bit x to bit y
unsigned SPIFlash::_createMask(unsigned x, unsigned y) {
unsigned r = 0;
for (unsigned i=x; i<=y; i++) {
r |= 1 << i;
}
return r;
}
//Checks to see if page overflow is permitted and assists with determining next address to read/write.
//Sets the global address variable
bool SPIFlash::_addressCheck(uint32_t _addr, uint32_t size) {
uint32_t _submittedAddress = _addr;
uint8_t _errorcode = error();
if (_errorcode == UNKNOWNCAP || _errorcode == NORESPONSE) {
return false;
}
if (!_chip.capacity) {
_troubleshoot(CALLBEGIN);
return false;
}
//Serial.print("_chip.capacity: ");
//Serial.println(_chip.capacity, HEX);
if (_submittedAddress + size >= _chip.capacity) {
//Serial.print("_submittedAddress + size: ");
//Serial.println(_submittedAddress + size, HEX);
#ifdef DISABLEOVERFLOW
_troubleshoot(OUTOFBOUNDS);
return false; // At end of memory - (!pageOverflow)
#else
_addressOverflow = ((_submittedAddress + size) - _chip.capacity);
_currentAddress = _addr;
//Serial.print("_addressOverflow: ");
//Serial.println(_addressOverflow, HEX);
return true; // At end of memory - (pageOverflow)
#endif
}
else {
_addressOverflow = false;
_currentAddress = _addr;
return true; // Not at end of memory if (address < _chip.capacity)
}
//Serial.print("_currentAddress: ");
//Serial.println(_currentAddress, HEX);
}
// Checks to see if the block of memory has been previously written to
bool SPIFlash::_notPrevWritten(uint32_t _addr, uint32_t size) {
//uint8_t _dat;
_beginSPI(READDATA);
for (uint32_t i = 0; i < size; i++) {
if (_nextByte(READ) != 0xFF) {
CHIP_DESELECT;
_troubleshoot(PREVWRITTEN);
return false;
}
}
CHIP_DESELECT
return true;
}
//Double checks all parameters before calling a read or write. Comes in two variants
//Takes address and returns the address if true, else returns false. Throws an error if there is a problem.
bool SPIFlash::_prep(uint8_t opcode, uint32_t _addr, uint32_t size) {
// If the flash memory is >= 256 MB enable 4-byte addressing
if (_chip.manufacturerID == WINBOND_MANID && _addr >= MB(16)) {
if (!_enable4ByteAddressing()) { // If unable to enable 4-byte addressing
return false;
} // TODO: Add SFDP compatibility here
}
switch (opcode) {
case PAGEPROG:
//Serial.print(F("Address being prepped: "));
//Serial.println(_addr);
#ifndef HIGHSPEED
if(_isChipPoweredDown() || !_addressCheck(_addr, size) || !_notPrevWritten(_addr, size) || !_notBusy() || !_writeEnable()) {
return false;
}
#else
if (_isChipPoweredDown() || !_addressCheck(_addr, size) || !_notBusy() || !_writeEnable()) {
return false;
}
#endif
return true;
break;
case ERASEFUNC:
if(_isChipPoweredDown() || !_addressCheck(_addr, size) || !_notBusy() || !_writeEnable()) {
return false;
}
return true;
break;
default:
if (_isChipPoweredDown() || !_addressCheck(_addr, size) || !_notBusy()) {
return false;
}
#ifdef ENABLEZERODMA
_delay_us(3500L);
#endif
return true;
break;
}
}
// Transfer Address.
bool SPIFlash::_transferAddress(void) {
if (address4ByteEnabled) {
_nextByte(WRITE, Highest(_currentAddress));
}
_nextByte(WRITE, Higher(_currentAddress));
_nextByte(WRITE, Hi(_currentAddress));
_nextByte(WRITE, Lo(_currentAddress));
return true;
}
bool SPIFlash::_startSPIBus(void) {
#ifndef SPI_HAS_TRANSACTION
noInterrupts();
#endif
#if defined (ARDUINO_ARCH_SAM)
due.SPIInit(DUE_SPI_CLK);
#elif defined (ARDUINO_ARCH_SAMD)
#ifdef SPI_HAS_TRANSACTION
_spi->beginTransaction(_settings);
#else
_spi->setClockDivider(SPI_CLOCK_DIV_4);
_spi->setDataMode(SPI_MODE0);
_spi->setBitOrder(MSBFIRST);
#endif
#if defined ENABLEZERODMA
dma_init();
#endif
#else
#if defined (ARDUINO_ARCH_AVR)
//save current SPI settings
_SPCR = SPCR;
_SPSR = SPSR;
#endif
#ifdef SPI_HAS_TRANSACTION
SPI.beginTransaction(_settings);
#else
SPI.setClockDivider(SPI_CLOCK_DIV4);
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
#endif
#endif
SPIBusState = true;
return true;
}
//Initiates SPI operation - but data is not transferred yet. Always call _prep() before this function (especially when it involves writing or reading to/from an address)
bool SPIFlash::_beginSPI(uint8_t opcode) {
if (!SPIBusState) {
_startSPIBus();
}
CHIP_SELECT
switch (opcode) {
case READDATA:
_nextByte(WRITE, opcode);
_transferAddress();
break;
case PAGEPROG:
_nextByte(WRITE, opcode);
_transferAddress();
break;
case FASTREAD:
_nextByte(WRITE, opcode);
_nextByte(WRITE, DUMMYBYTE);
_transferAddress();
break;
case SECTORERASE:
_nextByte(WRITE, opcode);
_transferAddress();
break;
case BLOCK32ERASE:
_nextByte(WRITE, opcode);
_transferAddress();
break;
case BLOCK64ERASE:
_nextByte(WRITE, opcode);
_transferAddress();
break;
default:
_nextByte(WRITE, opcode);
break;
}
return true;
}
//SPI data lines are left open until _endSPI() is called
//Reads/Writes next byte. Call 'n' times to read/write 'n' number of bytes. Should be called after _beginSPI()
uint8_t SPIFlash::_nextByte(char IOType, uint8_t data) {
#if defined (ARDUINO_ARCH_SAMD)
#ifdef ENABLEZERODMA
union {
uint8_t dataBuf[1];
uint8_t val;
} rxData, txData;
txData.val = data;
spi_transfer(txData.dataBuf, rxData.dataBuf, 1);
return rxData.val;
#else
return xfer(data);
#endif
#else
return xfer(data);
#endif
}
//Reads/Writes next int. Call 'n' times to read/write 'n' number of integers. Should be called after _beginSPI()
uint16_t SPIFlash::_nextInt(uint16_t data) {
#if defined (ARDUINO_ARCH_SAMD)
return _spi->transfer16(data);
#else
return SPI.transfer16(data);
#endif
}
//Reads/Writes next data buffer. Should be called after _beginSPI()
void SPIFlash::_nextBuf(uint8_t opcode, uint8_t *data_buffer, uint32_t size) {
switch (opcode) {
case READDATA:
#if defined (ARDUINO_ARCH_SAM)
due.SPIRecByte(&(*data_buffer), size);
#elif defined (ARDUINO_ARCH_SAMD)
#ifdef ENABLEZERODMA
spi_read(&(*data_buffer), size);
#else
_spi->transfer(&data_buffer[0], size);
#endif
#elif defined (ARDUINO_ARCH_AVR)
SPI.transfer(&(*data_buffer), size);
#else
uint8_t *_dataAddr = &(*data_buffer);
for (uint16_t i = 0; i < size; i++) {
*_dataAddr = xfer(NULLBYTE);
_dataAddr++;
}
#endif
break;
case PAGEPROG:
#if defined (ARDUINO_ARCH_SAM)
due.SPISendByte(&(*data_buffer), size);
#elif defined (ARDUINO_ARCH_SAMD)
#ifdef ENABLEZERODMA
spi_write(&(*data_buffer), size);
#else
_spi->transfer(&data_buffer[0], size);
#endif
#elif defined (ARDUINO_ARCH_AVR)
SPI.transfer(&(*data_buffer), size);
#else
for (uint16_t i = 0; i < size; i++) {
xfer(*_dataAddr);
_dataAddr++;
}
#endif
break;
}
}
//Stops all operations. Should be called after all the required data is read/written from repeated _nextByte() calls
void SPIFlash::_endSPI(void) {
CHIP_DESELECT
if (address4ByteEnabled) { // If the previous operation enabled 4-byte addressing, disable it
_disable4ByteAddressing();
}
#ifdef SPI_HAS_TRANSACTION
#if defined (ARDUINO_ARCH_SAMD)
_spi->endTransaction();
#else
SPI.endTransaction();
#endif
#else
interrupts();
#endif
#if defined (ARDUINO_ARCH_AVR)
SPCR = _SPCR;
SPSR = _SPSR;
#endif
SPIBusState = false;
}
// Checks if status register 1 can be accessed - used to check chip status, during powerdown and power up and for debugging
uint8_t SPIFlash::_readStat1(void) {
_beginSPI(READSTAT1);
stat1 = _nextByte(READ);
CHIP_DESELECT
return stat1;
}
// Checks if status register 2 can be accessed, if yes, reads and returns it
uint8_t SPIFlash::_readStat2(void) {
_beginSPI(READSTAT2);
stat2 = _nextByte(READ);
//stat2 = _nextByte(READ);
CHIP_DESELECT
return stat2;
}
// Checks if status register 3 can be accessed, if yes, reads and returns it
uint8_t SPIFlash::_readStat3(void) {
_beginSPI(READSTAT3);
stat3 = _nextByte(READ);
//stat2 = _nextByte(READ);
CHIP_DESELECT
return stat3;
}
// Checks to see if 4-byte addressing is already enabled and if not, enables it
bool SPIFlash::_enable4ByteAddressing(void) {
if (_readStat3() & ADS) {
return true;
}
_beginSPI(ADDR4BYTE_EN);
CHIP_DESELECT
if (_readStat3() & ADS) {
address4ByteEnabled = true;
return true;
}
else {
_troubleshoot(UNABLETO4BYTE);
return false;
}
}
// Checks to see if 4-byte addressing is already disabled and if not, disables it
bool SPIFlash::_disable4ByteAddressing(void) {
if (!(_readStat3() & ADS)) { // If 4 byte addressing is disabled (default state)
return true;
}
_beginSPI(ADDR4BYTE_DIS);
CHIP_DESELECT
if (_readStat3() & ADS) {
_troubleshoot(UNABLETO3BYTE);
return false;
}
address4ByteEnabled = false;
return true;
}
// Checks the erase/program suspend flag before enabling/disabling a program/erase suspend operation
bool SPIFlash::_noSuspend(void) {
switch (_chip.manufacturerID) {
case WINBOND_MANID:
if(_readStat2() & SUS) {
_troubleshoot(SYSSUSPEND);
return false;
}
return true;
break;
case MICROCHIP_MANID:
_readStat1();
if(stat1 & WSE || stat1 & WSP) {
_troubleshoot(SYSSUSPEND);
return false;
}
}
return true;
}
// Checks to see if chip is powered down. If it is, retrns true. If not, returns false.
bool SPIFlash::_isChipPoweredDown(void) {
if (chipPoweredDown) {
_troubleshoot(CHIPISPOWEREDDOWN);
return true;
}
else {
return false;
}
}
// Polls the status register 1 until busy flag is cleared or timeout
bool SPIFlash::_notBusy(uint32_t timeout) {
_delay_us(WINBOND_WRITE_DELAY);
uint32_t _time = micros();
do {
_readStat1();
if (!(stat1 & BUSY))
{
return true;
}
} while ((micros() - _time) < timeout);
if (timeout <= (micros() - _time)) {
_troubleshoot(CHIPBUSY);
return false;
}
return true;
}
//Enables writing to chip by setting the WRITEENABLE bit
bool SPIFlash::_writeEnable(bool _troubleshootEnable) {
_beginSPI(WRITEENABLE);
CHIP_DESELECT
if (!(_readStat1() & WRTEN)) {
if (_troubleshootEnable) {
_troubleshoot(CANTENWRITE);
}
return false;
}
return true;
}
//Disables writing to chip by setting the Write Enable Latch (WEL) bit in the Status Register to 0
//_writeDisable() is not required under the following conditions because the Write Enable Latch (WEL) flag is cleared to 0
// i.e. to write disable state:
// Power-up, Write Disable, Page Program, Quad Page Program, Sector Erase, Block Erase, Chip Erase, Write Status Register,
// Erase Security Register and Program Security register
bool SPIFlash::_writeDisable(void) {
_beginSPI(WRITEDISABLE);
CHIP_DESELECT
return true;
}
//Checks the device ID to establish storage parameters
bool SPIFlash::_getManId(uint8_t *b1, uint8_t *b2) {
if(!_notBusy()) {
return false;
}
_beginSPI(MANID);
_nextByte(READ);
_nextByte(READ);
_nextByte(READ);
*b1 = _nextByte(READ);
*b2 = _nextByte(READ);
CHIP_DESELECT
return true;
}
//Checks for presence of chip by requesting JEDEC ID
bool SPIFlash::_getJedecId(void) {
if(!_notBusy()) {
return false;
}
_beginSPI(JEDECID);
_chip.manufacturerID = _nextByte(READ); // manufacturer id
_chip.memoryTypeID = _nextByte(READ); // memory type
_chip.capacityID = _nextByte(READ); // capacity
CHIP_DESELECT
if (!_chip.manufacturerID) {
_troubleshoot(NORESPONSE);
return false;
}
else {
return true;
}
}
bool SPIFlash::_disableGlobalBlockProtect(void) {
if (_chip.memoryTypeID == SST25) {
_readStat1();
uint8_t _tempStat1 = stat1 & 0xC3;
_beginSPI(WRITESTATEN);
CHIP_DESELECT
_beginSPI(WRITESTAT1);
_nextByte(WRITE, _tempStat1);
CHIP_DESELECT
}
else if (_chip.memoryTypeID == SST26) {
if(!_notBusy()) {
return false;
}
_writeEnable();
_delay_us(10);
_beginSPI(ULBPR);
CHIP_DESELECT
_delay_us(50);
_writeDisable();
}
return true;
}
//Identifies the chip
bool SPIFlash::_chipID(uint32_t flashChipSize) {
//set some default values
kb4Erase.supported = kb32Erase.supported = kb64Erase.supported = chipErase.supported = true;
kb4Erase.opcode = SECTORERASE;
kb32Erase.opcode = BLOCK32ERASE;
kb64Erase.opcode = BLOCK64ERASE;
kb4Erase.time = BUSY_TIMEOUT;
kb32Erase.time = kb4Erase.time * 8;
kb64Erase.time = kb32Erase.time * 4;
kb256Erase.supported = false;
chipErase.opcode = CHIPERASE;
chipErase.time = kb64Erase.time * 100L;
_getJedecId();
for (uint8_t i = 0; i < sizeof(_supportedManID); i++) {
if (_chip.manufacturerID == _supportedManID[i]) {
_chip.supportedMan = true;
break;
}
}
for (uint8_t i = 0; i < sizeof(_altChipEraseReq); i++) {
if (_chip.memoryTypeID == _altChipEraseReq[i]) {
chipErase.opcode = ALT_CHIPERASE;
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Begin SFDP ID section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#ifdef USES_SFDP
if (_checkForSFDP()) {
_getSFDPFlashParam();
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ End SFDP ID section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
if (_chip.supportedMan) {
#ifdef RUNDIAGNOSTIC
Serial.println("No Chip size defined by user. Automated identification initiated.");
#endif
//Identify capacity
for (uint8_t j = 0; j < sizeof(_capID); j++) {
if (_chip.capacityID == _capID[j]) {
_chip.capacity = (_memSize[j]);
_chip.supported = true;
#ifdef RUNDIAGNOSTIC
Serial.println("Chip identified. This chip is fully supported by the library.");
#endif
return true;
}
}
}
else {
_troubleshoot(UNKNOWNCHIP); //Error code for unidentified capacity
return false;
}
if (!_chip.capacity) {
if (flashChipSize) {
// If a custom chip size is defined
#ifdef RUNDIAGNOSTIC
Serial.println("Custom Chipsize defined");
#endif
_chip.capacity = flashChipSize;
_chip.supported = false;
return true;
}
else {
_troubleshoot(UNKNOWNCAP);
return false;
}
}
return false; //Failsafe
}
//Troubleshooting function. Called when #ifdef RUNDIAGNOSTIC is uncommented at the top of this file.
void SPIFlash::_troubleshoot(uint8_t _code, bool printoverride) {
diagnostics.troubleshoot(_code, printoverride);
}

View File

@ -0,0 +1,356 @@
/* Arduino SPIMemory Library v.3.1.0
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 19/05/2015
* Modified by @boseji <salearj@hotmail.com> - 02/03/2017
* Modified by Prajwal Bhattaram - 24/02/2018
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "SPIFlash.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Private Functions that retrieve date from the SFDP tables //
// - if the flash chip supports SFDP //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// This function returns the SFDP table requested as an array of 32 bit integers
bool SPIFlash::_getSFDPTable(uint32_t _address, uint8_t *data_buffer, uint8_t numberOfDWords) {
if(!_notBusy()) {
return false;
}
_beginSPI(READSFDP);
_nextByte(WRITE, Higher(_address));
_nextByte(WRITE, Hi(_address));
_nextByte(WRITE, Lo(_address));
_nextByte(WRITE, DUMMYBYTE);
_nextBuf(READDATA, &(*data_buffer), numberOfDWords*4); //*4 to convert from dWords to bytes
CHIP_DESELECT
return true;
}
// This function returns a custom length of data from the SFDP table requested as an array of 8 bit integers (bytes)
bool SPIFlash::_getSFDPData(uint32_t _address, uint8_t *data_buffer, uint8_t numberOfBytes) {
if(!_notBusy()) {
return false;
}
_beginSPI(READSFDP);
_nextByte(WRITE, Higher(_address));
_nextByte(WRITE, Hi(_address));
_nextByte(WRITE, Lo(_address));
_nextByte(WRITE, DUMMYBYTE);
_nextBuf(READDATA, &(*data_buffer), numberOfBytes); //*4 to convert from dWords to bytes
CHIP_DESELECT
return true;
}
//dWordNumber can be between 1 to 256
uint32_t SPIFlash::_getSFDPdword(uint32_t _tableAddress, uint8_t dWordNumber) {
if(!_notBusy()) {
return false;
}
union {
uint32_t dWord;
uint8_t byteArray[4];
} SFDPdata;
uint32_t _address = ADDRESSOFSFDPDWORD(_tableAddress, dWordNumber);
_getSFDPData(_address, &(*SFDPdata.byteArray), sizeof(uint32_t));
return SFDPdata.dWord;
}
//startByte is the byte from which the 16-bit integer starts and can be between 1 to 256
uint16_t SPIFlash::_getSFDPint(uint32_t _tableAddress, uint8_t dWordNumber, uint8_t startByte) {
if(!_notBusy()) {
return false;
}
union {
uint16_t word;
uint8_t byteArray[2];
} SFDPdata;
uint32_t _address = ADDRESSOFSFDPBYTE(_tableAddress, dWordNumber, startByte);
_getSFDPData(_address, &(*SFDPdata.byteArray), sizeof(uint16_t));
return SFDPdata.word;
}
//byteNumber can be between 1 to 256
uint8_t SPIFlash::_getSFDPbyte(uint32_t _tableAddress, uint8_t dWordNumber, uint8_t byteNumber) {
if(!_notBusy()) {
return false;
}
uint8_t SFDPdataByte;
uint32_t _address = ADDRESSOFSFDPBYTE(_tableAddress, dWordNumber, byteNumber);
_getSFDPData(_address, &SFDPdataByte, sizeof(uint8_t));
return SFDPdataByte;
}
//bitNumber can be between 0 to 31
bool SPIFlash::_getSFDPbit(uint32_t _tableAddress, uint8_t dWordNumber, uint8_t bitNumber) {
return(_getSFDPdword(_tableAddress, dWordNumber) & (0x01 << bitNumber));
}
uint32_t SPIFlash::_getSFDPTableAddr(uint32_t paramHeaderNum) {
uint32_t _tableAddr = _getSFDPdword(paramHeaderNum * 8, 0x02); // Each parameter header table is 8 bytes long
Highest(_tableAddr) = 0x00; // Top byte in the dWord containing the table address is always 0xFF.
return _tableAddr;
}
bool SPIFlash::_checkForSFDP(void) {
if (_getSFDPdword(SFDP_HEADER_ADDR, SFDP_SIGNATURE_DWORD) == SFDPSIGNATURE) {
_chip.sfdpAvailable = true;
#ifdef RUNDIAGNOSTIC
Serial.println("SFDP available");
#endif
}
else {
_troubleshoot(NOSFDP);
_chip.sfdpAvailable = false;
}
return _chip.sfdpAvailable;
}
uint32_t SPIFlash::_calcSFDPEraseTimeUnits(uint8_t _unitBits) {
switch (_unitBits) {
case MS1:
return 1000L;
break;
case MS16:
return (16L*1000L);
break;
case MS128:
return (128L*1000L);
break;
case S1:
return (1000L*1000L);
}
return false;
}
void SPIFlash::_getSFDPEraseParam(void) {
// Get sector erase details if available on SFDP Tables
if (_noOfBasicParamDwords >= SFDP_ERASE1_INSTRUCTION_DWORD) {
uint32_t _eraseInfoAddress;
uint8_t _eraseInfo[8];
uint8_t _eraseExists = 0;
uint8_t _count;
uint32_t _units;
union {
uint32_t dword;
uint8_t byte[4];
} _eraseTime;
_eraseInfoAddress = ADDRESSOFSFDPDWORD(_BasicParamTableAddr, SFDP_ERASE1_INSTRUCTION_DWORD);
_getSFDPData(_eraseInfoAddress, &(*_eraseInfo), 8);
for (uint8_t i = 0; i < 8; i++) {
_eraseExists += _eraseInfo[i];
if (!_eraseExists) { // If faulty SFDP read, then revert to defaults
kb4Erase.supported = kb32Erase.supported = kb64Erase.supported = chipErase.supported = true;
kb4Erase.opcode = SECTORERASE;
kb32Erase.opcode = BLOCK32ERASE;
kb64Erase.opcode = BLOCK64ERASE;
kb4Erase.time = BUSY_TIMEOUT;
kb32Erase.time = kb4Erase.time * 8;
kb64Erase.time = kb32Erase.time * 4;
kb256Erase.supported = false;
chipErase.opcode = CHIPERASE;
chipErase.time = kb64Erase.time * 100L;
_troubleshoot(NOSFDPERASEPARAM);
}
}
for (uint8_t i = 0; i < 8; i++) {
if ((i % 2) == 0) {
switch ((_eraseInfo[i])) {
case KB4ERASE_TYPE:
kb4Erase.supported = true;
kb4Erase.opcode = _eraseInfo[i+1];
break;
case KB32ERASE_TYPE:
kb32Erase.supported = true;
kb32Erase.opcode = _eraseInfo[i+1];
break;
case KB64ERASE_TYPE:
kb64Erase.supported = true;
kb64Erase.opcode = _eraseInfo[i+1];
break;
case KB256ERASE_TYPE:
kb256Erase.supported = true;
kb256Erase.opcode = _eraseInfo[i+1];
break;
}
}
}
// Some flash memory chips have information about sector erase times in DWORD 10 of SFDP Basic param table.
if (_noOfBasicParamDwords >= SFDP_SECTOR_ERASE_TIME_DWORD) {
_eraseTime.dword = _getSFDPdword(_BasicParamTableAddr, SFDP_SECTOR_ERASE_TIME_DWORD);
_eraseTimeMultiplier = _eraseTime.byte[0];
setUpperNibble(_eraseTimeMultiplier, 0b0000);
_eraseTimeMultiplier = 2 * (_eraseTimeMultiplier + 1); // Refer JESD216B Page 21
for (uint8_t i = 0; i < 8; i++) {
if ((i % 2) == 0) {
switch ((_eraseInfo[i])) {
case KB4ERASE_TYPE:
_count = ( ( ( (_eraseTime.byte[1] & _createMask(0, 0)) << 5) | ( (_eraseTime.byte[0] & _createMask(4, 7)) ) >> 4) + 1);
_units = _calcSFDPEraseTimeUnits((_eraseTime.byte[1] & _createMask(1, 2)) >> 1);
kb4Erase.time = (_count * _units * _eraseTimeMultiplier);
break;
case KB32ERASE_TYPE:
_count = (((_eraseTime.byte[1] & _createMask(3, 7)) >> 3) + 1);
_units = _calcSFDPEraseTimeUnits(_eraseTime.byte[2] & _createMask(0, 1));
kb32Erase.time = (_count * _units * _eraseTimeMultiplier);
break;
case KB64ERASE_TYPE:
_count = (((_eraseTime.byte[2] & _createMask(2, 6)) >> 2) + 1);
_units = _calcSFDPEraseTimeUnits(((_eraseTime.byte[2] & _createMask(7, 7)) >> 7) | (_eraseTime.byte[3] & _createMask(0,0)) << 1);
kb64Erase.time = (_count * _units * _eraseTimeMultiplier);
break;
case KB256ERASE_TYPE:
_count = (((_eraseTime.byte[3] & _createMask(1, 5)) >> 1) + 1);
_units = _calcSFDPEraseTimeUnits((_eraseTime.byte[3] & _createMask(6, 7)) >> 6);
kb64Erase.time = (_count * _units) * _eraseTimeMultiplier;
break;
}
}
}
// Some flash memory chips have information about chip erase times in DWORD 11 of SFDP Basic param table.
if (_noOfBasicParamDwords >= SFDP_CHIP_ERASE_TIME_DWORD) {
// Get chip erase details
_eraseInfoAddress = ADDRESSOFSFDPDWORD(_BasicParamTableAddr, DWORD(11));
_getSFDPData(_eraseInfoAddress, &(*_eraseInfo), 8);
chipErase.supported = true; // chipErase.opcode is set in _chipID().
_count = (((_eraseTime.byte[3] & _createMask(0, 4))) + 1);
_units = _calcSFDPEraseTimeUnits((_eraseTime.byte[3] & _createMask(5, 6)) >> 5);
chipErase.time = (_count * _units) * _eraseTimeMultiplier;
}
}
else { //If flash memory does not have any sfdp information about sector erase times
_troubleshoot(NOSFDPERASETIME);
kb4Erase.time = BUSY_TIMEOUT;
kb32Erase.time = kb4Erase.time * 8;
kb64Erase.time = kb32Erase.time * 4;
kb256Erase.supported = false;
chipErase.opcode = CHIPERASE;
chipErase.time = kb64Erase.time * 100L;
}
}
else {
_troubleshoot(NOSFDPERASEPARAM);
}
}
// Gets IO timing information from SFDP tables - if available.
void SPIFlash::_getSFDPProgramTimeParam(void) {
if (_noOfBasicParamDwords >= SFDP_PROGRAM_TIME_DWORD) {
union {
uint32_t dword;
uint8_t byte[4];
} _sfdp;
uint8_t _count;
uint32_t _units;
_sfdp.dword= _getSFDPdword(_BasicParamTableAddr, SFDP_PROGRAM_TIME_DWORD);
//Calculate Program time multiplier
_prgmTimeMultiplier = (2 * ((_sfdp.byte[1] >> 4) + 1));
//Serial.print("_prgmTimeMultiplier: ");
//Serial.println(_prgmTimeMultiplier);
// Get pageSize
//setUpperNibble(_eraseTimeMultiplier, 0b0000);
_pageSize = setUpperNibble(_sfdp.byte[1], 0b0000);
_pageSize *= 2;
//Serial.print("_pageSize: ");
//Serial.println(_pageSize);
//Calculate Page Program time
_count = (((_sfdp.byte[1] & _createMask(0, 4))) + 1);
((_sfdp.byte[1] & _createMask(7, 7)) >> 7) ? (_units = 64) : (_units = 8);
_pagePrgmTime = (_count * _units) * _prgmTimeMultiplier;
//Serial.print("_pagePrgmTime: ");
//Serial.println(_pagePrgmTime);
//Calculate First Byte Program time
_count = ( (((_sfdp.byte[1] & _createMask(6, 7)) >> 4) | (((_sfdp.byte[2] & _createMask(6, 7))) >> 6)) + 1);
((_sfdp.byte[2] & _createMask(5, 5)) >> 5) ? (_units = 8) : (_units = 1);
_byteFirstPrgmTime = (_count * _units) * _prgmTimeMultiplier;
//Serial.print("_byteFirstPrgmTime :");
//Serial.println(_byteFirstPrgmTime);
//Calculate Additional Byte Program time
_count = ( ((_sfdp.byte[2] & _createMask(4, 1)) >> 1) + 1);
(_sfdp.byte[2] & _createMask(0, 0)) ? (_units = 8) : (_units = 1);
_byteAddnlPrgmTime = (_count * _units) * _prgmTimeMultiplier;
//Serial.print("_byteAddnlPrgmTime :");
//Serial.println(_byteAddnlPrgmTime);
}
else {
_pageSize = SPI_PAGESIZE;
_pagePrgmTime = BUSY_TIMEOUT;
_byteFirstPrgmTime = BUSY_TIMEOUT;
_byteAddnlPrgmTime = BUSY_TIMEOUT;
_troubleshoot(NOSFDPPROGRAMTIMEPARAM);
}
}
// Reads and stores any required values from the Basic Flash Parameter table
bool SPIFlash::_getSFDPFlashParam(void) {
_noOfParamHeaders = _getSFDPbyte(SFDP_HEADER_ADDR, SFDP_NPH_DWORD, SFDP_NPH_BYTE) + 1; // Number of parameter headers is 0 based - i.e. 0x00 means there is 1 header.
if (_noOfParamHeaders > 1) {
_SectorMapParamTableAddr = _getSFDPTableAddr(SFDP_SECTOR_MAP_PARAM_TABLE_NO);
}
_noOfBasicParamDwords = _getSFDPbyte(SFDP_BASIC_PARAM_TABLE_HDR_ADDR, SFDP_PARAM_TABLE_LENGTH_DWORD, SFDP_PARAM_TABLE_LENGTH_BYTE);
_BasicParamTableAddr = _getSFDPTableAddr(SFDP_BASIC_PARAM_TABLE_NO);
// Calculate chip capacity
_chip.capacity = _getSFDPdword(_BasicParamTableAddr, SFDP_MEMORY_DENSITY_DWORD);
uint8_t _multiplier = Highest(_chip.capacity); //----
Highest(_chip.capacity) = 0x00; // |
if (_multiplier <= 0x0F) { // |
_chip.capacity = (_chip.capacity + 1) * (_multiplier + 1); // |---> This section calculates the chip capacity as
} // |---> detailed in JESD216B
else { // |
_chip.capacity = ((_chip.capacity + 1) * 2); // |
} //----
#ifdef RUNDIAGNOSTIC
Serial.println("Chip identified using sfdp. Most of this chip's functions are supported by the library.");
#endif
// Get Erase Parameters if available
_getSFDPEraseParam();
//Get Program time Parameters
_getSFDPProgramTimeParam();
// TODO Update the use of Program time parameters across the library
//Serial.print("dWord 9: 0x");
//Serial.println(_getSFDPdword(_BasicParamTableAddr, DWORD(9)), HEX);
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ End SFDP ID section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

View File

@ -0,0 +1,30 @@
/* Arduino SPIMemory Library v.3.2.0
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 19/05/2015
* Modified by @boseji <salearj@hotmail.com> - 02/03/2017
* Modified by Prajwal Bhattaram - 24/02/2018
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "SPIMemory.h"
SPIMemory SPIMemory; // default instantiation of SPIMemory object

View File

@ -0,0 +1,134 @@
/* Arduino SPIMemory Library v.3.2.1
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 18/04/2018
* Modified by Prajwal Bhattaram - 21/05/2018
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef SPIMEMORY_H
#define SPIMEMORY_H
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Uncomment the code below to enable SFDP discovery - especially //
// if using an unsupported chip //
// //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//#define USES_SFDP //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Uncomment the code below to run a diagnostic if your flash //
// does not respond //
// //
// Error codes will be generated and returned on functions //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//#define RUNDIAGNOSTIC //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Uncomment the code below to increase the speed of the library //
// by disabling _notPrevWritten() //
// //
// Make sure the sectors being written to have been erased beforehand //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//#define HIGHSPEED //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Uncomment the code below to disable overflow and force data //
// to only be written to the last address of the flash memory //
// and not rollover to address 0x00 when the end is reached //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//#define DISABLEOVERFLOW //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Comment out the code below to disable DMA mode on SAMD based //
// platforms (In ALPHA) //
// //
// Change the ZERO_SPISERCOM define below to use other SPI ports //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//#define ENABLEZERODMA //
//#define ZERO_SPISERCOM SERCOM4 //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#include <Arduino.h>
#include <SPI.h>
#include "defines.h"
#include "SPIFlash.h"
#include "diagnostics.h"
#if defined (ARDUINO_ARCH_SAM)
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include "DMASAM.h"
#endif
#if defined (ARDUINO_ARCH_SAMD)
#if defined (ENABLEZERODMA)
#include "DMASAMD.h"
#endif
#endif
#if defined (BOARD_RTL8195A)
#ifdef __cplusplus
extern "C" {
#endif
#include "gpio_api.h"
#include "PinNames.h"
#ifdef __cplusplus
}
#endif
#endif
#ifndef ARCH_STM32
#if defined(ARDUINO_ARCH_STM32) || defined(__STM32F1__) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F0xx)
#define ARCH_STM32
#endif
#endif
#if defined (ARDUINO_ARCH_SAM) || defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_ESP8266) || defined (SIMBLEE) || defined (ARDUINO_ARCH_ESP32) || defined (BOARD_RTL8195A) || defined(ARCH_STM32) || defined(ESP32) || defined(NRF5)
// RTL8195A included - @boseji <salearj@hotmail.com> 02.03.17
#define _delay_us(us) delayMicroseconds(us)
#else
#include <util/delay.h>
#endif
#define LIBVER 3
#define LIBSUBVER 2
#define BUGFIXVER 1
class SPIMemory {
public:
//------------------------------------ Constructor ------------------------------------//
SPIMemory(void) {};
~SPIMemory(void) {};
//------------------------------- Public functions -----------------------------------//
//------------------------------- Public variables -----------------------------------//
};
extern SPIMemory SPIMem; //default SPIMemory instance;
#endif // _SPIMEMORY_H_

View File

@ -0,0 +1,290 @@
/* Arduino SPIMemory Library v.3.2.1
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 19/05/2015
* Modified by Prajwal Bhattaram - 21/05/2018
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
// Defines and variables specific to SAM architecture
#if defined (ARDUINO_ARCH_SAM)
#define CHIP_SELECT digitalWrite(csPin, LOW);
#define CHIP_DESELECT digitalWrite(csPin, HIGH);
#define xfer due.SPITransfer
#define BEGIN_SPI due.SPIBegin();
extern char _end;
extern "C" char *sbrk(int i);
//char *ramstart=(char *)0x20070000;
//char *ramend=(char *)0x20088000;
// Specific access configuration for Chip select pin. Includes specific to RTL8195A to access GPIO HAL - @boseji <salearj@hotmail.com> 02.03.17
#elif defined (BOARD_RTL8195A)
#define CHIP_SELECT gpio_write(&csPin, 0);
#define CHIP_DESELECT gpio_write(&csPin, 1);
#define xfer(n) SPI.transfer(n)
#define BEGIN_SPI SPI.begin();
// Defines and variables specific to SAMD architecture
#elif defined (ARDUINO_ARCH_SAMD) || defined(ARCH_STM32)
#define CHIP_SELECT digitalWrite(csPin, LOW);
#define CHIP_DESELECT digitalWrite(csPin, HIGH);
#define xfer(n) _spi->transfer(n)
#define BEGIN_SPI _spi->begin();
#else
#define CHIP_SELECT digitalWrite(csPin, LOW);
#define CHIP_DESELECT digitalWrite(csPin, HIGH);
#define xfer(n) SPI.transfer(n)
#define BEGIN_SPI SPI.begin();
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Common Instructions //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#define MANID 0x90
#define PAGEPROG 0x02
#define READDATA 0x03
#define FASTREAD 0x0B
#define WRITEDISABLE 0x04
#define READSTAT1 0x05
#define READSTAT2 0x35
#define READSTAT3 0x15
#define WRITESTATEN 0x50
#define WRITESTAT1 0x01
#define WRITESTAT2 0x31
#define WRITESTAT3 0x11
#define WRITEENABLE 0x06
#define ADDR4BYTE_EN 0xB7
#define ADDR4BYTE_DIS 0xE9
#define SECTORERASE 0x20
#define BLOCK32ERASE 0x52
#define BLOCK64ERASE 0xD8
#define CHIPERASE 0x60
#define ALT_CHIPERASE 0xC7 // Some flash chips use a different chip erase command
#define SUSPEND 0x75
#define ID 0x90
#define RESUME 0x7A
#define JEDECID 0x9F
#define POWERDOWN 0xB9
#define RELEASE 0xAB
#define READSFDP 0x5A
#define UNIQUEID 0x4B
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// General size definitions //
// B = Bytes; KiB = Kilo Bytes; MiB = Mega Bytes //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#define B(x) uint32_t(x*BYTE)
#define KB(x) uint32_t(x*KiB)
#define MB(x) uint32_t(x*MiB)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// SFDP related defines //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#define DWORD(x) x
#define FIRSTBYTE 0x01
#define SFDPSIGNATURE 0x50444653
#define ADDRESSOFSFDPDWORD(x,y) x+((y - 1) * 4)
#define ADDRESSOFSFDPBYTE(x,y,z) x+((y - 1) * 4)+(z - 1)
#define KB4ERASE_TYPE 0x0C
#define KB32ERASE_TYPE 0x0F
#define KB64ERASE_TYPE 0x10
#define KB256ERASE_TYPE 0x12
#define MS1 0b00000000
#define MS16 0b00000001
#define MS128 0b00000010
#define S1 0b00000011
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Fixed SFDP addresses //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#define SFDP_HEADER_ADDR 0x00
#define SFDP_SIGNATURE_DWORD 0x01
#define SFDP_NPH_DWORD 0x02
#define SFDP_NPH_BYTE 0x03
#define SFDP_PARAM_TABLE_LENGTH_DWORD 0x01
#define SFDP_PARAM_TABLE_LENGTH_BYTE 0x04
#define SFDP_BASIC_PARAM_TABLE_HDR_ADDR 0x08
#define SFDP_BASIC_PARAM_TABLE_NO 0x01
#define SFDP_MEMORY_DENSITY_DWORD 0x02
#define SFDP_SECTOR_MAP_PARAM_TABLE_NO 0x02
#define SFDP_ERASE1_BYTE 0x01
#define SFDP_ERASE1_INSTRUCTION_DWORD 0x08
#define SFDP_ERASE2_INSTRUCTION_DWORD 0x09
#define SFDP_SECTOR_ERASE_TIME_DWORD 0x0A
#define SFDP_CHIP_ERASE_TIME_DWORD 0x0B
#define SFDP_PROGRAM_TIME_DWORD 0x0B
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Chip specific instructions //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~ Winbond ~~~~~~~~~~~~~~~~~~~~~~~~~//
#define WINBOND_MANID 0xEF
#define SPI_PAGESIZE 0x100
#define WINBOND_WRITE_DELAY 0x02
#define WINBOND_WREN_TIMEOUT 10L
//~~~~~~~~~~~~~~~~~~~~~~~~ Microchip ~~~~~~~~~~~~~~~~~~~~~~~~//
#define MICROCHIP_MANID 0xBF
#define SST25 0x25
#define SST26 0x26
#define ULBPR 0x98 //Global Block Protection Unlock (Ref sections 4.1.1 & 5.37 of datasheet)
//~~~~~~~~~~~~~~~~~~~~~~~~ Cypress ~~~~~~~~~~~~~~~~~~~~~~~~//
#define CYPRESS_MANID 0x01
//~~~~~~~~~~~~~~~~~~~~~~~~ Adesto ~~~~~~~~~~~~~~~~~~~~~~~~//
#define ADESTO_MANID 0x1F
//~~~~~~~~~~~~~~~~~~~~~~~~ Micron ~~~~~~~~~~~~~~~~~~~~~~~~//
#define MICRON_MANID 0x20
#define M25P40 0x20
//~~~~~~~~~~~~~~~~~~~~~~~~ ON ~~~~~~~~~~~~~~~~~~~~~~~~//
#define ON_MANID 0x62
//~~~~~~~~~~~~~~~~~~~~~~~~ AMIC ~~~~~~~~~~~~~~~~~~~~~~~~//
#define AMIC_MANID 0x37
#define A25L512 0x30
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Definitions //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#define BUSY 0x01
#if defined (ARDUINO_ARCH_ESP32)
#define SPI_CLK 20000000 //Hz equivalent of 20MHz
#else
#define SPI_CLK 104000000 //Hz equivalent of 104MHz
#endif
#define WRTEN 0x02
#define SUS 0x80
#define WSE 0x04
#define WSP 0x08
#define ADS 0x01 // Current Address mode in Status register 3
#define DUMMYBYTE 0xEE
#define NULLBYTE 0x00
#define NULLINT 0x0000
#define NO_CONTINUE 0x00
#define PASS 0x01
#define FAIL 0x00
#define NOOVERFLOW false
#define NOERRCHK false
#define VERBOSE true
#define PRINTOVERRIDE true
#define ERASEFUNC 0xEF
#define BUSY_TIMEOUT 1000000000L
#define arrayLen(x) (sizeof(x) / sizeof(*x))
#define lengthOf(x) (sizeof(x))/sizeof(byte)
#define BYTE 1L
#define KiB 1024L
#define MiB KiB * KiB
#define S 1000L
#define TIME_TO_PROGRAM(x) (_byteFirstPrgmTime + (_byteAddnlPrgmTime * (x - 1)) )
#if defined (ARDUINO_ARCH_ESP8266)
#define CS 15
#elif defined (ARDUINO_ARCH_SAMD)
#define CS 10
/*********************************************************************************************
// Declaration of the Default Chip select pin name for RTL8195A
// Note: This has been shifted due to a bug identified in the HAL layer SPI driver
// @ref http://www.amebaiot.com/en/questions/forum/facing-issues-with-spi-interface-to-w25q32/
// Note: Please use any pin other than GPIOC_0 which is the D10 marked in the kit
// Original edit by @boseji <salearj@hotmail.com> 02.03.17
// Modified by Prajwal Bhattaram <marzogh@icloud.com> 14.4.17
**********************************************************************************************/
#elif defined (BOARD_RTL8195A)
#define CS PC_4
#else
#define CS SS
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Arduino Due DMA definitions //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Use SAM3X DMAC if nonzero
#define USE_SAM3X_DMAC 1
// Use extra Bus Matrix arbitration fix if nonzero
#define USE_SAM3X_BUS_MATRIX_FIX 0
// Time in ms for DMA receive timeout
#define SAM3X_DMA_TIMEOUT 100
// chip select register number
#define SPI_CHIP_SEL 3
// DMAC receive channel
#define SPI_DMAC_RX_CH 1
// DMAC transmit channel
#define SPI_DMAC_TX_CH 0
// DMAC Channel HW Interface Number for SPI TX.
#define SPI_TX_IDX 1
// DMAC Channel HW Interface Number for SPI RX.
#define SPI_RX_IDX 2
// Set DUE SPI clock div (any integer from 2 - 255)
#define DUE_SPI_CLK 2
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// List of Supported data types //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#define _BYTE_ 0x01
#define _CHAR_ 0x02
#define _WORD_ 0x03
#define _SHORT_ 0x04
#define _ULONG_ 0x05
#define _LONG_ 0x06
#define _FLOAT_ 0x07
#define _STRING_ 0x08
#define _BYTEARRAY_ 0x09
#define _CHARARRAY_ 0x0A
#define _STRUCT_ 0x0B
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Bit shift macros //
// Thanks to @VitorBoss //
// https://github.com/Marzogh/SPIMemory/issues/76 //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#define Lo(param) ((char *)&param)[0] //0x000y
#define Hi(param) ((char *)&param)[1] //0x00y0
#define Higher(param) ((char *)&param)[2] //0x0y00
#define Highest(param) ((char *)&param)[3] //0xy000
#define Low(param) ((int *)&param)[0] //0x00yy
#define Top(param) ((int *)&param)[1] //0xyy00
// Set bit and clear bit
// x -> byte, y -> bit
#define setBit(x, y) x |= (1 << y)
#define clearBit(x, y) x &= ~(1 << y)
#define toggleBit(x, y) x ^= (1 << y)
// Query to see if bit is set or cleared.
// x -> byte, y -> bit
#define bitIsSet(x, y) x & (1 << y)
#define bitIsClear(x, y) !(x & (1 << y))
//Set nibbles
// x -> byte, y -> value to set
#define setLowerNibble(x, y) x &= 0xF0; x |= (y & 0x0F) // Clear out the lower nibble // OR in the desired mask
#define setUpperNibble(x, y) x &= 0x0F; x |= (y & 0xF0) // Clear out the lower nibble // OR in the desired mask
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#ifndef LED_BUILTIN //fix for boards without that definition
#define LED_BUILTIN 13
#endif

View File

@ -0,0 +1,158 @@
/* Arduino SPIMemory Library v.3.2.0
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 14/11/2016
* Modified by Prajwal Bhattaram - 20/04/2018
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "diagnostics.h"
//Subfunctions for troubleshooting function
void Diagnostics::_printErrorCode(void) {
Serial.print("Error code: 0x");
if (errorcode < 0x10) {
Serial.print("0");
}
Serial.println(errorcode, HEX);
}
void Diagnostics::_printSupportLink(void) {
Serial.print("If this does not help resolve/clarify this issue, ");
Serial.println("please raise an issue at http://www.github.com/Marzogh/SPIMemory/issues with the details of what your were doing when this error occurred");
}
//Troubleshooting function. Called when #ifdef RUNDIAGNOSTIC is uncommented at the top of SPIMemory.h.
void Diagnostics::troubleshoot(uint8_t _code, bool printoverride) {
bool _printoverride;
errorcode = _code;
#if defined (RUNDIAGNOSTIC) && !defined (ARDUINO_ARCH_AVR)
_printoverride = true;
#elif defined (RUNDIAGNOSTIC) && defined (ARDUINO_ARCH_AVR)
_printErrorCode();
#endif
#if !defined (RUNDIAGNOSTIC)
_printoverride = printoverride;
#endif
if (_printoverride) {
#if defined (ARDUINO_ARCH_AVR)
_printErrorCode();
#else
switch (_code) {
case SUCCESS:
Serial.println("Function executed successfully");
break;
case NORESPONSE:
Serial.println("Check your wiring. Flash chip is non-responsive.");
break;
case CALLBEGIN:
Serial.println("*constructor_of_choice*.begin() was not called in void setup()");
break;
case UNKNOWNCHIP:
Serial.println("Unable to identify chip. Are you sure this chip is supported?");
Serial.println("Chip details:");
break;
case UNKNOWNCAP:
Serial.println("Unable to identify capacity. Is this chip officially supported? If not, please define a `CAPACITY` constant and include it in flash.begin(CAPACITY).");
break;
case CHIPBUSY:
Serial.println("Chip is busy.");
Serial.println("Make sure all pins have been connected properly");
break;
case OUTOFBOUNDS:
Serial.println("Page overflow has been disabled and the address called exceeds the memory");
break;
case CANTENWRITE:
Serial.println("Unable to Enable Writing to chip.");
Serial.println("Please make sure the HOLD & WRITEPROTECT pins are pulled up to VCC");
break;
case PREVWRITTEN:
Serial.println("This sector already contains data.");
Serial.println("Please make sure the sectors being written to are erased.");
break;
case LOWRAM:
Serial.println("You are running low on SRAM. Please optimise your program for better RAM usage");
/*#if defined (ARDUINO_ARCH_SAM)
Serial.print("Current Free SRAM: ");
Serial.println(freeRAM());
#endif*/
break;
case UNSUPPORTEDFUNC:
Serial.println("This function is not supported by the flash memory hardware.");
break;
case SYSSUSPEND:
Serial.println("Unable to suspend/resume operation.");
break;
case ERRORCHKFAIL:
Serial.println("Write Function has failed errorcheck.");
break;
case UNABLETO4BYTE:
Serial.println("Unable to enable 4-byte addressing.");
break;
case UNABLETO3BYTE:
Serial.println("Unable to disable 4-byte addressing.");
break;
case CHIPISPOWEREDDOWN:
Serial.println("The Flash chip is currently powered down.");
break;
case NOSFDP:
Serial.println("The Flash chip does not support SFDP.");
break;
case NOSFDPERASEPARAM:
Serial.println("Unable to read Erase Parameters from chip. Reverting to library defaults.");
break;
case NOSFDPERASETIME:
Serial.println("Unable to read erase times from flash memory. Reverting to library defaults.");
break;
case NOSFDPPROGRAMTIMEPARAM:
Serial.println("Unable to read program times from flash memory. Reverting to library defaults.");
break;
default:
Serial.println("Unknown error");
break;
}
if (_code == ERRORCHKFAIL || _code == CANTENWRITE || _code == UNKNOWNCHIP || _code == NORESPONSE) {
_printSupportLink();
}
#endif
}
}
Diagnostics diagnostics; // default instantiation of Diagnostics object

View File

@ -0,0 +1,77 @@
/* Arduino SPIMemory Library v.3.1.0
* Copyright (C) 2017 by Prajwal Bhattaram
* Created by Prajwal Bhattaram - 19/05/2015
* Modified by @boseji <salearj@hotmail.com> - 02/03/2017
* Modified by Prajwal Bhattaram - 24/02/2018
*
* This file is part of the Arduino SPIMemory Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library 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 Library 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 v3.0
* along with the Arduino SPIMemory Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef DIAGNOSTICS_H
#define DIAGNOSTICS_H
#include "SPIMemory.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// List of Error codes //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#define SUCCESS 0x00
#define CALLBEGIN 0x01
#define UNKNOWNCHIP 0x02
#define UNKNOWNCAP 0x03
#define CHIPBUSY 0x04
#define OUTOFBOUNDS 0x05
#define CANTENWRITE 0x06
#define PREVWRITTEN 0x07
#define LOWRAM 0x08
#define SYSSUSPEND 0x09
#define ERRORCHKFAIL 0x0A
#define NORESPONSE 0x0B
#define UNSUPPORTEDFUNC 0x0C
#define UNABLETO4BYTE 0x0D
#define UNABLETO3BYTE 0x0E
#define CHIPISPOWEREDDOWN 0x0F
#define NOSFDP 0x10
#define NOSFDPERASEPARAM 0x11
#define NOSFDPERASETIME 0x12
#define NOSFDPPROGRAMTIMEPARAM 0x13
#define UNKNOWNERROR 0xFE
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
class Diagnostics {
public:
//------------------------------------ Constructor ------------------------------------//
Diagnostics(void){};
~Diagnostics(void){};
//------------------------------- Public functions -----------------------------------//
void troubleshoot(uint8_t _code, bool printoverride = false);
uint8_t errorcode;
private:
void _printErrorCode(void);
void _printSupportLink(void);
};
extern Diagnostics diagnostics; ///< default Diagnostics instance
#endif

View File

@ -15,6 +15,8 @@ void storeLastBaro(byte);
void storeCalibrationValue(uint16_t, byte);
byte readEEPROMVersion();
void storeEEPROMVersion(byte);
void storePageCRC32(byte, uint32_t);
uint32_t readPageCRC32(byte);
#if defined(CORE_STM32) || defined(CORE_TEENSY)
#define EEPROM_MAX_WRITE_BLOCK 64 //The maximum number of write operations that will be performed in one go. If we try to write to the EEPROM too fast (Each write takes ~3ms) then the rest of the system can hang)
@ -68,6 +70,8 @@ Current layout of EEPROM data (Version 3) is as follows (All sizes are in bytes)
| 1500 |192 | CANBUS config and data (Table 10_) |
| 1692 |192 | Table 11 - General settings |
| |
| 2514 |44 | Table CRC32 values. Last table first|
| 2558 |1 | Last recorded Baro value |
| 2559 |512 | Calibration data (O2) |
| 3071 |512 | Calibration data (IAT) |
| 3583 |512 | Calibration data (CLT) |
@ -141,6 +145,7 @@ Current layout of EEPROM data (Version 3) is as follows (All sizes are in bytes)
#define EEPROM_CONFIG10_END 2094
//Calibration data is stored at the end of the EEPROM (This is in case any further calibration tables are needed as they are large blocks)
#define EEPROM_PAGE_CRC32 2514 //Size of this is 4 * <number of pages> (CRC32 = 32 bits)
#define EEPROM_LAST_BARO 2558
#define EEPROM_CALIBRATION_O2 2559
#define EEPROM_CALIBRATION_IAT 3071

View File

@ -8,24 +8,21 @@ A full copy of the license may be found in the projects root directory
#include "globals.h"
#include "table.h"
#include "comms.h"
#if defined(CORE_SAMD21)
#include "src/FlashStorage/FlashAsEEPROM.h"
#else
#include <EEPROM.h>
#endif
#include EEPROM_LIB_H //This is defined in the board .h files
#include "storage.h"
void writeAllConfig()
{
writeConfig(1);
if (eepromWritesPending == false) { writeConfig(2); }
if (eepromWritesPending == false) { writeConfig(3); }
if (eepromWritesPending == false) { writeConfig(4); }
if (eepromWritesPending == false) { writeConfig(5); }
if (eepromWritesPending == false) { writeConfig(6); }
if (eepromWritesPending == false) { writeConfig(7); }
if (eepromWritesPending == false) { writeConfig(8); }
if (eepromWritesPending == false) { writeConfig(9); }
if (eepromWritesPending == false) { writeConfig(10); }
writeConfig(veSetPage);
if (eepromWritesPending == false) { writeConfig(veMapPage); }
if (eepromWritesPending == false) { writeConfig(ignMapPage); }
if (eepromWritesPending == false) { writeConfig(ignSetPage); }
if (eepromWritesPending == false) { writeConfig(afrMapPage); }
if (eepromWritesPending == false) { writeConfig(afrSetPage); }
if (eepromWritesPending == false) { writeConfig(boostvvtPage); }
if (eepromWritesPending == false) { writeConfig(seqFuelPage); }
if (eepromWritesPending == false) { writeConfig(canbusPage); }
if (eepromWritesPending == false) { writeConfig(warmupPage); }
}
@ -648,10 +645,50 @@ void writeCalibration()
}
/*
Takes a page number and CRC32 value then stores it in the relevant place in EEPROM
Note: Each pages requires 4 bytes for its CRC32. These are stored in reverse page order (ie the last page is store first in EEPROM)
*/
void storePageCRC32(byte pageNo, uint32_t crc32_val)
{
uint16_t address; //Start address for the relevant page
address = EEPROM_PAGE_CRC32 + ((NUM_PAGES - pageNo) * 4);
//One = Most significant -> Four = Least significant byte
byte four = (crc32_val & 0xFF);
byte three = ((crc32_val >> 8) & 0xFF);
byte two = ((crc32_val >> 16) & 0xFF);
byte one = ((crc32_val >> 24) & 0xFF);
//Write the 4 bytes into the eeprom memory.
EEPROM.update(address, four);
EEPROM.update(address + 1, three);
EEPROM.update(address + 2, two);
EEPROM.update(address + 3, one);
}
/*
Retrieves and returns the 4 byte CRC32 for a given page from EEPROM
*/
uint32_t readPageCRC32(byte pageNo)
{
uint16_t address; //Start address for the relevant page
address = EEPROM_PAGE_CRC32 + ((NUM_PAGES - pageNo) * 4);
//Read the 4 bytes from the eeprom memory.
uint32_t four = EEPROM.read(address);
uint32_t three = EEPROM.read(address + 1);
uint32_t two = EEPROM.read(address + 2);
uint32_t one = EEPROM.read(address + 3);
//Return the recomposed long by using bitshift.
return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}
// Utility functions.
// By having these in this file, it prevents other files from calling EEPROM functions directly. This is useful due to differences in the EEPROM libraries on different devces
byte readLastBaro() { return EEPROM.read(EEPROM_LAST_BARO); }
void storeLastBaro(byte newValue) { EEPROM.update(EEPROM_LAST_BARO, newValue); }
void storeCalibrationValue(uint16_t location, byte value) { EEPROM.update(location, value); } //This is essentially just an abstraction for EEPROM.update()
byte readEEPROMVersion() { return EEPROM.read(EEPROM_DATA_VERSION); }
void storeEEPROMVersion(byte newVersion) { EEPROM.update(EEPROM_DATA_VERSION, newVersion); }
void storeEEPROMVersion(byte newVersion) { EEPROM.update(EEPROM_DATA_VERSION, newVersion); }

View File

@ -19,6 +19,13 @@ Hence we will preload the timer with 131 cycles to leave 125 until overflow (1ms
#ifndef TIMERS_H
#define TIMERS_H
volatile bool tachoAlt = false;
#define TACHO_PULSE_HIGH() *tach_pin_port |= (tach_pin_mask)
#define TACHO_PULSE_LOW() *tach_pin_port &= ~(tach_pin_mask)
enum TachoOutputStatus {DEACTIVE, READY, ACTIVE}; //The 3 statuses that the tacho output pulse can have
volatile uint8_t tachoEndTime; //The time (in ms) that the tacho pulse needs to end at
volatile TachoOutputStatus tachoOutputFlag;
volatile byte loop33ms;
volatile byte loop66ms;
volatile byte loop100ms;
@ -32,7 +39,7 @@ volatile uint16_t last250msLoopCount = 1000; //Set to effectively random number
#if defined (CORE_TEENSY)
IntervalTimer lowResTimer;
void oneMSInterval();
#elif defined(CORE_STM32)
#elif defined(CORE_STM32_OFFICIAL) || defined(CORE_STM32_GENERIC)
void oneMSInterval();
#endif
void initialiseTimers();

View File

@ -62,6 +62,35 @@ void oneMSInterval() //Most ARM chips can simply call a function
if(ignitionSchedule4.Status == RUNNING) { if( (ignitionSchedule4.startTime < targetOverdwellTime) && (configPage4.useDwellLim) && (isCrankLocked != true) ) { endCoil4Charge(); ignitionSchedule4.Status = OFF; } }
if(ignitionSchedule5.Status == RUNNING) { if( (ignitionSchedule5.startTime < targetOverdwellTime) && (configPage4.useDwellLim) && (isCrankLocked != true) ) { endCoil5Charge(); ignitionSchedule5.Status = OFF; } }
//Tacho output check
//Tacho is flagged as being ready for a pulse by the ignition outputs.
if(tachoOutputFlag == READY)
{
//Check for half speed tacho
if( (configPage2.tachoDiv == 0) || (tachoAlt == true) )
{
TACHO_PULSE_LOW();
//ms_counter is cast down to a byte as the tacho duration can only be in the range of 1-6, so no extra resolution above that is required
tachoEndTime = (uint8_t)ms_counter + configPage2.tachoDuration;
tachoOutputFlag = ACTIVE;
}
else
{
//Don't run on this pulse (Half speed tacho)
tachoOutputFlag = DEACTIVE;
}
tachoAlt = !tachoAlt; //Flip the alternating value incase half speed tacho is in use.
}
else if(tachoOutputFlag == ACTIVE)
{
//If the tacho output is already active, check whether it's reached it's end time
if((uint8_t)ms_counter >= tachoEndTime)
{
TACHO_PULSE_HIGH();
tachoOutputFlag = DEACTIVE;
}
}
//30Hz loop
@ -95,7 +124,7 @@ void oneMSInterval() //Most ARM chips can simply call a function
{
loop250ms = 0; //Reset Counter
BIT_SET(TIMER_mask, BIT_TIMER_4HZ);
#if defined(CORE_STM32) //debug purpose, only visal for running code
#if defined(CORE_STM32) //debug purpose, only visual for running code
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
#endif
@ -143,7 +172,8 @@ void oneMSInterval() //Most ARM chips can simply call a function
//Check whether fuel pump priming is complete
if(fpPrimed == false)
{
if(currentStatus.secl >= configPage2.fpPrime)
//fpPrimeTime is the time that the pump priming started. This is 0 on startup, but can be changed if the unit has been running on USB power and then had the ignition turned on (Which starts the priming again)
if( (currentStatus.secl - fpPrimeTime) >= configPage2.fpPrime)
{
fpPrimed = true; //Mark the priming as being completed
if(currentStatus.RPM == 0)
@ -193,7 +223,7 @@ void oneMSInterval() //Most ARM chips can simply call a function
#if defined(CORE_AVR) //AVR chips use the ISR for this
//Reset Timer2 to trigger in another ~1ms
TCNT2 = 131; //Preload timer2 with 100 cycles, leaving 156 till overflow.
TIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
//TIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
#endif
}

View File

@ -6,11 +6,7 @@
*/
#include "globals.h"
#include "storage.h"
#if defined(CORE_SAMD21)
#include "src/FlashStorage/FlashAsEEPROM.h"
#else
#include <EEPROM.h>
#endif
#include EEPROM_LIB_H //This is defined in the board .h files
void doUpdates()
{

View File

@ -8,6 +8,6 @@ These are some utility functions and variables used through the main code
void setResetControlPinState();
byte pinTranslate(byte);
uint32_t calculateCRC32(byte);
#endif // UTILS_H

View File

@ -13,6 +13,10 @@
#include "globals.h"
#include "utils.h"
#include "decoders.h"
#include "comms.h"
#include "src/FastCRC/FastCRC.h"
FastCRC32 CRC32;
//This function performs a translation between the pin list that appears in TS and the actual pin numbers
//For the digital IO, this will simply return the same number as the rawPin value as those are mapped directly.
@ -51,3 +55,118 @@ void setResetControlPinState()
break;
}
}
/*
Calculates and returns the CRC32 value of a given page of memory
*/
uint32_t calculateCRC32(byte pageNo)
{
uint32_t CRC32_val;
byte raw_value;
void* pnt_configPage;
//This sucks (again) for all the 3D map pages that have to have a translation performed
switch(pageNo)
{
case veMapPage:
//Confirmed working
raw_value = getPageValue(veMapPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[veMapPage]; x++)
//for(uint16_t x=1; x< 288; x++)
{
raw_value = getPageValue(veMapPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case veSetPage:
//Confirmed working
pnt_configPage = &configPage2; //Create a pointer to Page 1 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage2) );
break;
case ignMapPage:
//Confirmed working
raw_value = getPageValue(ignMapPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[ignMapPage]; x++)
{
raw_value = getPageValue(ignMapPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case ignSetPage:
//Confirmed working
pnt_configPage = &configPage4; //Create a pointer to Page 4 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage4) );
break;
case afrMapPage:
//Confirmed working
raw_value = getPageValue(afrMapPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[afrMapPage]; x++)
{
raw_value = getPageValue(afrMapPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case afrSetPage:
//Confirmed working
pnt_configPage = &configPage6; //Create a pointer to Page 4 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage6) );
break;
case boostvvtPage:
//Confirmed working
raw_value = getPageValue(boostvvtPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[boostvvtPage]; x++)
{
raw_value = getPageValue(boostvvtPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case seqFuelPage:
//Confirmed working
raw_value = getPageValue(seqFuelPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[seqFuelPage]; x++)
{
raw_value = getPageValue(seqFuelPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case canbusPage:
//Confirmed working
pnt_configPage = &configPage9; //Create a pointer to Page 9 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage9) );
break;
case warmupPage:
//Confirmed working
pnt_configPage = &configPage10; //Create a pointer to Page 10 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage10) );
break;
default:
break;
}
return CRC32_val;
}