Merge pull request #7 from VIPQualityPost/layout

Rev 3 layout
This commit is contained in:
VIPQualityPost
2023-11-17 17:44:31 -05:00
committed by GitHub
432 changed files with 340635 additions and 189688 deletions

View File

@@ -1 +0,0 @@
Ref,Val,Package,PosX,PosY,Rot,Side
1 Ref Val Package PosX PosY Rot Side

View File

@@ -1,70 +0,0 @@
Ref,Val,Package,PosX,PosY,Rot,Side
"C101","10u","C_0805_2012Metric",-8.275000,16.250000,0.000000,top
"C102","10u","C_0805_2012Metric",-8.275000,14.225000,180.000000,top
"C103","10u","C_0805_2012Metric",-8.275000,12.175000,0.000000,top
"C104","10u","C_0805_2012Metric",8.375000,16.250000,0.000000,top
"C105","10u","C_0805_2012Metric",8.375000,12.175000,180.000000,top
"C106","10u","C_0805_2012Metric",6.025000,-5.725000,180.000000,top
"C107","10u","C_0805_2012Metric",8.375000,14.225000,180.000000,top
"C108","10u","C_0805_2012Metric",6.025000,-7.750000,180.000000,top
"C201","47p","C_0402_1005Metric",-6.600000,-3.900000,180.000000,top
"C202","47p","C_0402_1005Metric",-4.600000,-3.900000,0.000000,top
"C301","1u","C_0402_1005Metric",-14.200000,-1.425000,-135.000000,top
"C302","100n","C_0402_1005Metric",-12.025000,-3.600000,-135.000000,top
"C303","100n","C_0402_1005Metric",-9.285589,7.560589,135.000000,top
"C304","100n","C_0402_1005Metric",-4.774651,0.910051,45.000000,top
"C305","20p","C_0402_1005Metric",-16.089411,8.715589,-135.000000,top
"C306","20p","C_0402_1005Metric",-15.800000,4.650000,135.000000,top
"C307","100n","C_0402_1005Metric",-13.525000,-2.100000,-135.000000,top
"C308","100n","C_0402_1005Metric",-11.335589,8.989411,45.000000,top
"C401","100n","C_0402_1005Metric",1.600000,4.025000,180.000000,top
"C501","10u","C_0805_2012Metric",6.025000,-9.775000,180.000000,top
"C502","1u","C_0402_1005Metric",11.500000,-7.775000,90.000000,top
"C503","10u","C_0805_2012Metric",15.825000,-4.200000,90.000000,top
"C601","10n","C_0402_1005Metric",12.825000,-1.450000,180.000000,top
"C602","220n","C_0402_1005Metric",11.000000,-3.750000,-90.000000,top
"C701","100n","C_0402_1005Metric",-6.180000,6.500000,180.000000,top
"C702","100n","C_0402_1005Metric",3.800000,6.475000,180.000000,top
"C703","100n","C_0402_1005Metric",-1.480000,6.461143,180.000000,top
"D101","SS210","D_SMA",15.600000,2.125000,-90.000000,top
"D301","USER","D_0603_1608Metric",-15.237500,-6.375000,0.000000,top
"D302","FAULT","D_0603_1608Metric",-15.237500,-8.025000,0.000000,top
"D501","BZT52C30S","D_SOD-323",7.800000,9.450000,-90.000000,top
"D502","BZT52C30S","D_SOD-323",9.825000,9.150000,-90.000000,top
"D601","OCD","D_0603_1608Metric",13.025000,0.550000,-90.000000,top
"D602","1N4148WS","D_SOD-323",13.150000,-4.925000,180.000000,top
"D603","1N4148WS","D_SOD-323",13.150000,-2.925000,0.000000,top
"FID101","Fiducial","Fiducial_0.5mm_Mask1mm",-16.400000,10.775000,0.000000,top
"FID102","Fiducial","Fiducial_0.5mm_Mask1mm",-16.050000,-9.800000,0.000000,top
"FID103","Fiducial","Fiducial_0.5mm_Mask1mm",16.200000,10.325000,0.000000,top
"J201","USB_C_Receptacle_USB2.0","USB-C-SMD_GT-USB-7052_1",-9.239716,-9.250629,-45.000000,top
"JP101","TERM","R_0402_1005Metric",5.875000,-4.075000,0.000000,top
"L501","33u","L_1210_3225Metric",14.525000,-7.550000,0.000000,top
"Q501","MMBT5551","SOT-23",12.762500,8.150000,0.000000,top
"R101","5k1","R_0402_1005Metric",-4.175000,-10.875000,90.000000,top
"R102","5k1","R_0402_1005Metric",-7.650000,-5.375000,90.000000,top
"R103","120R","R_0402_1005Metric",5.875000,-3.000000,0.000000,top
"R104","0R","R_0402_1005Metric",3.925000,-3.000000,180.000000,top
"R105","0R","R_0402_1005Metric",3.925000,-4.075000,0.000000,top
"R301","5k1","R_0402_1005Metric",-15.800000,-5.050000,180.000000,top
"R302","5k1","R_0402_1005Metric",-11.350000,-4.325000,-135.000000,top
"R303","51k","R_0402_1005Metric",-8.250000,10.575000,180.000000,top
"R304","3k","R_0402_1005Metric",-8.250000,9.500000,180.000000,top
"R306","10k","R_0402_1005Metric",-10.625000,8.275000,45.000000,top
"R401","10k","R_0402_1005Metric",-15.800000,-4.000000,180.000000,top
"R501","200k","R_0402_1005Metric",10.890000,-9.360000,180.000000,top
"R502","51k","R_0402_1005Metric",9.365000,-9.810000,-90.000000,top
"R503","22k","R_0402_1005Metric",8.290000,-9.810000,-90.000000,top
"R504","5R","R_0603_1608Metric",15.500000,7.925000,90.000000,top
"R505","15k","R_0402_1005Metric",9.850000,7.000000,0.000000,top
"R601","5k1","R_0402_1005Metric",15.865000,-1.950000,0.000000,top
"U201","SRV05-4","SOT-23-6",-5.400000,-6.500000,-90.000000,top
"U301","STM32G431","QFN-48-1EP_7x7mm_P0.5mm_EP5.6x5.6mm",-10.349651,1.935051,-45.000000,top
"U302","SN65HVD230","SOIC-8_3.9x4.9mm_P1.27mm",0.550000,-8.045000,0.000000,top
"U401","MT6835","MagnTek TSSOP-16-1EP_4.4x5mm_P0.65mm",0.000000,0.000000,90.000000,top
"U501","MAX15062CATA+T","TDFN-8_L2.0-W2.0-P0.50-BL",9.215000,-7.660000,-90.000000,top
"U601","L6226QTR","VQFN-32_L5.0-W5.0-P0.50-TL-EP",7.700000,2.750000,-135.000000,top
"U701","ACS711KEXLT-15AB-T","IC-SMD_ACS711KEXLT-15AB-T",-5.300000,9.050000,180.000000,top
"U702","ACS711KEXLT-15AB-T","IC-SMD_ACS711KEXLT-15AB-T",4.650000,9.000000,180.000000,top
"U703","ACS711KEXLT-15AB-T","IC-SMD_ACS711KEXLT-15AB-T",-0.600000,9.000000,180.000000,top
"Y301","12MHz","Crystal_SMD_3225-4Pin_3.2x2.5mm",-14.476777,7.076142,-135.000000,top
1 Ref Val Package PosX PosY Rot Side
2 C101 10u C_0805_2012Metric -8.275000 16.250000 0.000000 top
3 C102 10u C_0805_2012Metric -8.275000 14.225000 180.000000 top
4 C103 10u C_0805_2012Metric -8.275000 12.175000 0.000000 top
5 C104 10u C_0805_2012Metric 8.375000 16.250000 0.000000 top
6 C105 10u C_0805_2012Metric 8.375000 12.175000 180.000000 top
7 C106 10u C_0805_2012Metric 6.025000 -5.725000 180.000000 top
8 C107 10u C_0805_2012Metric 8.375000 14.225000 180.000000 top
9 C108 10u C_0805_2012Metric 6.025000 -7.750000 180.000000 top
10 C201 47p C_0402_1005Metric -6.600000 -3.900000 180.000000 top
11 C202 47p C_0402_1005Metric -4.600000 -3.900000 0.000000 top
12 C301 1u C_0402_1005Metric -14.200000 -1.425000 -135.000000 top
13 C302 100n C_0402_1005Metric -12.025000 -3.600000 -135.000000 top
14 C303 100n C_0402_1005Metric -9.285589 7.560589 135.000000 top
15 C304 100n C_0402_1005Metric -4.774651 0.910051 45.000000 top
16 C305 20p C_0402_1005Metric -16.089411 8.715589 -135.000000 top
17 C306 20p C_0402_1005Metric -15.800000 4.650000 135.000000 top
18 C307 100n C_0402_1005Metric -13.525000 -2.100000 -135.000000 top
19 C308 100n C_0402_1005Metric -11.335589 8.989411 45.000000 top
20 C401 100n C_0402_1005Metric 1.600000 4.025000 180.000000 top
21 C501 10u C_0805_2012Metric 6.025000 -9.775000 180.000000 top
22 C502 1u C_0402_1005Metric 11.500000 -7.775000 90.000000 top
23 C503 10u C_0805_2012Metric 15.825000 -4.200000 90.000000 top
24 C601 10n C_0402_1005Metric 12.825000 -1.450000 180.000000 top
25 C602 220n C_0402_1005Metric 11.000000 -3.750000 -90.000000 top
26 C701 100n C_0402_1005Metric -6.180000 6.500000 180.000000 top
27 C702 100n C_0402_1005Metric 3.800000 6.475000 180.000000 top
28 C703 100n C_0402_1005Metric -1.480000 6.461143 180.000000 top
29 D101 SS210 D_SMA 15.600000 2.125000 -90.000000 top
30 D301 USER D_0603_1608Metric -15.237500 -6.375000 0.000000 top
31 D302 FAULT D_0603_1608Metric -15.237500 -8.025000 0.000000 top
32 D501 BZT52C30S D_SOD-323 7.800000 9.450000 -90.000000 top
33 D502 BZT52C30S D_SOD-323 9.825000 9.150000 -90.000000 top
34 D601 OCD D_0603_1608Metric 13.025000 0.550000 -90.000000 top
35 D602 1N4148WS D_SOD-323 13.150000 -4.925000 180.000000 top
36 D603 1N4148WS D_SOD-323 13.150000 -2.925000 0.000000 top
37 FID101 Fiducial Fiducial_0.5mm_Mask1mm -16.400000 10.775000 0.000000 top
38 FID102 Fiducial Fiducial_0.5mm_Mask1mm -16.050000 -9.800000 0.000000 top
39 FID103 Fiducial Fiducial_0.5mm_Mask1mm 16.200000 10.325000 0.000000 top
40 J201 USB_C_Receptacle_USB2.0 USB-C-SMD_GT-USB-7052_1 -9.239716 -9.250629 -45.000000 top
41 JP101 TERM R_0402_1005Metric 5.875000 -4.075000 0.000000 top
42 L501 33u L_1210_3225Metric 14.525000 -7.550000 0.000000 top
43 Q501 MMBT5551 SOT-23 12.762500 8.150000 0.000000 top
44 R101 5k1 R_0402_1005Metric -4.175000 -10.875000 90.000000 top
45 R102 5k1 R_0402_1005Metric -7.650000 -5.375000 90.000000 top
46 R103 120R R_0402_1005Metric 5.875000 -3.000000 0.000000 top
47 R104 0R R_0402_1005Metric 3.925000 -3.000000 180.000000 top
48 R105 0R R_0402_1005Metric 3.925000 -4.075000 0.000000 top
49 R301 5k1 R_0402_1005Metric -15.800000 -5.050000 180.000000 top
50 R302 5k1 R_0402_1005Metric -11.350000 -4.325000 -135.000000 top
51 R303 51k R_0402_1005Metric -8.250000 10.575000 180.000000 top
52 R304 3k R_0402_1005Metric -8.250000 9.500000 180.000000 top
53 R306 10k R_0402_1005Metric -10.625000 8.275000 45.000000 top
54 R401 10k R_0402_1005Metric -15.800000 -4.000000 180.000000 top
55 R501 200k R_0402_1005Metric 10.890000 -9.360000 180.000000 top
56 R502 51k R_0402_1005Metric 9.365000 -9.810000 -90.000000 top
57 R503 22k R_0402_1005Metric 8.290000 -9.810000 -90.000000 top
58 R504 5R R_0603_1608Metric 15.500000 7.925000 90.000000 top
59 R505 15k R_0402_1005Metric 9.850000 7.000000 0.000000 top
60 R601 5k1 R_0402_1005Metric 15.865000 -1.950000 0.000000 top
61 U201 SRV05-4 SOT-23-6 -5.400000 -6.500000 -90.000000 top
62 U301 STM32G431 QFN-48-1EP_7x7mm_P0.5mm_EP5.6x5.6mm -10.349651 1.935051 -45.000000 top
63 U302 SN65HVD230 SOIC-8_3.9x4.9mm_P1.27mm 0.550000 -8.045000 0.000000 top
64 U401 MT6835 MagnTek TSSOP-16-1EP_4.4x5mm_P0.65mm 0.000000 0.000000 90.000000 top
65 U501 MAX15062CATA+T TDFN-8_L2.0-W2.0-P0.50-BL 9.215000 -7.660000 -90.000000 top
66 U601 L6226QTR VQFN-32_L5.0-W5.0-P0.50-TL-EP 7.700000 2.750000 -135.000000 top
67 U701 ACS711KEXLT-15AB-T IC-SMD_ACS711KEXLT-15AB-T -5.300000 9.050000 180.000000 top
68 U702 ACS711KEXLT-15AB-T IC-SMD_ACS711KEXLT-15AB-T 4.650000 9.000000 180.000000 top
69 U703 ACS711KEXLT-15AB-T IC-SMD_ACS711KEXLT-15AB-T -0.600000 9.000000 180.000000 top
70 Y301 12MHz Crystal_SMD_3225-4Pin_3.2x2.5mm -14.476777 7.076142 -135.000000 top

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.8*%
%TF.CreationDate,2023-11-08T18:05:15-05:00*%
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-11-17T16:04:09-05:00*%
%TF.ProjectId,lemon-pepper,6c656d6f-6e2d-4706-9570-7065722e6b69,rev?*%
%TF.SameCoordinates,PX8b85c60PY52f2218*%
%TF.FileFunction,Soldermask,Bot*%
%TF.FilePolarity,Negative*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.8) date 2023-11-08 18:05:15*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-11-17 16:04:09*
%MOMM*%
%LPD*%
G01*
@@ -28,62 +28,75 @@ G04 Aperture macros list*
20,1,$1+$1,$4,$5,$6,$7,0*
20,1,$1+$1,$6,$7,$8,$9,0*
20,1,$1+$1,$8,$9,$2,$3,0*%
%AMHorizOval*
0 Thick line with rounded ends*
0 $1 width*
0 $2 $3 position (X,Y) of the first rounded end (center of the circle)*
0 $4 $5 position (X,Y) of the second rounded end (center of the circle)*
0 Add line between two ends*
20,1,$1,$2,$3,$4,$5,0*
0 Add two circle primitives to create the rounded ends*
1,1,$1,$2,$3*
1,1,$1,$4,$5*%
G04 Aperture macros list end*
%ADD10C,0.990600*%
%ADD11C,0.700000*%
%ADD12C,1.300000*%
%ADD13O,1.750000X1.750000*%
%ADD11C,1.000000*%
%ADD12HorizOval,0.600000X0.141421X-0.141421X-0.141421X0.141421X0*%
%ADD13C,0.660000*%
%ADD14O,1.750000X2.000000*%
%ADD15C,5.800000*%
%ADD16RoundRect,0.250000X0.600000X0.725000X-0.600000X0.725000X-0.600000X-0.725000X0.600000X-0.725000X0*%
%ADD15O,1.750000X1.750000*%
%ADD16C,5.800000*%
%ADD17O,1.700000X1.950000*%
%ADD18RoundRect,0.250000X0.600000X0.725000X-0.600000X0.725000X-0.600000X-0.725000X0.600000X-0.725000X0*%
G04 APERTURE END LIST*
D10*
%TO.C,J301*%
X-9718333Y-15684655D03*
X-4925529Y-13718012D03*
X-4537805Y-15712678D03*
X-4925529Y-13718012D03*
X-9718333Y-15684655D03*
%TD*%
D11*
%TO.C,J201*%
X-11891366Y-6598979D03*
X-9073223Y-12467336D03*
X-6032664Y-9426777D03*
X-12467336Y-9073223D03*
X-9426777Y-6032664D03*
D12*
X-9416493Y-6033293D03*
X-12457052Y-9073852D03*
X-6022380Y-9427406D03*
X-9062939Y-12467965D03*
X-6598350Y-11901650D03*
D13*
X-11901650Y-6598350D03*
%TD*%
D13*
D14*
%TO.C,M101*%
X-3700000Y14900000D03*
D14*
X-3700000Y12150000D03*
D13*
X-1200000Y14900000D03*
D14*
X-1200000Y12150000D03*
D13*
X1300000Y14900000D03*
D14*
X1300000Y12150000D03*
D13*
X3800000Y12150000D03*
D15*
X3800000Y14900000D03*
D14*
X3800000Y12150000D03*
%TD*%
X1300000Y12150000D03*
D15*
%TO.C,H101*%
X-13000000Y-13000000D03*
X13000000Y-13000000D03*
X-13000000Y13000000D03*
X13000000Y13000000D03*
X1300000Y14900000D03*
D14*
X-1200000Y12150000D03*
D15*
X-1200000Y14900000D03*
D14*
X-3700000Y12150000D03*
D15*
X-3700000Y14900000D03*
%TD*%
D16*
%TO.C,J101*%
X7225000Y-14725000D03*
%TO.C,H101*%
X13000000Y-13000000D03*
X13000000Y13000000D03*
X-13000000Y-13000000D03*
X-13000000Y13000000D03*
%TD*%
D17*
X4725000Y-14725000D03*
X2225000Y-14725000D03*
%TO.C,J101*%
X-275000Y-14725000D03*
X2225000Y-14725000D03*
X4725000Y-14725000D03*
D18*
X7225000Y-14725000D03*
%TD*%
M02*

View File

@@ -1,23 +1,15 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.8*%
%TF.CreationDate,2023-11-08T18:05:15-05:00*%
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-11-17T16:04:09-05:00*%
%TF.ProjectId,lemon-pepper,6c656d6f-6e2d-4706-9570-7065722e6b69,rev?*%
%TF.SameCoordinates,PX8b85c60PY52f2218*%
%TF.FileFunction,Paste,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.8) date 2023-11-08 18:05:15*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-11-17 16:04:09*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
%ADD10C,1.300000*%
G04 APERTURE END LIST*
D10*
%TO.C,J201*%
X-9416493Y-6033293D03*
X-12457052Y-9073852D03*
X-6022380Y-9427406D03*
X-9062939Y-12467965D03*
%TD*%
M02*

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.8*%
%TF.CreationDate,2023-11-08T18:05:15-05:00*%
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-11-17T16:04:09-05:00*%
%TF.ProjectId,lemon-pepper,6c656d6f-6e2d-4706-9570-7065722e6b69,rev?*%
%TF.SameCoordinates,PX8b85c60PY52f2218*%
%TF.FileFunction,Profile,NP*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.8) date 2023-11-08 18:05:15*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-11-17 16:04:09*
%MOMM*%
%LPD*%
G01*
@@ -17,32 +17,32 @@ G04 APERTURE END LIST*
D10*
%TO.C,H101*%
X13000000Y-17500000D02*
X-13000000Y-17500000D01*
X-17500000Y-13000000D02*
X-17500000Y13000000D01*
X17500000Y13000000D02*
X17500000Y-13000000D01*
X-13000000Y17500000D02*
X13000000Y17500000D01*
X-17500000Y-13000000D02*
G75*
G03*
X-13000000Y-17500000I4499998J-2D01*
G01*
X13000000Y-17500000D02*
G75*
G03*
X17500000Y-13000000I2J4499998D01*
G01*
X-13000000Y17500000D02*
G75*
G03*
X-17500000Y13000000I0J-4500000D01*
X17500000Y-13000000I0J4500000D01*
G01*
X17500000Y13000000D02*
G75*
G03*
X13000000Y17500000I-4500000J0D01*
G01*
X-17500000Y-13000000D02*
G75*
G03*
X-13000000Y-17500000I4499998J-2D01*
G01*
X-13000000Y17500000D02*
G75*
G03*
X-17500000Y13000000I-2J-4499998D01*
G01*
X17500000Y13000000D02*
X17500000Y-13000000D01*
X13000000Y-17500000D02*
X-13000000Y-17500000D01*
X-13000000Y17500000D02*
X13000000Y17500000D01*
X-17500000Y-13000000D02*
X-17500000Y13000000D01*
%TD*%
M02*

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,31 @@
M48
; DRILL file {KiCad 7.0.8} date 2023 November 08, Wednesday 18:05:16
; DRILL file {KiCad 7.0.9} date Friday, November 17, 2023 at 04:04:11PM
; FORMAT={-:-/ absolute / inch / decimal}
; #@! TF.CreationDate,2023-11-08T18:05:16-05:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.8
; #@! TF.CreationDate,2023-11-17T16:04:11-05:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.9
; #@! TF.FileFunction,NonPlated,1,4,NPTH
FMAT,2
INCH
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T1C0.0390
T1C0.0236
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T2C0.0260
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T3C0.0390
%
G90
G05
T1
T2
X-0.4686Y-0.2598
T3
X-0.3826Y-0.6175
X-0.1939Y-0.5401
X-0.1787Y-0.6186
T1
G00X-0.2653Y-0.463
M15
G01X-0.2542Y-0.4741
M16
G05
T0
M30

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
M48
; DRILL file {KiCad 7.0.8} date 2023 November 08, Wednesday 18:05:16
; DRILL file {KiCad 7.0.9} date Friday, November 17, 2023 at 04:04:11PM
; FORMAT={-:-/ absolute / inch / decimal}
; #@! TF.CreationDate,2023-11-08T18:05:16-05:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.8
; #@! TF.CreationDate,2023-11-17T16:04:11-05:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.9
; #@! TF.FileFunction,Plated,1,4,PTH
FMAT,2
INCH
@@ -15,167 +15,176 @@ T3C0.0157
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T4C0.0276
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T5C0.0354
T5C0.0374
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T6C0.0374
T6C0.0413
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T7C0.0413
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T8C0.1220
T7C0.1220
%
G90
G05
T1
X-0.6378Y0.1486
X-0.626Y0.0236
X-0.623Y0.0679
X-0.6043Y0.0344
X-0.6033Y-0.001
X-0.6024Y0.1289
X-0.6535Y0.0856
X-0.627Y0.128
X-0.627Y0.0856
X-0.626Y0.061
X-0.626Y0.0157
X-0.6112Y-0.2008
X-0.6112Y-0.2234
X-0.6111Y-0.1781
X-0.6033Y0.1289
X-0.6033Y-0.0226
X-0.5846Y0.1447
X-0.5778Y-0.0266
X-0.5807Y-0.0059
X-0.5768Y-0.0315
X-0.5679Y0.1604
X-0.5601Y-0.0068
X-0.5502Y0.1772
X-0.4764Y0.3238
X-0.4887Y0.3401
X-0.4705Y-0.2175
X-0.4646Y0.3071
X-0.4429Y-0.2146
X-0.4355Y-0.2504
X-0.3799Y-0.3484
X-0.3504Y-0.0974
X-0.3465Y-0.3799
X-0.3356Y0.247
X-0.3169Y0.2264
X-0.3112Y0.307
X-0.2992Y0.2057
X-0.2963Y0.3278
X-0.2844Y-0.0886
X-0.2835Y-0.0423
X-0.2736Y0.2047
X-0.2657Y-0.0207
X-0.2618Y-0.0876
X-0.2185Y0.1378
X-0.2067Y0.2244
X-0.1939Y0.1378
X-0.185Y0.2077
X-0.171Y0.1378
X-0.3641Y-0.1245
X-0.3494Y0.2598
X-0.3474Y-0.3799
X-0.3465Y-0.1083
X-0.3346Y0.2411
X-0.3137Y0.2251
X-0.3031Y-0.4262
X-0.2982Y0.2087
X-0.2963Y-0.0571
X-0.2756Y0.4252
X-0.2756Y0.3996
X-0.2736Y-0.0571
X-0.251Y-0.0561
X-0.2392Y-0.4272
X-0.2116Y-0.4065
X-0.2037Y0.1053
X-0.2025Y0.2943
X-0.2025Y0.2687
X-0.1811Y0.1053
X-0.1565Y0.1053
X-0.1329Y0.1053
X-0.124Y-0.2667
X-0.1063Y-0.1535
X-0.0797Y0.312
X-0.0482Y-0.1565
X-0.0236Y-0.1575
X-0.0128Y0.2549
X-0.0116Y0.3642
X0.0024Y-0.1575
X0.0305Y-0.1575
X0.0305Y-0.1831
X0.0915Y-0.1909
X0.0925Y-0.1673
X0.0561Y-0.1575
X0.0593Y0.2953
X0.0593Y0.2697
X0.1033Y-0.1565
X0.1663Y0.1476
X0.188Y0.1683
X0.2057Y0.2559
X0.2057Y0.187
X0.2234Y0.2052
X0.3602Y-0.0226
X0.4065Y0.0177
X0.4331Y0.0177
X0.4567Y0.0167
X0.1949Y0.1713
X0.2109Y0.1873
X0.2372Y0.2165
X0.3509Y-0.0211
X0.3686Y-0.003
X0.3809Y-0.0482
X0.3858Y0.0117
X0.4055Y0.0315
X0.4272Y-0.2224
X0.5335Y0.1831
T2
X-0.1457Y0.6604
X-0.0472Y0.6604
X0.0512Y0.6604
X0.1496Y0.6604
T3
X-0.5856Y-0.1053
X-0.576Y-0.0984
X-0.5069Y-0.1075
X-0.4587Y0.0778
X-0.4541Y-0.1263
X-0.442Y-0.3885
X-0.4587Y0.0768
X-0.4547Y-0.3858
X-0.4143Y-0.1241
X-0.4085Y0.124
X-0.4085Y0.0768
X-0.4085Y0.0266
X-0.3968Y-0.4339
X-0.3829Y-0.4518
X-0.3593Y0.0778
X-0.3396Y0.3228
X-0.3297Y-0.2776
X-0.2865Y-0.3382
X-0.2638Y0.2992
X-0.3467Y-0.2776
X-0.3091Y0.3415
X-0.2972Y-0.5118
X-0.2746Y-0.3445
X-0.247Y0.6398
X-0.2461Y0.56
X-0.2461Y0.4793
X-0.2461Y-0.6545
X-0.2146Y0.3521
X-0.2124Y-0.251
X-0.2116Y-0.0394
X-0.1954Y-0.0404
X-0.186Y-0.3504
X-0.1728Y-0.0089
X-0.1669Y0.002
X-0.0945Y-0.6506
X-0.0935Y-0.5748
X-0.0866Y-0.4409
X-0.0787Y0.2943
X-0.0778Y0.002
X-0.0768Y-0.0512
X-0.0669Y-0.4783
X-0.0443Y-0.4409
X-0.0295Y0.3501
X-0.0246Y-0.4783
X-0.0118Y-0.3425
X-0.0098Y-0.2677
X0.0197Y-0.4783
X0.0581Y-0.2411
X0.0875Y0.0187
X0.0875Y0.0236
X0.0876Y0.063
X0.0915Y-0.4665
X0.124Y0.2909
X0.1368Y-0.4291
X0.1368Y-0.4656
X0.1535Y0.0256
X0.1604Y0.2195
X0.1604Y0.1851
X0.1683Y0.1083
X0.1772Y0.3494
X0.1791Y0.0
X0.187Y-0.063
X0.2146Y0.0069
X0.2392Y0.2825
X0.2461Y0.6398
X0.247Y0.561
X0.2483Y0.4793
X0.2608Y0.2559
X0.2608Y0.1102
X0.2982Y0.2559
X0.3022Y0.1083
X0.3022Y0.0679
X0.3031Y0.1486
X0.3415Y0.1073
X0.3553Y-0.1722
X0.3661Y-0.2451
X0.3638Y-0.248
X0.3878Y-0.1969
X0.3902Y0.2406
X0.3917Y-0.1565
X0.3937Y-0.0679
X0.5364Y0.2598
X0.5374Y0.2116
X0.5374Y0.1644
X0.5374Y0.1191
X0.5856Y-0.2293
X0.4168Y0.214
X0.4459Y0.186
X0.5679Y-0.1398
X0.5856Y-0.2648
X0.5856Y-0.3002
X0.5856Y-0.3337
X0.6102Y0.2352
X0.6104Y0.3051
X0.623Y-0.1289
X0.623Y-0.0856
X0.6614Y-0.2323
T4
X-0.4682Y-0.2598
X-0.4908Y-0.3572
X-0.3711Y-0.2375
X-0.3572Y-0.4908
X-0.2375Y-0.3711
T5
X-0.4904Y-0.3572
X-0.3707Y-0.2375
X-0.3568Y-0.4909
X-0.2371Y-0.3712
T6
X-0.0108Y-0.5797
X0.0876Y-0.5797
X0.186Y-0.5797
X0.2844Y-0.5797
T7
T6
X-0.1457Y0.4783
X-0.0472Y0.4783
X0.0512Y0.4783
X0.1496Y0.4783
T8
T7
X-0.5118Y0.5118
X-0.5118Y-0.5118
X0.5118Y0.5118
X0.5118Y-0.5118
T6
T5
G00X-0.1516Y0.5866
M15
G01X-0.1398Y0.5866

View File

@@ -3,9 +3,9 @@
"GenerationSoftware": {
"Vendor": "KiCad",
"Application": "Pcbnew",
"Version": "7.0.8"
"Version": "7.0.9"
},
"CreationDate": "2023-11-08T18:05:15-05:00"
"CreationDate": "2023-11-17T16:04:09-05:00"
},
"GeneralSpecs": {
"ProjectId": {
@@ -24,8 +24,8 @@
"DesignRules": [
{
"Layers": "Outer",
"PadToPad": 0.1,
"PadToTrack": 0.1,
"PadToPad": 0.0,
"PadToTrack": 0.0,
"TrackToTrack": 0.1,
"MinLineWidth": 0.1,
"TrackToRegion": 0.2,
@@ -36,7 +36,7 @@
"PadToPad": 0.0,
"PadToTrack": 0.0,
"TrackToTrack": 0.1,
"MinLineWidth": 0.2,
"MinLineWidth": 0.1,
"TrackToRegion": 0.2,
"RegionToRegion": 0.2
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.8*%
%TF.CreationDate,2023-11-08T18:05:49-05:00*%
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-11-17T16:04:48-05:00*%
%TF.ProjectId,lemon-pepper,6c656d6f-6e2d-4706-9570-7065722e6b69,rev?*%
%TF.SameCoordinates,PX8b85c60PY52f2218*%
%TF.FileFunction,Soldermask,Bot*%
%TF.FilePolarity,Negative*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.8) date 2023-11-08 18:05:49*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-11-17 16:04:48*
%MOMM*%
%LPD*%
G01*
@@ -28,62 +28,75 @@ G04 Aperture macros list*
20,1,$1+$1,$4,$5,$6,$7,0*
20,1,$1+$1,$6,$7,$8,$9,0*
20,1,$1+$1,$8,$9,$2,$3,0*%
%AMHorizOval*
0 Thick line with rounded ends*
0 $1 width*
0 $2 $3 position (X,Y) of the first rounded end (center of the circle)*
0 $4 $5 position (X,Y) of the second rounded end (center of the circle)*
0 Add line between two ends*
20,1,$1,$2,$3,$4,$5,0*
0 Add two circle primitives to create the rounded ends*
1,1,$1,$2,$3*
1,1,$1,$4,$5*%
G04 Aperture macros list end*
%ADD10C,5.800000*%
%ADD11C,0.990600*%
%ADD12C,0.700000*%
%ADD13C,1.300000*%
%ADD14O,1.750000X1.750000*%
%ADD12C,1.000000*%
%ADD13HorizOval,0.600000X0.141421X-0.141421X-0.141421X0.141421X0*%
%ADD14C,0.660000*%
%ADD15O,1.750000X2.000000*%
%ADD16RoundRect,0.250000X0.600000X0.725000X-0.600000X0.725000X-0.600000X-0.725000X0.600000X-0.725000X0*%
%ADD16O,1.750000X1.750000*%
%ADD17O,1.700000X1.950000*%
%ADD18RoundRect,0.250000X0.600000X0.725000X-0.600000X0.725000X-0.600000X-0.725000X0.600000X-0.725000X0*%
G04 APERTURE END LIST*
D10*
%TO.C,H101*%
X-15500000Y-15500000D03*
X15500000Y-15500000D03*
X-15500000Y15500000D03*
X-15500000Y-15500000D03*
X15500000Y15500000D03*
X15500000Y-15500000D03*
%TD*%
D11*
%TO.C,J301*%
X-9718333Y-15684655D03*
X-4925529Y-13718012D03*
X-4537805Y-15712678D03*
X-4925529Y-13718012D03*
X-9718333Y-15684655D03*
%TD*%
D12*
%TO.C,J201*%
X-11891366Y-6598979D03*
X-9073223Y-12467336D03*
X-6032664Y-9426777D03*
X-12467336Y-9073223D03*
X-9426777Y-6032664D03*
D13*
X-9416493Y-6033293D03*
X-12457052Y-9073852D03*
X-6022380Y-9427406D03*
X-9062939Y-12467965D03*
X-6598350Y-11901650D03*
D14*
X-11901650Y-6598350D03*
%TD*%
D14*
D15*
%TO.C,M101*%
X-3700000Y14900000D03*
D15*
X-3700000Y12150000D03*
D14*
X-1200000Y14900000D03*
D15*
X-1200000Y12150000D03*
D14*
X1300000Y14900000D03*
D15*
X1300000Y12150000D03*
D14*
X3800000Y12150000D03*
D16*
X3800000Y14900000D03*
D15*
X3800000Y12150000D03*
%TD*%
X1300000Y12150000D03*
D16*
%TO.C,J101*%
X7225000Y-14725000D03*
X1300000Y14900000D03*
D15*
X-1200000Y12150000D03*
D16*
X-1200000Y14900000D03*
D15*
X-3700000Y12150000D03*
D16*
X-3700000Y14900000D03*
%TD*%
D17*
X4725000Y-14725000D03*
X2225000Y-14725000D03*
%TO.C,J101*%
X-275000Y-14725000D03*
X2225000Y-14725000D03*
X4725000Y-14725000D03*
D18*
X7225000Y-14725000D03*
%TD*%
M02*

View File

@@ -1,23 +1,15 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.8*%
%TF.CreationDate,2023-11-08T18:05:49-05:00*%
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-11-17T16:04:47-05:00*%
%TF.ProjectId,lemon-pepper,6c656d6f-6e2d-4706-9570-7065722e6b69,rev?*%
%TF.SameCoordinates,PX8b85c60PY52f2218*%
%TF.FileFunction,Paste,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.8) date 2023-11-08 18:05:49*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-11-17 16:04:47*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
%ADD10C,1.300000*%
G04 APERTURE END LIST*
D10*
%TO.C,J201*%
X-9416493Y-6033293D03*
X-12457052Y-9073852D03*
X-6022380Y-9427406D03*
X-9062939Y-12467965D03*
%TD*%
M02*

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.8*%
%TF.CreationDate,2023-11-08T18:05:49-05:00*%
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-11-17T16:04:48-05:00*%
%TF.ProjectId,lemon-pepper,6c656d6f-6e2d-4706-9570-7065722e6b69,rev?*%
%TF.SameCoordinates,PX8b85c60PY52f2218*%
%TF.FileFunction,Profile,NP*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.8) date 2023-11-08 18:05:49*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-11-17 16:04:48*
%MOMM*%
%LPD*%
G01*
@@ -16,33 +16,33 @@ G04 APERTURE LIST*
G04 APERTURE END LIST*
D10*
%TO.C,H101*%
X15500000Y-21000000D02*
X-15500000Y-21000000D01*
X-21000000Y-15500000D02*
X-21000000Y15500000D01*
X-15500000Y21000000D02*
X15500000Y21000000D01*
X15500000Y-21000000D02*
X-15500000Y-21000000D01*
X21000000Y15500000D02*
X21000000Y-15500000D01*
X-15500000Y21000000D02*
X15500000Y21000000D01*
G75*
G03*
X-21000000Y15500000I1J-5500001D01*
G01*
X-21000000Y-15500000D02*
G75*
G03*
X-15500000Y-21000000I5500001J1D01*
G01*
X15500000Y-21000000D02*
G75*
G03*
X21000000Y-15500000I-1J5500001D01*
G01*
X-15500000Y21000000D02*
G75*
G03*
X-21000000Y15500000I0J-5500000D01*
G01*
X21000000Y15500000D02*
G75*
G03*
X15500000Y21000000I-5500000J0D01*
G01*
X15500000Y-21000000D02*
G75*
G03*
X21000000Y-15500000I0J5500000D01*
G01*
%TD*%
M02*

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,31 @@
M48
; DRILL file {KiCad 7.0.8} date 2023 November 08, Wednesday 18:05:51
; DRILL file {KiCad 7.0.9} date Friday, November 17, 2023 at 04:04:50PM
; FORMAT={-:-/ absolute / inch / decimal}
; #@! TF.CreationDate,2023-11-08T18:05:51-05:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.8
; #@! TF.CreationDate,2023-11-17T16:04:50-05:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.9
; #@! TF.FileFunction,NonPlated,1,4,NPTH
FMAT,2
INCH
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T1C0.0390
T1C0.0236
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T2C0.0260
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T3C0.0390
%
G90
G05
T1
T2
X-0.4686Y-0.2598
T3
X-0.3826Y-0.6175
X-0.1939Y-0.5401
X-0.1787Y-0.6186
T1
G00X-0.2653Y-0.463
M15
G01X-0.2542Y-0.4741
M16
G05
T0
M30

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
M48
; DRILL file {KiCad 7.0.8} date 2023 November 08, Wednesday 18:05:51
; DRILL file {KiCad 7.0.9} date Friday, November 17, 2023 at 04:04:50PM
; FORMAT={-:-/ absolute / inch / decimal}
; #@! TF.CreationDate,2023-11-08T18:05:51-05:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.8
; #@! TF.CreationDate,2023-11-17T16:04:50-05:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.9
; #@! TF.FileFunction,Plated,1,4,PTH
FMAT,2
INCH
@@ -15,167 +15,176 @@ T3C0.0157
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T4C0.0276
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T5C0.0354
T5C0.0374
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T6C0.0374
T6C0.0413
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T7C0.0413
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T8C0.1220
T7C0.1220
%
G90
G05
T1
X-0.6378Y0.1486
X-0.626Y0.0236
X-0.623Y0.0679
X-0.6043Y0.0344
X-0.6033Y-0.001
X-0.6024Y0.1289
X-0.6535Y0.0856
X-0.627Y0.128
X-0.627Y0.0856
X-0.626Y0.061
X-0.626Y0.0157
X-0.6112Y-0.2008
X-0.6112Y-0.2234
X-0.6111Y-0.1781
X-0.6033Y0.1289
X-0.6033Y-0.0226
X-0.5846Y0.1447
X-0.5778Y-0.0266
X-0.5807Y-0.0059
X-0.5768Y-0.0315
X-0.5679Y0.1604
X-0.5601Y-0.0068
X-0.5502Y0.1772
X-0.4764Y0.3238
X-0.4887Y0.3401
X-0.4705Y-0.2175
X-0.4646Y0.3071
X-0.4429Y-0.2146
X-0.4355Y-0.2504
X-0.3799Y-0.3484
X-0.3504Y-0.0974
X-0.3465Y-0.3799
X-0.3356Y0.247
X-0.3169Y0.2264
X-0.3112Y0.307
X-0.2992Y0.2057
X-0.2963Y0.3278
X-0.2844Y-0.0886
X-0.2835Y-0.0423
X-0.2736Y0.2047
X-0.2657Y-0.0207
X-0.2618Y-0.0876
X-0.2185Y0.1378
X-0.2067Y0.2244
X-0.1939Y0.1378
X-0.185Y0.2077
X-0.171Y0.1378
X-0.3641Y-0.1245
X-0.3494Y0.2598
X-0.3474Y-0.3799
X-0.3465Y-0.1083
X-0.3346Y0.2411
X-0.3137Y0.2251
X-0.3031Y-0.4262
X-0.2982Y0.2087
X-0.2963Y-0.0571
X-0.2756Y0.4252
X-0.2756Y0.3996
X-0.2736Y-0.0571
X-0.251Y-0.0561
X-0.2392Y-0.4272
X-0.2116Y-0.4065
X-0.2037Y0.1053
X-0.2025Y0.2943
X-0.2025Y0.2687
X-0.1811Y0.1053
X-0.1565Y0.1053
X-0.1329Y0.1053
X-0.124Y-0.2667
X-0.1063Y-0.1535
X-0.0797Y0.312
X-0.0482Y-0.1565
X-0.0236Y-0.1575
X-0.0128Y0.2549
X-0.0116Y0.3642
X0.0024Y-0.1575
X0.0305Y-0.1575
X0.0305Y-0.1831
X0.0915Y-0.1909
X0.0925Y-0.1673
X0.0561Y-0.1575
X0.0593Y0.2953
X0.0593Y0.2697
X0.1033Y-0.1565
X0.1663Y0.1476
X0.188Y0.1683
X0.2057Y0.2559
X0.2057Y0.187
X0.2234Y0.2052
X0.3602Y-0.0226
X0.4065Y0.0177
X0.4331Y0.0177
X0.4567Y0.0167
X0.1949Y0.1713
X0.2109Y0.1873
X0.2372Y0.2165
X0.3509Y-0.0211
X0.3686Y-0.003
X0.3809Y-0.0482
X0.3858Y0.0117
X0.4055Y0.0315
X0.4272Y-0.2224
X0.5335Y0.1831
T2
X-0.1457Y0.6604
X-0.0472Y0.6604
X0.0512Y0.6604
X0.1496Y0.6604
T3
X-0.5856Y-0.1053
X-0.576Y-0.0984
X-0.5069Y-0.1075
X-0.4587Y0.0778
X-0.4541Y-0.1263
X-0.442Y-0.3885
X-0.4587Y0.0768
X-0.4547Y-0.3858
X-0.4143Y-0.1241
X-0.4085Y0.124
X-0.4085Y0.0768
X-0.4085Y0.0266
X-0.3968Y-0.4339
X-0.3829Y-0.4518
X-0.3593Y0.0778
X-0.3396Y0.3228
X-0.3297Y-0.2776
X-0.2865Y-0.3382
X-0.2638Y0.2992
X-0.3467Y-0.2776
X-0.3091Y0.3415
X-0.2972Y-0.5118
X-0.2746Y-0.3445
X-0.247Y0.6398
X-0.2461Y0.56
X-0.2461Y0.4793
X-0.2461Y-0.6545
X-0.2146Y0.3521
X-0.2124Y-0.251
X-0.2116Y-0.0394
X-0.1954Y-0.0404
X-0.186Y-0.3504
X-0.1728Y-0.0089
X-0.1669Y0.002
X-0.0945Y-0.6506
X-0.0935Y-0.5748
X-0.0866Y-0.4409
X-0.0787Y0.2943
X-0.0778Y0.002
X-0.0768Y-0.0512
X-0.0669Y-0.4783
X-0.0443Y-0.4409
X-0.0295Y0.3501
X-0.0246Y-0.4783
X-0.0118Y-0.3425
X-0.0098Y-0.2677
X0.0197Y-0.4783
X0.0581Y-0.2411
X0.0875Y0.0187
X0.0875Y0.0236
X0.0876Y0.063
X0.0915Y-0.4665
X0.124Y0.2909
X0.1368Y-0.4291
X0.1368Y-0.4656
X0.1535Y0.0256
X0.1604Y0.2195
X0.1604Y0.1851
X0.1683Y0.1083
X0.1772Y0.3494
X0.1791Y0.0
X0.187Y-0.063
X0.2146Y0.0069
X0.2392Y0.2825
X0.2461Y0.6398
X0.247Y0.561
X0.2483Y0.4793
X0.2608Y0.2559
X0.2608Y0.1102
X0.2982Y0.2559
X0.3022Y0.1083
X0.3022Y0.0679
X0.3031Y0.1486
X0.3415Y0.1073
X0.3553Y-0.1722
X0.3661Y-0.2451
X0.3638Y-0.248
X0.3878Y-0.1969
X0.3902Y0.2406
X0.3917Y-0.1565
X0.3937Y-0.0679
X0.5364Y0.2598
X0.5374Y0.2116
X0.5374Y0.1644
X0.5374Y0.1191
X0.5856Y-0.2293
X0.4168Y0.214
X0.4459Y0.186
X0.5679Y-0.1398
X0.5856Y-0.2648
X0.5856Y-0.3002
X0.5856Y-0.3337
X0.6102Y0.2352
X0.6104Y0.3051
X0.623Y-0.1289
X0.623Y-0.0856
X0.6614Y-0.2323
T4
X-0.4682Y-0.2598
X-0.4908Y-0.3572
X-0.3711Y-0.2375
X-0.3572Y-0.4908
X-0.2375Y-0.3711
T5
X-0.4904Y-0.3572
X-0.3707Y-0.2375
X-0.3568Y-0.4909
X-0.2371Y-0.3712
T6
X-0.0108Y-0.5797
X0.0876Y-0.5797
X0.186Y-0.5797
X0.2844Y-0.5797
T7
T6
X-0.1457Y0.4783
X-0.0472Y0.4783
X0.0512Y0.4783
X0.1496Y0.4783
T8
T7
X-0.6102Y0.6102
X-0.6102Y-0.6102
X0.6102Y0.6102
X0.6102Y-0.6102
T6
T5
G00X-0.1516Y0.5866
M15
G01X-0.1398Y0.5866

View File

@@ -3,9 +3,9 @@
"GenerationSoftware": {
"Vendor": "KiCad",
"Application": "Pcbnew",
"Version": "7.0.8"
"Version": "7.0.9"
},
"CreationDate": "2023-11-08T18:05:49-05:00"
"CreationDate": "2023-11-17T16:04:48-05:00"
},
"GeneralSpecs": {
"ProjectId": {
@@ -24,8 +24,8 @@
"DesignRules": [
{
"Layers": "Outer",
"PadToPad": 0.1,
"PadToTrack": 0.1,
"PadToPad": 0.0,
"PadToTrack": 0.0,
"TrackToTrack": 0.1,
"MinLineWidth": 0.1,
"TrackToRegion": 0.2,
@@ -36,7 +36,7 @@
"PadToPad": 0.0,
"PadToTrack": 0.0,
"TrackToTrack": 0.1,
"MinLineWidth": 0.2,
"MinLineWidth": 0.1,
"TrackToRegion": 0.2,
"RegionToRegion": 0.2
}

View File

@@ -2,7 +2,7 @@ Comment,Designator,Footprint,JLCPCB Part #
10u,"C101, C102, C103, C104, C105, C106, C107, C108, C501, C503",805,C440198
47p,"C201, C202",402,C1567
1u,"C301, C502",402,C52923
100n,"C302, C303, C304, C307, C308, C401, C701, C702, C703",402,C307331
100n,"C302, C303, C304, C307, C308, C401, C701, C702, C703, C704",402,C307331
20p,"C305, C306",402,C1554
10n,C601,402,C15195
220n,C602,402,C16772
@@ -12,23 +12,24 @@ FAULT,D302,603,C2286
BZT52C30S,"D501, D502",SOD-323,C22622
OCD,D601,603,C2286
1N4148WS,"D602, D603",SOD-323,C2128
USB_C_Receptacle_USB2.0,J201,GT-USB-7052_1,C963223
USB_C_Receptacle_USB2.0,J201,GT-USB-7052,C963223
33u,L501,1210,C394994
MMBT5551,Q501,SOT-23,C2145
MMBT5551,Q501,,C2145
5k1,"R101, R102, R301, R302, R601",402,C25905
120R,R103,402,C25079
0R,"R104, R105",402,C17168
10k,"R306, R401",402,C25744
200k,R501,402,C25764
51k,R502,402,C25794
22k,R503,402,C25768
0R,"R104, R105, R501, R702, R704",402,C17168
51k,R303,402,C25794
3k3,R304,402,C25890
10k,"R305, R306, R401",402,C25744
5R,R504,603,C22936
15k,R505,402,C25756
20m,"R701, R703",1206,C393094
USER,SW301,B3U-3000P,C963349
SRV05-4,U201,SOT-23-6,C7420376
STM32G431,U301,QFN48,C529356
STM32G431,U301,QFN-48,C529356
SN65HVD230,U302,SOIC-8,C12084
MT6835,U401,TSSOP-16,C2932578
MAX15062CATA+T,U501,TDFN-8,C1121853
MAX15062AATA+T,U501,TDFN-8,C2846801
L6226QTR,U601,VQFN-32,C95357
ACS711KEXLT-15AB-T,"U701, U702, U703",ACS711KEXLT-15AB-T,C146412
MAX49925,"U701, U703",DFN-10,C19193612
12MHz,Y301,3225,C9002
1 Comment Designator Footprint JLCPCB Part #
2 10u C101, C102, C103, C104, C105, C106, C107, C108, C501, C503 805 C440198
3 47p C201, C202 402 C1567
4 1u C301, C502 402 C52923
5 100n C302, C303, C304, C307, C308, C401, C701, C702, C703 C302, C303, C304, C307, C308, C401, C701, C702, C703, C704 402 C307331
6 20p C305, C306 402 C1554
7 10n C601 402 C15195
8 220n C602 402 C16772
12 BZT52C30S D501, D502 SOD-323 C22622
13 OCD D601 603 C2286
14 1N4148WS D602, D603 SOD-323 C2128
15 USB_C_Receptacle_USB2.0 J201 GT-USB-7052_1 GT-USB-7052 C963223
16 33u L501 1210 C394994
17 MMBT5551 Q501 SOT-23 C2145
18 5k1 R101, R102, R301, R302, R601 402 C25905
19 120R R103 402 C25079
20 0R R104, R105 R104, R105, R501, R702, R704 402 C17168
21 10k 51k R306, R401 R303 402 C25744 C25794
22 200k 3k3 R501 R304 402 C25764 C25890
23 51k 10k R502 R305, R306, R401 402 C25794 C25744
22k R503 402 C25768
24 5R R504 603 C22936
25 15k R505 402 C25756
26 20m R701, R703 1206 C393094
27 USER SW301 B3U-3000P C963349
28 SRV05-4 U201 SOT-23-6 C7420376
29 STM32G431 U301 QFN48 QFN-48 C529356
30 SN65HVD230 U302 SOIC-8 C12084
31 MT6835 U401 TSSOP-16 C2932578
32 MAX15062CATA+T MAX15062AATA+T U501 TDFN-8 C1121853 C2846801
33 L6226QTR U601 VQFN-32 C95357
34 ACS711KEXLT-15AB-T MAX49925 U701, U702, U703 U701, U703 ACS711KEXLT-15AB-T DFN-10 C146412 C19193612
35 12MHz Y301 3225 C9002

View File

@@ -7,62 +7,66 @@ C105,8.375,12.175,top,180
C106,6.025,-5.725,top,180
C107,8.375,14.225,top,180
C108,6.025,-7.75,top,180
C201,-6.6,-3.9,top,180
C202,-4.6,-3.9,top,0
C201,-7.225,-3.9,top,180
C202,-5.1,-3.9,top,0
C301,-14.2,-1.425,top,-135
C302,-12.025,-3.6,top,-135
C303,-9.285589,7.560589,top,135
C303,-9.275,8.65,top,0
C304,-4.774651,0.910051,top,45
C305,-16.089411,8.715589,top,-135
C306,-15.8,4.65,top,135
C305,-15.725,8.925,top,-135
C306,-15.675,4.8,top,135
C307,-13.525,-2.1,top,-135
C308,-11.335589,8.989411,top,45
C401,1.6,4.025,top,180
C308,-9.725,10.1,top,90
C401,0.95,4.05,top,180
C501,6.025,-9.775,top,180
C502,11.5,-7.775,top,90
C503,15.825,-4.2,top,90
C601,11.75,-0.975,top,90
C602,11,-3.75,top,-90
C701,-6.18,6.5,top,180
C702,3.8,6.475,top,180
C703,-1.48,6.461143,top,180
D101,15.6,2.125,top,-90
D301,-15.2375,-6.375,top,0
D302,-15.2375,-8.025,top,0
D501,7.8,9.45,top,-90
C601,7.225,-1.7,top,0
C602,7.225,-2.7,top,0
C701,-7.825,7.25,top,-90
C702,-3.89375,10.025,top,0
C703,0.95,5.475,top,0
C704,2.75625,10.025,top,0
D101,13.825,-0.675,top,0
D301,-14.825,-7.175,top,0
D302,-14.825,-8.8,top,0
D501,7.8,9.15,top,-90
D502,9.825,9.15,top,-90
D601,13.025,-0.4,top,-90
D602,13.15,-4.925,top,180
D603,13.15,-2.925,top,0
FID101,-16.4,10.775,top,0
FID102,-16.05,-9.8,top,0
FID103,16.2,10.325,top,0
J201,-9.239716,-9.250629,top,-45
JP101,5.875,-4.075,top,0
D601,12.275,-4.825,top,0
D602,4.625,-3.35,top,0
D603,4.625,-1.375,top,180
J201,-9.348995,-9.151005,top,-45
L501,14.525,-7.55,top,0
Q501,12.7625,8.15,top,0
Q501,13.275,8,top,0
R101,-4.175,-10.875,top,90
R102,-7.65,-5.375,top,90
R103,5.875,-3,top,0
R104,3.925,-3,top,180
R105,3.925,-4.075,top,0
R301,-15.8,-5.05,top,180
R103,1.4,-4.775,top,180
R104,3.325,-4.775,top,0
R105,-2.45,-4.775,top,0
R301,-14.275,-5.825,top,180
R302,-11.35,-4.325,top,-135
R306,-10.625,8.275,top,45
R401,-15.8,-4,top,180
R303,-8.25,10.65,top,180
R304,-8.25,9.65,top,180
R305,12.875,2.275,top,-90
R306,-10.725,9.125,top,90
R401,-14.275,-4.725,top,180
R501,10.89,-9.36,top,180
R502,9.365,-9.81,top,-90
R503,8.29,-9.81,top,-90
R504,15.5,7.925,top,90
R504,12.625,4.75,top,90
R505,9.85,7,top,0
R601,15.865,-1.95,top,0
R601,14.3,-5.025,top,-90
R701,-1.79375,8.275,top,90
R702,-5.81875,10.025,top,180
R703,4.85625,8.275,top,90
R704,0.85625,10.025,top,180
SW301,15.1,3.825,top,-90
U201,-5.4,-6.5,top,-90
U301,-10.349651,1.935051,top,-45
U302,0.55,-8.045,top,0
U401,0,0,top,90
U501,9.215,-7.66,top,-90
U601,7.7,2.75,top,-135
U701,-5.3,9.05,top,180
U702,4.65,9,top,180
U703,-0.6,9,top,180
Y301,-14.476777,7.076142,top,-135
U701,-5.11875,7.75,top,180
U703,1.53125,7.75,top,180
Y301,-14.325,7.175,top,-135
1 Designator Mid X Mid Y Layer Rotation
7 C106 6.025 -5.725 top 180
8 C107 8.375 14.225 top 180
9 C108 6.025 -7.75 top 180
10 C201 -6.6 -7.225 -3.9 top 180
11 C202 -4.6 -5.1 -3.9 top 0
12 C301 -14.2 -1.425 top -135
13 C302 -12.025 -3.6 top -135
14 C303 -9.285589 -9.275 7.560589 8.65 top 135 0
15 C304 -4.774651 0.910051 top 45
16 C305 -16.089411 -15.725 8.715589 8.925 top -135
17 C306 -15.8 -15.675 4.65 4.8 top 135
18 C307 -13.525 -2.1 top -135
19 C308 -11.335589 -9.725 8.989411 10.1 top 45 90
20 C401 1.6 0.95 4.025 4.05 top 180
21 C501 6.025 -9.775 top 180
22 C502 11.5 -7.775 top 90
23 C503 15.825 -4.2 top 90
24 C601 11.75 7.225 -0.975 -1.7 top 90 0
25 C602 11 7.225 -3.75 -2.7 top -90 0
26 C701 -6.18 -7.825 6.5 7.25 top 180 -90
27 C702 3.8 -3.89375 6.475 10.025 top 180 0
28 C703 -1.48 0.95 6.461143 5.475 top 180 0
29 D101 C704 15.6 2.75625 2.125 10.025 top -90 0
30 D301 D101 -15.2375 13.825 -6.375 -0.675 top 0
31 D302 D301 -15.2375 -14.825 -8.025 -7.175 top 0
32 D501 D302 7.8 -14.825 9.45 -8.8 top -90 0
33 D501 7.8 9.15 top -90
34 D502 9.825 9.15 top -90
35 D601 13.025 12.275 -0.4 -4.825 top -90 0
36 D602 13.15 4.625 -4.925 -3.35 top 180 0
37 D603 13.15 4.625 -2.925 -1.375 top 0 180
38 FID101 J201 -16.4 -9.348995 10.775 -9.151005 top 0 -45
FID102 -16.05 -9.8 top 0
FID103 16.2 10.325 top 0
J201 -9.239716 -9.250629 top -45
JP101 5.875 -4.075 top 0
39 L501 14.525 -7.55 top 0
40 Q501 12.7625 13.275 8.15 8 top 0
41 R101 -4.175 -10.875 top 90
42 R102 -7.65 -5.375 top 90
43 R103 5.875 1.4 -3 -4.775 top 0 180
44 R104 3.925 3.325 -3 -4.775 top 180 0
45 R105 3.925 -2.45 -4.075 -4.775 top 0
46 R301 -15.8 -14.275 -5.05 -5.825 top 180
47 R302 -11.35 -4.325 top -135
48 R306 R303 -10.625 -8.25 8.275 10.65 top 45 180
49 R401 R304 -15.8 -8.25 -4 9.65 top 180
50 R305 12.875 2.275 top -90
51 R306 -10.725 9.125 top 90
52 R401 -14.275 -4.725 top 180
53 R501 10.89 -9.36 top 180
54 R502 9.365 -9.81 top -90
55 R503 8.29 -9.81 top -90
56 R504 15.5 12.625 7.925 4.75 top 90
57 R505 9.85 7 top 0
58 R601 15.865 14.3 -1.95 -5.025 top 0 -90
59 R701 -1.79375 8.275 top 90
60 R702 -5.81875 10.025 top 180
61 R703 4.85625 8.275 top 90
62 R704 0.85625 10.025 top 180
63 SW301 15.1 3.825 top -90
64 U201 -5.4 -6.5 top -90
65 U301 -10.349651 1.935051 top -45
66 U302 0.55 -8.045 top 0
67 U401 0 0 top 90
68 U501 9.215 -7.66 top -90
69 U601 7.7 2.75 top -135
70 U701 -5.3 -5.11875 9.05 7.75 top 180
71 U702 U703 4.65 1.53125 9 7.75 top 180
72 U703 Y301 -0.6 -14.325 9 7.175 top 180 -135
Y301 -14.476777 7.076142 top -135

Binary file not shown.

Binary file not shown.

Submodule firmware/lib/Arduino-FOC deleted from f9e9a2d29e

Submodule firmware/lib/Arduino-FOC-drivers deleted from c66b9db82a

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Richard Unger
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.

View File

@@ -0,0 +1,117 @@
# SimpleFOC Driver and Support Library
![Library Compile](https://github.com/simplefoc/Arduino-FOC-drivers/workflows/Library%20Compile/badge.svg)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![Release](https://www.ardu-badge.com/badge/SimpleFOCDrivers.svg?)
This library contains an assortment of drivers and supporting code for SimpleFOC.
The intent is to keep the core of SimpleFOC clean, and thus easy to maintain, understand and port to different platforms. In addition to this core, there are various drivers and supporting code which has grown around SimpleFOC, and which we would like to make available to the community.
## New Release
v1.0.6 - Released July 2023, for Simple FOC 2.3.1 or later
What's changed since 1.0.5?
- Added AS5600 Sensor Driver
- Bugfixes [included](https://github.com/simplefoc/Arduino-FOC-drivers/issues?q=is%3Aissue+milestone%3A1.0.6+)
## What is included
What is here? See the sections below. Each driver or function should come with its own more detailed README.
### Motor/Gate driver ICs
- [TMC6200 driver](src/drivers/tmc6200/) - SPI driver for Trinamics TMC6200 motor driver IC.
- [DRV8316 driver](src/drivers/drv8316/) - SPI driver for TI's DRV8316 motor driver IC.
### Encoders
- [AS5048A SPI driver](src/encoders/as5048a/) - SPI driver for the AMS AS5048A absolute position magnetic rotary encoder IC.
- [AS5047 SPI driver](src/encoders/as5047/) - SPI driver for the AMS AS5047P and AS5047D absolute position magnetic rotary encoder ICs.
- [AS5047U SPI driver](src/encoders/as5047u/) - SPI driver for the AMS AS5047U absolute position magnetic rotary encoder ICs.
- [AS5600 I2C driver](src/encoders/as5600/) - I2C driver for the AMS AS5600 and AS5600L absolute position magnetic rotary encoder ICs.
- [MA730 SPI driver](src/encoders/ma730/) - SPI driver for the MPS MagAlpha MA730 absolute position magnetic rotary encoder IC.
- [MA730 SSI driver](src/encoders/ma730/) - SSI driver for the MPS MagAlpha MA730 absolute position magnetic rotary encoder IC.
- [AS5145 SSI driver](src/encoders/as5145/) - SSI driver for the AMS AS5145 and AS5045 absolute position magnetic rotary encoder ICs.
- [TLE5012B SPI driver](src/encoders/tle5012b/) - SPI (half duplex) driver for TLE5012B absolute position magnetic rotary encoder IC.
- [STM32 Hardware Encoder](src/encoders/stm32hwencoder/) - Hardware timer based encoder driver for ABI type quadrature encoders.
- [SC60228 SPI driver](src/encoders/sc60228/) - SPI driver for SemiMent SC60288 magnetic encoder IC.
- [MA330 SPI driver](src/encoders/ma330/) - SPI driver for the MPS MagAlpha MA330 absolute position magnetic rotary encoder IC.
- [MT6816 SPI driver](src/encoders/mt6816/) - SPI driver for the MagnTek MT6816 absolute position magnetic rotary encoder IC.
- [MT6701 SSI driver](src/encoders/mt6701/) - SSI driver for the MagnTek MT6701 absolute position magnetic rotary encoder IC.
- [MT6835 SPI driver](src/encoders/mt6835/) - SPI driver for the MagnTek MT6835 21 bit magnetic rotary encoder IC.
- [STM32 PWM sensor driver](src/encoders/stm32pwmsensor/) - STM32 native timer-based driver for PWM angle sensors.
- [SmoothingSensor](src/encoders/smoothing/) - A SimpleFOC Sensor wrapper implementation which adds angle extrapolation.
### Communications
- [I2CCommander I2C driver](src/comms/i2c/) - I2C communications protocol and drivers for both controller and target devices, based on register abstraction
- [STM32 SpeedDir Input](src/comms/stm32speeddir/) - Control target velocity with PWM speed and direction inputs
- [SerialBinaryCommander](src/comms/serial/) - Serial communications with binary protocol, based on register abstraction
- [Telemetry](src/comms/telemetry/) - Telemetry abstraction, based on registers
- [SerialASCIITelemetry](src/comms/serial/) - Serial communications with ascii protocol, based on register abstraction
### Settings
Load and store SimpleFOC motor settings, based on register abstraction.
- [SAMD NVM storage driver](src/settings/samd/) - Store settings to the NVM flash memory in your SAMD MCU
- [CAT24 I2C EEPROM storage driver](src/settings/i2c/) - Store settings to CAT24 I2C EEPROMs
### Utilities
- [STM32 PWM Input driver](src/utilities/stm32pwm/) - PWM Input driver for STM32 MCUs. Accurately measure PWM inputs with zero MCU overhead.
## How to use
#### Arduino Library Manager
The simplest way to get hold of the library is directly by using Arduino IDE and its integrated Library Manager.
- Open Arduino IDE and start Arduino Library Manager by clicking: `Tools > Manage Libraries...`.
- Search for `Simple FOC drivers` library and install the latest version.
- Reopen Arduino IDE and you should have the library examples in `File > Examples > Simple FOC drivers`.
#### Using Github website
- Go to the [github repository](https://github.com/simplefoc/Arduino-FOC-drivers)
- Click first on `Clone or Download > Download ZIP`.
- Unzip it and place it in `Arduino Libraries` folder. Windows: `Documents > Arduino > libraries`.
- Reopen Arduino IDE and you should have the library examples in `File > Examples > Simple FOC drivers`.
#### Using parts
You can copy parts of the library, for example to minimize your code size, or make it easier to add adaptations of your own.
If you do so, please be sure to adhere to and include the [LICENSE](https://github.com/simplefoc/Arduino-FOC-drivers/LICENSE).
## Further Documentation
Find out more information about the Arduino SimpleFOC project on the [docs website](https://docs.simplefoc.com/)
## Release History
What's changed since 1.0.4?
- Added smoothing sensor by [@dekutree64](https://github.com/dekutree64)
- Added TMD6200 SPI driver by [@YaseenTwati](https://github.com/YaseenTwati)
- Added HybridStepperMotor by [@VIPQualityPost](https://github.com/VIPQualityPost)
- New Settings abstraction to load and save SimpleFOC settings and calibration
- New Settings driver: SAMDNVMSettingsStorage
- SimpleFOCRegisters abstraction, mapping SimpleFOC parameters to virtual "Registers"
- Updated I2CCommander to use the new registers abstraction
- Bugfixes [included](https://github.com/simplefoc/Arduino-FOC-drivers/issues?q=is%3Aissue+milestone%3A1.0.5+)
What's changed since 1.0.3?
- New Comms/Input: STM32SpeedDirCommander
- New Utility: STM32PWMInput
- Fixed MT6835 driver bugs
- Improved AS5047 driver, fixed bugs
- Improved AS5047U driver, fixed bugs
What's changed since 1.0.2?
- New Sensor: MT6835
- Fixed bugs
What's changed since 1.0.1?
- Calibrated sensor by @MarethyuPrefect
- New Sensors: MT6701, MA330, MT6816
- Fixed bugs

View File

@@ -0,0 +1,123 @@
#include "Arduino.h"
#include <Wire.h>
#include <SimpleFOC.h>
#include <SimpleFOCDrivers.h>
#include "drivers/drv8316/drv8316.h"
BLDCMotor motor = BLDCMotor(11);
DRV8316Driver3PWM driver = DRV8316Driver3PWM(2,3,4,7,false); // use the right pins for your setup!
#define ENABLE_A 0
#define ENABLE_B 1
#define ENABLE_C 6
void printDRV8316Status() {
DRV8316Status status = driver.getStatus();
Serial.println("DRV8316 Status:");
Serial.print("Fault: ");
Serial.println(status.isFault());
Serial.print("Buck Error: ");
Serial.print(status.isBuckError());
Serial.print(" Undervoltage: ");
Serial.print(status.isBuckUnderVoltage());
Serial.print(" OverCurrent: ");
Serial.println(status.isBuckOverCurrent());
Serial.print("Charge Pump UnderVoltage: ");
Serial.println(status.isChargePumpUnderVoltage());
Serial.print("OTP Error: ");
Serial.println(status.isOneTimeProgrammingError());
Serial.print("OverCurrent: ");
Serial.print(status.isOverCurrent());
Serial.print(" Ah: ");
Serial.print(status.isOverCurrent_Ah());
Serial.print(" Al: ");
Serial.print(status.isOverCurrent_Al());
Serial.print(" Bh: ");
Serial.print(status.isOverCurrent_Bh());
Serial.print(" Bl: ");
Serial.print(status.isOverCurrent_Bl());
Serial.print(" Ch: ");
Serial.print(status.isOverCurrent_Ch());
Serial.print(" Cl: ");
Serial.println(status.isOverCurrent_Cl());
Serial.print("OverTemperature: ");
Serial.print(status.isOverTemperature());
Serial.print(" Shutdown: ");
Serial.print(status.isOverTemperatureShutdown());
Serial.print(" Warning: ");
Serial.println(status.isOverTemperatureWarning());
Serial.print("OverVoltage: ");
Serial.println(status.isOverVoltage());
Serial.print("PowerOnReset: ");
Serial.println(status.isPowerOnReset());
Serial.print("SPI Error: ");
Serial.print(status.isSPIError());
Serial.print(" Address: ");
Serial.print(status.isSPIAddressError());
Serial.print(" Clock: ");
Serial.print(status.isSPIClockFramingError());
Serial.print(" Parity: ");
Serial.println(status.isSPIParityError());
if (status.isFault())
driver.clearFault();
delayMicroseconds(1); // ensure 400ns delay
DRV8316_PWMMode val = driver.getPWMMode();
Serial.print("PWM Mode: ");
Serial.println(val);
delayMicroseconds(1); // ensure 400ns delay
bool lock = driver.isRegistersLocked();
Serial.print("Lock: ");
Serial.println(lock);
}
void setup() {
Serial.begin(115200);
while (!Serial);
delay(1);
Serial.println("Initializing...");
pinMode(ENABLE_A, OUTPUT);
digitalWrite(ENABLE_A, 1); // enable
pinMode(ENABLE_B, OUTPUT);
digitalWrite(ENABLE_B, 1); // enable
pinMode(ENABLE_C, OUTPUT);
digitalWrite(ENABLE_C, 1); // enable
driver.voltage_power_supply = 12;
driver.init();
motor.linkDriver(&driver);
motor.controller = MotionControlType::velocity_openloop;
motor.voltage_limit = 3;
motor.velocity_limit = 20;
motor.init();
Serial.println("Init complete...");
delay(100);
printDRV8316Status();
}
// velocity set point variable
float target_velocity = 7.0;
void loop() {
//delay(100);
//driver.setPwm(7.4/4, 7.4/2, 7.4/4*3);
motor.move(target_velocity);
}

View File

@@ -0,0 +1,111 @@
#include "Arduino.h"
#include <Wire.h>
#include <SimpleFOC.h>
#include <SimpleFOCDrivers.h>
#include "drivers/drv8316/drv8316.h"
BLDCMotor motor = BLDCMotor(11);
DRV8316Driver6PWM driver = DRV8316Driver6PWM(0,1,2,3,4,6,7,false); // use the right pins for your setup!
void printDRV8316Status() {
DRV8316Status status = driver.getStatus();
Serial.println("DRV8316 Status:");
Serial.print("Fault: ");
Serial.println(status.isFault());
Serial.print("Buck Error: ");
Serial.print(status.isBuckError());
Serial.print(" Undervoltage: ");
Serial.print(status.isBuckUnderVoltage());
Serial.print(" OverCurrent: ");
Serial.println(status.isBuckOverCurrent());
Serial.print("Charge Pump UnderVoltage: ");
Serial.println(status.isChargePumpUnderVoltage());
Serial.print("OTP Error: ");
Serial.println(status.isOneTimeProgrammingError());
Serial.print("OverCurrent: ");
Serial.print(status.isOverCurrent());
Serial.print(" Ah: ");
Serial.print(status.isOverCurrent_Ah());
Serial.print(" Al: ");
Serial.print(status.isOverCurrent_Al());
Serial.print(" Bh: ");
Serial.print(status.isOverCurrent_Bh());
Serial.print(" Bl: ");
Serial.print(status.isOverCurrent_Bl());
Serial.print(" Ch: ");
Serial.print(status.isOverCurrent_Ch());
Serial.print(" Cl: ");
Serial.println(status.isOverCurrent_Cl());
Serial.print("OverTemperature: ");
Serial.print(status.isOverTemperature());
Serial.print(" Shutdown: ");
Serial.print(status.isOverTemperatureShutdown());
Serial.print(" Warning: ");
Serial.println(status.isOverTemperatureWarning());
Serial.print("OverVoltage: ");
Serial.println(status.isOverVoltage());
Serial.print("PowerOnReset: ");
Serial.println(status.isPowerOnReset());
Serial.print("SPI Error: ");
Serial.print(status.isSPIError());
Serial.print(" Address: ");
Serial.print(status.isSPIAddressError());
Serial.print(" Clock: ");
Serial.print(status.isSPIClockFramingError());
Serial.print(" Parity: ");
Serial.println(status.isSPIParityError());
if (status.isFault())
driver.clearFault();
delayMicroseconds(1); // ensure 400ns delay
DRV8316_PWMMode val = driver.getPWMMode();
Serial.print("PWM Mode: ");
Serial.println(val);
delayMicroseconds(1); // ensure 400ns delay
bool lock = driver.isRegistersLocked();
Serial.print("Lock: ");
Serial.println(lock);
}
void setup() {
Serial.begin(115200);
while (!Serial);
delay(1);
Serial.println("Initializing...");
driver.voltage_power_supply = 12;
driver.init();
motor.linkDriver(&driver);
motor.controller = MotionControlType::velocity_openloop;
motor.voltage_limit = 3;
motor.velocity_limit = 20;
motor.init();
Serial.println("Init complete...");
delay(100);
printDRV8316Status();
}
// velocity set point variable
float target_velocity = 7.0;
void loop() {
motor.move(target_velocity);
}

View File

@@ -0,0 +1,86 @@
/**
* Torque control example using voltage control loop.
*
* Most of the low-end BLDC driver boards doesn't have current measurement therefore SimpleFOC offers
* you a way to control motor torque by setting the voltage to the motor instead hte current.
*
* This makes the BLDC motor effectively a DC motor, and you can use it in a same way.
*/
#include <SimpleFOC.h>
#include <SimpleFOCDrivers.h>
#include "encoders/calibrated/CalibratedSensor.h"
// magnetic sensor instance - SPI
MagneticSensorSPI sensor = MagneticSensorSPI(AS5048_SPI, PB6);
// BLDC motor & driver instance
BLDCMotor motor = BLDCMotor(11);
BLDCDriver3PWM driver = BLDCDriver3PWM(PB4,PC7,PB10,PA9);
// instantiate the calibrated sensor object
CalibratedSensor sensor_calibrated = CalibratedSensor(sensor);
// voltage set point variable
float target_voltage = 2;
// instantiate the commander
Commander command = Commander(Serial);
void doTarget(char* cmd) { command.scalar(&target_voltage, cmd); }
void setup() {
SPI.setMISO(PB14);
SPI.setMOSI(PB15);
SPI.setSCLK(PB13);
sensor.init();
// Link motor to sensor
motor.linkSensor(&sensor);
// power supply voltage
driver.voltage_power_supply = 20;
driver.init();
motor.linkDriver(&driver);
// aligning voltage
motor.voltage_sensor_align = 8;
motor.voltage_limit = 20;
// set motion control loop to be used
motor.controller = MotionControlType::torque;
// use monitoring with serial
Serial.begin(115200);
// comment out if not needed
motor.useMonitoring(Serial);
motor.monitor_variables = _MON_VEL;
motor.monitor_downsample = 10; // default 10
// initialize motor
motor.init();
// set voltage to run calibration
sensor_calibrated.voltage_calibration = 6;
// Running calibration
sensor_calibrated.calibrate(motor);
//Serial.println("Calibrating Sensor Done.");
// Linking sensor to motor object
motor.linkSensor(&sensor_calibrated);
// calibrated init FOC
motor.initFOC();
// add target command T
command.add('T', doTarget, "target voltage");
Serial.println(F("Motor ready."));
Serial.println(F("Set the target voltage using serial terminal:"));
_delay(1000);
}
void loop() {
motor.loopFOC();
motor.move(target_voltage);
command.run();
motor.monitor();
}

View File

@@ -0,0 +1,106 @@
/**
* Comprehensive BLDC motor control example using magnetic sensor MT6816
*
* Using serial terminal user can send motor commands and configure the motor and FOC in real-time:
* - configure PID controller constants
* - change motion control loops
* - monitor motor variabels
* - set target values
* - check all the configuration values
*
* See more info in docs.simplefoc.com/commander_interface
*/
#include <SimpleFOC.h>
#include <SimpleFOCDrivers.h>
#include <encoders/mt6816/MagneticSensorMT6816.h>
// magnetic sensor instance - MT6816 SPI mode
MagneticSensorMT6816 sensor = MagneticSensorMT6816(5);
// BLDC motor & driver instance
BLDCMotor motor = BLDCMotor(7);
BLDCDriver3PWM driver = BLDCDriver3PWM(32, 25, 26, 33);
// Inline Current Sense instance
InlineCurrentSense current_sense = InlineCurrentSense(0.01f, 50.0f, 35, 34);
// commander interface
Commander command = Commander(Serial);
void onMotor(char* cmd){ command.motor(&motor, cmd); }
void setup() {
// initialise magnetic sensor hardware
sensor.init();
// link the motor to the sensor
motor.linkSensor(&sensor);
// driver config
// power supply voltage [V]
driver.voltage_power_supply = 12;
driver.init();
// link driver
motor.linkDriver(&driver);
// choose FOC modulation
motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
// set control loop type to be used
motor.controller = MotionControlType::torque;
// contoller configuration based on the control type
motor.PID_velocity.P = 0.2f;
motor.PID_velocity.I = 20;
motor.PID_velocity.D = 0;
// default voltage_power_supply
motor.voltage_limit = 12;
// velocity low pass filtering time constant
motor.LPF_velocity.Tf = 0.01f;
// angle loop velocity limit
motor.velocity_limit = 50;
// use monitoring with serial for motor init
// monitoring port
Serial.begin(115200);
// comment out if not needed
motor.useMonitoring(Serial);
current_sense.linkDriver(&driver);
current_sense.init();
current_sense.gain_b *= -1;
current_sense.skip_align = true;
motor.linkCurrentSense(&current_sense);
// initialise motor
motor.init();
// align encoder and start FOC
motor.initFOC();
// set the inital target value
motor.target = 2;
// define the motor id
command.add('A', onMotor, "motor");
// Run user commands to configure and the motor (find the full command list in docs.simplefoc.com)
Serial.println(F("Motor commands sketch | Initial motion control > torque/voltage : target 2V."));
_delay(1000);
}
void loop() {
// iterative setting FOC phase voltage
motor.loopFOC();
// iterative function setting the outter loop target
// velocity, position or voltage
// if tatget not set in parameter uses motor.target variable
motor.move();
// user communication
command.run();
}

View File

@@ -0,0 +1,141 @@
/**
*
* Hall sensor velocity motion control example, modified to demonstrate usage of SmoothingSensor
* Steps:
* 1) Configure the motor and sensor
* 2) Run the code
* 3) Set the target velocity (in radians per second) from serial terminal
* 4) Try with and without smoothing to see the difference (send E1 and E0 commands from serial terminal)
*
*
*
* NOTE :
* > Specifically for Arduino UNO example code for running velocity motion control using a hall sensor
* > Since Arduino UNO doesn't have enough interrupt pins we have to use software interrupt library PciManager.
*
* > If running this code with Nucleo or Bluepill or any other board which has more than 2 interrupt pins
* > you can supply doC directly to the sensor.enableInterrupts(doA,doB,doC) and avoid using PciManger
*
*/
#include <SimpleFOC.h>
// software interrupt library
#include <PciManager.h>
#include <PciListenerImp.h>
#include <SimpleFOCDrivers.h>
#include <encoders/smoothing/SmoothingSensor.h>
// BLDC motor & driver instance
BLDCMotor motor = BLDCMotor(11);
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 5, 6, 8);
// Stepper motor & driver instance
//StepperMotor motor = StepperMotor(50);
//StepperDriver4PWM driver = StepperDriver4PWM(9, 5, 10, 6, 8);
// hall sensor instance
HallSensor sensor = HallSensor(2, 3, 4, 11);
// wrapper instance
SmoothingSensor smooth(sensor, motor);
// Interrupt routine intialisation
// channel A and B callbacks
void doA(){sensor.handleA();}
void doB(){sensor.handleB();}
void doC(){sensor.handleC();}
// If no available hadware interrupt pins use the software interrupt
PciListenerImp listenerIndex(sensor.pinC, doC);
// velocity set point variable
float target_velocity = 0;
// instantiate the commander
Commander command = Commander(Serial);
void doTarget(char* cmd) { command.scalar(&target_velocity, cmd); }
void enableSmoothing(char* cmd) {
float enable;
command.scalar(&enable, cmd);
motor.linkSensor(enable == 0 ? (Sensor*)&sensor : (Sensor*)&smooth);
}
void setup() {
// initialize sensor sensor hardware
sensor.init();
sensor.enableInterrupts(doA, doB); //, doC);
// software interrupts
PciManager.registerListener(&listenerIndex);
// set SmoothingSensor phase correction for hall sensors
smooth.phase_correction = -_PI_6;
// link the SmoothingSensor to the motor
motor.linkSensor(&smooth);
// driver config
// power supply voltage [V]
driver.voltage_power_supply = 12;
driver.init();
// link the motor and the driver
motor.linkDriver(&driver);
// aligning voltage [V]
motor.voltage_sensor_align = 3;
// set motion control loop to be used
motor.controller = MotionControlType::velocity;
// contoller configuration
// default parameters in defaults.h
// velocity PI controller parameters
motor.PID_velocity.P = 0.2f;
motor.PID_velocity.I = 2;
motor.PID_velocity.D = 0;
// default voltage_power_supply
motor.voltage_limit = 6;
// jerk control using voltage voltage ramp
// default value is 300 volts per sec ~ 0.3V per millisecond
motor.PID_velocity.output_ramp = 1000;
// velocity low pass filtering time constant
motor.LPF_velocity.Tf = 0.01f;
// use monitoring with serial
Serial.begin(115200);
// comment out if not needed
motor.useMonitoring(Serial);
// initialize motor
motor.init();
// align sensor and start FOC
motor.initFOC();
// add target command T
command.add('T', doTarget, "target voltage");
// add smoothing enable/disable command E (send E0 to use hall sensor alone, or E1 to use smoothing)
command.add('E', enableSmoothing, "enable smoothing");
Serial.println(F("Motor ready."));
Serial.println(F("Set the target velocity using serial terminal:"));
_delay(1000);
}
void loop() {
// main FOC algorithm function
// the faster you run this function the better
// Arduino UNO loop ~1kHz
// Bluepill loop ~10kHz
motor.loopFOC();
// Motion control function
// velocity, position or voltage (defined in motor.controller)
// this function can be run at much lower frequency than loopFOC() function
// You can also use motor.move() and set the motor.target in the code
motor.move(target_velocity);
// function intended to be used with serial plotter to monitor motor variables
// significantly slowing the execution down!!!!
// motor.monitor();
// user communication
command.run();
}

View File

@@ -0,0 +1,11 @@
SimpleFOC KEYWORD1
DRV8316 KEYWORD1
AS5048A KEYWORD1
AS5047 KEYWORD1
AS5145 KEYWORD1
MA730 KEYWORD1
MT6835 KEYWORD1
SC60228 KEYWORD1
TLE5012B KEYWORD1
I2CCommander KEYWORD1
STM32HWEncoder KEYWORD1

View File

@@ -0,0 +1,11 @@
name=SimpleFOCDrivers
version=1.0.6
author=Simplefoc <info@simplefoc.com>
maintainer=Simplefoc <info@simplefoc.com>
sentence=A library of supporting drivers for SimpleFOC. Motor drivers chips, encoder chips, current sensing and supporting code.
paragraph=SimpleFOC runs BLDC and Stepper motors using the FOC algorithm. This library supports the core SimpleFOC code by adding support for specific hardware like motor driver ICs, encoders, current sensing and other supporting code.
category=Device Control
url=https://docs.simplefoc.com
architectures=*
includes=SimpleFOCDrivers.h
depends=Simple FOC

View File

@@ -0,0 +1,11 @@
#ifndef __SIMPLEFOC_DRIVERS_H__
#define __SIMPLEFOC_DRIVERS_H__
// empty header file. In Arduino IDE, include this header to enable the
// IDE to "find" the library.
// Then include the headers for the individual drivers/sensors/utilities
// that you want to use.
#endif

View File

@@ -0,0 +1,5 @@
# SimpleFOC communications support code
This folder contains classes to support you communicating between MCUs running SimpleFOC, and other systems.

View File

@@ -0,0 +1,131 @@
#include "./RegisterReceiver.h"
#include "BLDCMotor.h"
void RegisterReceiver::setRegister(SimpleFOCRegister reg, FOCMotor* motor){
// write a register value for the given motor
switch(reg) {
case REG_ENABLE:
readByte((uint8_t*)&(motor->enabled));
break;
case REG_MODULATION_MODE:
readByte((uint8_t*)&(motor->foc_modulation));
break;
case REG_TORQUE_MODE:
readByte((uint8_t*)&(motor->torque_controller));
break;
case REG_CONTROL_MODE:
readByte((uint8_t*)&(motor->controller));
break;
case REG_TARGET:
readFloat(&(motor->target));
break;
case REG_VEL_PID_P:
readFloat(&(motor->PID_velocity.P));
break;
case REG_VEL_PID_I:
readFloat(&(motor->PID_velocity.I));
break;
case REG_VEL_PID_D:
readFloat(&(motor->PID_velocity.D));
break;
case REG_VEL_LPF_T:
readFloat(&(motor->LPF_velocity.Tf));
break;
case REG_ANG_PID_P:
readFloat(&(motor->P_angle.P));
break;
case REG_VEL_LIMIT:
readFloat(&(motor->velocity_limit));
break;
case REG_VEL_MAX_RAMP:
readFloat(&(motor->PID_velocity.output_ramp));
break;
case REG_CURQ_PID_P:
readFloat(&(motor->PID_current_q.P));
break;
case REG_CURQ_PID_I:
readFloat(&(motor->PID_current_q.I));
break;
case REG_CURQ_PID_D:
readFloat(&(motor->PID_current_q.D));
break;
case REG_CURQ_LPF_T:
readFloat(&(motor->LPF_current_q.Tf));
break;
case REG_CURD_PID_P:
readFloat(&(motor->PID_current_d.P));
break;
case REG_CURD_PID_I:
readFloat(&(motor->PID_current_d.I));
break;
case REG_CURD_PID_D:
readFloat(&(motor->PID_current_d.D));
break;
case REG_CURD_LPF_T:
readFloat(&(motor->LPF_current_d.Tf));
break;
case REG_VOLTAGE_LIMIT:
readFloat(&(motor->voltage_limit));
break;
case REG_CURRENT_LIMIT:
readFloat(&(motor->current_limit));
break;
case REG_MOTION_DOWNSAMPLE:
readByte((uint8_t*)&(motor->motion_downsample));
break;
case REG_DRIVER_VOLTAGE_LIMIT:
readFloat(&(((BLDCMotor*)motor)->driver->voltage_limit));
break;
case REG_PWM_FREQUENCY:
readInt((uint32_t*)&(((BLDCMotor*)motor)->driver->pwm_frequency));
break;
case REG_ZERO_ELECTRIC_ANGLE:
readFloat(&(motor->zero_electric_angle));
break;
case REG_SENSOR_DIRECTION:
readByte((uint8_t*)&(motor->sensor_direction));
break;
case REG_ZERO_OFFSET:
readFloat(&(motor->sensor_offset));
break;
case REG_PHASE_RESISTANCE:
readFloat(&(motor->phase_resistance));
break;
case REG_KV:
readFloat(&(motor->KV_rating));
break;
case REG_INDUCTANCE:
readFloat(&(motor->phase_inductance));
break;
case REG_POLE_PAIRS:
readByte((uint8_t*)&(motor->pole_pairs));
break;
// unknown register or read-only register (no write) or can't handle in superclass
case REG_STATUS:
case REG_ANGLE:
case REG_POSITION:
case REG_VELOCITY:
case REG_SENSOR_ANGLE:
case REG_VOLTAGE_Q:
case REG_VOLTAGE_D:
case REG_CURRENT_Q:
case REG_CURRENT_D:
case REG_CURRENT_A:
case REG_CURRENT_B:
case REG_CURRENT_C:
case REG_CURRENT_ABC:
case REG_SYS_TIME:
case REG_NUM_MOTORS:
case REG_MOTOR_ADDRESS:
case REG_ENABLE_ALL:
case REG_REPORT:
default:
break;
}
};

View File

@@ -0,0 +1,15 @@
#pragma once
#include "./SimpleFOCRegisters.h"
#include "common/base_classes/FOCMotor.h"
class RegisterReceiver {
protected:
virtual void setRegister(SimpleFOCRegister reg, FOCMotor* motor);
virtual uint8_t readByte(uint8_t* valueToSet) = 0;
virtual uint8_t readFloat(float* valueToSet) = 0;
virtual uint8_t readInt(uint32_t* valueToSet) = 0;
};

View File

@@ -0,0 +1,193 @@
#include "./RegisterSender.h"
#include "common/foc_utils.h"
#include "BLDCMotor.h"
bool RegisterSender::sendRegister(SimpleFOCRegister reg, FOCMotor* motor){
// write a register value for the given motor
switch(reg) {
case REG_STATUS:
writeByte(motor->motor_status);
break;
case REG_ENABLE:
writeByte(motor->enabled);
break;
case REG_MODULATION_MODE:
writeByte(motor->foc_modulation);
break;
case REG_TORQUE_MODE:
writeByte(motor->torque_controller);
break;
case REG_CONTROL_MODE:
writeByte(motor->controller);
break;
case REG_TARGET:
writeFloat(motor->target);
break;
case REG_ANGLE:
writeFloat(motor->shaft_angle);
break;
case REG_POSITION:
if (motor->sensor) {
writeInt(motor->sensor->getFullRotations());
writeFloat(motor->sensor->getMechanicalAngle());
}
else {
writeInt(motor->shaft_angle/_2PI);
writeFloat(fmod(motor->shaft_angle, _2PI));
}
break;
case REG_VELOCITY:
writeFloat(motor->shaft_velocity);
break;
case REG_SENSOR_ANGLE:
if (motor->sensor)
writeFloat(motor->sensor->getAngle()); // stored angle
else
writeFloat(motor->shaft_angle);
break;
case REG_VOLTAGE_Q:
writeFloat(motor->voltage.q);
break;
case REG_VOLTAGE_D:
writeFloat(motor->voltage.d);
break;
case REG_CURRENT_Q:
writeFloat(motor->current.q);
break;
case REG_CURRENT_D:
writeFloat(motor->current.d);
break;
case REG_CURRENT_A:
if (motor->current_sense)
writeFloat(motor->current_sense->getPhaseCurrents().a);
else
writeFloat(0.0f);
break;
case REG_CURRENT_B:
if (motor->current_sense)
writeFloat(motor->current_sense->getPhaseCurrents().b);
else
writeFloat(0.0f);
break;
case REG_CURRENT_C:
if (motor->current_sense)
writeFloat(motor->current_sense->getPhaseCurrents().c);
else
writeFloat(0.0f);
break;
case REG_CURRENT_ABC:
if (motor->current_sense) {
PhaseCurrent_s currents = motor->current_sense->getPhaseCurrents();
writeFloat(currents.a);
writeFloat(currents.b);
writeFloat(currents.c);
}
else {
writeFloat(0.0f);
writeFloat(0.0f);
writeFloat(0.0f);
}
break;
case REG_VEL_PID_P:
writeFloat(motor->PID_velocity.P);
break;
case REG_VEL_PID_I:
writeFloat(motor->PID_velocity.I);
break;
case REG_VEL_PID_D:
writeFloat(motor->PID_velocity.D);
break;
case REG_VEL_LPF_T:
writeFloat(motor->LPF_velocity.Tf);
break;
case REG_ANG_PID_P:
writeFloat(motor->P_angle.P);
break;
case REG_VEL_LIMIT:
writeFloat(motor->velocity_limit);
break;
case REG_VEL_MAX_RAMP:
writeFloat(motor->PID_velocity.output_ramp);
break;
case REG_CURQ_PID_P:
writeFloat(motor->PID_current_q.P);
break;
case REG_CURQ_PID_I:
writeFloat(motor->PID_current_q.I);
break;
case REG_CURQ_PID_D:
writeFloat(motor->PID_current_q.D);
break;
case REG_CURQ_LPF_T:
writeFloat(motor->LPF_current_q.Tf);
break;
case REG_CURD_PID_P:
writeFloat(motor->PID_current_d.P);
break;
case REG_CURD_PID_I:
writeFloat(motor->PID_current_d.I);
break;
case REG_CURD_PID_D:
writeFloat(motor->PID_current_d.D);
break;
case REG_CURD_LPF_T:
writeFloat(motor->LPF_current_d.Tf);
break;
case REG_VOLTAGE_LIMIT:
writeFloat(motor->voltage_limit);
break;
case REG_CURRENT_LIMIT:
writeFloat(motor->current_limit);
break;
case REG_MOTION_DOWNSAMPLE:
writeByte((uint8_t)motor->motion_downsample);
break;
case REG_DRIVER_VOLTAGE_LIMIT:
writeFloat(((BLDCMotor*)motor)->driver->voltage_limit);
break;
case REG_PWM_FREQUENCY:
writeInt(((BLDCMotor*)motor)->driver->pwm_frequency);
break;
case REG_ZERO_ELECTRIC_ANGLE:
writeFloat(motor->zero_electric_angle);
break;
case REG_SENSOR_DIRECTION:
writeByte((int8_t)motor->sensor_direction);
break;
case REG_ZERO_OFFSET:
writeFloat(motor->sensor_offset);
break;
case REG_PHASE_RESISTANCE:
writeFloat(motor->phase_resistance);
break;
case REG_KV:
writeFloat(motor->KV_rating);
break;
case REG_INDUCTANCE:
writeFloat(motor->phase_inductance);
break;
case REG_POLE_PAIRS:
writeByte((uint8_t)motor->pole_pairs);
break;
case REG_SYS_TIME:
// TODO how big is millis()? Same on all platforms?
writeInt((int)millis());
break;
// unknown register or write only register (no read) or can't handle in superclass
case REG_NUM_MOTORS:
case REG_REPORT:
case REG_MOTOR_ADDRESS:
case REG_ENABLE_ALL:
default:
writeByte(0); // TODO what to send in this case?
return false;
}
return true;
};

View File

@@ -0,0 +1,18 @@
#pragma once
#include "./SimpleFOCRegisters.h"
#include "common/base_classes/FOCMotor.h"
/**
* Register sending functionality is shared by Commander and Telemetry implementations.
* Since the code to access all the registers is quite large, it makes sense to abstract it out,
* and also make sure registers are addressed in the same way for all implementations.
*/
class RegisterSender {
protected:
virtual bool sendRegister(SimpleFOCRegister reg, FOCMotor* motor);
virtual uint8_t writeByte(uint8_t value) = 0;
virtual uint8_t writeFloat(float value) = 0;
virtual uint8_t writeInt(uint32_t value) = 0;
};

View File

@@ -0,0 +1,72 @@
#pragma once
#include <inttypes.h>
// this constant is changed each time the registers definition are changed *in an incompatible way*. This means that just adding new registers
// does not change the version, but removing or changing the meaning of existing registers does, or changing the number of an existing register.
#define SIMPLEFOC_REGISTERS_VERSION 0x01
typedef enum : uint8_t {
REG_STATUS = 0x00, // RO - 1 byte (motor status)
REG_MOTOR_ADDRESS = 0x01, // R/W - 1 byte
REG_REPORT = 0x02, // R/W - Write: variable, Read: variable, up to 32 bytes
REG_ENABLE_ALL = 0x03, // WO - 1 byte
REG_ENABLE = 0x04, // R/W - 1 byte
REG_CONTROL_MODE = 0x05, // R/W - 1 byte
REG_TORQUE_MODE = 0x06, // R/W - 1 byte
REG_MODULATION_MODE = 0x07, // R/W - 1 byte
REG_TARGET = 0x08, // R/W - float
REG_ANGLE = 0x09, // RO - float
REG_POSITION = 0x10, // RO - int32_t full rotations + float position (0-2PI, in radians) (4 bytes + 4 bytes)
REG_VELOCITY = 0x11, // RO - float
REG_SENSOR_ANGLE = 0x12, // RO - float
REG_VOLTAGE_Q = 0x20, // RO - float
REG_VOLTAGE_D = 0x21, // RO - float
REG_CURRENT_Q = 0x22, // RO - float
REG_CURRENT_D = 0x23, // RO - float
REG_CURRENT_A = 0x24, // RO - float
REG_CURRENT_B = 0x25, // RO - float
REG_CURRENT_C = 0x26, // RO - float
REG_CURRENT_ABC = 0x27, // RO - 3xfloat = 12 bytes
REG_CURRENT_DC = 0x28, // RO - float
REG_VEL_PID_P = 0x30, // R/W - float
REG_VEL_PID_I = 0x31, // R/W - float
REG_VEL_PID_D = 0x32, // R/W - float
REG_VEL_LPF_T = 0x33, // R/W - float
REG_ANG_PID_P = 0x34, // R/W - float
REG_VEL_LIMIT = 0x35, // R/W - float
REG_VEL_MAX_RAMP = 0x36, // R/W - float
REG_CURQ_PID_P = 0x40, // R/W - float
REG_CURQ_PID_I = 0x41, // R/W - float
REG_CURQ_PID_D = 0x42, // R/W - float
REG_CURQ_LPF_T = 0x43, // R/W - float
REG_CURD_PID_P = 0x44, // R/W - float
REG_CURD_PID_I = 0x45, // R/W - float
REG_CURD_PID_D = 0x46, // R/W - float
REG_CURD_LPF_T = 0x47, // R/W - float
REG_VOLTAGE_LIMIT = 0x50, // R/W - float
REG_CURRENT_LIMIT = 0x51, // R/W - float
REG_MOTION_DOWNSAMPLE = 0x52, // R/W - uint32_t
REG_DRIVER_VOLTAGE_LIMIT = 0x53,// R/W - float
REG_PWM_FREQUENCY = 0x54, // R/W - uint32_t
REG_ZERO_ELECTRIC_ANGLE = 0x60, // RO - float
REG_SENSOR_DIRECTION = 0x61, // RO - 1 byte
REG_ZERO_OFFSET = 0x62, // R/W - float
REG_POLE_PAIRS = 0x63, // RO - uint32_t
REG_PHASE_RESISTANCE = 0x64, // R/W - float
REG_KV = 0x65, // R/W - float
REG_INDUCTANCE = 0x66, // R/W - float
REG_NUM_MOTORS = 0x70, // RO - 1 byte
REG_SYS_TIME = 0x71, // RO - uint32_t
} SimpleFOCRegister;

View File

@@ -0,0 +1,424 @@
#include "I2CCommander.h"
I2CCommander::I2CCommander(TwoWire* wire) : _wire(wire) {
};
I2CCommander::~I2CCommander(){};
void I2CCommander::init(uint8_t address) {
_address = address;
};
void I2CCommander::addMotor(FOCMotor* motor){
if (numMotors<I2CCOMMANDER_MAX_MOTORS_COMMANDABLE){
motors[numMotors] = motor;
numMotors++;
}
};
bool I2CCommander::readBytes(void* valueToSet, uint8_t numBytes){
if (_wire->available()>=numBytes){
byte* bytes = (byte*)valueToSet;
for (int i=0;i<numBytes;i++)
*bytes++ = _wire->read();
return true;
}
commanderror = true;
return false;
};
void I2CCommander::writeFloat(float value){
_wire->write((byte*)&value, 4);
};
void I2CCommander::onReceive(int numBytes){
lastcommanderror = commanderror;
lastcommandregister = curRegister;
commanderror = false;
if (numBytes>=1) { // set the current register
curRegister = static_cast<SimpleFOCRegister>(_wire->read());
}
if (numBytes>1) { // read from i2c and update value represented by register as well...
if (!receiveRegister(curMotor, curRegister, numBytes))
commanderror = true;
}
if (numBytes<1)
commanderror = true;
};
void I2CCommander::onRequest(){
commanderror = false;
if (!sendRegister(curMotor, curRegister))
commanderror = true;
};
/*
Reads values from I2C bus and updates the motor's values.
Currently this isn't really thread-safe, but works ok in practice on 32-bit MCUs.
Do not use on 8-bit architectures where the 32 bit writes may not be atomic!
Plan to make this safe: the writes should be buffered, and not actually executed
until in the main loop by calling commander->run();
the run() method disables interrupts while the updates happen.
*/
bool I2CCommander::receiveRegister(uint8_t motorNum, uint8_t registerNum, int numBytes) {
int val;
switch (registerNum) {
case REG_MOTOR_ADDRESS:
val = _wire->read(); // reading one more byte is definately ok, since numBytes>1
if (val>=0 && val<numMotors)
curMotor = val;
else
commanderror = true;
break;
case REG_REPORT:
if (numBytes>=3 && (numBytes&0x01)==1) { // numBytes must be odd, since we have register and n pairs of motor/register numbers
val = (numBytes-1)/2;
if (val>I2CCOMMANDER_MAX_REPORT_REGISTERS)
val = I2CCOMMANDER_MAX_REPORT_REGISTERS;
for (int i=0;i<val;i++){
reportMotors[i] = _wire->read();
reportRegisters[i] = _wire->read();
}
}
else
commanderror = true;
break;
case REG_ENABLE_ALL:
val = _wire->read();
for (int i=0; i<numMotors; i++)
(val>0)?motors[i]->enable():motors[i]->disable();
break;
case REG_ENABLE:
val = _wire->read();
(val>0)?motors[motorNum]->enable():motors[motorNum]->disable();
break;
case REG_CONTROL_MODE:
val = _wire->read();
if (val>=0 && val<=4) // TODO these enums don't have assigned constants
motors[motorNum]->controller = static_cast<MotionControlType>(val);
else
commanderror = true;
break;
case REG_TORQUE_MODE:
val = _wire->read();
if (val>=0 && val<=2)
motors[motorNum]->torque_controller = static_cast<TorqueControlType>(val);
else
commanderror = true;
break;
case REG_MODULATION_MODE:
val = _wire->read();
if (val>=0 && val<=3)
motors[motorNum]->foc_modulation = static_cast<FOCModulationType>(val);
else
commanderror = true;
break;
case REG_TARGET:
readBytes(&(motors[motorNum]->target), 4);
break;
case REG_VEL_PID_P:
readBytes(&(motors[motorNum]->PID_velocity.P), 4);
break;
case REG_VEL_PID_I:
readBytes(&(motors[motorNum]->PID_velocity.I), 4);
break;
case REG_VEL_PID_D:
readBytes(&(motors[motorNum]->PID_velocity.D), 4);
break;
case REG_VEL_LPF_T:
readBytes(&(motors[motorNum]->LPF_velocity.Tf), 4);
break;
case REG_ANG_PID_P:
readBytes(&(motors[motorNum]->P_angle.P), 4);
break;
case REG_VEL_LIMIT:
readBytes(&(motors[motorNum]->velocity_limit), 4);
break;
case REG_VEL_MAX_RAMP:
readBytes(&(motors[motorNum]->PID_velocity.output_ramp), 4);
break;
case REG_CURQ_PID_P:
readBytes(&(motors[motorNum]->PID_current_q.P), 4);
break;
case REG_CURQ_PID_I:
readBytes(&(motors[motorNum]->PID_current_q.I), 4);
break;
case REG_CURQ_PID_D:
readBytes(&(motors[motorNum]->PID_current_q.D), 4);
break;
case REG_CURQ_LPF_T:
readBytes(&(motors[motorNum]->LPF_current_q.Tf), 4);
break;
case REG_CURD_PID_P:
readBytes(&(motors[motorNum]->PID_current_d.P), 4);
break;
case REG_CURD_PID_I:
readBytes(&(motors[motorNum]->PID_current_d.I), 4);
break;
case REG_CURD_PID_D:
readBytes(&(motors[motorNum]->PID_current_d.D), 4);
break;
case REG_CURD_LPF_T:
readBytes(&(motors[motorNum]->LPF_current_d.Tf), 4);
break;
case REG_VOLTAGE_LIMIT:
readBytes(&(motors[motorNum]->voltage_limit), 4);
break;
case REG_CURRENT_LIMIT:
readBytes(&(motors[motorNum]->current_limit), 4);
break;
case REG_MOTION_DOWNSAMPLE:
readBytes(&(motors[motorNum]->motion_downsample), 4);
break;
case REG_ZERO_OFFSET:
readBytes(&(motors[motorNum]->sensor_offset), 4);
break;
// RO registers
case REG_STATUS:
case REG_ANGLE:
case REG_POSITION:
case REG_VELOCITY:
case REG_SENSOR_ANGLE:
case REG_VOLTAGE_Q:
case REG_VOLTAGE_D:
case REG_CURRENT_Q:
case REG_CURRENT_D:
case REG_CURRENT_A:
case REG_CURRENT_B:
case REG_CURRENT_C:
case REG_CURRENT_ABC:
case REG_ZERO_ELECTRIC_ANGLE:
case REG_SENSOR_DIRECTION:
case REG_POLE_PAIRS:
case REG_PHASE_RESISTANCE:
case REG_NUM_MOTORS:
case REG_SYS_TIME:
default: // unknown register
return false;
}
return true;
}
/*
Reads values from motor/sensor and writes them to I2C bus. Intended to be run
from the Wire.onRequest interrupt.
Assumes atomic 32 bit reads. On 8-bit arduino this assumption does not hold and this
code is not safe on those platforms. You might read "half-written" floats.
A solution might be to maintain a complete set of shadow registers in the commander
class, and update them in the run() method (which runs with interrupts off). Not sure
of the performance impact of all those 32 bit read/writes though. In any case, since
I use only 32 bit MCUs I'll leave it as an excercise to the one who needs it. ;-)
On 32 bit platforms the implication is that reads will occur atomically, so data will
be intact, but they can occur at any time during motor updates, so different values might
not be in a fully consistent state (i.e. phase A current might be from the current iteration
but phase B current from the previous iteration).
*/
bool I2CCommander::sendRegister(uint8_t motorNum, uint8_t registerNum) {
// read the current register
switch(registerNum) {
case REG_STATUS:
_wire->write(curMotor);
_wire->write((uint8_t)lastcommandregister);
_wire->write((uint8_t)lastcommanderror+1);
for (int i=0;(i<numMotors && i<28); i++) { // at most 28 motors, so we can fit in one packet
_wire->write(motors[i]->motor_status);
}
break;
case REG_MOTOR_ADDRESS:
_wire->write(curMotor);
break;
case REG_REPORT:
for (int i=0;i<numReportRegisters;i++)
if (reportRegisters[i]!=REG_REPORT) // prevent recursion
sendRegister(reportMotors[i], reportRegisters[i]);
break;
case REG_ENABLE:
_wire->write(motors[motorNum]->enabled);
break;
case REG_MODULATION_MODE:
_wire->write(motors[motorNum]->foc_modulation);
break;
case REG_TORQUE_MODE:
_wire->write(motors[motorNum]->torque_controller);
break;
case REG_CONTROL_MODE:
_wire->write(motors[motorNum]->controller);
break;
case REG_TARGET:
writeFloat(motors[motorNum]->target);
break;
case REG_ANGLE:
writeFloat(motors[motorNum]->shaft_angle);
break;
case REG_VELOCITY:
writeFloat(motors[motorNum]->shaft_velocity);
break;
case REG_SENSOR_ANGLE:
if (motors[motorNum]->sensor)
writeFloat(motors[motorNum]->sensor->getAngle()); // stored angle
else
writeFloat(motors[motorNum]->shaft_angle);
break;
case REG_VOLTAGE_Q:
writeFloat(motors[motorNum]->voltage.q);
break;
case REG_VOLTAGE_D:
writeFloat(motors[motorNum]->voltage.d);
break;
case REG_CURRENT_Q:
writeFloat(motors[motorNum]->current.q);
break;
case REG_CURRENT_D:
writeFloat(motors[motorNum]->current.d);
break;
case REG_CURRENT_A:
if (motors[motorNum]->current_sense) // TODO check if current sense can be called from inside this callback function
writeFloat(motors[motorNum]->current_sense->getPhaseCurrents().a);
else
writeFloat(0.0f);
break;
case REG_CURRENT_B:
if (motors[motorNum]->current_sense)
writeFloat(motors[motorNum]->current_sense->getPhaseCurrents().b);
else
writeFloat(0.0f);
break;
case REG_CURRENT_C:
if (motors[motorNum]->current_sense)
writeFloat(motors[motorNum]->current_sense->getPhaseCurrents().c);
else
writeFloat(0.0f);
break;
case REG_CURRENT_ABC:
if (motors[motorNum]->current_sense) {
PhaseCurrent_s currents = motors[motorNum]->current_sense->getPhaseCurrents();
writeFloat(currents.a);
writeFloat(currents.b);
writeFloat(currents.c);
}
else {
writeFloat(0.0f);
writeFloat(0.0f);
writeFloat(0.0f);
}
break;
case REG_VEL_PID_P:
writeFloat(motors[motorNum]->PID_velocity.P);
break;
case REG_VEL_PID_I:
writeFloat(motors[motorNum]->PID_velocity.I);
break;
case REG_VEL_PID_D:
writeFloat(motors[motorNum]->PID_velocity.D);
break;
case REG_VEL_LPF_T:
writeFloat(motors[motorNum]->LPF_velocity.Tf);
break;
case REG_ANG_PID_P:
writeFloat(motors[motorNum]->P_angle.P);
break;
case REG_VEL_LIMIT:
writeFloat(motors[motorNum]->velocity_limit);
break;
case REG_VEL_MAX_RAMP:
writeFloat(motors[motorNum]->PID_velocity.output_ramp);
break;
case REG_CURQ_PID_P:
writeFloat(motors[motorNum]->PID_current_q.P);
break;
case REG_CURQ_PID_I:
writeFloat(motors[motorNum]->PID_current_q.I);
break;
case REG_CURQ_PID_D:
writeFloat(motors[motorNum]->PID_current_q.D);
break;
case REG_CURQ_LPF_T:
writeFloat(motors[motorNum]->LPF_current_q.Tf);
break;
case REG_CURD_PID_P:
writeFloat(motors[motorNum]->PID_current_d.P);
break;
case REG_CURD_PID_I:
writeFloat(motors[motorNum]->PID_current_d.I);
break;
case REG_CURD_PID_D:
writeFloat(motors[motorNum]->PID_current_d.D);
break;
case REG_CURD_LPF_T:
writeFloat(motors[motorNum]->LPF_current_d.Tf);
break;
case REG_VOLTAGE_LIMIT:
writeFloat(motors[motorNum]->voltage_limit);
break;
case REG_CURRENT_LIMIT:
writeFloat(motors[motorNum]->current_limit);
break;
case REG_MOTION_DOWNSAMPLE:
_wire->write((int)motors[motorNum]->motion_downsample); // TODO int will have different sizes on different platforms
// but using uint32 doesn't compile clean on all, e.g. RP2040
break;
case REG_ZERO_ELECTRIC_ANGLE:
writeFloat(motors[motorNum]->zero_electric_angle);
break;
case REG_SENSOR_DIRECTION:
_wire->write((int8_t)motors[motorNum]->sensor_direction);
break;
case REG_ZERO_OFFSET:
writeFloat(motors[motorNum]->sensor_offset);
break;
case REG_PHASE_RESISTANCE:
writeFloat(motors[motorNum]->phase_resistance);
break;
case REG_POLE_PAIRS:
_wire->write((int)motors[motorNum]->pole_pairs);
break;
case REG_SYS_TIME:
// TODO how big is millis()? Same on all platforms?
_wire->write((int)millis());
break;
case REG_NUM_MOTORS:
_wire->write(numMotors);
break;
// unknown register or write only register (no read)
case REG_ENABLE_ALL:
default:
_wire->write(0); // TODO what to send in this case?
return false;
}
return true;
}

View File

@@ -0,0 +1,49 @@
#ifndef SIMPLEFOC_I2CCOMMANDER_H
#define SIMPLEFOC_I2CCOMMANDER_H
#include "Arduino.h"
#include "Wire.h"
#include "common/base_classes/FOCMotor.h"
#include "../SimpleFOCRegisters.h"
#ifndef I2CCOMMANDER_MAX_MOTORS_COMMANDABLE
#define I2CCOMMANDER_MAX_MOTORS_COMMANDABLE 4
#endif
#define I2CCOMMANDER_MIN_VELOCITY_FOR_MOTOR_MOVING 0.1f // in rad/s
#define I2CCOMMANDER_MAX_REPORT_REGISTERS 8
class I2CCommander {
public:
I2CCommander(TwoWire* wire = &Wire);
~I2CCommander();
void addMotor(FOCMotor* motor); // first add motors
virtual void init(uint8_t address); // then init
void onReceive(int numBytes);
void onRequest();
protected:
void writeFloat(float value);
bool readBytes(void* valueToSet, uint8_t numBytes);
virtual bool sendRegister(uint8_t motorNum, uint8_t registerNum);
virtual bool receiveRegister(uint8_t motorNum, uint8_t registerNum, int numBytes);
uint8_t _address;
TwoWire* _wire;
uint8_t numMotors = 0;
uint8_t curMotor = 0;
SimpleFOCRegister curRegister = REG_STATUS;
bool commanderror = false;
bool lastcommanderror = false;
uint8_t lastcommandregister = REG_STATUS;
FOCMotor* motors[I2CCOMMANDER_MAX_MOTORS_COMMANDABLE];
uint8_t numReportRegisters = 0;
uint8_t reportMotors[I2CCOMMANDER_MAX_REPORT_REGISTERS];
uint8_t reportRegisters[I2CCOMMANDER_MAX_REPORT_REGISTERS];
};
#endif

View File

@@ -0,0 +1,59 @@
#include "I2CCommanderMaster.h"
I2CCommanderMaster::I2CCommanderMaster(int maxMotors) : maxMotors(maxMotors), motors(new I2CRemoteMotor[maxMotors]) {
};
I2CCommanderMaster::~I2CCommanderMaster(){
};
void I2CCommanderMaster::init(){
};
// TODO handle multiple motors per target
void I2CCommanderMaster::addI2CMotors(uint8_t i2cAddress, uint8_t motorCount, TwoWire *wire){
for (int i=0;i<motorCount;i++){
if (numMotors<maxMotors){
motors[numMotors] = I2CRemoteMotor{ .wire=wire, .address=i2cAddress };
numMotors++;
}
}
};
int I2CCommanderMaster::writeRegister(int motor, SimpleFOCRegister registerNum, void* data, uint8_t size){
motors[motor].wire->beginTransmission(motors[motor].address);
motors[motor].wire->write((uint8_t)registerNum);
motors[motor].wire->write((uint8_t*)data, size);
motors[motor].wire->endTransmission();
return size;
};
int I2CCommanderMaster::readRegister(int motor, SimpleFOCRegister registerNum, void* data, uint8_t size){
motors[motor].wire->beginTransmission(motors[motor].address);
int numWrite = motors[motor].wire->write((uint8_t)registerNum); // TODO check return value
motors[motor].wire->endTransmission();
if (numWrite==1)
return readLastUsedRegister(motor, data, size);
return 0;
};
int I2CCommanderMaster::readLastUsedRegister(int motor, void* data, uint8_t size){
int numRead = motors[motor].wire->requestFrom(motors[motor].address, size);
if (numRead==size)
motors[motor].wire->readBytes((uint8_t*)data, size);
else {
return 0;
}
return numRead;
};

View File

@@ -0,0 +1,42 @@
#ifndef SIMPLEFOC_I2CCOMMANDER_H
#define SIMPLEFOC_I2CCOMMANDER_H
#include <Arduino.h>
#include <Wire.h>
#include "../SimpleFOCRegisters.h"
#define I2COMMANDER_DEFAULT_MAX_REMOTE_MOTORS 4
typedef struct {
TwoWire* wire;
uint8_t address;
} I2CRemoteMotor;
class I2CCommanderMaster {
public:
I2CCommanderMaster(int maxMotors = I2COMMANDER_DEFAULT_MAX_REMOTE_MOTORS);
~I2CCommanderMaster();
void init();
void addI2CMotors(uint8_t i2cAddress, uint8_t motorCount, TwoWire *wire = &Wire);
int writeRegister(int motor, SimpleFOCRegister registerNum, void* data, uint8_t size);
int readRegister(int motor, SimpleFOCRegister registerNum, void* data, uint8_t size);
int readLastUsedRegister(int motor, void* data, uint8_t size);
// Motor intialization interface for convenience - think about how this will best work
// i.e. which parameters should be set by i2c and which should be hard-coded, and where config info is saved
// TODO bool initializeMotor(int motor);
private:
int maxMotors;
int numMotors = 0;
I2CRemoteMotor* motors;
};
#endif

View File

@@ -0,0 +1,98 @@
# I2CCommander
Implementation of SimpleFOC Commander for I2C communication bus.
This code takes the point of view that the motor driver (the "muscle") is the I2C target device, and another MCU/CPU (the "brain") is the I2C controller device, which is coordinating one or more motor drivers on the same or different I2C buses. Each motor driver can offer one or more motors for control via the I2CCommander. So fairly flexible setups are possible, with multiple motors per driver, multiple drivers per I2C bus and multiple I2C buses on the brain MCU.
## Warning
This is new code, and has not been extensively tested. Your milage may vary. That said, basic use cases have been tested, and we would certainly appreciate feedback and help with testing it out.
In particular, there are concurrency issues with reading/writing the SimpleFOC motor values from I2C while the motor is running. These should be solved soon in an upcoming version.
**Do not run on 8-bit MCUs!** The code currently assumes atomic 32 bit reads, so running on Arduino UNO or Nano is unfortunately a no-go.
## Using
As would be expected for I2C, each target device needs a unique I2C address on its bus, and setting up and discovering these addresses is out-of-scope for I2CCommander. Setting up and configuring the TwoWire objects (which pins, speed, etc...) is also out of scope and finished, initialized TwoWire objects must be passed to I2CCommander. If you don't specify a different reference, the standard *Wire* object is assumed.
Communication with the motor drivers happens via a register paradigm. The driver board offers many registers, some of which can be read, some can be written, and some are read/write. The controller MCU sends I2C messages to the target device to read or write a register as desired. The size of the data to be read/written depends on the register, and must be known by the controller. See Registers, below, for more details on the individual registers.
Since each target motor driver can handle multiple motors, one of the registers contains the currently selected motor. Most of the other registers then operate on the currently selected motor. There are some exceptions, like REG_ENABLE_ALL - which operates on all the motors, or REG_STATUS, which returns stati for all the motors.
### Target device (motor driver)
The target device (motor driver) initializes and uses an instance of I2CCommander. Only one instance is needed for all attached motors:
```c++
#include "Arduino.h"
#include <Wire.h>
#include <SimpleFOC.h>
#include "SimpleFOCDrivers.h"
#include "comms/i2c/I2CCommander.h"
// commander instance
uint8_t i2c_addr = 0x60; // can be anything you choose
I2CCommander commander;
// interrupt callbacks
void onReceive(int numBytes) { commander.onReceive(numBytes); }
void onRequest() { commander.onRequest(); }
// ... other variables, like sensor, etc...
BLDCMotor motor = BLDCMotor(POLE_PAIRS);
void setup() {
// ...other setup code
Wire.setClock(400000); // use same speed on controller device
Wire.begin(i2c_addr, true); // initialize i2c in target mode
commander.addMotor(&motor); // add a motor
//commander.addMotor(&motor2); // you could add more than one motor
commander.init(i2c_addr); // initialize commander
Wire.onReceive(onReceive); // connect the interrupt handlers
Wire.onRequest(onRequest);
}
```
### Controller device ("brain" MCU)
On the controller device, you use an instance of I2CCommanderMaster, which you initialize by adding one or more target devices to it:
```c++
#include "Arduino.h"
#include <Wire.h>
#include <SimpleFOC.h>
#include "SimpleFOCDrivers.h"
#include "comms/i2c/I2CCommanderMaster.h"
#define TARGET_I2C_ADDRESS 0x60
I2CCommanderMaster commander;
void setup() {
// ...other setup code
Wire.setClock(400000); // use same speed on target device!
Wire.begin(); // initialize i2c in controller mode
commander.addI2CMotors(TARGET_I2C_ADDRESS, 1); // add target device, it has 1 motor
//commander.addI2CMotors(TARGET_I2C_ADDRESS2, 1); // you could add another target device on the same bus
//commander.addI2CMotors(TARGET_I2C_ADDRESS, 1, &wire2); // or on a different i2c bus
commander.init(); // init commander
Wire.onReceive(onReceive); // connect the interrupt handlers
Wire.onRequest(onRequest);
}
```
## Extending
The API is somewhat opinionated, and unlike the standard serial commander, currently does not support hooking in your own variables for reading/writing to them via I2C. This is because I2C is a bit less flexible than Serial.
If you want to extend I2CCommander please subclass it and override the functions `sendRegister` and `receiveRegister` to add new registers. Use register numbers above 0x80 to prevent collisions with the standard registers.

View File

@@ -0,0 +1,49 @@
# Serial communications classes
Serial communications classes for register-based control and telemetry from SimpleFOC.
## SerialASCIITelemetry
:warning: unfinished, untested
Telemetry class that sends telemetry as ASCII on a serial port. Similar to the classic "monitoring" functionality of SimpleFOC, but allows you to configure telemetry based on most of the defined registers.
Usage:
```c++
SerialASCIITelemetry telemetry = SerialASCIITelemetry(); // number of float digits to display
void setup() {
...
telemetry.addMotor(&motor);
telemetry.setTelemetryRegisters(2, [REG_VELOCITY, REG_VOLTAGE_Q]);
telemetry.init();
...
}
void loop() {
motor.move();
motor.loopFOC();
telemetry.run();
}
```
Some options are supported:
```c++
telemetry.floatPrecision = 4; // send floats with 4 decimal places
telemetry.min_elapsed_time = 0; // microseconds between sending telemetry
telemetry.downsample = 100; // send every this many loop iterations
```
## SerialBinaryCommander
:warning: unfinished, untested!
Control SimpleFOC via a binary protocol over the serial port. The standard SimpleFOC registers are used.
TODO document the protocol

View File

@@ -0,0 +1,65 @@
#include "./SerialASCIITelemetry.h"
SerialASCIITelemetry::SerialASCIITelemetry(int floatPrecision) : Telemetry() {
this->floatPrecision = floatPrecision;
};
SerialASCIITelemetry::~SerialASCIITelemetry(){
};
void SerialASCIITelemetry::init(Print* print){
this->_print = print;
this->Telemetry::init();
};
void SerialASCIITelemetry::sendHeader() {
if (numRegisters > 0) {
writeChar('H');
writeChar(' ');
for (uint8_t i = 0; i < numRegisters; i++) {
writeByte(registers_motor[i]);
writeChar(':');
writeByte(registers[i]);
if (i < numRegisters-1)
writeChar(' ');
};
writeChar('\n');
};
};
void SerialASCIITelemetry::sendTelemetry(){
if (numRegisters > 0) {
for (uint8_t i = 0; i < numRegisters; i++) {
sendRegister(static_cast<SimpleFOCRegister>(registers[i]), motors[registers_motor[i]]);
if (i < numRegisters-1)
writeChar(' ');
};
writeChar('\n');
}
};
uint8_t SerialASCIITelemetry::writeChar(char value){
return _print->print(value);
};
uint8_t SerialASCIITelemetry::writeByte(uint8_t value){
return _print->print(value);
};
uint8_t SerialASCIITelemetry::writeFloat(float value){
return _print->print(value, floatPrecision);
};
uint8_t SerialASCIITelemetry::writeInt(uint32_t value){
return _print->print(value);
};

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Arduino.h"
#include "../telemetry/Telemetry.h"
class SerialASCIITelemetry : public Telemetry {
public:
SerialASCIITelemetry(int floatPrecision = 2);
virtual ~SerialASCIITelemetry();
void init(Print* print = &Serial);
protected:
uint8_t writeByte(uint8_t value) override;
uint8_t writeFloat(float value) override;
uint8_t writeInt(uint32_t value) override;
uint8_t writeChar(char value);
void sendTelemetry() override;
void sendHeader() override;
Print* _print;
int floatPrecision = 2;
};

View File

@@ -0,0 +1,153 @@
#include "SerialBinaryCommander.h"
SerialBinaryCommander::SerialBinaryCommander(bool echo) : Telemetry(), echo(echo) {
};
SerialBinaryCommander::~SerialBinaryCommander(){
};
void SerialBinaryCommander::init(Stream &serial) {
_serial = &serial;
this->Telemetry::init();
};
void SerialBinaryCommander::run() {
if (_serial->available()>2) { // TODO make this work with partial packets...
uint8_t command = _serial->read();
uint8_t registerNum = _serial->read();
uint8_t motorNum = _serial->read();
if (command==SERIALBINARY_COMMAND_READ){
startFrame(registerNum, SERIALBINARY_FRAMETYPE_REGISTER); // TODO call is incorrect
sendRegister(static_cast<SimpleFOCRegister>(registerNum), motors[motorNum]);
endFrame();
}
else if (command==SERIALBINARY_COMMAND_WRITE) {
setRegister(static_cast<SimpleFOCRegister>(registerNum), motors[motorNum]);
if (echo) {
startFrame(registerNum, SERIALBINARY_FRAMETYPE_REGISTER); // TODO call is incorrect
sendRegister(static_cast<SimpleFOCRegister>(registerNum), motors[motorNum]);
endFrame();
}
}
}
// and handle the telemetry
this->Telemetry::run();
};
uint8_t SerialBinaryCommander::readBytes(void* valueToSet, uint8_t numBytes){
if (_serial->available()<numBytes)
return 0;
uint8_t* value = (uint8_t*)valueToSet;
for (uint8_t i=0; i<numBytes; i++) {
value[i] = _serial->read();
}
return numBytes;
};
uint8_t SerialBinaryCommander::writeBytes(void* valueToSend, uint8_t numBytes){
uint8_t* value = (uint8_t*)valueToSend;
for (uint8_t i=0; i<numBytes; i++) {
_serial->write(value[i]);
}
return numBytes;
};
void SerialBinaryCommander::startFrame(uint8_t frameSize, uint8_t type){
_serial->write(0xA5);
_serial->write((uint8_t)((type<<6)|frameSize));
};
void SerialBinaryCommander::endFrame(){
_serial->write(0x5A);
};
uint8_t SerialBinaryCommander::writeByte(uint8_t value){
return writeBytes(&value, 1);
};
uint8_t SerialBinaryCommander::writeFloat(float value){
return writeBytes(&value, 4);
};
uint8_t SerialBinaryCommander::writeInt(uint32_t value){
return writeBytes(&value, 4);
};
uint8_t SerialBinaryCommander::readByte(uint8_t* valueToSet){
return readBytes(valueToSet, 1);
};
uint8_t SerialBinaryCommander::readFloat(float* valueToSet){
return readBytes(valueToSet, 4);
};
uint8_t SerialBinaryCommander::readInt(uint32_t* valueToSet){
return readBytes(valueToSet, 4);
};
void SerialBinaryCommander::sendHeader() {
if (numRegisters > 0) {
startFrame(numRegisters*2, TELEMETRY_FRAMETYPE_HEADER);
for (uint8_t i = 0; i < numRegisters; i++) {
writeByte(registers[i]);
writeByte(registers_motor[i]);
};
endFrame();
};
};
void SerialBinaryCommander::sendTelemetry(){
if (numRegisters > 0) {
startFrame(frameSize, TELEMETRY_FRAMETYPE_DATA);
for (uint8_t i = 0; i < numRegisters; i++) {
sendRegister(static_cast<SimpleFOCRegister>(registers[i]), motors[registers_motor[i]]);
};
endFrame();
}
};

View File

@@ -0,0 +1,57 @@
#pragma once
#include <Arduino.h>
#include "../RegisterReceiver.h"
#include "../telemetry/Telemetry.h"
#ifndef COMMS_MAX_REPORT_REGISTERS
#define COMMS_MAX_REPORT_REGISTERS 7
#endif
#define SERIALBINARY_FRAMETYPE_REGISTER 0x03
#define SERIALBINARY_COMMAND_READ 0x00
#define SERIALBINARY_COMMAND_WRITE 0x80
class SerialBinaryCommander : public Telemetry, public RegisterReceiver {
public:
SerialBinaryCommander(bool echo = false);
virtual ~SerialBinaryCommander();
void init(Stream &serial = Serial);
void run();
bool echo;
protected:
virtual void startFrame(uint8_t frameSize, uint8_t type = TELEMETRY_FRAMETYPE_DATA);
virtual void endFrame();
uint8_t readBytes(void* valueToSet, uint8_t numBytes);
uint8_t writeBytes(void* valueToSend, uint8_t numBytes);
uint8_t writeByte(uint8_t value) override;
uint8_t writeFloat(float value) override;
uint8_t writeInt(uint32_t value) override;
uint8_t readByte(uint8_t* valueToSet) override;
uint8_t readFloat(float* valueToSet) override;
uint8_t readInt(uint32_t* valueToSet) override;
void sendTelemetry() override;
void sendHeader() override;
uint8_t curMotor = 0;
SimpleFOCRegister curRegister = REG_STATUS;
bool commanderror = false;
bool lastcommanderror = false;
uint8_t lastcommandregister = REG_STATUS;
uint8_t numReportRegisters = 0;
uint8_t reportMotors[COMMS_MAX_REPORT_REGISTERS];
SimpleFOCRegister reportRegisters[COMMS_MAX_REPORT_REGISTERS];
Stream* _serial;
};

View File

@@ -0,0 +1,58 @@
# STM32SpeedDirInput
Control SimpleFOC using PWM speed and direction inputs.
Based on STM32 timer PWM-Input capabilities, which means this can only be used on STM32 MCUs. It can cover a wide range of PWM frequencies, and runs without MCU overhead in the timer hardware.
## Setup
The PWM speed input should be connected to either channel 1 or channel 2 of a general purpose or advanced control timer on your STM32 MCU. Suitable timers are:
- Advanced control timers: TIM1, TIM8
- General purpose timers (32 bit): TIM2, TIM5
- General purpose timers (16 bit): TIM3, TIM4, TIM9, TIM12
If in doubt, check in STM32CubeIDE and see if the PWM-Input mode can be enabled (under "Combined Channels") for the timer.
The optional direction input can be connected to any GPIO pin. By default a high level direction input is associated with a positive velocity value, while a low level direction input results in a negative velocity value. To reverse this, set the option `dir_positive_high = false`
The direction input is optional - if not provided, you can control the direction from software using the `direction` field.
The velocity values returned are in the range `min_speed` to `max_speed`, while the input PWM duty cycle should lie within the range `min_pwm` to `max_pwm`. Actual input values smaller than `min_pwm` will be treated as `min_pwm`, values larger than `max_pwm` will be treated as `max_pwm`. The behaviour for 100% or 0% duty cycles is undefined, and they should be avoided.
## Usage
Use it like this:
```c++
#include "comms/stm32speeddir/STM32SpeedDirInput.h"
// some example pins - the speed pin has to be on channel 1 or 2 of a timer
#define PIN_SPEED PC6
#define PIN_DIRECTION PB8
STM32SpeedDirInput speed_dir = STM32SpeedDirInput(PIN_SPEED, PIN_DIRECTION);
float target = 0.0f;
void setup(){
...
speed_dir.min_speed = 10.0f; // 10rad/s min speed
speed_dir.max_speed = 100.0f; // 100rad/s max speed
speed_dir.min_pwm = 5.0f; // 5% min duty cycle
speed_dir.max_pwm = 95.0f; // 95% max duty cycle
speed_dir.init();
...
}
void loop(){
target = speed_dir.getTargetVelocity();
motor.move(target);
motor.loopFOC();
}
```

View File

@@ -0,0 +1,29 @@
#include "./STM32SpeedDirInput.h"
#if defined(_STM32_DEF_)
STM32SpeedDirInput::STM32SpeedDirInput(int pin_speed, int pin_dir) : STM32PWMInput(pin_speed) {
_pin_speed = pin_speed;
_pin_dir = pin_dir;
};
STM32SpeedDirInput::~STM32SpeedDirInput(){};
int STM32SpeedDirInput::init(){
pinMode(_pin_dir, INPUT);
return STM32PWMInput::initialize();
};
float STM32SpeedDirInput::getTargetVelocity(){
if (_pin_dir != NOT_SET)
direction = digitalRead(_pin_dir);
float speed = getDutyCyclePercent();
speed = constrain(speed, min_pwm, max_pwm);
speed = (speed - min_pwm)/(max_pwm - min_pwm) * (max_speed - min_speed) + min_speed;
return (direction == dir_positive_high) ? speed : -speed;
};
#endif

View File

@@ -0,0 +1,31 @@
#pragma once
#include "Arduino.h"
#if defined(_STM32_DEF_)
#include "common/foc_utils.h"
#include "utilities/stm32pwm/STM32PWMInput.h"
class STM32SpeedDirInput : public STM32PWMInput {
public:
STM32SpeedDirInput(int pin_speed, int pin_dir = NOT_SET);
~STM32SpeedDirInput();
int init();
float getTargetVelocity();
float min_speed = 0; // min speed in rad/s
float max_speed = 100; // max speed in rad/s
float min_pwm = 5.0; // min duty cycle in %
float max_pwm = 95.0; // max duty cycle in %
bool dir_positive_high = true; // true if the direction pin is high when the motor is spinning in the positive direction
bool direction = true; // direction of rotation, default positive
protected:
int _pin_speed;
int _pin_dir;
};
#endif

View File

@@ -0,0 +1,42 @@
# SimpleFOC Telemetry
:warning: unfinished, untested
A flexible abstraction for telemetry (monitoring) of SimpleFOC systems.
The telemetry implementation is based on the SimpleFOC registers, and allows you to send telemetry for any (readable) register. Telemetry supports multiple motors.
The concept allows you to choose registers which are then sent by the telemetry automatically, on a regular schedule.
The method of sending depends on the type of telemetry you add to your program. There are telemetry drivers for:
- Serial ASCII telemetry
- Serial Binary telemetry
- and more drivers will be added in the future
Multiple motors can be added to the same telemetry, to monitor several motors at the same time. The registers reported by telemetry can be changed at run-time. Multiple instances of telemetry can be used to monitor different sets of values at different time intervals, or to send to multiple channels at the same time.
## Usage
Using telemetry is simple:
```c++
SerialASCIITelemetry telemetry = SerialASCIITelemetry();
...
void setup() {
...
telemetry.addMotor(&motor);
telemetry.setTelemetryRegisters(3, { REG_TARGET, REG_ANGLE, REG_VELOCITY });
telemetry.init();
...
}
void loop() {
motor.move();
motor.loopFOC();
telemetry.run();
}
```

View File

@@ -0,0 +1,67 @@
#include "./Telemetry.h"
Telemetry::Telemetry() {
this->numRegisters = 0;
};
Telemetry::~Telemetry(){
};
void Telemetry::setTelemetryRegisters(uint8_t numRegisters, uint8_t* registers, uint8_t* motors){
if (numRegisters<=TELEMETRY_MAX_REGISTERS) {
this->numRegisters = numRegisters;
for (uint8_t i=0; i<numRegisters; i++) {
this->registers[i] = registers[i];
if (motors!=NULL)
this->registers_motor[i] = motors[i];
else
this->registers_motor[i] = 0;
}
}
};
void Telemetry::init() {
headerSent = false;
};
void Telemetry::run() {
if (numRegisters<1)
return;
if (!headerSent) {
sendHeader();
headerSent = true;
}
if (downsampleCnt++ < downsample) return;
downsampleCnt = 0;
if (min_elapsed_time > 0) {
long now = _micros();
if (now - last_run_time < min_elapsed_time) return;
last_run_time = now;
}
sendTelemetry();
}
void Telemetry::addMotor(FOCMotor* motor) {
if (numMotors < TELEMETRY_MAX_MOTORS) {
motors[numMotors] = motor;
numMotors++;
}
};

View File

@@ -0,0 +1,53 @@
#pragma once
#include "../SimpleFOCRegisters.h"
#include "../RegisterSender.h"
#ifndef TELEMETRY_MAX_REGISTERS
#define TELEMETRY_MAX_REGISTERS 8
#endif
#ifndef TELEMETRY_MAX_MOTORS
#define TELEMETRY_MAX_MOTORS 4
#endif
#define DEF_TELEMETRY_DOWNSAMPLE 100
typedef enum : uint8_t {
TELEMETRY_FRAMETYPE_DATA = 0x01,
TELEMETRY_FRAMETYPE_HEADER = 0x02
} TelemetryFrameType;
class Telemetry : public RegisterSender {
public:
Telemetry();
virtual ~Telemetry();
virtual void init();
void addMotor(FOCMotor* motor);
void setTelemetryRegisters(uint8_t numRegisters, uint8_t* registers, uint8_t* motors = NULL);
void run();
uint16_t downsample = DEF_TELEMETRY_DOWNSAMPLE;
uint32_t min_elapsed_time = 0;
protected:
virtual void sendTelemetry() = 0;
virtual void sendHeader() = 0;
FOCMotor* motors[TELEMETRY_MAX_MOTORS];
uint8_t numMotors = 0;
uint8_t numRegisters;
uint8_t registers[TELEMETRY_MAX_REGISTERS];
uint8_t registers_motor[TELEMETRY_MAX_REGISTERS];
uint8_t frameSize;
bool headerSent;
long last_run_time = 0;
uint16_t downsampleCnt = 0;
};

View File

@@ -0,0 +1,175 @@
# DRV8316 SimpleFOC driver
The DRV8316 is a integrated FET, integrated current sensing 3-phase BLDC driver IC from Texas Instruments, including all protections and many cool configuration optons. See https://www.ti.com/product/DRV8316 for more information.
This driver includes a DRV8316 SPI driver, and specific subclasses for SimpleFOCs BLDCDriver3PWM and BLDCDriver6PWM generic drivers. The code is designed to be "Ardunio compatible" and should work with any of the hardware architectures supported by SimpleFOC.
## Hardware setup
To use the DRV8316 you will have to connect the following:
- GND
- SPI MOSI
- SPI MISO
- SPI CLK
- SPI nCS
- INH_U - connect to motor PWM pin
- INH_V - connect to motor PWM pin
- INH_W - connect to motor PWM pin
- INL_U - connect to motor PWM pin for 6-PWM, or to digital out (or pull up to VCC) for 3-PWM operation
- INL_V - connect to motor PWM pin for 6-PWM, or to digital out (or pull up to VCC) for 3-PWM operation
- INL_W - connect to motor PWM pin for 6-PWM, or to digital out (or pull up to VCC) for 3-PWM operation
Optionally, but probably useful:
- nFault - digital in, active low
- DRVOFF - digital out
For current sensting:
- VREF - either connect to DAC output of the MCU or a fixed voltage reference.
- ISENA - connect to analog in
- ISENB - connect to analog in
- ISENC - connect to analog in
For current limiting:
- ILIM (same as VREF) - connect it to a DAC output of the MCU if you want to control the current limit from the MCU
## Usage
Usage is quite easy, especially if you already know SimpleFOC. See also the [examples](https://github.com/simplefoc/Arduino-FOC-drivers/examples/drivers/drv8316/)
```c++
#include "Arduino.h"
#include <Wire.h>
#include <SimpleFOC.h>
#include <Math.h>
#include "SimpleFOCDrivers.h"
#include "drivers/drv8316/drv8316.h"
BLDCMotor motor = BLDCMotor(11);
DRV8316Driver6PWM driver = DRV8316Driver6PWM(A3,0,A4,1,2,6,7,false); // MKR1010 6-PWM
//... normal simpleFOC init code...
```
Or, for 3-PWM:
```c++
#include "Arduino.h"
#include <Wire.h>
#include <SimpleFOC.h>
#include <Math.h>
#include "SimpleFOCDrivers.h"
#include "drivers/drv8316/drv8316.h"
BLDCMotor motor = BLDCMotor(11);
DRV8316Driver3PWM driver = DRV8316Driver3PWM(A3,A4,2,7,false); // MKR1010 3-PWM
// these are examples, for 3-PWM you could use any output pins as the enable pins.
#define ENABLE_A 0
#define ENABLE_B 1
#define ENABLE_C 6
void setup() {
pinMode(ENABLE_A, OUTPUT);
digitalWrite(ENABLE_A, 1); // enable
pinMode(ENABLE_B, OUTPUT);
digitalWrite(ENABLE_B, 1); // enable
pinMode(ENABLE_C, OUTPUT);
digitalWrite(ENABLE_C, 1); // enable
//... normal simpleFOC init code...
}
```
You can use the driver's features. In general you can do this at any time, but certain features only make sense at setup-time (e.g. setting the PWM mode, which is handled automatically by the DRV8316Driver3PWM class, or setting the current limit, which you generally want to get done before applying power to the motor).
Driver usage, for example reading and printing the complete status:
```c++
DRV8316Status status = driver.getStatus();
Serial.println("DRV8316 Status:");
Serial.print("Fault: ");
Serial.println(status.isFault());
Serial.print("Buck Error: ");
Serial.print(status.isBuckError());
Serial.print(" Undervoltage: ");
Serial.print(status.isBuckUnderVoltage());
Serial.print(" OverCurrent: ");
Serial.println(status.isBuckOverCurrent());
Serial.print("Charge Pump UnderVoltage: ");
Serial.println(status.isChargePumpUnderVoltage());
Serial.print("OTP Error: ");
Serial.println(status.isOneTimeProgrammingError());
Serial.print("OverCurrent: ");
Serial.print(status.isOverCurrent());
Serial.print(" Ah: ");
Serial.print(status.isOverCurrent_Ah());
Serial.print(" Al: ");
Serial.print(status.isOverCurrent_Al());
Serial.print(" Bh: ");
Serial.print(status.isOverCurrent_Bh());
Serial.print(" Bl: ");
Serial.print(status.isOverCurrent_Bl());
Serial.print(" Ch: ");
Serial.print(status.isOverCurrent_Ch());
Serial.print(" Cl: ");
Serial.println(status.isOverCurrent_Cl());
Serial.print("OverTemperature: ");
Serial.print(status.isOverTemperature());
Serial.print(" Shutdown: ");
Serial.print(status.isOverTemperatureShutdown());
Serial.print(" Warning: ");
Serial.println(status.isOverTemperatureWarning());
Serial.print("OverVoltage: ");
Serial.println(status.isOverVoltage());
Serial.print("PowerOnReset: ");
Serial.println(status.isPowerOnReset());
Serial.print("SPI Error: ");
Serial.print(status.isSPIError());
Serial.print(" Address: ");
Serial.print(status.isSPIAddressError());
Serial.print(" Clock: ");
Serial.print(status.isSPIClockFramingError());
Serial.print(" Parity: ");
Serial.println(status.isSPIParityError());
if (status.isFault())
driver.clearFault();
delayMicroseconds(1); // ensure 400ns delay
DRV8316_PWMMode val = driver.getPWMMode();
Serial.print("PWM Mode: ");
Serial.println(val);
delayMicroseconds(1); // ensure 400ns delay
bool lock = driver.isRegistersLocked();
Serial.print("Lock: ");
Serial.println(lock);
```
Setting options can be conveniently done via the provided setter methods. All documented registers and options are available via the driver, and the option values can be accessed via enums.
Leave at least 400ns delay between reading and/or writing options to ensure you don't talk to the DRV8316 too quickly:
```c++
driver.setPWM100Frequency(DRV8316_PWM100DUTY::FREQ_40KHz);
delayMicroseconds(1); // ensure 400ns delay
driver.setBuckVoltage(DRV8316_BuckVoltage::VB_5V7);
```
### Current sensing
TODO...
### Current limiting
TODO...

View File

@@ -0,0 +1,586 @@
#include "./drv8316.h"
void DRV8316Driver3PWM::init(SPIClass* _spi) {
DRV8316Driver::init(_spi);
setRegistersLocked(false);
delayMicroseconds(1);
DRV8316Driver::setPWMMode(DRV8316_PWMMode::PWM3_Mode);
BLDCDriver3PWM::init();
};
void DRV8316Driver6PWM::init(SPIClass* _spi) {
DRV8316Driver::init(_spi);
setRegistersLocked(false);
delayMicroseconds(1);
DRV8316Driver::setPWMMode(DRV8316_PWMMode::PWM6_Mode); // default mode is 6-PWM
BLDCDriver6PWM::init();
};
/*
* SPI setup:
*
* capture on falling, propagate on rising =
* MSB first
*
* 16 bit words
* outgoing: R/W:1 addr:6 parity:1 data:8
* incoming: status:8 data:8
*
* on reads, incoming data is content of register being read
* on writes, incomnig data is content of register being written
*
*
*/
void handleInterrupt() {
}
void DRV8316Driver::init(SPIClass* _spi) {
// TODO make SPI speed configurable
spi = _spi;
settings = SPISettings(1000000, MSBFIRST, SPI_MODE1);
//setup pins
pinMode(cs, OUTPUT);
digitalWrite(cs, HIGH); // switch off
//SPI has an internal SPI-device counter, it is possible to call "begin()" from different devices
spi->begin();
if (_isset(nFault)) {
pinMode(nFault, INPUT);
// TODO add interrupt handler on the nFault pin if configured
// add configuration for how to handle faults... idea: interrupt handler calls a callback, depending on the type of fault
// consider what would be a useful configuration in practice? What do we want to do on a fault, e.g. over-temperature for example?
//attachInterrupt(digitalPinToInterrupt(nFault), handleInterrupt, PinStatus::FALLING);
}
};
bool DRV8316Driver::getParity(uint16_t data) {
//PARITY = XNOR(CMD, A5..A0, D7..D0)
uint8_t par = 0;
for (int i=0;i<16;i++) {
if (((data)>>i) & 0x0001)
par+=1;
}
return (par&0x01)==0x01; // even number of bits means true
}
uint16_t DRV8316Driver::readSPI(uint8_t addr) {
digitalWrite(cs, 0);
spi->beginTransaction(settings);
uint16_t data = (((addr<<1) | 0x80)<<8)|0x0000;
if (getParity(data))
data |= 0x0100;
uint16_t result = spi->transfer16(data);
spi->endTransaction();
digitalWrite(cs, 1);
// Serial.print("SPI Read Result: ");
// Serial.print(data, HEX);
// Serial.print(" -> ");
// Serial.println(result, HEX);
return result;
}
uint16_t DRV8316Driver::writeSPI(uint8_t addr, uint8_t value) {
digitalWrite(cs, 0);
spi->beginTransaction(settings);
uint16_t data = ((addr<<1)<<8)|value;
if (getParity(data))
data |= 0x0100;
uint16_t result = spi->transfer16(data);
spi->endTransaction();
digitalWrite(cs, 1);
// Serial.print("SPI Write Result: ");
// Serial.print(data, HEX);
// Serial.print(" -> ");
// Serial.println(result, HEX);
return result;
}
DRV8316Status DRV8316Driver::getStatus() {
IC_Status data;
Status__1 data1;
Status__2 data2;
uint16_t result = readSPI(IC_Status_ADDR);
data.reg = (result & 0x00FF);
delayMicroseconds(1); // delay at least 400ns between operations
result = readSPI(Status__1_ADDR);
data1.reg = (result & 0x00FF);
delayMicroseconds(1); // delay at least 400ns between operations
result = readSPI(Status__2_ADDR);
data2.reg = (result & 0x00FF);
return DRV8316Status(data, data1, data2);
}
void DRV8316Driver::clearFault() {
uint16_t result = readSPI(Control__2_ADDR);
Control__2 data;
data.reg = (result & 0x00FF);
data.CLR_FLT |= 1;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__2_ADDR, data.reg);
};
bool DRV8316Driver::isRegistersLocked(){
uint16_t result = readSPI(Control__1_ADDR);
Control__1 data;
data.reg = (result & 0x00FF);
return data.REG_LOCK==REG_LOCK_LOCK;
}
void DRV8316Driver::setRegistersLocked(bool lock){
uint16_t result = readSPI(Control__1_ADDR);
Control__1 data;
data.reg = (result & 0x00FF);
data.REG_LOCK = lock?REG_LOCK_LOCK:REG_LOCK_UNLOCK;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__1_ADDR, data.reg);
}
DRV8316_PWMMode DRV8316Driver::getPWMMode() {
uint16_t result = readSPI(Control__2_ADDR);
Control__2 data;
data.reg = (result & 0x00FF);
return (DRV8316_PWMMode)data.PWM_MODE;
};
void DRV8316Driver::setPWMMode(DRV8316_PWMMode pwmMode){
uint16_t result = readSPI(Control__2_ADDR);
Control__2 data;
data.reg = (result & 0x00FF);
data.PWM_MODE = pwmMode;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__2_ADDR, data.reg);
};
DRV8316_Slew DRV8316Driver::getSlew() {
uint16_t result = readSPI(Control__2_ADDR);
Control__2 data;
data.reg = (result & 0x00FF);
return (DRV8316_Slew)data.SLEW;
};
void DRV8316Driver::setSlew(DRV8316_Slew slewRate) {
uint16_t result = readSPI(Control__2_ADDR);
Control__2 data;
data.reg = (result & 0x00FF);
data.SLEW = slewRate;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__2_ADDR, data.reg);
};
DRV8316_SDOMode DRV8316Driver::getSDOMode() {
uint16_t result = readSPI(Control__2_ADDR);
Control__2 data;
data.reg = (result & 0x00FF);
return (DRV8316_SDOMode)data.SDO_MODE;
};
void DRV8316Driver::setSDOMode(DRV8316_SDOMode sdoMode) {
uint16_t result = readSPI(Control__2_ADDR);
Control__2 data;
data.reg = (result & 0x00FF);
data.SDO_MODE = sdoMode;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__2_ADDR, data.reg);
};
bool DRV8316Driver::isOvertemperatureReporting(){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
return data.OTW_REP==OTW_REP_ENABLE;
};
void DRV8316Driver::setOvertemperatureReporting(bool reportFault){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
data.OTW_REP = reportFault?OTW_REP_ENABLE:OTW_REP_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__3_ADDR, data.reg);
};
bool DRV8316Driver::isSPIFaultReporting(){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
return data.SPI_FLT_REP==SPI_FLT_REP_ENABLE;
}
void DRV8316Driver::setSPIFaultReporting(bool reportFault){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
data.SPI_FLT_REP = reportFault?SPI_FLT_REP_ENABLE:SPI_FLT_REP_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__3_ADDR, data.reg);
}
bool DRV8316Driver::isOvervoltageProtection(){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
return data.OVP_EN==OVP_EN_ENABLE;
};
void DRV8316Driver::setOvervoltageProtection(bool enabled){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
data.OVP_EN = enabled?OVP_EN_ENABLE:OVP_EN_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__3_ADDR, data.reg);
};
DRV8316_OVP DRV8316Driver::getOvervoltageLevel(){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
return (DRV8316_OVP)data.OVP_SEL;
};
void DRV8316Driver::setOvervoltageLevel(DRV8316_OVP voltage){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
data.OVP_SEL = voltage;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__3_ADDR, data.reg);
};
DRV8316_PWM100DUTY DRV8316Driver::getPWM100Frequency(){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
return (DRV8316_PWM100DUTY)data.PWM_100_DUTY_SEL;
};
void DRV8316Driver::setPWM100Frequency(DRV8316_PWM100DUTY freq){
uint16_t result = readSPI(Control__3_ADDR);
Control__3 data;
data.reg = (result & 0x00FF);
data.PWM_100_DUTY_SEL = freq;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__3_ADDR, data.reg);
};
DRV8316_OCPMode DRV8316Driver::getOCPMode(){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
return (DRV8316_OCPMode)data.OCP_MODE;
};
void DRV8316Driver::setOCPMode(DRV8316_OCPMode ocpMode){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
data.OCP_MODE = ocpMode;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__4_ADDR, data.reg);
};
DRV8316_OCPLevel DRV8316Driver::getOCPLevel(){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
return (DRV8316_OCPLevel)data.OCP_LVL;
};
void DRV8316Driver::setOCPLevel(DRV8316_OCPLevel amps){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
data.OCP_LVL = amps;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__4_ADDR, data.reg);
};
DRV8316_OCPRetry DRV8316Driver::getOCPRetryTime(){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
return (DRV8316_OCPRetry)data.OCP_RETRY;
};
void DRV8316Driver::setOCPRetryTime(DRV8316_OCPRetry ms){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
data.OCP_RETRY = ms;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__4_ADDR, data.reg);
};
DRV8316_OCPDeglitch DRV8316Driver::getOCPDeglitchTime(){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
return (DRV8316_OCPDeglitch)data.OCP_DEG;
};
void DRV8316Driver::setOCPDeglitchTime(DRV8316_OCPDeglitch ms){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
data.OCP_DEG = ms;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__4_ADDR, data.reg);
};
bool DRV8316Driver::isOCPClearInPWMCycleChange(){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
return data.OCP_CBC==OCP_CBC_ENABLE;
};
void DRV8316Driver::setOCPClearInPWMCycleChange(bool enable){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
data.OCP_CBC = enable?OCP_CBC_ENABLE:OCP_CBC_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__4_ADDR, data.reg);
};
bool DRV8316Driver::isDriverOffEnabled(){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
return data.DRV_OFF==DRV_OFF_ENABLE;
};
void DRV8316Driver::setDriverOffEnabled(bool enabled){
uint16_t result = readSPI(Control__4_ADDR);
Control__4 data;
data.reg = (result & 0x00FF);
data.DRV_OFF = enabled?DRV_OFF_ENABLE:DRV_OFF_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__4_ADDR, data.reg);
};
DRV8316_CSAGain DRV8316Driver::getCurrentSenseGain(){
uint16_t result = readSPI(Control__5_ADDR);
Control__5 data;
data.reg = (result & 0x00FF);
return (DRV8316_CSAGain)data.CSA_GAIN;
};
void DRV8316Driver::setCurrentSenseGain(DRV8316_CSAGain gain){
uint16_t result = readSPI(Control__5_ADDR);
Control__5 data;
data.reg = (result & 0x00FF);
data.CSA_GAIN = gain;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__5_ADDR, data.reg);
};
bool DRV8316Driver::isActiveSynchronousRectificationEnabled(){
uint16_t result = readSPI(Control__5_ADDR);
Control__5 data;
data.reg = (result & 0x00FF);
return data.EN_ASR==EN_ASR_ENABLE;
};
void DRV8316Driver::setActiveSynchronousRectificationEnabled(bool enabled){
uint16_t result = readSPI(Control__5_ADDR);
Control__5 data;
data.reg = (result & 0x00FF);
data.EN_ASR = enabled?EN_ASR_ENABLE:EN_ASR_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__5_ADDR, data.reg);
};
bool DRV8316Driver::isActiveAsynchronousRectificationEnabled(){
uint16_t result = readSPI(Control__5_ADDR);
Control__5 data;
data.reg = (result & 0x00FF);
return data.EN_AAR==EN_AAR_ENABLE;
};
void DRV8316Driver::setActiveAsynchronousRectificationEnabled(bool enabled){
uint16_t result = readSPI(Control__5_ADDR);
Control__5 data;
data.reg = (result & 0x00FF);
data.EN_AAR = enabled?EN_AAR_ENABLE:EN_AAR_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__5_ADDR, data.reg);
};
DRV8316_Recirculation DRV8316Driver::getRecirculationMode(){
uint16_t result = readSPI(Control__5_ADDR);
Control__5 data;
data.reg = (result & 0x00FF);
return (DRV8316_Recirculation)data.ILIM_RECIR;
};
void DRV8316Driver::setRecirculationMode(DRV8316_Recirculation recirculationMode){
uint16_t result = readSPI(Control__5_ADDR);
Control__5 data;
data.reg = (result & 0x00FF);
data.ILIM_RECIR = recirculationMode;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__5_ADDR, data.reg);
};
bool DRV8316Driver::isBuckEnabled(){
uint16_t result = readSPI(Control__6_ADDR);
Control__6 data;
data.reg = (result & 0x00FF);
return data.BUCK_DIS==BUCK_DIS_BUCK_ENABLE;
};
void DRV8316Driver::setBuckEnabled(bool enabled){
uint16_t result = readSPI(Control__6_ADDR);
Control__6 data;
data.reg = (result & 0x00FF);
data.BUCK_DIS = enabled?BUCK_DIS_BUCK_ENABLE:BUCK_DIS_BUCK_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__6_ADDR, data.reg);
};
DRV8316_BuckVoltage DRV8316Driver::getBuckVoltage(){
uint16_t result = readSPI(Control__6_ADDR);
Control__6 data;
data.reg = (result & 0x00FF);
return (DRV8316_BuckVoltage)data.BUCK_SEL;
};
void DRV8316Driver::setBuckVoltage(DRV8316_BuckVoltage volts){
uint16_t result = readSPI(Control__6_ADDR);
Control__6 data;
data.reg = (result & 0x00FF);
data.BUCK_SEL = volts;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__6_ADDR, data.reg);
};
DRV8316_BuckCurrentLimit DRV8316Driver::getBuckCurrentLimit(){
uint16_t result = readSPI(Control__6_ADDR);
Control__6 data;
data.reg = (result & 0x00FF);
return (DRV8316_BuckCurrentLimit)data.BUCK_CL;
};
void DRV8316Driver::setBuckCurrentLimit(DRV8316_BuckCurrentLimit mamps){
uint16_t result = readSPI(Control__6_ADDR);
Control__6 data;
data.reg = (result & 0x00FF);
data.BUCK_CL = mamps;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__6_ADDR, data.reg);
};
bool DRV8316Driver::isBuckPowerSequencingEnabled(){
uint16_t result = readSPI(Control__6_ADDR);
Control__6 data;
data.reg = (result & 0x00FF);
return data.BUCK_PS_DIS==BUCK_PS_DIS_ENABLE;
};
void DRV8316Driver::setBuckPowerSequencingEnabled(bool enabled){
uint16_t result = readSPI(Control__6_ADDR);
Control__6 data;
data.reg = (result & 0x00FF);
data.BUCK_PS_DIS = enabled?BUCK_PS_DIS_ENABLE:BUCK_PS_DIS_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__6_ADDR, data.reg);
};
DRV8316_DelayTarget DRV8316Driver::getDelayTarget(){
uint16_t result = readSPI(Control__10_ADDR);
Control__10 data;
data.reg = (result & 0x00FF);
return (DRV8316_DelayTarget)data.DLY_TARGET;
};
void DRV8316Driver::setDelayTarget(DRV8316_DelayTarget us){
uint16_t result = readSPI(Control__10_ADDR);
Control__10 data;
data.reg = (result & 0x00FF);
data.DLY_TARGET = us;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__10_ADDR, data.reg);
};
bool DRV8316Driver::isDelayCompensationEnabled(){
uint16_t result = readSPI(Control__10_ADDR);
Control__10 data;
data.reg = (result & 0x00FF);
return data.DLYCMP_EN==DLYCMP_EN_ENABLE;
};
void DRV8316Driver::setDelayCompensationEnabled(bool enabled){
uint16_t result = readSPI(Control__10_ADDR);
Control__10 data;
data.reg = (result & 0x00FF);
data.DLYCMP_EN = enabled?DLYCMP_EN_ENABLE:DLYCMP_EN_DISABLE;
delayMicroseconds(1); // delay at least 400ns between operations
result = writeSPI(Control__10_ADDR, data.reg);
};

View File

@@ -0,0 +1,316 @@
#ifndef SIMPLEFOC_DRV8316
#define SIMPLEFOC_DRV8316
#include "Arduino.h"
#include <SPI.h>
#include <drivers/BLDCDriver3PWM.h>
#include <drivers/BLDCDriver6PWM.h>
#include "./drv8316_registers.h"
enum DRV8316_PWMMode {
PWM6_Mode = 0b00,
PWM6_CurrentLimit_Mode = 0b01,
PWM3_Mode = 0b10,
PWM3_CurrentLimit_Mode = 0b11
};
enum DRV8316_SDOMode {
SDOMode_OpenDrain = 0b0,
SDOMode_PushPull = 0b1
};
enum DRV8316_Slew {
Slew_25Vus = 0b00,
Slew_50Vus = 0b01,
Slew_150Vus = 0b10,
Slew_200Vus = 0b11
};
enum DRV8316_OVP {
OVP_SEL_32V = 0b0,
OVP_SEL_20V = 0b1
};
enum DRV8316_PWM100DUTY {
FREQ_20KHz = 0b0,
FREQ_40KHz = 0b1
};
enum DRV8316_OCPMode {
Latched_Fault = 0b00,
AutoRetry_Fault = 0b01,
ReportOnly = 0b10,
NoAction = 0b11
};
enum DRV8316_OCPLevel {
Curr_16A = 0b0,
Curr_24A = 0b1
};
enum DRV8316_OCPRetry {
Retry5ms = 0b0,
Retry500ms = 0b1
};
enum DRV8316_OCPDeglitch {
Deglitch_0us2 = 0b00,
Deglitch_0us6 = 0b01,
Deglitch_1us1 = 0b10,
Deglitch_1us6 = 0b11
};
enum DRV8316_CSAGain {
Gain_0V15 = 0b00,
Gain_0V1875 = 0b01,
Gain_0V25 = 0b10,
Gain_0V375 = 0b11
};
enum DRV8316_Recirculation {
BrakeMode = 0b00, // FETs
CoastMode = 0b01 // Diodes
};
enum DRV8316_BuckVoltage {
VB_3V3 = 0b00,
VB_5V = 0b01,
VB_4V = 0b10,
VB_5V7 = 0b11
};
enum DRV8316_BuckCurrentLimit {
Limit_600mA = 0b00,
Limit_150mA = 0b01
};
enum DRV8316_DelayTarget {
Delay_0us = 0x0,
Delay_0us4 = 0x1,
Delay_0us6 = 0x2,
Delay_0us8 = 0x3,
Delay_1us = 0x4,
Delay_1us2 = 0x5,
Delay_1us4 = 0x6,
Delay_1us6 = 0x7,
Delay_1us8 = 0x8,
Delay_2us = 0x9,
Delay_2us2 = 0xA,
Delay_2us4 = 0xB,
Delay_2us6 = 0xC,
Delay_2us8 = 0xD,
Delay_3us = 0xE,
Delay_3us2 = 0xF
};
class DRV8316ICStatus {
public:
DRV8316ICStatus(IC_Status status) : status(status) {};
~DRV8316ICStatus() {};
bool isFault() { return status.FAULT==0b1; };
bool isOverTemperature() { return status.OT==0b1; };
bool isOverCurrent() { return status.OCP==0b1; };
bool isOverVoltage() { return status.OVP==0b1; };
bool isSPIError() { return status.SPI_FLT==0b1; };
bool isBuckError() { return status.BK_FLT==0b1; };
bool isPowerOnReset() { return status.NPOR==0b1; };
IC_Status status;
};
class DRV8316Status1 {
public:
DRV8316Status1(Status__1 status1) : status1(status1) {};
~DRV8316Status1() {};
bool isOverCurrent_Ah() { return status1.OCP_HA==0b1; };
bool isOverCurrent_Al() { return status1.OCP_LA==0b1; };
bool isOverCurrent_Bh() { return status1.OCP_HB==0b1; };
bool isOverCurrent_Bl() { return status1.OCP_LB==0b1; };
bool isOverCurrent_Ch() { return status1.OCP_HC==0b1; };
bool isOverCurrent_Cl() { return status1.OCP_LC==0b1; };
bool isOverTemperatureShutdown() { return status1.OTS==0b1; };
bool isOverTemperatureWarning() { return status1.OTW==0b1; };
Status__1 status1;
};
class DRV8316Status2 {
public:
DRV8316Status2(Status__2 status2) : status2(status2) {};
~DRV8316Status2() {};
bool isOneTimeProgrammingError() { return status2.OTP_ERR==0b1; };
bool isBuckOverCurrent() { return status2.BUCK_OCP==0b1; };
bool isBuckUnderVoltage() { return status2.BUCK_UV==0b1; };
bool isChargePumpUnderVoltage() { return status2.VCP_UV==0b1; };
bool isSPIParityError() { return status2.SPI_PARITY==0b1; };
bool isSPIClockFramingError() { return status2.SPI_SCLK_FLT==0b1; };
bool isSPIAddressError() { return status2.SPI_ADDR_FLT==0b1; };
Status__2 status2;
};
class DRV8316Status : public DRV8316ICStatus, public DRV8316Status1, public DRV8316Status2 {
public:
DRV8316Status(IC_Status status, Status__1 status1, Status__2 status2) : DRV8316ICStatus(status), DRV8316Status1(status1), DRV8316Status2(status2) {};
~DRV8316Status() {};
};
class DRV8316Driver {
public:
DRV8316Driver(int cs, bool currentLimit = false, int nFault = NOT_SET) : currentLimit(currentLimit), cs(cs), nFault(nFault), spi(&SPI), settings(1000000, MSBFIRST, SPI_MODE1) {};
virtual ~DRV8316Driver() {};
virtual void init(SPIClass* _spi = &SPI);
void clearFault(); // TODO check for fault condition methods
DRV8316Status getStatus();
bool isRegistersLocked();
void setRegistersLocked(bool lock);
DRV8316_PWMMode getPWMMode();
void setPWMMode(DRV8316_PWMMode pwmMode);
DRV8316_Slew getSlew();
void setSlew(DRV8316_Slew slewRate);
DRV8316_SDOMode getSDOMode();
void setSDOMode(DRV8316_SDOMode sdoMode);
bool isOvertemperatureReporting();
void setOvertemperatureReporting(bool reportFault);
bool isSPIFaultReporting();
void setSPIFaultReporting(bool reportFault);
bool isOvervoltageProtection();
void setOvervoltageProtection(bool enabled);
DRV8316_OVP getOvervoltageLevel();
void setOvervoltageLevel(DRV8316_OVP voltage);
DRV8316_PWM100DUTY getPWM100Frequency();
void setPWM100Frequency(DRV8316_PWM100DUTY freq);
DRV8316_OCPMode getOCPMode();
void setOCPMode(DRV8316_OCPMode ocpMode);
DRV8316_OCPLevel getOCPLevel();
void setOCPLevel(DRV8316_OCPLevel amps);
DRV8316_OCPRetry getOCPRetryTime();
void setOCPRetryTime(DRV8316_OCPRetry ms);
DRV8316_OCPDeglitch getOCPDeglitchTime();
void setOCPDeglitchTime(DRV8316_OCPDeglitch ms);
bool isOCPClearInPWMCycleChange();
void setOCPClearInPWMCycleChange(bool enable);
bool isDriverOffEnabled();
void setDriverOffEnabled(bool enabled);
DRV8316_CSAGain getCurrentSenseGain();
void setCurrentSenseGain(DRV8316_CSAGain gain);
bool isActiveSynchronousRectificationEnabled();
void setActiveSynchronousRectificationEnabled(bool enabled);
bool isActiveAsynchronousRectificationEnabled();
void setActiveAsynchronousRectificationEnabled(bool enabled);
DRV8316_Recirculation getRecirculationMode();
void setRecirculationMode(DRV8316_Recirculation recirculationMode);
bool isBuckEnabled();
void setBuckEnabled(bool enabled);
DRV8316_BuckVoltage getBuckVoltage();
void setBuckVoltage(DRV8316_BuckVoltage volts);
DRV8316_BuckCurrentLimit getBuckCurrentLimit();
void setBuckCurrentLimit(DRV8316_BuckCurrentLimit mamps);
bool isBuckPowerSequencingEnabled();
void setBuckPowerSequencingEnabled(bool enabled);
DRV8316_DelayTarget getDelayTarget();
void setDelayTarget(DRV8316_DelayTarget us);
bool isDelayCompensationEnabled();
void setDelayCompensationEnabled(bool enabled);
private:
uint16_t readSPI(uint8_t addr);
uint16_t writeSPI(uint8_t addr, uint8_t data);
bool getParity(uint16_t data);
bool currentLimit;
int cs;
int nFault;
SPIClass* spi;
SPISettings settings;
};
class DRV8316Driver3PWM : public DRV8316Driver, public BLDCDriver3PWM {
public:
DRV8316Driver3PWM(int phA,int phB,int phC, int cs, bool currentLimit = false, int en = NOT_SET, int nFault = NOT_SET) :
DRV8316Driver(cs, currentLimit, nFault), BLDCDriver3PWM(phA, phB, phC, en) { enable_active_high=false; };
virtual ~DRV8316Driver3PWM() {};
virtual void init(SPIClass* _spi = &SPI) override;
};
class DRV8316Driver6PWM : public DRV8316Driver, public BLDCDriver6PWM {
public:
DRV8316Driver6PWM(int phA_h,int phA_l,int phB_h,int phB_l,int phC_h,int phC_l, int cs, bool currentLimit = false, int en = NOT_SET, int nFault = NOT_SET) :
DRV8316Driver(cs, currentLimit, nFault), BLDCDriver6PWM(phA_h, phA_l, phB_h, phB_l, phC_h, phC_l, en) { enable_active_high=false; };
virtual ~DRV8316Driver6PWM() {};
virtual void init(SPIClass* _spi = &SPI) override;
};
#endif

View File

@@ -0,0 +1,184 @@
#ifndef SIMPLEFOC_DRV8316_REGISTERS
#define SIMPLEFOC_DRV8316_REGISTERS
#define IC_Status_ADDR 0x0
#define Status__1_ADDR 0x1
#define Status__2_ADDR 0x2
#define Control__1_ADDR 0x3
#define Control__2_ADDR 0x4
#define Control__3_ADDR 0x5
#define Control__4_ADDR 0x6
#define Control__5_ADDR 0x7
#define Control__6_ADDR 0x8
#define Control__10_ADDR 0xC
#define REG_LOCK_LOCK 0b110
#define REG_LOCK_UNLOCK 0b011
#define CLR_FAULT_CLR 0b1
#define OTW_REP_ENABLE 0b1
#define OTW_REP_DISABLE 0b0
#define SPI_FLT_REP_ENABLE 0b0
#define SPI_FLT_REP_DISABLE 0b1
#define OVP_EN_ENABLE 0b1
#define OVP_EN_DISABLE 0b0
#define OCP_CBC_ENABLE 0b1
#define OCP_CBC_DISABLE 0b0
#define DRV_OFF_ENABLE 0b1
#define DRV_OFF_DISABLE 0b0
#define EN_ASR_ENABLE 0b1
#define EN_ASR_DISABLE 0b0
#define EN_AAR_ENABLE 0b1
#define EN_AAR_DISABLE 0b0
#define BUCK_DIS_BUCK_DISABLE 0b1
#define BUCK_DIS_BUCK_ENABLE 0b0
#define BUCK_PS_DIS_DISABLE 0b1
#define BUCK_PS_DIS_ENABLE 0b0
#define DLYCMP_EN_ENABLE 0b1
#define DLYCMP_EN_DISABLE 0b0
typedef union {
struct {
uint8_t REG_LOCK:3;
uint8_t :5;
};
uint8_t reg;
} Control__1;
typedef union {
struct {
uint8_t CLR_FLT:1;
uint8_t PWM_MODE:2;
uint8_t SLEW:2;
uint8_t SDO_MODE:1;
uint8_t :2;
};
uint8_t reg;
} Control__2;
typedef union {
struct {
uint8_t OTW_REP:1;
uint8_t SPI_FLT_REP:1;
uint8_t OVP_EN:1;
uint8_t OVP_SEL:1;
uint8_t PWM_100_DUTY_SEL:1;
uint8_t :3;
};
uint8_t reg;
} Control__3;
typedef union {
struct {
uint8_t OCP_MODE:2;
uint8_t OCP_LVL:1;
uint8_t OCP_RETRY:1;
uint8_t OCP_DEG:2;
uint8_t OCP_CBC:1;
uint8_t DRV_OFF:1;
};
uint8_t reg;
} Control__4;
typedef union {
struct {
uint8_t CSA_GAIN:2;
uint8_t EN_ASR:1;
uint8_t EN_AAR:1;
uint8_t :2;
uint8_t ILIM_RECIR:1;
uint8_t :1;
};
uint8_t reg;
} Control__5;
typedef union {
struct {
uint8_t BUCK_DIS:1;
uint8_t BUCK_SEL:2;
uint8_t BUCK_CL:1;
uint8_t BUCK_PS_DIS:1;
uint8_t :2;
};
uint8_t reg;
} Control__6;
typedef union {
struct {
uint8_t DLY_TARGET:4;
uint8_t DLYCMP_EN:1;
uint8_t :3;
};
uint8_t reg;
} Control__10;
typedef union {
struct {
uint8_t FAULT:1;
uint8_t OT:1;
uint8_t OVP:1;
uint8_t NPOR:1;
uint8_t OCP:1;
uint8_t SPI_FLT:1;
uint8_t BK_FLT:1;
uint8_t :1;
};
uint8_t reg;
} IC_Status;
typedef union {
struct {
uint8_t OCP_LA:1;
uint8_t OCP_HA:1;
uint8_t OCP_LB:1;
uint8_t OCP_HB:1;
uint8_t OCP_LC:1;
uint8_t OCP_HC:1;
uint8_t OTS:1;
uint8_t OTW:1;
};
uint8_t reg;
} Status__1;
typedef union {
struct {
uint8_t SPI_ADDR_FLT:1;
uint8_t SPI_SCLK_FLT:1;
uint8_t SPI_PARITY:1;
uint8_t VCP_UV:1;
uint8_t BUCK_UV:1;
uint8_t BUCK_OCP:1;
uint8_t OTP_ERR:1;
uint8_t :1;
};
uint8_t reg;
} Status__2;
#endif

View File

@@ -0,0 +1,42 @@
# SimpleFOC STSPIN32G4 Driver
This driver initializes the PWM stage of the STSPIN32G4, and provides access to its configuration via I2C.
:warning: in development!
## Setup
Since there are currently no standard boards for Arduino based on the STSPIN32G4 you will need a custom board definition, associated linker script and project setup to compile for your board. These topics are out of scope for this driver, but you can find a working example for the [FunQi STSPIN32G4 board](TODO link) [here](TODO link);
Once you can compile for your board, and flash it with a "blinky" test sketch, then you're ready to try SimpleFOC and more complex code.
## Usage
Basic usage, as you can see it is very simple. Since the pins are all pre-defined due to internal connections, setup
is easier than with the standard drivers. Here is an example for open loop mode:
```c++
#include <Arduino.h>
#include "SimpleFOC.h"
#include "SimpleFOCDrivers.h"
#include "drivers/stspin32g4/STSPIN32G4.h"
STSPIN32G4 driver = STSPIN32G4();
BLDCMotor motor = BLDCMotor(7);
void setup() {
driver.voltage_power_supply = 12.0f;
driver.init();
motor.voltage_limit = driver.voltage_limit / 2.0f;
motor.controller = MotionControlType::velocity_openloop;
motor.linkDriver(&driver);
motor.init();
}
void loop(){
motor.move(5.0f); // 5 rad/s open loop
delayMicroseconds(100); // STM32G4 is very fast, add a delay in open loop if we do nothing else
}
```

View File

@@ -0,0 +1,182 @@
#include "./STSPIN32G4.h"
#ifdef ARDUINO_GENERIC_G431VBTX
STSPIN32G4::STSPIN32G4() : BLDCDriver6PWM(STSPIN32G4_PIN_INUH, STSPIN32G4_PIN_INUL, STSPIN32G4_PIN_INVH,
STSPIN32G4_PIN_INVL, STSPIN32G4_PIN_INWH, STSPIN32G4_PIN_INWL),
_wire(STSPIN32G4_PIN_SDA, STSPIN32G4_PIN_SCL) {
};
STSPIN32G4::~STSPIN32G4(){
_wire.end();
};
int STSPIN32G4::init(){
// init pins
pinMode(STSPIN32G4_PIN_WAKE, OUTPUT);
digitalWrite(STSPIN32G4_PIN_WAKE, LOW);
pinMode(STSPIN32G4_PIN_READY, INPUT_PULLUP);
pinMode(STSPIN32G4_PIN_FAULT, INPUT_PULLUP);
int result = this->BLDCDriver6PWM::init();
if(result == 0) return result;
setPwm(0,0,0); // set the phases to off
// init I2C
_wire.begin();
delayMicroseconds(50); // give driver a moment to wake up
clearFaults(); // clear the faults
// TODO init fault monitor
return isReady() ? 1 : 0;
};
void STSPIN32G4::wake() {
digitalWrite(STSPIN32G4_PIN_WAKE, HIGH);
delayMicroseconds(50); // 50ms high pulse to wake up
digitalWrite(STSPIN32G4_PIN_WAKE, LOW);
};
void STSPIN32G4::sleep() {
digitalWrite(STSPIN32G4_PIN_WAKE, LOW);
writeRegister(STSPIN32G4_REG_STBY, 0x01);
};
bool STSPIN32G4::isReady(){
return digitalRead(STSPIN32G4_PIN_READY)==HIGH;
};
bool STSPIN32G4::isFault(){
return digitalRead(STSPIN32G4_PIN_FAULT)==LOW;
};
STSPIN32G4Status STSPIN32G4::status(){
STSPIN32G4Status result;
result.reg = readRegister(STSPIN32G4_REG_STATUS);
return result;
};
void STSPIN32G4::lock(){
writeRegister(STSPIN32G4_REG_LOCK, 0x00);
};
void STSPIN32G4::unlock(){
writeRegister(STSPIN32G4_REG_LOCK, 0xF0);
};
STSPIN32G4NFault STSPIN32G4::getNFaultRegister(){
STSPIN32G4NFault result;
result.reg = readRegister(STSPIN32G4_REG_NFAULT);
return result;
};
STSPIN32G4Ready STSPIN32G4::getReadyRegister(){
STSPIN32G4Ready result;
result.reg = readRegister(STSPIN32G4_REG_READY);
return result;
};
STSPIN32G4Logic STSPIN32G4::getLogicRegister(){
STSPIN32G4Logic result;
result.reg = readRegister(STSPIN32G4_REG_LOGIC);
return result;
};
STSPIN32G4PowMng STSPIN32G4::getPowMngRegister(){
STSPIN32G4PowMng result;
result.reg = readRegister(STSPIN32G4_REG_POWMNG);
return result;
};
void STSPIN32G4::setNFaultRegister(STSPIN32G4NFault value){
writeRegister(STSPIN32G4_REG_NFAULT, value.reg);
};
void STSPIN32G4::setReadyRegister(STSPIN32G4Ready value){
writeRegister(STSPIN32G4_REG_READY, value.reg);
};
void STSPIN32G4::setLogicRegister(STSPIN32G4Logic value){
writeRegister(STSPIN32G4_REG_LOGIC, value.reg);
};
void STSPIN32G4::setPowMngRegister(STSPIN32G4PowMng value){
writeRegister(STSPIN32G4_REG_POWMNG, value.reg);
};
void STSPIN32G4::resetRegisters(){
// write 0xFF to reset register
writeRegister(STSPIN32G4_REG_RESET, 0xFF);
};
void STSPIN32G4::clearFaults(){
// write 0xFF to clear faults
writeRegister(STSPIN32G4_REG_CLEAR, 0xFF);
};
uint8_t STSPIN32G4::readRegister(uint8_t reg){
uint8_t result = 0;
_wire.beginTransmission(STSPIN32G4_I2C_ADDR);
_wire.write(reg);
_wire.endTransmission(false);
_wire.requestFrom(STSPIN32G4_I2C_ADDR, 1);
result = _wire.read();
return result;
};
void STSPIN32G4::writeRegister(uint8_t reg, uint8_t val){
_wire.beginTransmission(STSPIN32G4_I2C_ADDR);
_wire.write(reg);
_wire.write(val);
_wire.endTransmission();
};
#endif

View File

@@ -0,0 +1,148 @@
#pragma once
#include "Arduino.h"
#ifdef ARDUINO_GENERIC_G431VBTX
#include "Wire.h"
#include "drivers/BLDCDriver6PWM.h"
#define STSPIN32G4_PIN_INUL PE8
#define STSPIN32G4_PIN_INUH PE9
#define STSPIN32G4_PIN_INVL PE10
#define STSPIN32G4_PIN_INVH PE11
#define STSPIN32G4_PIN_INWL PE12
#define STSPIN32G4_PIN_INWH PE13
#define STSPIN32G4_PIN_WAKE PE7
#define STSPIN32G4_PIN_READY PE14
#define STSPIN32G4_PIN_FAULT PE15
#define STSPIN32G4_PIN_SDA PC9
#define STSPIN32G4_PIN_SCL PC8
#define STSPIN32G4_I2C_ADDR 0b1000111
#define STSPIN32G4_REG_POWMNG 0x01
#define STSPIN32G4_REG_LOGIC 0x02
#define STSPIN32G4_REG_READY 0x07
#define STSPIN32G4_REG_NFAULT 0x08
#define STSPIN32G4_REG_CLEAR 0x09
#define STSPIN32G4_REG_STBY 0x0A
#define STSPIN32G4_REG_LOCK 0x0B
#define STSPIN32G4_REG_RESET 0x0C
#define STSPIN32G4_REG_STATUS 0x80
union STSPIN32G4Status {
struct {
uint8_t vcc_uvlo:1;
uint8_t thsd:1;
uint8_t vds_p:1;
uint8_t reset:1;
uint8_t unused:3;
uint8_t lock:1;
};
uint8_t reg;
};
union STSPIN32G4NFault {
struct {
uint8_t vcc_uvlo_flt:1;
uint8_t thsd_flt:1;
uint8_t vds_p_flt:1;
uint8_t unused:5;
};
uint8_t reg;
};
union STSPIN32G4Ready {
struct {
uint8_t vcc_uvlo_rdy:1;
uint8_t thsd_rdy:1;
uint8_t unused:1;
uint8_t stby_rdy:1;
uint8_t unused2:4;
};
uint8_t reg;
};
union STSPIN32G4Logic {
struct {
uint8_t ilock:1;
uint8_t dtmin:1;
uint8_t vds_p_deg:2;
uint8_t unused:4;
};
uint8_t reg;
};
union STSPIN32G4PowMng {
struct {
uint8_t vcc_val:2;
uint8_t unused:2;
uint8_t stby_reg_en:1;
uint8_t vcc_dis:1;
uint8_t reg3v3_dis:1;
uint8_t unused2:1;
};
uint8_t reg;
};
class STSPIN32G4 : public BLDCDriver6PWM {
public:
STSPIN32G4();
~STSPIN32G4();
int init() override;
void wake();
void sleep();
bool isReady();
bool isFault();
STSPIN32G4Status status();
void lock();
void unlock();
STSPIN32G4NFault getNFaultRegister();
STSPIN32G4Ready getReadyRegister();
STSPIN32G4Logic getLogicRegister();
STSPIN32G4PowMng getPowMngRegister();
void setNFaultRegister(STSPIN32G4NFault value);
void setReadyRegister(STSPIN32G4Ready value);
void setLogicRegister(STSPIN32G4Logic value);
void setPowMngRegister(STSPIN32G4PowMng value);
void resetRegisters();
void clearFaults();
protected:
uint8_t readRegister(uint8_t reg);
void writeRegister(uint8_t reg, uint8_t val);
TwoWire _wire;
};
#endif

View File

@@ -0,0 +1,147 @@
# TMC6200 SimpleFOC Driver
by [@YaseenTwati](https://github.com/YaseenTwati)
The TMC6200 is a Universal high voltage BLDC/PMSM/Servo MOSFET 3-halfbridge gate-driver with in line motor current
sensing. External MOSFETs for up to 100A motor current.
See https://www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC6200_datasheet_Rev1.05.pdf for more information.
## Hardware setup
To use the TMC6200 you will have to connect the following:
- GND
- VCC_IO - 3.3V or 5V
- SPE - pull up to VCC to enable SPI mode
- SPI MOSI ( SDI )
- SPI MISO ( SDO )
- SPI CLK
- SPI nCS
- DRV_EN - connect to digital out (or pull up to VCC)
- UH - connect to motor PWM pin
- VH - connect to motor PWM pin
- WH - connect to motor PWM pin
- UL - connect to motor PWM pin for 6-PWM, or to digital out (or pull up to VCC) for 3-PWM operation
- VL - connect to motor PWM pin for 6-PWM, or to digital out (or pull up to VCC) for 3-PWM operation
- WL - connect to motor PWM pin for 6-PWM, or to digital out (or pull up to VCC) for 3-PWM operation
Optionally, but useful:
- FAULT - digital in, active high
For current sensing:
- CURU - connect to analog in
- CURV - connect to analog in
- CURW - connect to analog in
## Usage
Usage is quite easy, especially if you already know SimpleFOC. See also the [examples](https://github.com/simplefoc/Arduino-FOC-drivers/examples/drivers/drv8316/)
```c++
#include "Arduino.h"
#include <SimpleFOC.h>
#include "SimpleFOCDrivers.h"
#include "drivers/tmc6200/TMC6200.hpp"
BLDCMotor motor = BLDCMotor(15);
TMC6200Driver6PWM driver = DRV8316Driver6PWM(UH, UL, VH, VL, WH, WL, nCS, DRV_EN);
//... normal simpleFOC init code...
```
Or, for 3-PWM:
```c++
#include "Arduino.h"
#include <SimpleFOC.h>
#include "SimpleFOCDrivers.h"
#include "drivers/tmc6200/TMC6200.hpp"
BLDCMotor motor = BLDCMotor(15);
TMC6200Driver3PWM driver = TMC6200Driver3PWM(UH, MOTOR_VH, MOTOR_WH, nCS, DRV_EN);
void setup() {
pinMode(UL, OUTPUT);
pinMode(VL, OUTPUT);
pinMode(WL, OUTPUT);
digitalWrite(WL, HIGH);
digitalWrite(UL, HIGH);
digitalWrite(VL, HIGH);
//... normal simpleFOC init code...
}
```
### Validating the SPI Connection
You can validate the SPI connection by checking the value of VERSION field in IOIN register. The value should be 0x10.
```c++
if(driver.getInputs().VERSION != TMC6200_VERSION){
// something is wrong with the spi connection
}
```
### Current Sensing
The gain of the internal current amplifiers can be set to 5, 10 or 20 through `setCurrentSenseGain()`
```c++
driver.setCurrentSenseGain(TMC6200_AmplificationGain::_5);
//driver.setCurrentSenseGain(TMC6200_AmplificationGain::_10);
//driver.setCurrentSenseGain(TMC6200_AmplificationGain::_20);
```
The sense amplifiers can also be turned off ( they are on by default ), through `setCurrentSenseAmplifierState()`
```c++
driver.setCurrentSenseAmplifierState(false);
```
### Driver Strength
The strength of the mosfet drivers can be controlled through `setDriverStrength()`
```c++
driver.setDriverStrength(TMC6200_DRVStrength::Weak);
//driver.setDriverStrength(TMC6200_DRVStrength::WeakTC); // (medium above OTPW level)
//driver.setDriverStrength(TMC6200_DRVStrength::Medium);
//driver.setDriverStrength(TMC6200_DRVStrength::Strong);
```
### Handling Faults
The fault line will go high if a fault occurs such as a short, an interrupt can be used to handle it.
Note that some faults will disable the driver and will require the DRV_EN to be cycled to clear the fault.
```c++
// somewhere in setup
attachInterrupt(digitalPinToInterrupt(FAULT), handleFault, RISING);
```
```c++
void handleFault()
{
// you can read the status register to see what happened
TMC6200GStatus status = driver.getStatus();
Serial.print("hasUShorted: "); Serial.println(status.hasUShorted());
Serial.print("hasVShorted: "); Serial.println(status.hasVShorted());
Serial.print("hasWShorted: "); Serial.println(status.hasWShorted());
Serial.print("isUShortedToGround: "); Serial.println(status.isUShortedToGround());
Serial.print("isUShortedToSupply: "); Serial.println(status.isUShortedToSupply());
Serial.print("isVShortedToGround: "); Serial.println(status.isVShortedToGround());
Serial.print("isVShortedToSupply: "); Serial.println(status.isVShortedToSupply());
Serial.print("isWShortedToGround: "); Serial.println(status.isWShortedToGround());
Serial.print("isWShortedToSupply: "); Serial.println(status.isWShortedToSupply());
Serial.print("isOverTemperaturePreWarning: "); Serial.println(status.isOverTemperaturePreWarning());
Serial.print("isChargePumpUnderVoltage: "); Serial.println(status.isChargePumpUnderVoltage());
// the driver must be cycled to clear the fault
digitalWrite(DRV_EN, LOW);
delayMicrosockets(someSmallDelay);
digitalWrite(DRV_EN, HIGH);
}
```
The driver provides other features such as controlling the tolerences of short detection and the BBM cycle time and so on, setting the options can be conveniently done via the provided setter methods. All documented registers and options are available via the driver, and the option values can be accessed via enums.

View File

@@ -0,0 +1,221 @@
#include "TMC6200.hpp"
void TMC6200Driver::init(SPIClass *_spi) {
spi = _spi;
settings = SPISettings(500000, MSBFIRST, SPI_MODE3);
pinMode(csPin, OUTPUT);
digitalWrite(csPin, HIGH);
}
// TMC6200_GCONF ------
void TMC6200Driver::setDriverState(bool state) {
TMC6200_GCONF gConf;
gConf.reg = readRegister(TMC6200_GCONF_REG);
gConf.DISABLE = state ? 0 : 1;
writeRegister(TMC6200_GCONF_REG, gConf.reg);
}
void TMC6200Driver::setPWMMode(TMC6200_PWMMode pwmMode) {
TMC6200_GCONF gConf;
gConf.reg = readRegister(TMC6200_GCONF_REG);
gConf.SINGLE_LINE = pwmMode;
writeRegister(TMC6200_GCONF_REG, gConf.reg);
}
void TMC6200Driver::setFaultDirect(TMC6200_FaultDirect faultDirect) {
TMC6200_GCONF gConf;
gConf.reg = readRegister(TMC6200_GCONF_REG);
gConf.FAULT_DIRECT = faultDirect;
writeRegister(TMC6200_GCONF_REG, gConf.reg);
}
void TMC6200Driver::setCurrentSenseAmplifierState(bool state) {
TMC6200_GCONF gConf;
gConf.reg = readRegister(TMC6200_GCONF_REG);
gConf.AMPLIFIER_OFF = state ? 0 : 1;
writeRegister(TMC6200_GCONF_REG, gConf.reg);
}
void TMC6200Driver::setCurrentSenseGain(TMC6200_AmplificationGain amplificationGain) {
TMC6200_GCONF gConf;
gConf.reg = readRegister(TMC6200_GCONF_REG);
gConf.AMPLIFICATION = amplificationGain;
writeRegister(TMC6200_GCONF_REG, gConf.reg);
gConf.reg = readRegister(TMC6200_GCONF_REG);
}
// TMC6200_DRV_CONF ------
void TMC6200Driver::setOverTemperatureThreshold(TMC6200_OTSelect otLevel) {
TMC6200_DRV_CONF drvConf;
drvConf.reg = readRegister(TMC6200_DRV_CONF_REG);
drvConf.OT_SELECT = otLevel;
writeRegister(TMC6200_DRV_CONF_REG, drvConf.reg);
}
void TMC6200Driver::setDriverStrength(TMC6200_DRVStrength strength) {
TMC6200_DRV_CONF drvConf;
drvConf.reg = readRegister(TMC6200_DRV_CONF_REG);
drvConf.DRV_STRENGTH = strength;
writeRegister(TMC6200_DRV_CONF_REG, drvConf.reg);
}
void TMC6200Driver::setBBMCycles(uint8_t clockCycles) {
TMC6200_DRV_CONF drvConf;
drvConf.reg = readRegister(TMC6200_DRV_CONF_REG);
drvConf.BBMCLKS = clockCycles;
writeRegister(TMC6200_DRV_CONF_REG, drvConf.reg);
}
// TMC6200_SHORT_CONF ------
void TMC6200Driver::setShortDelay(TMC6200_ShortDelay shortDelay) {
TMC6200_SHORT_CONF shortConf;
shortConf.reg = readRegister(TMC6200_SHORT_CONF_REG);
shortConf.SHORT_DELAY = shortDelay;
writeRegister(TMC6200_SHORT_CONF_REG, shortConf.reg);
}
void TMC6200Driver::shortFilter(TMC6200_ShortFilter shortFilter) {
TMC6200_SHORT_CONF shortConf;
shortConf.reg = readRegister(TMC6200_SHORT_CONF_REG);
shortConf.SHORT_FILTER = shortFilter;
writeRegister(TMC6200_SHORT_CONF_REG, shortConf.reg);
}
void TMC6200Driver::setShortToSupplySensitivityLevel(uint8_t level) {
TMC6200_SHORT_CONF shortConf;
shortConf.reg = readRegister(TMC6200_SHORT_CONF_REG);
shortConf.S2VS_LEVEL = level;
writeRegister(TMC6200_SHORT_CONF_REG, shortConf.reg);
}
void TMC6200Driver::setShortToGroundSensitivityLevel(uint8_t level) {
TMC6200_SHORT_CONF shortConf;
shortConf.reg = readRegister(TMC6200_SHORT_CONF_REG);
shortConf.S2G_LEVEL = level;
writeRegister(TMC6200_SHORT_CONF_REG, shortConf.reg);
}
void TMC6200Driver::setShortRetries(uint8_t retries) {
TMC6200_SHORT_CONF shortConf;
shortConf.reg = readRegister(TMC6200_SHORT_CONF_REG);
shortConf.RETRY = retries;
writeRegister(TMC6200_SHORT_CONF_REG, shortConf.reg);
}
void TMC6200Driver::setParallelProtect(TMC6200_ParallelProtect parallelProtect) {
TMC6200_SHORT_CONF shortConf;
shortConf.reg = readRegister(TMC6200_SHORT_CONF_REG);
shortConf.PROTECT_PARALLEL = parallelProtect;
writeRegister(TMC6200_SHORT_CONF_REG, shortConf.reg);
}
void TMC6200Driver::setShortToGroundDetectionState(bool state) {
TMC6200_SHORT_CONF shortConf;
shortConf.reg = readRegister(TMC6200_SHORT_CONF_REG);
shortConf.DISABLE_S2G = state ? 0 : 1;
writeRegister(TMC6200_SHORT_CONF_REG, shortConf.reg);
}
void TMC6200Driver::setShortToSupplyDetectionState(bool state) {
TMC6200_SHORT_CONF shortConf;
shortConf.reg = readRegister(TMC6200_SHORT_CONF_REG);
shortConf.DISABLE_S2VS = state ? 0 : 1;
writeRegister(TMC6200_SHORT_CONF_REG, shortConf.reg);
}
// TMC6200_IOIN ------
TMC6200_IOIN TMC6200Driver::getInputs() {
TMC6200_IOIN ioin;
ioin.reg = readRegister(TMC6200_IOIN_REG);
return ioin;
}
// TMC6200_GSTAT ------
TMC6200GStatus TMC6200Driver::getStatus() {
TMC6200_GSTAT gstat;
gstat.reg = readRegister(TMC6200_GSTAT_REG);
return {gstat};
}
void TMC6200Driver::setStatus(TMC6200_GSTAT status) {
writeRegister(TMC6200_GSTAT_REG, status.reg);
}
uint32_t TMC6200Driver::readRegister(uint8_t addr) {
digitalWrite(csPin, LOW);
spi->beginTransaction(settings);
// Address
spi->transfer(TMC_ADDRESS(addr));
// 32bit Data
uint32_t value = 0;
value |= (spi->transfer(0x00) << 24);
value |= (spi->transfer(0x00) << 16);
value |= (spi->transfer(0x00) << 8);
value |= (spi->transfer(0x00) << 0);
spi->end();
digitalWrite(csPin, HIGH);
return value;
}
void TMC6200Driver::writeRegister(uint8_t addr, uint32_t data) {
digitalWrite(csPin, LOW);
spi->beginTransaction(settings);
// Address
spi->transfer(addr | TMC_WRITE_BIT);
// 32bit Data
spi->transfer(0xFF & (data >> 24));
spi->transfer(0xFF & (data >> 16));
spi->transfer(0xFF & (data >> 8));
spi->transfer(0xFF & (data >> 0));
spi->end();
digitalWrite(csPin, HIGH);
}
// --------------------
void TMC6200Driver3PWM::init(SPIClass *_spi) {
TMC6200Driver::init(_spi);
delayMicroseconds(1);
TMC6200Driver::setPWMMode(TMC6200_PWMMode::SingleLine);
BLDCDriver3PWM::init();
};
void TMC6200Driver6PWM::init(SPIClass *_spi) {
TMC6200Driver::init(_spi);
delayMicroseconds(1);
TMC6200Driver::setPWMMode(TMC6200_PWMMode::Individual);
BLDCDriver6PWM::init();
};

View File

@@ -0,0 +1,171 @@
#pragma once
#include <Arduino.h>
#include <SPI.h>
#include <drivers/BLDCDriver3PWM.h>
#include <drivers/BLDCDriver6PWM.h>
#include "TMC6200_Registers.hpp"
#define TMC6200_VERSION 0x10
enum TMC6200_PWMMode {
Individual = 0, // 6PWM
SingleLine = 1, // 3PWM
};
enum TMC6200_AmplificationGain {
_5 = 0,
_10 = 1,
_Also_10 = 2, // maybe just remove this
_20 = 3,
};
enum TMC6200_ShortDelay {
_750nS = 0,
_1500nS = 1,
};
enum TMC6200_ShortFilter {
_100nS = 0,
_1uS = 1,
_2uS = 2,
_3uS = 3,
};
enum TMC6200_ParallelProtect {
Detected = 0,
All = 1,
};
enum TMC6200_FaultDirect {
AtLeastOneBridgeDown = 0,
EachProtectiveAction = 1,
};
enum TMC6200_OTSelect {
_150 = 0b00,
_143 = 0b01,
_136 = 0b10,
_120 = 0b11,
};
enum TMC6200_DRVStrength {
Weak = 0b00,
WeakTC = 0b01, // (medium above OTPW level)
Medium = 0b10,
Strong = 0b11,
};
class TMC6200GStatus {
public:
TMC6200GStatus(TMC6200_GSTAT status) : status(status) {};
bool isReset() const { return status.RESET == 0b1; };
bool isOverTemperaturePreWarning() const { return status.DRV_OTPW == 0b1; };
bool isOverTemperature() const { return status.DRV_OT == 0b1; };
bool isChargePumpUnderVoltage() const { return status.UV_CP == 0b1; };
bool hasUShorted() const { return status.SHORT_DET_U == 0b1; };
bool hasVShorted() const { return status.SHORT_DET_V == 0b1; };
bool hasWShorted() const { return status.SHORT_DET_W == 0b1; };
bool isUShortedToGround() const { return status.S2GU == 0b1; };
bool isUShortedToSupply() const { return status.S2VSU == 0b1; };
bool isVShortedToGround() const { return status.S2GV == 0b1; };
bool isVShortedToSupply() const { return status.S2VSV == 0b1; };
bool isWShortedToGround() const { return status.S2GW == 0b1; };
bool isWShortedToSupply() const { return status.S2VSW == 0b1; };
TMC6200_GSTAT status;
};
class TMC6200Driver {
public:
TMC6200Driver(int csPin) : csPin(csPin), spi(&SPI), settings(1000000, MSBFIRST, SPI_MODE3) {};
virtual ~TMC6200Driver() {};
virtual void init(SPIClass *_spi);
void setDriverState(bool state);
void setPWMMode(TMC6200_PWMMode pwmMode);
void setFaultDirect(TMC6200_FaultDirect faultDirect);
void setCurrentSenseGain(TMC6200_AmplificationGain amplificationGain);
void setOverTemperatureThreshold(TMC6200_OTSelect otLevel);
void setDriverStrength(TMC6200_DRVStrength strength);
void setCurrentSenseAmplifierState(bool state);
void setShortDelay(TMC6200_ShortDelay shortDelay);
void shortFilter(TMC6200_ShortFilter shortFilter);
void setShortToSupplySensitivityLevel(uint8_t level);
void setShortToGroundSensitivityLevel(uint8_t level);
void setShortRetries(uint8_t retries);
void setParallelProtect(TMC6200_ParallelProtect parallelProtect);
void setShortToGroundDetectionState(bool state);
void setShortToSupplyDetectionState(bool state);
void setBBMCycles(uint8_t clockCycles);
void setStatus(TMC6200_GSTAT status);
TMC6200GStatus getStatus();
TMC6200_IOIN getInputs();
private:
uint32_t readRegister(uint8_t addr);
void writeRegister(uint8_t addr, uint32_t data);
int csPin;
SPIClass *spi;
SPISettings settings;
};
class TMC6200Driver3PWM : public TMC6200Driver, public BLDCDriver3PWM {
public:
TMC6200Driver3PWM(int phA, int phB, int phC, int cs, int en = NOT_SET) :
TMC6200Driver(cs), BLDCDriver3PWM(phA, phB, phC, en) {};
~TMC6200Driver3PWM() override = default;
virtual void init(SPIClass *_spi = &SPI) override;
};
class TMC6200Driver6PWM : public TMC6200Driver, public BLDCDriver6PWM {
public:
TMC6200Driver6PWM(int phA_h, int phA_l, int phB_h, int phB_l, int phC_h, int phC_l, int cs, int en = NOT_SET) :
TMC6200Driver(cs), BLDCDriver6PWM(phA_h, phA_l, phB_h, phB_l, phC_h, phC_l, en) {};
~TMC6200Driver6PWM() override = default;
virtual void init(SPIClass *_spi = &SPI) override;
};

View File

@@ -0,0 +1,111 @@
#pragma once
// ------
#define TMC_WRITE_BIT 0x80
#define TMC_ADDRESS_MASK 0x7F
#define TMC_ADDRESS(x) ((x) & (TMC_ADDRESS_MASK))
// ------
#define TMC6200_GCONF_REG 0x00 // RW
#define TMC6200_GSTAT_REG 0x01 // RW + WC
#define TMC6200_IOIN_REG 0x04 // R
#define TMC6200_OTP_PROG_REG 0x06 // not used
#define TMC6200_OTP_READ_REG 0x07 // not used
#define TMC6200_FACTORY_CONF_REG 0x08 // not used
#define TMC6200_SHORT_CONF_REG 0x09 // RW
#define TMC6200_DRV_CONF_REG 0x0A // RW
typedef union {
struct {
uint32_t DISABLE: 1;
uint32_t SINGLE_LINE: 1;
uint32_t FAULT_DIRECT: 1;
uint32_t : 1;
uint32_t AMPLIFICATION: 2;
uint32_t AMPLIFIER_OFF: 1;
uint32_t TMC6200_TEST_MODE: 1;
uint32_t : 24;
};
uint32_t reg;
} TMC6200_GCONF;
typedef union {
struct {
uint32_t RESET: 1;
uint32_t DRV_OTPW: 1;
uint32_t DRV_OT: 1;
uint32_t UV_CP: 1;
uint32_t SHORT_DET_U: 1;
uint32_t S2GU: 1;
uint32_t S2VSU: 1;
uint32_t : 1;
uint32_t SHORT_DET_V: 1;
uint32_t S2GV: 1;
uint32_t S2VSV: 1;
uint32_t : 1;
uint32_t SHORT_DET_W: 1;
uint32_t S2GW: 1;
uint32_t S2VSW: 1;
uint32_t : 1;
};
uint32_t reg;
} TMC6200_GSTAT;
typedef union {
struct {
unsigned UL: 1;
uint32_t UH: 1;
uint32_t VL: 1;
uint32_t VH: 1;
uint32_t WL: 1;
uint32_t WH: 1;
uint32_t DRV_EN: 1;
uint32_t _reserved1: 1;
uint32_t OTPW: 1;
uint32_t OT136C: 1;
uint32_t OT143C: 1;
uint32_t OT150C: 1;
uint32_t _reserved2: 12;
uint32_t VERSION: 8;
};
uint32_t reg;
} TMC6200_IOIN;
typedef union {
struct {
uint32_t S2VS_LEVEL: 4;
uint32_t : 4;
uint32_t S2G_LEVEL: 4;
uint32_t : 4;
uint32_t SHORT_FILTER: 2;
uint32_t : 2;
uint32_t SHORT_DELAY: 1;
uint32_t : 4;
uint32_t RETRY: 2;
uint32_t : 2;
uint32_t PROTECT_PARALLEL: 1;
uint32_t DISABLE_S2G: 1;
uint32_t DISABLE_S2VS: 1;
uint32_t : 2;
};
uint32_t reg;
} TMC6200_SHORT_CONF;
typedef union {
struct {
uint32_t BBMCLKS: 5;
uint32_t : 11;
uint32_t OT_SELECT: 2;
uint32_t DRV_STRENGTH: 2;
uint32_t : 12;
};
uint32_t reg;
} TMC6200_DRV_CONF;

View File

@@ -0,0 +1,54 @@
#include "./A1334.h"
A1334::A1334(SPISettings settings, int nCS) : settings(settings), nCS(nCS) {
// nix
};
A1334::~A1334() {
};
void A1334::init(SPIClass* _spi) {
spi = _spi;
if (nCS>=0)
pinMode(nCS, OUTPUT);
digitalWrite(nCS, HIGH);
//SPI has an internal SPI-device counter, it is possible to call "begin()" from different devices
spi->begin();
readRawAngle(); // read an angle
};
A1334Angle A1334::readRawAngle() {
uint16_t command = A1334_REG_ANG;
uint16_t cmdResult = spi_transfer16(command); // TODO fast mode
cmdResult = spi_transfer16(command);
A1334Angle result = { .reg = cmdResult };
// TODO check parity
// errorflag = result.ef;
return result;
};
uint16_t A1334::spi_transfer16(uint16_t outdata) {
if (nCS>=0)
digitalWrite(nCS, 0);
spi->beginTransaction(settings);
uint16_t result = spi->transfer16(outdata);
spi->endTransaction();
if (nCS>=0)
digitalWrite(nCS, 1);
return result;
};

View File

@@ -0,0 +1,51 @@
#ifndef __A1334_H__
#define __A1334_H__
#include <Arduino.h>
#include <SPI.h>
#define A1334_CPR 4096
#define A1334_BITORDER MSBFIRST
static SPISettings A1334SPISettings(8000000, A1334_BITORDER, SPI_MODE3); // @suppress("Invalid arguments")
typedef union {
struct {
uint16_t angle:12;
uint16_t p:1;
uint16_t nf:1;
uint16_t ef:1;
uint16_t ridc:1;
};
uint16_t reg;
} A1334Angle;
#define A1334_REG_ANG 0x2000
class A1334 {
public:
A1334(SPISettings settings = A1334SPISettings, int nCS = -1);
virtual ~A1334();
virtual void init(SPIClass* _spi = &SPI);
A1334Angle readRawAngle(); // 10 or 12 bit angle value
protected:
uint16_t spi_transfer16(uint16_t outdata);
SPIClass* spi;
SPISettings settings;
int nCS = -1;
};
#endif

View File

@@ -0,0 +1,27 @@
#include "./MagneticSensorA1334.h"
#include "common/foc_utils.h"
#include "common/time_utils.h"
MagneticSensorA1334::MagneticSensorA1334(int nCS, SPISettings settings) : A1334(settings, nCS) {
}
MagneticSensorA1334::~MagneticSensorA1334(){
}
void MagneticSensorA1334::init(SPIClass* _spi) {
this->A1334::init(_spi);
this->Sensor::init();
}
float MagneticSensorA1334::getSensorAngle() {
A1334Angle angle_data = readRawAngle();
float result = ( angle_data.angle / (float)A1334_CPR) * _2PI;
// return the shaft angle
return result;
}

View File

@@ -0,0 +1,24 @@
#ifndef __MAGNETIC_SENSOR_A1334_H__
#define __MAGNETIC_SENSOR_A1334_H__
#include "common/base_classes/Sensor.h"
#include "./A1334.h"
class MagneticSensorA1334 : public Sensor, public A1334 {
public:
MagneticSensorA1334(int nCS = -1, SPISettings settings = A1334SPISettings);
virtual ~MagneticSensorA1334();
virtual float getSensorAngle() override;
virtual void init(SPIClass* _spi = &SPI);
private:
};
#endif

View File

@@ -0,0 +1,102 @@
#include "AEAT8800Q24.h"
AEAT8800Q24::AEAT8800Q24(int nCS, int pinNSL, SPISettings spiSettings, SPISettings ssiSettings) : nCS(nCS), pinNSL(pinNSL), spiSettings(spiSettings), ssiSettings(ssiSettings) {
};
AEAT8800Q24::~AEAT8800Q24(){
};
void AEAT8800Q24::init(SPIClass* _spi){
spi = _spi;
spi->beginTransaction(ssiSettings);
};
float AEAT8800Q24::getCurrentAngle() {
return ((float)readRawAngle())/(float)AEAT8800Q24_CPR * 2.0f * (float)PI;
}; // angle in radians, return current value
// reads angle via ssi
uint16_t AEAT8800Q24::readRawAngle() {
// digitalWrite(pinNSL, LOW); //TODO maybe we don't need it...
// // 300ns delay
// delayMicroseconds(1);
uint16_t value = spi->transfer16(0x0000);
uint8_t status = spi->transfer(0x00);
//digitalWrite(pinNSL, HIGH);
// // 200ns delay before next read...
// delayMicroseconds(1);
lastStatus.reg = status;
return value;
};
AEAT8800Q24_Status_t AEAT8800Q24::getLastStatus() {
return lastStatus;
}
uint16_t AEAT8800Q24::getZero(){
uint8_t value = readRegister(AEAT8800Q24_REG_ZERO_MSB);
return (value<<8)|readRegister(AEAT8800Q24_REG_ZERO_LSB);
};
void AEAT8800Q24::setZero(uint16_t value){
writeRegister(AEAT8800Q24_REG_ZERO_MSB, (value>>8)&0xFF);
writeRegister(AEAT8800Q24_REG_ZERO_LSB, value&0xFF);
};
AEAT8800Q24_CONF0_t AEAT8800Q24::getConf0(){
AEAT8800Q24_CONF0_t result;
result.reg = readRegister(AEAT8800Q24_REG_CONF0);
return result;
};
void AEAT8800Q24::setConf0(AEAT8800Q24_CONF0_t value){
writeRegister(AEAT8800Q24_REG_CONF0, value.reg);
};
AEAT8800Q24_CONF1_t AEAT8800Q24::getConf1(){
AEAT8800Q24_CONF1_t result;
result.reg = readRegister(AEAT8800Q24_REG_CONF1);
return result;
};
void AEAT8800Q24::setConf1(AEAT8800Q24_CONF1_t value){
writeRegister(AEAT8800Q24_REG_CONF1, value.reg);
};
AEAT8800Q24_CONF2_t AEAT8800Q24::getConf2(){
AEAT8800Q24_CONF2_t result;
result.reg = readRegister(AEAT8800Q24_REG_CONF2);
return result;
};
void AEAT8800Q24::setConf2(AEAT8800Q24_CONF2_t value){
writeRegister(AEAT8800Q24_REG_CONF2, value.reg);
};
uint16_t AEAT8800Q24::transfer16SPI(uint16_t outValue) {
// delay 1us between switching the CS line to SPI
delayMicroseconds(1);
if (nCS >= 0)
digitalWrite(nCS, LOW);
spi->endTransaction();
spi->beginTransaction(spiSettings);
uint16_t value = spi->transfer16(outValue);
spi->endTransaction();
if (nCS >= 0)
digitalWrite(nCS, HIGH);
// delay 1us between switching the CS line to SSI
delayMicroseconds(1);
spi->beginTransaction(ssiSettings);
return value;
};
uint8_t AEAT8800Q24::readRegister(uint8_t reg) {
uint16_t cmd = 0x8000 | ((reg&0x001F)<<8);
uint16_t value = transfer16SPI(cmd);
return value&0x00FF;
};
void AEAT8800Q24::writeRegister(uint8_t reg, uint8_t value) {
uint16_t cmd = 0x4000 | ((reg&0x1F)<<8) | value;
/*uint16_t result =*/ transfer16SPI(cmd);
};

View File

@@ -0,0 +1,119 @@
#ifndef __AEAT8800Q24_H__
#define __AEAT8800Q24_H__
#include "Arduino.h"
#include "SPI.h"
#define AEAT8800Q24_CPR 65536
#define AEAT8800Q24_REG_CUST0 0x00
#define AEAT8800Q24_REG_CUST1 0x01
#define AEAT8800Q24_REG_ZERO_LSB 0x02
#define AEAT8800Q24_REG_ZERO_MSB 0x03
#define AEAT8800Q24_REG_CONF0 0x04
#define AEAT8800Q24_REG_CONF1 0x05
#define AEAT8800Q24_REG_CONF2 0x06
#define AEAT8800Q24_REG_CONF_OTP 0x13
#define AEAT8800Q24_REG_CAL_OTP 0x1B
#define AEAT8800Q24_REG_CUST_OTP 0x11
#define AEAT8800Q24_REG_CAL 0x17
#define AEAT8800Q24_CONF_OTP_ON 0xA3
#define AEAT8800Q24_CAL_OTP_ON 0xA5
#define AEAT8800Q24_CUST_OTP_ON 0xA1
#define AEAT8800Q24_CAL_ON 0x02
#define AEAT8800Q24_CAL_OFF 0x00
typedef union {
struct {
uint8_t uvw_pwm_mode:1;
uint8_t pwm:2;
uint8_t iwidth:2;
uint8_t uvw:3;
};
uint8_t reg;
} AEAT8800Q24_CONF0_t;
typedef union {
struct {
uint8_t cpr1:4;
uint8_t hys:4;
};
uint8_t reg;
} AEAT8800Q24_CONF1_t;
typedef union {
struct {
uint8_t dir:1;
uint8_t zero_latency:1;
uint8_t abs_res:2;
uint8_t cpr2:4;
};
uint8_t reg;
} AEAT8800Q24_CONF2_t;
typedef union {
struct {
uint8_t mhi:1;
uint8_t mlo:1;
uint8_t ready:1;
uint8_t parity:1;
uint8_t :4;
};
uint8_t reg;
} AEAT8800Q24_Status_t;
#ifndef MSBFIRST
#define MSBFIRST BitOrder::MSBFIRST
#endif
static SPISettings AEAT8800Q24SPISettings(1000000, MSBFIRST, SPI_MODE3); // @suppress("Invalid arguments")
static SPISettings AEAT8800Q24SSISettings(4000000, MSBFIRST, SPI_MODE2); // @suppress("Invalid arguments")
class AEAT8800Q24 {
public:
AEAT8800Q24(int nCS, int pinNSL = MOSI, SPISettings spiSettings = AEAT8800Q24SPISettings, SPISettings ssiSettings = AEAT8800Q24SSISettings);
virtual ~AEAT8800Q24();
virtual void init(SPIClass* _spi = &SPI);
float getCurrentAngle(); // angle in radians, return current value
uint16_t readRawAngle(); // 14bit angle value
AEAT8800Q24_Status_t getLastStatus(); // get status associated with last angle read
uint16_t getZero();
void setZero(uint16_t);
AEAT8800Q24_CONF0_t getConf0();
void setConf0(AEAT8800Q24_CONF0_t);
AEAT8800Q24_CONF1_t getConf1();
void setConf1(AEAT8800Q24_CONF1_t);
AEAT8800Q24_CONF2_t getConf2();
void setConf2(AEAT8800Q24_CONF2_t);
private:
int nCS = -1;
int pinNSL = -1;
SPISettings spiSettings;
SPISettings ssiSettings;
SPIClass* spi;
AEAT8800Q24_Status_t lastStatus;
uint16_t transfer16SPI(uint16_t outValue);
uint8_t readRegister(uint8_t reg);
void writeRegister(uint8_t reg, uint8_t value);
};
#endif

View File

@@ -0,0 +1,24 @@
#include "./MagneticSensorAEAT8800Q24.h"
#include "common/foc_utils.h"
#include "common/time_utils.h"
MagneticSensorAEAT8800Q24::MagneticSensorAEAT8800Q24(int nCS, int pinNSL, SPISettings spiSettings, SPISettings ssiSettings) : AEAT8800Q24(nCS, pinNSL, spiSettings, ssiSettings) {
};
MagneticSensorAEAT8800Q24::~MagneticSensorAEAT8800Q24(){
};
void MagneticSensorAEAT8800Q24::init(SPIClass* _spi) {
this->AEAT8800Q24::init(_spi);
this->Sensor::init();
};
float MagneticSensorAEAT8800Q24::getSensorAngle() {
float angle_data = readRawAngle();
angle_data = ( angle_data / (float)AEAT8800Q24_CPR) * _2PI;
// return the shaft angle
return angle_data;
};

View File

@@ -0,0 +1,18 @@
#ifndef __MAGNETICSENSORAEAT8800Q24_H__
#define __MAGNETICSENSORAEAT8800Q24_H__
#include "common/base_classes/Sensor.h"
#include "./AEAT8800Q24.h"
class MagneticSensorAEAT8800Q24 : public Sensor, public AEAT8800Q24 {
public:
MagneticSensorAEAT8800Q24(int nCS, int pinNSL = MOSI, SPISettings spiSettings = AEAT8800Q24SPISettings, SPISettings ssiSettings = AEAT8800Q24SSISettings);
virtual ~MagneticSensorAEAT8800Q24();
virtual float getSensorAngle() override;
virtual void init(SPIClass* _spi = &SPI);
};
#endif

View File

@@ -0,0 +1,75 @@
# AEAT-8800-Q24 SimpleFOC driver
SPI/SSI driver for the absolute position magnetic rotary encoder. This encoder is not supported by the
normal SimpleFOC drivers due to its mixed SPI/SSI mode.
- access to the configuration registers of the AEAT-8800-Q24, enabling you to set parameters
- angles are read via SSI, with 16 bit (!) precision
- currently only 16 bit resolution is supported, don't lower the resolution
## Hardware setup
Connect as per normal for your SPI bus. No special hardware setup is needed to use this driver. nCS pin is
required.
Note that due to the way SSI and SPI share pins, you can normally only run one of these sensors per SPI bus.
## Software setup
Its actually easier to use than the standard SPI sensor class, because it is less generic:
```c++
#include "Arduino.h"
#include "Wire.h"
#include "SPI.h"
#include "SimpleFOC.h"
#include "SimpleFOCDrivers.h"
#include "encoders/aeat8800q24/MagneticSensorAEAT8800Q24.h"
#define SENSOR1_CS 5 // some digital pin that you're using as the nCS pin
MagneticSensorAEAT8800Q24 sensor1(SENSOR1_CS);
void setup() {
sensor1.init();
}
```
Set some options:
```c++
MagneticSensorAEAT8800Q24 sensor1(SENSOR1_CS, MOSI_pin, mySPISettings, mySSISettings);
```
Use another SPI bus:
```c++
void setup() {
sensor1.init(SPI2);
}
```
Here's how you can use it:
```c++
// get the angle, in radians, including full rotations
float a1 = sensor1.getAngle();
// get the velocity, in rad/s - note: you have to call getAngle() on a regular basis for it to work
float v1 = sensor1.getVelocity();
// get the angle, in radians, no full rotations
float a2 = sensor1.getCurrentAngle();
// get the raw 16 bit value
uint16_t raw = sensor1.readRawAngle();
// check status
float angle = sensor1.getSensorAngle();
AEAT8800Q24_Status_t status = sensor1.getLastStatus();
if (status.mlo)
Serial.println("Sensor magnet low error");
if (status.mhi)
Serial.println("Sensor magnet high error");
```

View File

@@ -0,0 +1,245 @@
/*
* AS5047.cpp
*
* Created on: 2 May 2021
* Author: runger
*/
#include "./AS5047.h"
AS5047::AS5047(SPISettings settings, int nCS) : settings(settings), nCS(nCS) {
// nix
}
AS5047::~AS5047() {
}
void AS5047::init(SPIClass* _spi) {
spi = _spi;
if (nCS>=0)
pinMode(nCS, OUTPUT);
digitalWrite(nCS, HIGH);
//SPI has an internal SPI-device counter, it is possible to call "begin()" from different devices
spi->begin();
readRawAngle(); // read an angle
}
float AS5047::getCurrentAngle(){
readCorrectedAngle();
return ((float)readCorrectedAngle())/(float)AS5047_CPR * 2.0f * (float)PI;
}
float AS5047::getFastAngle(){
return ((float)readCorrectedAngle())/(float)AS5047_CPR * 2.0f * (float)PI;
}
uint16_t AS5047::readRawAngle(){
uint16_t command = AS5047_ANGLEUNC_REG | AS5047_RW; // set r=1 and parity=0, result is 0x7FFE
uint16_t lastresult = spi_transfer16(command)&AS5047_RESULT_MASK;
return lastresult;
}
uint16_t AS5047::readCorrectedAngle(){
uint16_t command = AS5047_ANGLECOM_REG | AS5047_PARITY | AS5047_RW; // set r=1 and parity=1, result is 0xFFFF
uint16_t lastresult = spi_transfer16(command)&AS5047_RESULT_MASK;
return lastresult;
}
uint16_t AS5047::readMagnitude(){
uint16_t command = AS5047_MAGNITUDE_REG | AS5047_RW; // set r=1, result is 0x7FFD
/*uint16_t cmdresult =*/ spi_transfer16(command);
uint16_t result = nop();
return result;
}
bool AS5047::isErrorFlag(){
return errorflag;
}
AS5047Error AS5047::clearErrorFlag(){
uint16_t command = AS5047_ERROR_REG | AS5047_RW; // set r=1, result is 0x4001
/*uint16_t cmdresult =*/ spi_transfer16(command);
uint16_t result = nop();
AS5047Error err = {
.framingError = ((result&0x0001)!=0x0000),
.commandInvalid = ((result&0x0002)!=0x0000),
.parityError = ((result&0x0004)!=0x0000)
};
return err;
}
AS5047Settings1 AS5047::readSettings1(){
uint16_t command = AS5047_SETTINGS1_REG | AS5047_PARITY | AS5047_RW; // set r=1, result is 0xC018
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047Settings1 result = {
.reg = nop()
};
return result;
}
void AS5047::writeSettings1(AS5047Settings1 settings){
uint16_t command = AS5047_SETTINGS1_REG; // set r=0, result is 0x0018
spi_transfer16(command);
spi_transfer16(calcParity(settings.reg));
}
AS5047Settings2 AS5047::readSettings2(){
uint16_t command = AS5047_SETTINGS2_REG | AS5047_RW; // set r=1, result is 0x4019
spi_transfer16(command);
AS5047Settings2 result = {
.reg = nop()
};
return result;
}
void AS5047::writeSettings2(AS5047Settings2 settings){
uint16_t command = AS5047_SETTINGS2_REG | AS5047_PARITY; // set r=0, result is 0x8019
spi_transfer16(command);
spi_transfer16(calcParity(settings.reg));
}
AS5047Diagnostics AS5047::readDiagnostics(){
uint16_t command = AS5047_DIAGNOSTICS_REG | AS5047_PARITY | AS5047_RW; // set r=1, result is 0xFFFC
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047Diagnostics result = {
.reg = nop()
};
return result;
}
void AS5047::enablePWM(bool enable){
AS5047Settings1 settings = readSettings1();
if (settings.pwmon == enable) return; // no change
settings.pwmon = enable;
writeSettings1(settings);
}
void AS5047::enableABI(bool enable){
AS5047Settings1 settings = readSettings1();
uint8_t val = enable?0:1;
if (settings.uvw_abi == val) return; // no change
settings.uvw_abi = val;
writeSettings1(settings);
}
void AS5047::enableDEAC(bool enable){
AS5047Settings1 settings = readSettings1();
uint8_t val = enable?0:1;
if (settings.daecdis == val) return; // no change
settings.daecdis = enable?0:1;
writeSettings1(settings);
}
void AS5047::useCorrectedAngle(bool useCorrected){
AS5047Settings1 settings = readSettings1();
uint8_t val = useCorrected?0:1;
if (settings.dataselect == val) return; // no change
settings.dataselect = val;
writeSettings1(settings);
}
void AS5047::setHysteresis(uint8_t hyst){
if (hyst>3) hyst = 3;
uint8_t val = 3-hyst;
AS5047Settings2 settings = readSettings2();
if (settings.hys == val) return; // no change
settings.hys = val;
writeSettings2(settings);
}
void AS5047::setABIResolution(AS5047ABIRes res){
AS5047Settings1 settings = readSettings1();
uint8_t val = (res>>3)&0x01;
if (settings.abibin!=val || settings.uvw_abi!=0) {
settings.abibin = val;
settings.uvw_abi = 0;
writeSettings1(settings);
}
AS5047Settings2 settings2 = readSettings2();
val = (res&0x07);
if (settings2.abires!=val) {
settings2.abires = val;
writeSettings2(settings2);
}
}
uint16_t AS5047::setZero(uint16_t value){
uint16_t command = AS5047_ZPOSL_REG | AS5047_PARITY | AS5047_RW;
spi_transfer16(command);
AS5047ZPosL posL = {
.reg = nop()
};
command = AS5047_ZPOSM_REG | AS5047_PARITY;
spi_transfer16(command);
spi_transfer16(calcParity((value>>6)&0x00FF));
command = AS5047_ZPOSL_REG;
posL.zposl = value&0x003F;
spi_transfer16(command);
spi_transfer16(calcParity(posL.reg));
return getZero();
}
uint16_t AS5047::getZero() {
uint16_t command = AS5047_ZPOSM_REG | AS5047_RW;
spi_transfer16(command);
command = AS5047_ZPOSL_REG | AS5047_PARITY | AS5047_RW;
uint16_t result = spi_transfer16(command);
AS5047ZPosL posL = {
.reg = nop()
};
return ((result&0x00FF)<<6) | posL.zposl;
}
uint16_t AS5047::nop(){
uint16_t result = spi_transfer16(0xFFFF); // using 0xFFFF as nop instead of 0x0000, then next call to fastAngle will return an angle
return result&AS5047_RESULT_MASK;
}
uint16_t AS5047::calcParity(uint16_t data){
data = data & 0x7FFF;
int sum = 0;
for (int i=0;i<15;i++)
if ((data>>i)&0x0001)
sum++;
return (sum&0x01)==0x01?(data|0x8000):data;
}
uint16_t AS5047::spi_transfer16(uint16_t outdata) {
if (nCS>=0)
digitalWrite(nCS, 0);
spi->beginTransaction(settings);
uint16_t result = spi->transfer16(outdata);
spi->endTransaction();
if (nCS>=0)
digitalWrite(nCS, 1);
// TODO check parity
errorflag = ((result&AS5047_ERRFLG)>0);
return result;
}

View File

@@ -0,0 +1,167 @@
/*
* AS5047.h
*
* Created on: 2 May 2021
* Author: runger
*/
#ifndef AS5047_H_
#define AS5047_H_
#include "Arduino.h"
#include "SPI.h"
union AS5047Diagnostics {
struct {
uint16_t agc:8;
uint16_t lf:1;
uint16_t cof:1;
uint16_t magh:1;
uint16_t magl:1;
uint16_t unused:4;
};
uint16_t reg;
};
union AS5047ZPosM {
struct {
uint16_t zposm:8;
uint16_t unused:8;
};
uint16_t reg;
};
union AS5047ZPosL {
struct {
uint16_t zposl:6;
uint16_t comp_l_error_en:1;
uint16_t comp_h_error_en:1;
uint16_t unused:8;
};
uint16_t reg;
};
union AS5047Settings1 {
struct {
uint16_t reserved:1;
uint16_t noiseset:1;
uint16_t dir:1;
uint16_t uvw_abi:1;
uint16_t daecdis:1;
uint16_t abibin:1;
uint16_t dataselect:1;
uint16_t pwmon:1;
uint16_t unused:8;
};
uint16_t reg;
};
union AS5047Settings2 {
struct {
uint16_t uvwpp:3;
uint16_t hys:2;
uint16_t abires:3;
uint16_t unused:8;
};
uint16_t reg;
};
struct AS5047Error {
bool framingError;
bool commandInvalid;
bool parityError;
};
enum AS5047ABIRes : uint8_t {
AS5047_ABI_1024 = 0b1010,
AS5047_ABI_2048 = 0b1001,
AS5047_ABI_4096 = 0b1000,
AS5047_ABI_100 = 0b0111,
AS5047_ABI_200 = 0b0110,
AS5047_ABI_400 = 0b0101,
AS5047_ABI_800 = 0b0100,
AS5047_ABI_1200 = 0b0011,
AS5047_ABI_1600 = 0b0010,
AS5047_ABI_2000 = 0b0001,
AS5047_ABI_4000 = 0b0000
};
#define AS5047_CPR 16384
#define AS5047_ANGLECOM_REG 0x3FFF
#define AS5047_ANGLEUNC_REG 0x3FFE
#define AS5047_MAGNITUDE_REG 0x3FFD
#define AS5047_DIAGNOSTICS_REG 0x3FFC
#define AS5047_ERROR_REG 0x0001
#define AS5047_PROG_REG 0x0003
#define AS5047_ZPOSM_REG 0x0016
#define AS5047_ZPOSL_REG 0x0017
#define AS5047_SETTINGS1_REG 0x0018
#define AS5047_SETTINGS2_REG 0x0019
#define AS5047_PARITY 0x8000
#define AS5047_RW 0x4000
#define AS5047_ERRFLG 0x4000
#define AS5047_RESULT_MASK 0x3FFF
#define AS5047_BITORDER MSBFIRST
static SPISettings AS5047SPISettings(8000000, AS5047_BITORDER, SPI_MODE1); // @suppress("Invalid arguments")
class AS5047 {
public:
AS5047(SPISettings settings = AS5047SPISettings, int nCS = -1);
virtual ~AS5047();
virtual void init(SPIClass* _spi = &SPI);
float getCurrentAngle(); // angle in radians, return current value
float getFastAngle(); // angle in radians, return last value and read new
uint16_t readRawAngle(); // 14bit angle value
uint16_t readCorrectedAngle(); // 14bit corrected angle value
uint16_t readMagnitude(); // 14bit magnitude value
bool isErrorFlag();
AS5047Error clearErrorFlag();
AS5047Diagnostics readDiagnostics();
AS5047Settings1 readSettings1();
void writeSettings1(AS5047Settings1 settings);
AS5047Settings2 readSettings2();
void writeSettings2(AS5047Settings2 settings);
void enablePWM(bool enable);
void enableABI(bool enable);
void setABIResolution(AS5047ABIRes res);
void enableDEAC(bool enable);
void useCorrectedAngle(bool useCorrected);
void setHysteresis(uint8_t hyst);
uint16_t setZero(uint16_t);
uint16_t getZero();
uint16_t calcParity(uint16_t data);
private:
uint16_t nop();
uint16_t spi_transfer16(uint16_t outdata);
SPIClass* spi;
SPISettings settings;
bool errorflag = false;
int nCS = -1;
};
#endif /* AS5047_H_ */

View File

@@ -0,0 +1,29 @@
#include "./MagneticSensorAS5047.h"
#include "common/foc_utils.h"
#include "common/time_utils.h"
MagneticSensorAS5047::MagneticSensorAS5047(int nCS, bool fastMode, SPISettings settings) : AS5047(settings, nCS), fastMode(fastMode) {
}
MagneticSensorAS5047::~MagneticSensorAS5047(){
}
void MagneticSensorAS5047::init(SPIClass* _spi) {
this->AS5047::init(_spi);
this->Sensor::init();
}
float MagneticSensorAS5047::getSensorAngle() {
float angle_data = readRawAngle();
if (!fastMode) // read again to ensure current value
angle_data = readRawAngle();
angle_data = ( angle_data / (float)AS5047_CPR) * _2PI;
// return the shaft angle
return angle_data;
}

View File

@@ -0,0 +1,20 @@
#ifndef __MAGNETICSENSORAS5047PD_H__
#define __MAGNETICSENSORAS5047PD_H__
#include "common/base_classes/Sensor.h"
#include "./AS5047.h"
class MagneticSensorAS5047 : public Sensor, public AS5047 {
public:
MagneticSensorAS5047(int nCS = -1, bool fastMode = false, SPISettings settings = AS5047SPISettings);
virtual ~MagneticSensorAS5047();
virtual float getSensorAngle() override;
virtual void init(SPIClass* _spi = &SPI);
private:
bool fastMode = false;
};
#endif

View File

@@ -0,0 +1,82 @@
# AS5047 SimpleFOC driver
While the AS5047 absolute position magnetic rotary encoder is supported by the standard MagneticSensorSPI driver included in the base distribution, this AS5047-specific driver includes some optimisations:
- access to the other registers of the AS5047, including the magnitude value which can be used to check the magnet strength, and the diagnostics register
- access to the error state of the sensor, and ability to clear errors
- it has a fastMode setting, in which the sensor is sent only 1 command per getAngle() call - the value returned will be from previous getAngle() invocation
This driver should work with AS5047P and AS5047D models. The AS5047U has it's own driver [here](../as5047u/).
## Hardware setup
Connect as per normal for your SPI bus. No special hardware setup is needed to use this driver.
## Software setup
Its actually easier to use than the standard SPI sensor class, because it is less generic:
```c++
#include "Arduino.h"
#include "Wire.h"
#include "SPI.h"
#include "SimpleFOC.h"
#include "SimpleFOCDrivers.h"
#include "encoders/as5047/MagneticSensorAS5047.h"
#define SENSOR1_CS 5 // some digital pin that you're using as the nCS pin
MagneticSensorAS5047 sensor1(SENSOR1_CS);
void setup() {
sensor1.init();
}
```
Set some options:
```c++
MagneticSensorAS5047 sensor1(SENSOR1_CS, true, mySPISettings);
```
Use another SPI bus:
```c++
void setup() {
sensor1.init(SPI2);
}
```
Here's how you can use it:
```c++
// update the sensor (only needed if using the sensor without a motor)
sensor1.update();
// get the angle, in radians, including full rotations
float a1 = sensor1.getAngle();
// get the velocity, in rad/s - note: you have to call getAngle() on a regular basis for it to work
float v1 = sensor1.getVelocity();
// get the angle, in radians, no full rotations
float a2 = sensor1.getCurrentAngle();
// get the raw 14 bit value
uint16_t raw = sensor1.readRawAngle();
// read the CORDIC magnitude value, a measure of the magnet field strength
float m1 = sensor1.readMagnitude();
// check for errors
if (sensor1.isErrorFlag()) {
AS5047Error error = sensor1.clearErrorFlag();
if (error.parityError) { // also error.framingError, error.commandInvalid
// etc...
}
}
// get diagnostics
AS5047Diagnostics diagnostics = sensor1.readDiagnostics();
```

View File

@@ -0,0 +1,350 @@
/*
* AS5047U.cpp
*
* Created on: 24 Feb 2022
* Author: runger
*/
#include "./AS5047U.h"
AS5047U::AS5047U(SPISettings settings, int nCS) : settings(settings), nCS(nCS) {
// nix
}
AS5047U::~AS5047U() {
}
void AS5047U::init(SPIClass* _spi) {
spi = _spi;
if (nCS>=0)
pinMode(nCS, OUTPUT);
digitalWrite(nCS, HIGH);
//SPI has an internal SPI-device counter, it is possible to call "begin()" from different devices
spi->begin();
readRawAngle(); // read an angle
}
float AS5047U::getCurrentAngle(){
readCorrectedAngle();
return ((float)readCorrectedAngle())/(float)AS5047U_CPR * 2.0f * (float)PI;
}
float AS5047U::getFastAngle(){
return ((float)readCorrectedAngle())/(float)AS5047U_CPR * 2.0f * (float)PI;
}
uint16_t AS5047U::readRawAngle(){
uint16_t command = AS5047U_ANGLEUNC_REG | AS5047U_RW;
uint16_t lastresult = spi_transfer16(command)&AS5047U_RESULT_MASK;
return lastresult;
}
uint16_t AS5047U::readCorrectedAngle(){
uint16_t command = AS5047U_ANGLECOM_REG | AS5047U_RW;
uint16_t lastresult = spi_transfer16(command)&AS5047U_RESULT_MASK;
return lastresult;
}
uint16_t AS5047U::readMagnitude(){
uint16_t command = AS5047U_MAGNITUDE_REG | AS5047U_RW;
/*uint16_t cmdresult =*/ spi_transfer16(command);
uint16_t result = nop16();
return result;
}
uint16_t AS5047U::readVelocity(){
uint16_t command = AS5047U_VELOCITY_REG | AS5047U_RW;
/*uint16_t cmdresult =*/ spi_transfer16(command);
uint16_t result = nop16();
return result;
}
bool AS5047U::isErrorFlag(){
return errorflag;
}
bool AS5047U::isWarningFlag(){
return warningflag;
}
AS5047UError AS5047U::clearErrorFlag(){
uint16_t command = AS5047U_ERROR_REG | AS5047U_RW; // set r=1, result is 0x4001
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047UError result;
result.reg = nop16();
return result;
}
AS5047USettings1 AS5047U::readSettings1(){
uint16_t command = AS5047U_SETTINGS1_REG | AS5047U_RW; // set r=1, result is 0xC018
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047USettings1 result = {
.reg = nop16()
};
return result;
}
void AS5047U::writeSettings1(AS5047USettings1 settings){
writeRegister24(AS5047U_SETTINGS1_REG, settings.reg);
}
AS5047USettings2 AS5047U::readSettings2(){
uint16_t command = AS5047U_SETTINGS2_REG | AS5047U_RW; // set r=1, result is 0x4019
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047USettings2 result = {
.reg = nop16()
};
return result;
}
void AS5047U::writeSettings2(AS5047USettings2 settings){
writeRegister24(AS5047U_SETTINGS2_REG, settings.reg);
}
AS5047USettings3 AS5047U::readSettings3(){
uint16_t command = AS5047U_SETTINGS3_REG | AS5047U_RW;
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047USettings3 result = {
.reg = nop16()
};
return result;
}
void AS5047U::writeSettings3(AS5047USettings3 settings){
writeRegister24(AS5047U_SETTINGS3_REG, settings.reg);
}
AS5047UDiagnostics AS5047U::readDiagnostics(){
uint16_t command = AS5047U_DIAGNOSTICS_REG | AS5047U_RW;
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047UDiagnostics result = {
.reg = nop16()
};
return result;
}
uint8_t AS5047U::readAGC(){
uint16_t command = AS5047U_AGC_REG | AS5047U_RW;
/*uint16_t cmdresult =*/ spi_transfer16(command);
uint16_t result = nop16();
return result & 0x00FF;
};
uint8_t AS5047U::readECCCHK(){
uint16_t command = AS5047U_ECCCHK_REG | AS5047U_RW;
/*uint16_t cmdresult =*/ spi_transfer16(command);
uint16_t result = nop16();
return result & 0x007F;
};
AS5047UDisableSettings AS5047U::readDisableSettings(){
uint16_t command = AS5047U_DISABLE_REG | AS5047U_RW;
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047UDisableSettings result = {
.reg = nop16()
};
return result;
};
void AS5047U::writeDisableSettings(AS5047UDisableSettings settings){
writeRegister24(AS5047U_DISABLE_REG, settings.reg);
};
AS5047UECCSettings AS5047U::readECCSettings(){
uint16_t command = AS5047U_ECC_REG | AS5047U_RW;
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047UECCSettings result = {
.reg = nop16()
};
return result;
};
void AS5047U::writeECCSettings(AS5047UECCSettings settings){
writeRegister24(AS5047U_ECC_REG, settings.reg);
};
void AS5047U::enablePWM(bool enable, bool pwmOnWPin){
// AS5047UDisableSettings settings = readDisableSettings();
// if (settings.uvw_off==1) {
// settings.uvw_off = 0;
// writeDiableSettings(settings);
// }
AS5047USettings2 settings2 = readSettings2();
settings2.uvw_abi = pwmOnWPin?0:1;
settings2.pwm_on = enable;
writeSettings2(settings2);
}
void AS5047U::enableABI(bool enable){
AS5047UDisableSettings settings = readDisableSettings();
settings.abi_off = enable?0:1;
writeDisableSettings(settings);
delayMicroseconds(50);
if (enable) {
AS5047USettings2 settings2 = readSettings2();
settings2.uvw_abi = 0;
writeSettings2(settings2);
}
}
void AS5047U::enableUVW(bool enable){
AS5047UDisableSettings settings = readDisableSettings();
settings.uvw_off = enable?0:1;
writeDisableSettings(settings);
if (enable) {
AS5047USettings2 settings2 = readSettings2();
settings2.uvw_abi = 1;
writeSettings2(settings2);
}
}
uint16_t AS5047U::getZero(){
uint16_t command = AS5047U_ZPOSM_REG | AS5047U_RW;
spi_transfer16(command);
command = AS5047U_ZPOSL_REG | AS5047U_RW;
uint16_t result = spi_transfer16(command);
AS5047UZPosL posL = {
.reg = nop16()
};
return ((result&0x00FF)<<6) | posL.zposl;
}
uint16_t AS5047U::setZero(uint16_t value){
uint16_t command = AS5047U_ZPOSL_REG | AS5047U_RW;
/*uint16_t cmdresult =*/ spi_transfer16(command);
AS5047UZPosL posL = {
.reg = nop16()
};
posL.zposl = value&0x003F;
writeRegister24(AS5047U_ZPOSL_REG, posL.reg);
writeRegister24(AS5047U_ZPOSM_REG, (value>>6)&0x00FF);
return getZero();
}
uint16_t AS5047U::nop16(){
uint16_t result = spi_transfer16(0xFFFF); // using 0xFFFF as nop instead of 0x0000, then next call to fastAngle will return an angle
return result&AS5047U_RESULT_MASK;
}
uint16_t AS5047U::spi_transfer16(uint16_t outdata) {
if (nCS>=0)
digitalWrite(nCS, 0);
spi->beginTransaction(settings);
uint16_t result = spi->transfer16(outdata);
spi->endTransaction();
if (nCS>=0)
digitalWrite(nCS, 1);
errorflag = ((result&AS5047U_ERROR)>0);
warningflag = ((result&AS5047U_WARNING)>0);
return result;
}
uint8_t AS5047U::calcCRC(uint16_t data){
uint8_t crc = 0xC4; // Initial value
for (int i = 0; i < 16; i++) {
if ((crc ^ data) & 0x8000) {
crc = (crc << 1) ^ 0x1D;
} else {
crc <<= 1;
}
data <<= 1;
}
return crc ^ 0xFF;
}
uint16_t AS5047U::writeRegister24(uint16_t reg, uint16_t data) {
uint8_t buff[3];
buff[0] = (reg>>8)&0x3F;
buff[1] = reg&0xFF;
buff[2] = calcCRC(reg);
if (nCS>=0)
digitalWrite(nCS, 0);
spi->beginTransaction(settings);
spi->transfer(buff, 3);
spi->endTransaction();
if (nCS>=0)
digitalWrite(nCS, 1);
errorflag = ((buff[0]&0x40)>0);
warningflag = ((buff[0]&0x80)>0);
buff[0] = (data>>8)&0x3F;
buff[1] = data&0xFF;
buff[2] = calcCRC(data);
if (nCS>=0)
digitalWrite(nCS, 0);
spi->beginTransaction(settings);
spi->transfer(buff, 3);
spi->endTransaction();
if (nCS>=0)
digitalWrite(nCS, 1);
errorflag = ((buff[0]&0x40)>0);
warningflag = ((buff[0]&0x80)>0);
delayMicroseconds(50);
return buff[0]<<8 | buff[1];
}

View File

@@ -0,0 +1,225 @@
/*
* AS5047.h
*
* Created on: 24 Feb 2022
* Author: runger
*/
#ifndef AS5047U_H_
#define AS5047U_H_
#include "Arduino.h"
#include "SPI.h"
union AS5047UDisableSettings {
struct {
uint16_t :9;
uint16_t filter_disable:1;
uint16_t :4;
uint16_t abi_off:1;
uint16_t uvw_off:1;
};
uint16_t reg;
};
union AS5047UDiagnostics {
struct {
uint16_t spi_cnt:2;
uint16_t :1;
uint16_t agc_finished:1;
uint16_t off_finished:1;
uint16_t sin_finished:1;
uint16_t cos_finished:1;
uint16_t maghalf_flag:1;
uint16_t comp_h:1;
uint16_t comp_l:1;
uint16_t cordic_ovf:1;
uint16_t loops_finished:1;
uint16_t vdd_mode:1;
};
uint16_t reg;
};
union AS5047UZPosM {
struct {
uint16_t zposm:8;
};
uint16_t reg;
};
union AS5047UZPosL {
struct {
uint16_t zposl:6;
uint16_t dia1_en:1; // automotive version only
uint16_t dia2_en:1; // automotive version only
};
uint16_t reg;
};
union AS5047USettings1 {
struct {
uint16_t k_max:3;
uint16_t k_min:3;
uint16_t dia3_en:1; // not applicable
uint16_t dia4_en:1; // not applicable
};
uint16_t reg;
};
union AS5047USettings2 {
struct {
uint16_t iwidth:1;
uint16_t noiseset:1;
uint16_t dir:1;
uint16_t uvw_abi:1;
uint16_t daecdis:1;
uint16_t abi_dec:1;
uint16_t data_select:1;
uint16_t pwm_on:1;
};
uint16_t reg;
};
union AS5047USettings3 {
struct {
uint16_t uvwpp:3;
uint16_t hys:2;
uint16_t abires:3;
};
uint16_t reg;
};
union AS5047UECCSettings {
struct {
uint16_t ecc_chsum:7;
uint16_t ecc_en:1;
};
uint16_t reg;
};
struct AS5047UError {
struct {
uint16_t cordic_ovf:1;
uint16_t off_notfinished:1;
uint16_t :1;
uint16_t wdtst:1;
uint16_t crc_error:1;
uint16_t cmd_error:1;
uint16_t frame_error:1;
uint16_t p2ram_error:1;
uint16_t p2ram_warning:1;
uint16_t maghalf:1;
uint16_t agc_warning:1;
};
uint16_t reg;
};
#define AS5047U_CPR 16384
#define AS5047U_ANGLECOM_REG 0x3FFF
#define AS5047U_ANGLEUNC_REG 0x3FFE
#define AS5047U_MAGNITUDE_REG 0x3FFD
#define AS5047U_VELOCITY_REG 0x3FFC
#define AS5047U_SIN_REG 0x3FFA
#define AS5047U_COS_REG 0x3FFB
#define AS5047U_AGC_REG 0x3FF9
#define AS5047U_DIAGNOSTICS_REG 0x3FF5
#define AS5047U_ERROR_REG 0x0001
#define AS5047U_PROG_REG 0x0003
#define AS5047U_ECCCHK_REG 0x00D1
#define AS5047U_DISABLE_REG 0x0015
#define AS5047U_ZPOSM_REG 0x0016
#define AS5047U_ZPOSL_REG 0x0017
#define AS5047U_SETTINGS1_REG 0x0018
#define AS5047U_SETTINGS2_REG 0x0019
#define AS5047U_SETTINGS3_REG 0x001A
#define AS5047U_ECC_REG 0x001B
#define AS5047U_WARNING 0x8000
#define AS5047U_ERROR 0x4000
#define AS5047U_RW 0x4000
#define AS5047U_ERRFLG 0xC000
#define AS5047U_RESULT_MASK 0x3FFF
#define AS5047U_BITORDER MSBFIRST
static SPISettings AS5047USPISettings(8000000, AS5047U_BITORDER, SPI_MODE1); // @suppress("Invalid arguments")
class AS5047U {
public:
AS5047U(SPISettings settings = AS5047USPISettings, int nCS = -1);
virtual ~AS5047U();
virtual void init(SPIClass* _spi = &SPI);
float getCurrentAngle(); // angle in radians, return current value
float getFastAngle(); // angle in radians, return last value and read new
uint16_t readRawAngle(); // 14bit angle value
uint16_t readCorrectedAngle(); // 14bit corrected angle value
uint16_t readMagnitude(); // 14bit magnitude value
uint16_t readVelocity(); // 14bit magnitude value
bool isErrorFlag();
bool isWarningFlag();
AS5047UError clearErrorFlag();
AS5047UDiagnostics readDiagnostics();
uint8_t readAGC();
uint8_t readECCCHK();
AS5047UDisableSettings readDisableSettings();
void writeDisableSettings(AS5047UDisableSettings settings);
AS5047USettings1 readSettings1();
void writeSettings1(AS5047USettings1 settings);
AS5047USettings2 readSettings2();
void writeSettings2(AS5047USettings2 settings);
AS5047USettings3 readSettings3();
void writeSettings3(AS5047USettings3 settings);
AS5047UECCSettings readECCSettings();
void writeECCSettings(AS5047UECCSettings settings);
void enablePWM(bool enable, bool pwmOnWPin = true);
void enableABI(bool enable);
void enableUVW(bool enable);
uint16_t setZero(uint16_t);
uint16_t getZero();
private:
uint16_t nop16();
uint16_t spi_transfer16(uint16_t outdata);
uint8_t calcCRC(uint16_t data);
uint16_t writeRegister24(uint16_t reg, uint16_t data);
SPIClass* spi;
SPISettings settings;
bool errorflag = false;
bool warningflag = false;
int nCS = -1;
};
#endif /* AS5047U_H_ */

View File

@@ -0,0 +1,29 @@
#include "./MagneticSensorAS5047U.h"
#include "common/foc_utils.h"
#include "common/time_utils.h"
MagneticSensorAS5047U::MagneticSensorAS5047U(int nCS, bool fastMode, SPISettings settings) : AS5047U(settings, nCS), fastMode(fastMode) {
}
MagneticSensorAS5047U::~MagneticSensorAS5047U(){
}
void MagneticSensorAS5047U::init(SPIClass* _spi) {
this->AS5047U::init(_spi);
this->Sensor::init();
}
float MagneticSensorAS5047U::getSensorAngle() {
float angle_data = readRawAngle();
if (!fastMode) // read again to ensure current value
angle_data = readRawAngle();
angle_data = ( angle_data / (float)AS5047U_CPR) * _2PI;
// return the shaft angle
return angle_data;
}

Some files were not shown because too many files have changed in this diff Show More