@@ -1 +0,0 @@
|
||||
Ref,Val,Package,PosX,PosY,Rot,Side
|
||||
|
@@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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*
|
||||
|
||||
@@ -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
@@ -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
@@ -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:11 PM
|
||||
; 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
@@ -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:11 PM
|
||||
; 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
|
||||
|
||||
@@ -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
@@ -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*
|
||||
|
||||
@@ -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
@@ -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
@@ -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:50 PM
|
||||
; 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
@@ -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:50 PM
|
||||
; 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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
@@ -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
|
||||
|
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
21
firmware/lib/Arduino-FOC-drivers/LICENSE
Normal file
21
firmware/lib/Arduino-FOC-drivers/LICENSE
Normal 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.
|
||||
117
firmware/lib/Arduino-FOC-drivers/README.md
Normal file
117
firmware/lib/Arduino-FOC-drivers/README.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# SimpleFOC Driver and Support Library
|
||||
|
||||

|
||||
[](https://opensource.org/licenses/MIT)
|
||||

|
||||
|
||||
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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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(¤t_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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
11
firmware/lib/Arduino-FOC-drivers/keywords.txt
Normal file
11
firmware/lib/Arduino-FOC-drivers/keywords.txt
Normal 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
|
||||
11
firmware/lib/Arduino-FOC-drivers/library.properties
Normal file
11
firmware/lib/Arduino-FOC-drivers/library.properties
Normal 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
|
||||
11
firmware/lib/Arduino-FOC-drivers/src/SimpleFOCDrivers.h
Normal file
11
firmware/lib/Arduino-FOC-drivers/src/SimpleFOCDrivers.h
Normal 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
|
||||
|
||||
5
firmware/lib/Arduino-FOC-drivers/src/comms/README.md
Normal file
5
firmware/lib/Arduino-FOC-drivers/src/comms/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
# SimpleFOC communications support code
|
||||
|
||||
This folder contains classes to support you communicating between MCUs running SimpleFOC, and other systems.
|
||||
|
||||
131
firmware/lib/Arduino-FOC-drivers/src/comms/RegisterReceiver.cpp
Normal file
131
firmware/lib/Arduino-FOC-drivers/src/comms/RegisterReceiver.cpp
Normal 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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
193
firmware/lib/Arduino-FOC-drivers/src/comms/RegisterSender.cpp
Normal file
193
firmware/lib/Arduino-FOC-drivers/src/comms/RegisterSender.cpp
Normal 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;
|
||||
};
|
||||
|
||||
18
firmware/lib/Arduino-FOC-drivers/src/comms/RegisterSender.h
Normal file
18
firmware/lib/Arduino-FOC-drivers/src/comms/RegisterSender.h
Normal 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;
|
||||
};
|
||||
@@ -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;
|
||||
424
firmware/lib/Arduino-FOC-drivers/src/comms/i2c/I2CCommander.cpp
Normal file
424
firmware/lib/Arduino-FOC-drivers/src/comms/i2c/I2CCommander.cpp
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
98
firmware/lib/Arduino-FOC-drivers/src/comms/i2c/README.md
Normal file
98
firmware/lib/Arduino-FOC-drivers/src/comms/i2c/README.md
Normal 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.
|
||||
49
firmware/lib/Arduino-FOC-drivers/src/comms/serial/README.md
Normal file
49
firmware/lib/Arduino-FOC-drivers/src/comms/serial/README.md
Normal 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
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
```
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
```
|
||||
@@ -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++;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
175
firmware/lib/Arduino-FOC-drivers/src/drivers/drv8316/README.md
Normal file
175
firmware/lib/Arduino-FOC-drivers/src/drivers/drv8316/README.md
Normal 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...
|
||||
|
||||
586
firmware/lib/Arduino-FOC-drivers/src/drivers/drv8316/drv8316.cpp
Normal file
586
firmware/lib/Arduino-FOC-drivers/src/drivers/drv8316/drv8316.cpp
Normal 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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
316
firmware/lib/Arduino-FOC-drivers/src/drivers/drv8316/drv8316.h
Normal file
316
firmware/lib/Arduino-FOC-drivers/src/drivers/drv8316/drv8316.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
@@ -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
|
||||
147
firmware/lib/Arduino-FOC-drivers/src/drivers/tmc6200/README.md
Normal file
147
firmware/lib/Arduino-FOC-drivers/src/drivers/tmc6200/README.md
Normal 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.
|
||||
221
firmware/lib/Arduino-FOC-drivers/src/drivers/tmc6200/TMC6200.cpp
Normal file
221
firmware/lib/Arduino-FOC-drivers/src/drivers/tmc6200/TMC6200.cpp
Normal 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();
|
||||
};
|
||||
171
firmware/lib/Arduino-FOC-drivers/src/drivers/tmc6200/TMC6200.hpp
Normal file
171
firmware/lib/Arduino-FOC-drivers/src/drivers/tmc6200/TMC6200.hpp
Normal 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;
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
51
firmware/lib/Arduino-FOC-drivers/src/encoders/a1334/A1334.h
Normal file
51
firmware/lib/Arduino-FOC-drivers/src/encoders/a1334/A1334.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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");
|
||||
```
|
||||
245
firmware/lib/Arduino-FOC-drivers/src/encoders/as5047/AS5047.cpp
Normal file
245
firmware/lib/Arduino-FOC-drivers/src/encoders/as5047/AS5047.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
167
firmware/lib/Arduino-FOC-drivers/src/encoders/as5047/AS5047.h
Normal file
167
firmware/lib/Arduino-FOC-drivers/src/encoders/as5047/AS5047.h
Normal 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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
```
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
225
firmware/lib/Arduino-FOC-drivers/src/encoders/as5047u/AS5047U.h
Normal file
225
firmware/lib/Arduino-FOC-drivers/src/encoders/as5047u/AS5047U.h
Normal 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_ */
|
||||
@@ -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
Reference in New Issue
Block a user