From b7a901951e51e7333b4f62375480ff085d443ca7 Mon Sep 17 00:00:00 2001 From: wanlei Date: Sat, 6 May 2023 19:22:51 +0800 Subject: [PATCH] mcpwm: add foc svpwm generation open loop example --- .../mcpwm_foc_svpwm_open_loop/CMakeLists.txt | 8 + .../mcpwm/mcpwm_foc_svpwm_open_loop/README.md | 107 ++++++++++++ .../mcpwm_foc_svpwm_open_loop/img/6pwm.png | Bin 0 -> 14138 bytes .../img/line_diff.png | Bin 0 -> 14174 bytes .../img/phase_wave.png | Bin 0 -> 11734 bytes .../img/vector_coord.png | Bin 0 -> 11996 bytes .../main/CMakeLists.txt | 5 + .../main/Kconfig.projbuild | 12 ++ .../mcpwm_foc_svpwm_open_loop/main/app_main.c | 165 ++++++++++++++++++ .../main/foc/esp_foc.c | 152 ++++++++++++++++ .../main/foc/esp_foc.h | 73 ++++++++ .../main/idf_component.yml | 6 + .../main/svpwm/esp_svpwm.c | 113 ++++++++++++ .../main/svpwm/esp_svpwm.h | 80 +++++++++ .../pytest_foc_open_loop.py | 18 ++ 15 files changed, 739 insertions(+) create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/CMakeLists.txt create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/README.md create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/6pwm.png create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/line_diff.png create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/phase_wave.png create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/vector_coord.png create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/CMakeLists.txt create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/Kconfig.projbuild create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/app_main.c create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/foc/esp_foc.c create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/foc/esp_foc.h create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/idf_component.yml create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/svpwm/esp_svpwm.c create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/svpwm/esp_svpwm.h create mode 100644 examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/pytest_foc_open_loop.py diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/CMakeLists.txt b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/CMakeLists.txt new file mode 100644 index 0000000000..300e9dc2a4 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(mcpwm_foc_svpwm_generate) diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/README.md b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/README.md new file mode 100644 index 0000000000..1be28172d0 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/README.md @@ -0,0 +1,107 @@ +| Supported Targets | ESP32 | ESP32-C6 | ESP32-H2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | + +# MCPWM FOC SVPWM Generation Open Loop Example + +## Principle +This example shows how to use three pairs of PWM signals to realize FOC (Field-Oriented Control) via the MCPWM peripheral. With the internal dead time submodule, each pair of signals can drive one half-bridge circuit, so that they can be combined to drive a BLDC or PMSM motor, or a three-phase power inverter. + +This example realizes an open-loop FOC algorithm to calculate the target voltages for three phases, and then modulates these voltages into three pairs of PWM signals to form the SVPWM signal (or SPWM if build with menu config `ESP_FOC_USE_SVPWM` off), and finally inputs this SVPWM into six power MOSFETs to get the three-phase power system. +``` + ┌─────────┐ ┌───────┐ Va + Vq │ │ Valpha │ ├──────► + ──────►│ Inverse ├───────►│ │ + │ │ │ SVPWM │ Vb + Vd │ │ Vbeta │ ├──────► + ──────►│ Park ├───────►│ │ Vc + │ │ │ ├──────► + └────▲────┘ └───────┘ + │ + │theta +``` +The FOC sectors assignment and coord systems are as follow: + + +### Risks +These three-phase sine signals are generated at 50Hz by **open loop FOC**, please set a proper power supply, or don't let it work for a long time to avoid any potential **crash or damages** when using it to drive the motor. + +## How to Use + +### Hardware Required + +1. An ESP board with MCPWM peripheral supported (e.g. ESP32-S3-Motor-DevKit) +2. A three-phase gate driver, for example, the [DRV8302](https://www.ti.com.cn/product/zh-cn/DRV8302) +3. Six N-MOSFETs, for example, the [IRF540NS](https://www.infineon.com/cms/en/product/power/mosfet/12v-300v-n-channel-power-mosfet/irf540ns/) +4. A USB cable for Power supply and programming + +### Connection +Using only `delta/triangle` connect to the output. +``` + POWER + │ + ┌────────────────────────┐ ┌───────▼───────┐ + │ Enable│ │ │ + │ │ │ │ R1 + │ EXAMPLE_FOC_PWM_UH_GPIO├───────────┤ Bridge Driver │_________┌────┐____ + │ │ │ │ └────┘ | + │ EXAMPLE_FOC_PWM_UL_GPIO├───────────┤ │ | + │ │ │ and │ R2 |neutral line + │ EXAMPLE_FOC_PWM_VH_GPIO├───────────┤ │_________┌────┐____| + │ │ │ │ └────┘ | + │ EXAMPLE_FOC_PWM_VL_GPIO├───────────┤ MOSFET │ | + │ │ │ │ R3 | + │ EXAMPLE_FOC_PWM_WH_GPIO├───────────┤ │_________┌────┐____| + │ │ │ │ └────┘ + │ EXAMPLE_FOC_PWM_WL_GPIO├───────────┤ │ + └────────────────────────┘ └───────────────┘ +``` + + +### Build and Flash + +Select project Kconfig option `ESP_FOC_USE_SVPWM` by `idf.py menuconfig`: +- True: Using SVPWM modem, output saddle wave, the neutral point level is not const zero. +- False: Using SPWM modem, output standard sin wave, phases and lines level are all sin wave. + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + + +## Example Output + +Run the example, you will see the following output log: +``` +... +Hi ESP_SV +I (327) bsp_mcpwm: Disable MOSFET gate +I (337) gpio: GPIO[46]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (347) bsp_mcpwm: Create MCPWM timer +I (347) bsp_mcpwm: Create MCPWM operator +I (357) bsp_mcpwm: Connect operators to the same timer +I (357) bsp_mcpwm: Create comparators +I (367) bsp_mcpwm: Create PWM generators +I (367) gpio: GPIO[47]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (377) gpio: GPIO[21]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (387) gpio: GPIO[14]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (397) gpio: GPIO[13]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (407) gpio: GPIO[12]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (417) gpio: GPIO[11]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (427) bsp_mcpwm: Set generator actions +I (427) bsp_mcpwm: Setup deadtime +I (437) bsp_mcpwm: Start the MCPWM timer +I (437) bsp_mcpwm: Enable MOSFET gate +``` + +If you have an oscilloscope or a logic analyzer, you can see there are 7 segments in one PWM signal and all six PWM signals are center-aligned: + + +And if you monitor any output after the low-pass filter with the reference of the GROUND, there will be a saddle wave (or sin wave if build with the menu config `ESP_FOC_USE_SVPWM` off), two of them are as follow: + + +Do not surprise if you find it is not a `sin wave` while taking the GROUND as the reference, it is the characteristic of `SVPWM` modulation, because the voltage of `neutral line` is not zero for `SVPWM`. But if you monitor any `line-to-line` voltage (taking another signal as the reference) in this three-phase power system, you can still get the sin signal: + + +Please turn off config `ESP_FOC_USE_SVPWM` if the `neutral line` is required, then there will be a standard three-phase sin system. diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/6pwm.png b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/6pwm.png new file mode 100644 index 0000000000000000000000000000000000000000..f7a1858b1f4d4e715deb9de80f4529bd5f2585a2 GIT binary patch literal 14138 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 E08)1-6#xJL literal 0 HcmV?d00001 diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/line_diff.png b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/line_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..cd61fd82d9a6db8b1c6941f5d62af607aa1459bc GIT binary patch literal 14174 zcmeAS@N?(olHy`uVBq!ia0y~yV0^&9!0?KLiGhLPYZRjZ1A_vCr;B4q#hkaXZ>PT2 zS--R;`p}dKLK=#yOIm^t1s{rT@}422aZokD$!mxGjKCC5gZM{>lXb5}&TO&RFmYQ; zOYM}pfByFWzt{g?zUORhSM-y`o4(cUKiR$e?%TX_@0|CGTYf!>)4Heo@92@&OSZ_* zV?A{AiFDFKktVgrj}?mI{1|_&^7U2z|NFiDbvOPcVfSV)U#lm)&aXcC;hiS?Pm?&s zWgpzUT>j{;)041sJWt+>eDdvIaPo&!K-Kea%Wq|9|2`3NEM8MQIeo&)YaBh-y`zsj zdAw#;#=ki%{>;07{1z1Qf8TuHQr4f%c*1_ppH-J-RZe}IWSV}vykU)h^}klLTc_-d zrhdGB`s7BVKiEzi4Jf&O+@a)e~dtix2TUd9TyN zx6yd{;|c#cJ)f?bmc4lG8<2nZ`>y2P^1XhZSxt!Vtriwp&OJdKx3YhCvb_Ck-Ws>Z zcJF?nUAJEHPt2ZX!2ENzu;KZlIrF}F9Q;_q_%B2{H&uS-sUO)#42xczUz)nglq>Sf z@16z0`=&bS6s#2ZKX0Gf`-=6C-%83yOQ$K+FMl$!Qq4vp@ zMXsDyPEs$w1btgv!!YNCv;p(a-3sio>xDnPyvy=7vMn=3>+yO)kL(H8#V4%1))8IO zG10l!$we^B;!y4HmJ;sL6H4Aex3QtVa>;!V=t{IW-j;n$N%Y5y5zEnD|`04 z)qLFvnp-!2?h=c@ih@A z-Uh{M%XTLbZp9beCt6k;-zf|7F#F`%8=>(X~^U-q$V^XI$Qazl6Re)}GrEO$rO3M;;7 zuW}6bdU*Z(@-`RII(fT8_glZ!WWE1Axu)eg>t)8m`%0gmW)v*=u>HewW1pWQyn8lP zG~Ek5CNHRA5b#y`i=EC&X(`3K9JSpJ?S?Y@PED29oPR*8c1D+Wq@4CYH5Fql~BAE|X(Cdi8?C zn>*57#|^x+W@Pd^=(W2^JKT9~$nrNMp~XI;W@UT8>La`KRZlt<|7&^u{!jjYZ(rHA zTK0R%T=T7egZ~#ims(@}>Q4HWsLZunoSV-~T)c2R*!|hJqHZSYH(g_yaDip+KkGFdRrk*EX)lx3 zUfFKk!cl5rrZBgBYqhm?vSpC_O^)3HB`Wc8okoWCi+hxnmz&(E+jsT*=aq9aEq4{J zFW)Mi>XE+PV@I*-wTh;q44Vm$3!@$#d;jdj#R3^#pSKq_pAvdisKL10+9B)B24g7) zy*$UYo9@55UHha~j6LDeg64Atrr(z}cklerR9N?HMu*$djTUxCf35F7^0y}IyWKT` z<;*Sxs&b8; zqR<4v4}#141ss(wtfo_hugiHB%+YY()*XoB1-P~ z1Wu=(N1Ju*rF?Cs?Ww9MVZQL-9ecT2j=%<;zaLmld~8o$jObjj@epHyo$MlRW|zrV zZ!W+5`Nf=LvpL`G3RbH)rr&-pVN0sq>V8(C=WpjGbSw~FczE{IFt-_(HXn#cSj>6k zp|bL-K)nT)-Hi4b4;q%;NcNQGzRtTXlxxw$H`*JGIr&c6TwBEQwzL0|$fLzRFXt9s z2oy8xdL^@h`<(8)Kp#t~2OA?|Gq_CT+@HVYZs(uN>G*t6qw;Yzhz%R^TrNC_=Q%Rf z&?Mn_=ac}JUZ$@Ng^vQ5*G66HT3&wa+l*!58#&hnseGw;BQp25ESu%j!hg{g`~_!z zOIwfXa4nIT3>k^4c=VOu! zvt78F&z3eXo+za8CA9x~_<{~~7g-C=GM}^47i{EleC)j2y~%HGncwdUr@BpEPQ6?u z=RPuNeiyxxE_6}B&4S;t+I+*g*pmxB{<3=Z^!0WTP21vL*6W$47TwbMbMoWznTsTS zudQ~dF1~nRf9Oi-*McT14|py3^TX81VpV8Di);y>eDmazaQh$0O>X}j`dypz6Mipn z>;1Yq2E}x9_0F^p?^d0T z`~2Mfq~Qjs@~sXN1!@u=)MUTet!dhRq5qM!Xr!eEe=4WRzWRRurOO|8UwjZ4^zNQ7 z2k-mBR+s0ooot#6PkwpKNWQG}@Zkn=>x~L6ZRy?bBC6pPQ_ z_H?;Wk$qKr|Hq%Pi=+>4pB-VoY1MJx3kv2+X=|POIdprlHOJyXxEqHSWgx z@C@&XJ{RUa=Bl0Zy;(J`nBR_bh5Mb#?!}IIOEj+ansP{;-y9zBz}|53Jb!^pmU*19 zO-<9y^*rteooMwq68^RD-oki!j|(a3wp|Mf&&-fBGYjw6zh9ec%ehPK`Q_#X(xOc^ zg~S4;ex z9ZYXt1YcP!$1AsAT%csN(!=TR4Jvl+UggsKyk=i-0Vwo7<^{C_LEiKf|>QSKM!%hooo+m`W<#XISXLZVK= zCC&vew(YL^QDpa$`Guh4c88zB2R@nnFKBw_?-^%e6_s!yewoBV2FLnUJDfRwy?OEQ zWY-qej6J(QsWMwHcxZWZ(!8LIwQ}uT_79KGJ9g-Ad7g=}zT;sY&wX2e%-nobqn&%x zM#B@VtT))Iq7q*8EmUNA8`?kbcc=RTu`74#)rCy#lP^E?kd${;c)mJ%;lxWbCp_kO z{+CO3+I|s7argDPf&Y&G%Ws`>l)@-x-|(ojbbnt|s@e`ENEk|5HVv#AfdW z>0YMkH|H$ad*$Nv1&`UJqo>HR$e!G^yMK?y<9xv>cmM0Xn0xPK{?!_nMJhk?|Ncmt z%>9V-+G0nUP0oi`7t3CFaQK0nMi(3B#7p8!zq+>v_kWO2DAIi@-eRe?$dTb;27|bT ziP`L`Ah1>oxXt0uzx~IJGP{y7Ui(337KXTj6zqQx0c;bd{f818H z-I_10>iOnYhq;`P$b;@va^>$G3uZ5{VRmfiyFGVZd9c?fkt3Hb&lSo@Xkt@-)5&Jfb8oW+ z6Yw&Dc=77d$;e2aBbhA{a&)YTfk&Lx5iOXZpOb)S6d!o zzigxNAx*+P@KW2nrPDW=oW1xUeTLhIKugPQsxLNa#PLmV_;QP(_wuQ>OWbd-91gyEzP2xP_cG_14PP?Sj#+bh9+|Z-_sQB{eGAS{_%V0Q%PAq1 z?`6uvQ=_-JWn{>hsm)uE(dTy1Qz+2nLuB6?UDu~`TjJmBPuD(OYq~{b-(4Ht=a(;b ztaiVd7S6}}{&B(O#WOv-TjGz;S^u(5d_uX$PW7}ZR@UST4_IW)o1bMQXzbf9Z?2M2 zpltJX!CIw^_i@Dm2Yy5@xTBo6_R8b#sond2-#ataXvyU{{{0-ggi?#L7JZI&5S(%{ zZWFU=s8CPnh%8`&Js2QVFS{*_dE`bXH)LynA=r!UT2@bi7&-B{bc=&pGzZ++SNO7HbQ zp0+68#JT@F&;8g$la5PjiwqSVtGnbDJ1?33{o|%W<($>U7JDu3Fv)97I;t&ND9y{c z>&L|9moh$hZ1X%Lp`P(>&+&Jr@~>RqeP(f3^4I)ctfNrTj?Zf6MZ8ujepPt!$mxQ( zR(JCv@x^!aZpz!-o$BN{HDkgr|Bb9>F3MjVzFaUeb$nc?@?zGjjVw|her#DYW8*T9 z6P&v$*54H2{V(`nO2)Ef9zTxuC5T(Uf0j|RaaYulE<>ryt{39hyfA!qKtKE64u#f9 zS{D^qY^#h5C8P~IFWiuNr+%RFG4GoKhkZ3m52+a@&sbnveO8gZZf{0I9)m*kkCSC< zdsq*(dOg3p>!Oyc&FPEEO?s{~#cbWCFImaz;9>^1bWI(VUfX!0E?imM<#jem`72_sC!K zEr~3$k>QWh)gCHK+`ImfF|A0IW769~d#kDE8U>sDMbGxvZ)7f;%+gb{H(|n}j1PCO z^1S&IpufYt`A(W<7hB=I%}2`)NIS?*7TEGH*YolL#>XnGhDPE}7bfmlP#0L3`|$q5 z|I@Sn7qey*DEox$el-8^7jypuC)_z!PwQ;+Gn?jI$9(6B$+S;O<|cE+u4+s31%6y> zc$r~QrI60ig7stbs+{`Ek2i!P_q;ryJWQChDY=f7O4!|H8Y@W7jv9RqaZV=#BY$V7E3)^~pUd z7aqvHHaX;>CFGAlx>dY7Vb<+NhKGzd{g@G7l`gaV{Ny_ioK8bjK4hJ`NOibc?G-Gk0p;A61MW6@w1Q6Fy5F~vrVK?b~$@* zPE*?o%R?7$|7%+~KkEZaH#t5;tCqh=i?euYXO;ZBLnT(hFxLp+pholahQy8NAv5SS2jGl z!B@Uj*ksEkzx0YtTjc~iO-ww`9%eE8oMh6m-sn_B>c-1Oyjx!U{=V%07N+we88x!` zg%5UpTq+k~ZlJ)Ybm`{a^ZvHy*GlUPxhLOM_%%OD<%y1rr%!&??Qh?|Z=Cd4$=7_= z_NvEeQ(KKnoVHcXTjuHUVohQA?((LH#svWzR^Qb+Z9Vx!2CIpNf`Hyc&Rv#5_nCJc zw4Q97a@-*6$CTvW7j7Q54(4}Q3iDdZ*^H09@Rha}{;II+VQk(Eg^RrDI(s(v&l11T z%ijIaDd&Pk6I+woKE2htOdt6#u)J>icIg#QSLlTd<`ZkBHFp&1_aB{JJf+Ic zRPH3RZ9JP@mCSsuXLg6g4?eOx^mn1_D)y99AM+2!7)TjJ{?YxzcU)j``*T+{?$3{m z56KIfoOp0jeD(sti@b|eVsZ`I{sigQ&M=z!hBS6>E9Eu(Z2Vk@m-{qS87M2wS9`@4nGxajNidzEM|8f?euab6 zUa%cJ;;p%`-ziw_S6Exym0qnl(T5!K%B=UEczMIB?1qwC}YnuvD9Sk%O@* z-MnW(^)>;4xEU4c73zGi7xDV5XP22vg|n*U>EE08L9qUL6>pl?GMWEgk_t`z(z1Pz zwPOqv4=mO9nRzZ&s5i+W&m_!6xxC3-%J;$#VZkpEQ^d1sH8Q7`Ee2Tx^N=b_TmbbCrP^u_g7un9Auu{xOI2WMj!VJ=a(P*k+t^U z-{4~_YQwL|&GSrsdtRA!``gR)x9jey%>DlN`QQ5g8rSZ>Ss4uU9;;4Zm&XX0CNn;nl`m`(vN# zg8!Bmw_93$`F8j6%^C8tmw(Nf`b@KX@0Y@#U(W0Fd|dGG%l(-Q+jPzuFOZIsXVARd zo0rk?cl|y0+~OZnj~|AmPg~c#IJ08!`{#2RUmK<0U`)-aJK&P{=h`JstGi70Ec$bH z*44{fHK!h8wJ+1|cjHxXF~8&UJy!DZOoh0-0G{g}=?*71M)==d;m*9QVfwrP(TnvL zc&{hyTi05me8ekPJt9*l>c5UsT%nDl1GDqPXRobAGC|l+*)7@=>|3}Uw6Q#Ku7lv&LlJ|P_=FKySSqnaAvGCgX z3iT(qY&?F7bqTZ7k??~jHZK0O$dTdVt^4uHrw=VGm(>1hBDP36rP*VK`1hV*_6E0Y zsl|&v3aMtSDZa5c`6{VOh_gen;hqhhIg>ByQgtxt|XE)*c_4o&e zdws=$dz`P7FBa@pn!9mrQ{EG|K3NVaww_Yvnot(rHGf)q7TA|PjQe!zYD#AE!Hvc{ zBJ`yvsFY@SpS^gv>j?)}PqvfE7w4V@ijpoiHp_XOOj=Gkul`~2j$M&cHLK;Q)$}9P+)$`(Uwv)RFtn zCd~IIF)uk#DSWr!oVDDON%=>ulfOQ`VkjzQ5Oq*#p*e$1?7S(y?BC`Hh*$I$v zG9J{-5s=#XcjIoQbI}~UJ{Km;@((|nmoedz_b+`lvjz5k7bZ(^`vpG;J$<$J$WhhX zzI#s3XXKruC$65MySP~Y38$qhr(|N`0gp}Rg);VhQI|RVbE~HYXS>Ie1F8@A#M>#i zl$^53cyQKb+HNi9<~c7eO!{X%eZkj2^Cx$%zP>ImaQDLH0QM&@j&Meaq(+42Ppr~8 zWzl)2e^ZIG^7n`>GO7os@k%c=ky2F7vDJRnx=8O^ zC-sBR9GxdDSolS;{Zn_KkoloNSEmn+ef=_00bc}MnZs9j-bzo1{C3yDqnx| zG~im}=JT=_G|stj2MYPQB{m6^hOaL;r(P)Pa`A$DxYpkJ6+g`)K1*;<={x<+DmBCA z0%KOup)4IKsh4dEj^6A&`eEE!4;%yM{7Qda~C@^=2KoN`YIjOQ6*=-jtSaybUCM%vSRPf3y|LP>Y#z=W; zarlk`)e62{&8&x*CSTCm8=@gA!lRh6X}!OB9jDi!mlYAq_;#-jPiUUHPhb(t-&!00 zX%o%ze(-2HI%CWj3k5=t*TnD#wTo__K8M~<|^ zm$|DNxof>wE)o0`aJ2V=w`cCfia+j`JwLm{SsPV6jS&Om>HxSABle9%n}$scrR(r2k&5U_EtfXS#Ftg$G9& z7V5fMN&Q`Y{Kxgqye0X6RWDq)Q+vu*N3-ziYzAMBsE$dOO8MA(E-inzm-&I{&MAy0 z%a=@G{pR8<&uL}LtNUc#mT#|DZTVcMSRc7SdS}Cu#g1Xi{oi)!uxkeLb-t|XggFszrgxEkZ17Z}M{7by^bs<3(bZAlFXqTAQ8sFUKD261Xd|>F$wp zyPyB$?Q5I9pgn+{-z1fDSF>~ttBwDo#|bJ;O#+tR93P983fXF&lwSCSXW|Emid)aG zaJ1eLjhsI3ot8hojyTl4CiM)l$)mlHWySz8uVFZ%dNa^dQp z?XHiv*e`J8sN#@nSrE&*OkL_o<>7=o231!19($6!yd>S&o8I-fy-4xy>hcO;mnr)( z<#H>B)!&mrYXh$B^YEQt>BKo#+3kjK9!0;?DQ-ZxSj|kyEZrn6$^b>eG%z^DxSDKn5Al$tvI`~SzycK-fn>}tGfl4MJOm9QMCHb<>I-#In%?_<@u${I!+lc z{!~qA+5Kr=-}|0puV3uHy!iSv7T=QO6Lm^;H`Yz9O0mqJlP0Jm^vz|aztD9Vs|7Ok z0fF@{D z`{$Lp--XvFn|xk*Ztl~XS-Z6-v6pAWXf^3+9NOY!IpzB6$~zZ!ze@4=%PF-w_Wb7u zJ032VnXtjoXkJ*x2kBXf<)$pX;$3M5B6pMAJlM`Hkk)@mKCOusII!T8U4!48##h0f|IwpHpz%n^&&SXO!8RBap!8p-3!P6yO_vb-|po6-zvZ9*!kuu>qQ**PY{&){IDbJ zczI9>pH4tOpG5p|^}q)Yk1k%~Ise@cUk>zI<3C!d_{%Bad+#CmIcoJ z4HKeuCiNZPWyq=e@^PU2#oewQyJso>J|o_JCFjY72Ui~IxW><0P-x|Oi=!~8^~Ie6 z?iyK5&ecr=TxFrN->JkHuF?Bn>jb&c#d;pBY3PJgf0hb@9HC^Rf7xi%{= z;W?*7>|squ1{d`o?@LSy($BaHFFM%&%4wSNbgQ5X2RJkGSH79W(%UPbZuRJti`LGl z%KUaS0Z!qi4Tsb#tFm0vc+T_?GJH*DOVeB`RLeD8-V$&H^LEb;Y9f3!L+nBV3~ zNq?&*TY2AWp_@@JGmRQ$1k9Zza;-`ezL#Z1{s>ujJ$A0}m)~psZs)L^Rb;lla%-{A zjQB6>@3vV;m8%)PoKxdc!gTz1oJm~IT+4;k3#(<$Z~xSC%Z7I{cg`-u#;Wj`*UbA> z%I3XI;J?nh=lG-JhR(t)-KGRHW=TTX;@z>u0|S=O^BIaOADP;)RhP-Q6E| zcwV^Bn6#<8a#}2JL%=jpkc~kqfU&4ngE88N(45xknZQPS4 zX!7huCC9Dq($8$sGaed?GO38~ZM`tzTJO^)^1H;W=~#sKmWRQU z*;K^$J`A4iZXV|1EHdF?@N7xmj!h5e+PhpR-+lY;JG0!+auX(d753~^=D20sQ!Vk` z;9cSxBOW2gbaBqdEVhXgKjeWFie&|9@)e!Q`(g9GZ2RuJxktmiFBFy?V|mLGx5Ojw zxoVWWq}|yP69cs$vg?eGzTlmoX2ns{CuC(@b830RiL1dn$tT;ww!p2>NUB_;*%+-)#T^D2;K0Ds;_?fdhPp!0_ z1ci)VbFOspQ!+jp)%ag$$CX`&uFhjPWS^^A-?U(FQvcij({(P%^47C1Cgpv#=-PYH zA<25tM|aL!Mkzn7>)qe$#DqO8ov-tx2_t5*`GU5%%!W(4oO zZqU0;YjVe0zgsuB124t(x*gWY+!11x-?}ePiZ^q?jI8?PTY<+@Ijiy$oqli~6`$$I z!^P$E(zWc$G_lJ{#|*_!{k)OoWGMbA;_JESt&90?DlOa|BC(Qr%dZoEqRxF@>1;K< z$nkK=_QMZU?TTJ~s>zBjbUeJR?(p-_Z-Uo&g9{z2?}YNq55MWo_Ofl|js>n6Z-mdR zexux6a@bq8Q{2SeVN+Y<$Cc+7WtH9xv5n6Q412w5 zo@r%lxy5xuY~l}7PO0ZteC{swxjf~+fZ^M|e>1*jFJJajL@PVn*kiJ+ZA{iz7x%-O zGsG&7b_B zOBbuyRNQ{ll=gH^zh&NG-)`=8-WQ&&kY0AwG}z#fXTM<*?2R<@ zGI}H8CA@L%*R}l;jSYdat7X@&yXh_^w(f^%_r)DLj@!42?S5vqEi`w-`P8LHPb^t= z&DB{cG<5=_*`6HsjZBU+c5+H}PgHsFRA{~1ng!|Ccowz##mq20Uv%QC(3|&1uN#SJ z3%9&2m3zI3C3jZb&dQZ*n`bOLF8X8Y+pOJOip!IxxjJhYCto)a(|(+!z4CF8tCO6_ z7OCcy-ZywPYd_41I-K~RwszgE-c@qRO?-EsMX8y(S%~rewtlc}S0m%eNrA3TMi1H! z8!Y|zyUEpQ=Yj?2zurF8$EKs0wz77wpkI(@{Qk8PKQG=|`s&ppP3z#ozq1#p@2ZOi zIeI6jROe34UmBN8r_Nr$AI-V>jlYSA_ItlKRv9G)>r^rgHh*zUo|q+n`f731#H1QFSSCVbrDs)-(Ttvr$T=7>MTyLN47UA@Dcs{Ftq0if2 z)9u#e N>t(Hl@t+Y@$IHkoo>*X}p;&TdH)G}-ku@eP-y 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 Dp)0#$ literal 0 HcmV?d00001 diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/phase_wave.png b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/img/phase_wave.png new file mode 100644 index 0000000000000000000000000000000000000000..d0e9a63ad941580200ba2201700aa8e36569a32c GIT binary patch literal 11734 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 +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "driver/gpio.h" +#include "foc/esp_foc.h" +#include "svpwm/esp_svpwm.h" + +static const char *TAG = "example_foc"; + +#if CONFIG_IDF_TARGET_ESP32 +#define EXAMPLE_FOC_DRV_EN_GPIO 4 +#define EXAMPLE_FOC_DRV_FAULT_GPIO 5 +#define EXAMPLE_FOC_PWM_UH_GPIO 12 +#define EXAMPLE_FOC_PWM_UL_GPIO 13 +#define EXAMPLE_FOC_PWM_VH_GPIO 14 +#define EXAMPLE_FOC_PWM_VL_GPIO 15 +#define EXAMPLE_FOC_PWM_WH_GPIO 16 +#define EXAMPLE_FOC_PWM_WL_GPIO 17 + +#elif CONFIG_IDF_TARGET_ESP32S3 +#define EXAMPLE_FOC_DRV_EN_GPIO 46 +#define EXAMPLE_FOC_DRV_FAULT_GPIO 10 +#define EXAMPLE_FOC_PWM_UH_GPIO 47 +#define EXAMPLE_FOC_PWM_UL_GPIO 21 +#define EXAMPLE_FOC_PWM_VH_GPIO 14 +#define EXAMPLE_FOC_PWM_VL_GPIO 13 +#define EXAMPLE_FOC_PWM_WH_GPIO 12 +#define EXAMPLE_FOC_PWM_WL_GPIO 11 + +#elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 +#define EXAMPLE_FOC_DRV_EN_GPIO 1 +#define EXAMPLE_FOC_DRV_FAULT_GPIO 2 +#define EXAMPLE_FOC_PWM_UH_GPIO 3 +#define EXAMPLE_FOC_PWM_UL_GPIO 4 +#define EXAMPLE_FOC_PWM_VH_GPIO 5 +#define EXAMPLE_FOC_PWM_VL_GPIO 10 +#define EXAMPLE_FOC_PWM_WH_GPIO 11 +#define EXAMPLE_FOC_PWM_WL_GPIO 13 +#endif + +#define EXAMPLE_FOC_MCPWM_TIMER_RESOLUTION_HZ 10000000 // 10MHz, 1 tick = 0.1us +#define EXAMPLE_FOC_MCPWM_PERIOD 1000 // 1000 * 0.1us = 100us, 10KHz + +#define EXAMPLE_FOC_WAVE_FREQ 10 // 50Hz 3 phase AC wave +#define EXAMPLE_FOC_WAVE_AMPL 100 // Wave amplitude, Use up-down timer mode, max value should be (EXAMPLE_FOC_MCPWM_PERIOD/2) + + +void bsp_bridge_driver_init(void) +{ + gpio_config_t drv_en_config = { + .pin_bit_mask = 1ULL << EXAMPLE_FOC_DRV_EN_GPIO, + .mode = GPIO_MODE_OUTPUT, + }; + ESP_ERROR_CHECK(gpio_config(&drv_en_config)); +} + +void bsp_bridge_driver_enable(bool enable) +{ + ESP_LOGI(TAG, "%s MOSFET gate", enable ? "Enable" : "Disable"); + gpio_set_level(EXAMPLE_FOC_DRV_EN_GPIO, enable); +} + +bool inverter_update_cb(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_ctx) +{ + BaseType_t task_yield = pdFALSE; + xSemaphoreGiveFromISR(*((SemaphoreHandle_t *)user_ctx), &task_yield); + return task_yield; +} + +void app_main(void) +{ + ESP_LOGI(TAG, "Hello FOC"); + // counting semaphore used to sync update foc calculation when mcpwm timer updated + SemaphoreHandle_t update_semaphore = xSemaphoreCreateCounting(1, 0); + + foc_dq_coord_t dq_out = {_IQ(0), _IQ(0)}; + foc_ab_coord_t ab_out; + foc_uvw_coord_t uvw_out; + int uvw_duty[3]; + float elec_theta_deg = 0; + _iq elec_theta_rad; + + inverter_config_t cfg = { + .timer_config = { + .group_id = 0, + .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT, + .resolution_hz = EXAMPLE_FOC_MCPWM_TIMER_RESOLUTION_HZ, + .count_mode = MCPWM_TIMER_COUNT_MODE_UP_DOWN, //UP_DOWN mode will generate center align pwm wave, which can reduce MOSFET switch times on same effect, extend life + .period_ticks = EXAMPLE_FOC_MCPWM_PERIOD, + }, + .operator_config = { + .group_id = 0, + }, + .compare_config = { + .flags.update_cmp_on_tez = true, + }, + .gen_gpios = { + {EXAMPLE_FOC_PWM_UH_GPIO, EXAMPLE_FOC_PWM_UL_GPIO}, + {EXAMPLE_FOC_PWM_VH_GPIO, EXAMPLE_FOC_PWM_VL_GPIO}, + {EXAMPLE_FOC_PWM_WH_GPIO, EXAMPLE_FOC_PWM_WL_GPIO}, + }, + .dt_config = { + .posedge_delay_ticks = 5, + }, + .inv_dt_config = { + .negedge_delay_ticks = 5, + .flags.invert_output = true, + }, + }; + inverter_handle_t inverter1; + ESP_ERROR_CHECK(svpwm_new_inverter(&cfg, &inverter1)); + ESP_LOGI(TAG, "Inverter init OK"); + + mcpwm_timer_event_callbacks_t cbs = { + .on_full = inverter_update_cb, + }; + ESP_ERROR_CHECK(svpwm_inverter_register_cbs(inverter1, &cbs, &update_semaphore)); + ESP_ERROR_CHECK(svpwm_inverter_start(inverter1, MCPWM_TIMER_START_NO_STOP)); + ESP_LOGI(TAG, "Inverter start OK"); + + // Enable gate driver chip + bsp_bridge_driver_init(); + bsp_bridge_driver_enable(true); + + ESP_LOGI(TAG, "Start FOC"); + while (true) { + xSemaphoreTake(update_semaphore, portMAX_DELAY); + + // Calculate elec_theta_deg increase step of 50Hz output on 10000Hz call + elec_theta_deg += (EXAMPLE_FOC_WAVE_AMPL * 360.f) / (EXAMPLE_FOC_MCPWM_TIMER_RESOLUTION_HZ / EXAMPLE_FOC_WAVE_FREQ); + if (elec_theta_deg > 360) { + elec_theta_deg -= 360; + } + elec_theta_rad = _IQmpy(_IQ(elec_theta_deg), _IQ(M_PI / 180.f)); + + // In FOC motor control, we usually set Vd for alignment or weak-meg control, and set Vq for torque control. + // As here is open loop output, use Vd is enough, and coord aligned + dq_out.d = _IQ(EXAMPLE_FOC_WAVE_AMPL); + foc_inverse_park_transform(elec_theta_rad, &dq_out, &ab_out); + +#if CONFIG_ESP_FOC_USE_SVPWM + foc_svpwm_duty_calculate(&ab_out, &uvw_out); +#else // Use spwm (sin pwm) instead. (see menuconfig help to know difference between SVPWM and SPWM) + foc_inverse_clarke_transform(&ab_out, &uvw_out); +#endif + // Regular uvw data to (0 ~ (EXAMPLE_FOC_MCPWM_PERIOD/2)) + uvw_duty[0] = _IQtoF(_IQdiv2(uvw_out.u)) + (EXAMPLE_FOC_MCPWM_PERIOD / 4); + uvw_duty[1] = _IQtoF(_IQdiv2(uvw_out.v)) + (EXAMPLE_FOC_MCPWM_PERIOD / 4); + uvw_duty[2] = _IQtoF(_IQdiv2(uvw_out.w)) + (EXAMPLE_FOC_MCPWM_PERIOD / 4); + + // output pwm duty + ESP_ERROR_CHECK(svpwm_inverter_set_duty(inverter1, uvw_duty[0], uvw_duty[1], uvw_duty[2])); + } + + bsp_bridge_driver_enable(false); + ESP_ERROR_CHECK(svpwm_inverter_start(inverter1, MCPWM_TIMER_STOP_EMPTY)); + ESP_ERROR_CHECK(svpwm_del_inverter(inverter1)); +} diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/foc/esp_foc.c b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/foc/esp_foc.c new file mode 100644 index 0000000000..10ce368440 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/foc/esp_foc.c @@ -0,0 +1,152 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "esp_foc.h" + + +/** + * alpha = u - (v + w)sin(30) * (2/3), (2/3): Equal amplitude transformation const + * beta = (v - w)cos(30) * (2/3) + */ +void foc_clarke_transform (const foc_uvw_coord_t *v_uvw, foc_ab_coord_t *v_ab) +{ + const _iq foc_clark_k1_iq = _IQ(2.0 / 3.0); + const _iq foc_clark_k2_iq = _IQ(1.0 / 3.0); + const _iq foc_clark_k3_iq = _IQ(M_SQRT3 / 3.0); + + v_ab->alpha = _IQmpy(v_uvw->u, foc_clark_k1_iq) - _IQmpy(v_uvw->v + v_uvw->w, foc_clark_k2_iq); + v_ab->beta = _IQmpy(v_uvw->v - v_uvw->w, foc_clark_k3_iq); +} + +void foc_inverse_clarke_transform (const foc_ab_coord_t *v_ab, foc_uvw_coord_t *v_uvw) +{ + v_uvw->u = v_ab->alpha; + v_uvw->v = _IQdiv2(_IQmpy(v_ab->beta, _IQ(M_SQRT3)) - v_ab->alpha); + v_uvw->w = -v_uvw->u - v_uvw->v; +} + +void foc_park_transform (_iq theta_rad, const foc_ab_coord_t *v_ab, foc_dq_coord_t *v_dq) +{ + _iq sin = _IQsin(theta_rad); + _iq cos = _IQcos(theta_rad); + + v_dq->d = _IQmpy(v_ab->alpha, cos) + _IQmpy(v_ab->beta, sin); + v_dq->q = _IQmpy(v_ab->beta, cos) - _IQmpy(v_ab->alpha, sin); +} + +void foc_inverse_park_transform (_iq theta_rad, const foc_dq_coord_t *v_dq, foc_ab_coord_t *v_ab) +{ + _iq sin = _IQsin(theta_rad); + _iq cos = _IQcos(theta_rad); + + v_ab->alpha = _IQmpy(v_dq->d, cos) - _IQmpy(v_dq->q, sin); + v_ab->beta = _IQmpy(v_dq->q, cos) + _IQmpy(v_dq->d, sin); +} + +void foc_svpwm_duty_calculate(const foc_ab_coord_t *v_ab, foc_uvw_coord_t *out_uvw) +{ + int sextant; + if (v_ab->beta > 0.0f) { + if (v_ab->alpha > 0.0f) { + //quadrant I + if (v_ab->beta > _IQmpy(v_ab->alpha, _IQ(M_SQRT3))) { + sextant = 2; //sextant v2-v3 + } else { + sextant = 1; //sextant v1-v2 + } + } else { + //quadrant II + if (-v_ab->beta > _IQmpy(v_ab->alpha, _IQ(M_SQRT3))) { + sextant = 3; //sextant v3-v4 + } else { + sextant = 2; //sextant v2-v3 + } + } + } else { + if (v_ab->alpha > 0.0f) { + //quadrant IV + if (-v_ab->beta > _IQmpy(v_ab->alpha, _IQ(M_SQRT3))) { + sextant = 5; //sextant v5-v6 + } else { + sextant = 6; //sextant v6-v1 + } + } else { + //quadrant III + if (v_ab->beta > _IQmpy(v_ab->alpha, _IQ(M_SQRT3))) { + sextant = 4; //sextant v4-v5 + } else { + sextant = 5; //sextant v5-v6 + } + } + } + + switch (sextant) { + // sextant v1-v2 + case 1: { + _iq t1 = -_IQmpy(v_ab->alpha, _IQ(M_SQRT3)) + v_ab->beta; + _iq t2 = -_IQmpy2(v_ab->beta); + + // PWM timings + out_uvw->u = _IQdiv2(_IQ(1.F) - t1 - t2); + out_uvw->v = out_uvw->u + t1; + out_uvw->w = out_uvw->v + t2; + } break; + + // sextant v2-v3 + case 2: { + _iq t2 = -_IQmpy(v_ab->alpha, _IQ(M_SQRT3)) - v_ab->beta; + _iq t3 = _IQmpy(v_ab->alpha, _IQ(M_SQRT3)) - v_ab->beta; + + // PWM timings + out_uvw->v = _IQdiv2(_IQ(1.F) - t2 - t3); + out_uvw->u = out_uvw->v + t3; + out_uvw->w = out_uvw->u + t2; + } break; + + // sextant v3-v4 + case 3: { + _iq t3 = -_IQmpy2(v_ab->beta); + _iq t4 = _IQmpy(v_ab->alpha, _IQ(M_SQRT3)) + v_ab->beta; + + // PWM timings + out_uvw->v = _IQdiv2(_IQ(1.F) - t3 - t4); + out_uvw->w = out_uvw->v + t3; + out_uvw->u = out_uvw->w + t4; + } break; + + // sextant v4-v5 + case 4: { + _iq t4 = _IQmpy(v_ab->alpha, _IQ(M_SQRT3)) - v_ab->beta; + _iq t5 = _IQmpy2(v_ab->beta); + + // PWM timings + out_uvw->w = _IQdiv2(_IQ(1.F) - t4 - t5); + out_uvw->v = out_uvw->w + t5; + out_uvw->u = out_uvw->v + t4; + } break; + + // sextant v5-v6 + case 5: { + _iq t5 = _IQmpy(v_ab->alpha, _IQ(M_SQRT3)) + v_ab->beta; + _iq t6 = -_IQmpy(v_ab->alpha, _IQ(M_SQRT3)) + v_ab->beta; + + // PWM timings + out_uvw->w = _IQdiv2(_IQ(1.F) - t5 - t6); + out_uvw->u = out_uvw->w + t5; + out_uvw->v = out_uvw->u + t6; + } break; + + // sextant v6-v1 + case 6: { + _iq t6 = _IQmpy2(v_ab->beta); + _iq t1 = -_IQmpy(v_ab->alpha, _IQ(M_SQRT3)) - v_ab->beta; + + // PWM timings + out_uvw->u = _IQdiv2(_IQ(1.F) - t6 - t1); + out_uvw->w = out_uvw->u + t1; + out_uvw->v = out_uvw->w + t6; + } break; + } +} diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/foc/esp_foc.h b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/foc/esp_foc.h new file mode 100644 index 0000000000..c478a80d66 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/foc/esp_foc.h @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +// Use IQ18 type, range [-8,192 8,191.999 996 185] +// This definition should be added before including "IQmathLib.h" +#define GLOBAL_IQ 18 +#include "IQmathLib.h" + + +// 3-phase uvw coord data type +typedef struct foc_uvw_coord { + _iq u; // U phase data in IQ type + _iq v; // V phase data in IQ type + _iq w; // W phase data in IQ type +} foc_uvw_coord_t; + +// alpha-beta axis static coord data type +typedef struct foc_ab_coord { + _iq alpha; // alpha axis data in IQ type + _iq beta; // beta axis data in IQ type +} foc_ab_coord_t; + +//d-q (direct-quadrature) axis rotate coord data type +typedef struct foc_dq_coord { + _iq d; // direct axis data in IQ type + _iq q; // quadrature axis data in IQ type +} foc_dq_coord_t; + +/** + * @brief clark transform, to transform value in 3phase uvw system to static alpha_beta system + * + * @param[in] v_uvw data in 3-phase coord to be transformed + * @param[out] v_ab output data in alpha-beta coord + */ +void foc_clarke_transform (const foc_uvw_coord_t *v_uvw, foc_ab_coord_t *v_ab); + +/** + * @brief inverse clark transform, to transform value in alpha_beta system to 3phase uvw system + * + * @param[in] v_ab data in alpha-beta coord to be transformed + * @param[out] v_uvw output data in 3-phase coord + */ +void foc_inverse_clarke_transform (const foc_ab_coord_t *v_ab, foc_uvw_coord_t *v_uvw); + +/** + * @brief park transform, to transform value in static alpha_beta system to rotate d-q system + * + * @param[in] theta_rad theta of dq_coord refer to alpha-beta coord, in rad + * @param[in] v_ab data in alpha-beta coord to be transformed + * @param[out] v_dq output data in dq coord + */ +void foc_park_transform (_iq theta_rad, const foc_ab_coord_t *v_ab, foc_dq_coord_t *v_dq); + +/** + * @brief inverse park transform, to transform value in rotate d-q system to alpha_beta system + * + * @param[in] theta_rad theta of dq_coord refer to alpha-beta coord, in rad + * @param[in] v_dq data in dq coord to be transformed + * @param[out] v_ab output data in alpha-beta coord + */ +void foc_inverse_park_transform (_iq theta_rad, const foc_dq_coord_t *v_dq, foc_ab_coord_t *v_ab); + +/** + * @brief 7-segment svpwm modulation + * + * @param v_ab[in] input value in alpha-beta coord + * @param out_uvw[out] output modulated pwm duty in IQ type + */ +void foc_svpwm_duty_calculate (const foc_ab_coord_t *v_ab, foc_uvw_coord_t *out_uvw); diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/idf_component.yml b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/idf_component.yml new file mode 100644 index 0000000000..086ad7d5ab --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/iqmath: "^1.11.0" + ## Required IDF version + idf: + version: ">=5.0" diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/svpwm/esp_svpwm.c b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/svpwm/esp_svpwm.c new file mode 100644 index 0000000000..f05a1643f3 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/svpwm/esp_svpwm.c @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_log.h" +#include "esp_check.h" +#include "esp_svpwm.h" + +static const char *TAG = "esp_svpwm"; + +// mcpwm handler type +typedef struct mcpwm_svpwm_ctx { + mcpwm_timer_handle_t timer; + mcpwm_oper_handle_t operators[3]; + mcpwm_cmpr_handle_t comparators[3]; + mcpwm_gen_handle_t generators[3][2]; +} mcpwm_svpwm_ctx_t; + + +esp_err_t svpwm_new_inverter(const inverter_config_t *config, inverter_handle_t *ret_inverter) +{ + esp_err_t ret; + ESP_RETURN_ON_FALSE(config && ret_inverter, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + mcpwm_svpwm_ctx_t *svpwm_dev = calloc(1, sizeof(mcpwm_svpwm_ctx_t)); + if (!svpwm_dev) { + ESP_LOGE(TAG, "no memory"); + return ESP_ERR_NO_MEM; + } + ESP_GOTO_ON_ERROR(mcpwm_new_timer(&config->timer_config, &svpwm_dev->timer), err, TAG, "Create MCPWM timer failed"); + + for (int i = 0; i < 3; i++) { + ESP_GOTO_ON_ERROR(mcpwm_new_operator(&config->operator_config, &svpwm_dev->operators[i]), err, TAG, "Create MCPWM operator failed"); + ESP_GOTO_ON_ERROR(mcpwm_operator_connect_timer(svpwm_dev->operators[i], svpwm_dev->timer), err, TAG, "Connect operators to the same timer failed"); + } + + for (int i = 0; i < 3; i++) { + ESP_GOTO_ON_ERROR(mcpwm_new_comparator(svpwm_dev->operators[i], &config->compare_config, &svpwm_dev->comparators[i]), err, TAG, "Create comparators failed"); + ESP_GOTO_ON_ERROR(mcpwm_comparator_set_compare_value(svpwm_dev->comparators[i], 0), err, TAG, "Set comparators failed"); + } + + mcpwm_generator_config_t gen_config = {}; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + gen_config.gen_gpio_num = config->gen_gpios[i][j]; + ESP_GOTO_ON_ERROR(mcpwm_new_generator(svpwm_dev->operators[i], &gen_config, &svpwm_dev->generators[i][j]), err, TAG, "Create PWM generator pin %d failed", gen_config.gen_gpio_num); + } + } + + for (int i = 0; i < 3; i++) { + ESP_GOTO_ON_ERROR(mcpwm_generator_set_actions_on_compare_event(svpwm_dev->generators[i][0], + MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, svpwm_dev->comparators[i], MCPWM_GEN_ACTION_LOW), + MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, svpwm_dev->comparators[i], MCPWM_GEN_ACTION_HIGH), + MCPWM_GEN_COMPARE_EVENT_ACTION_END()), err, TAG, "Set generator actions failed"); + } + + for (int i = 0; i < 3; i++) { + ESP_GOTO_ON_ERROR(mcpwm_generator_set_dead_time(svpwm_dev->generators[i][0], svpwm_dev->generators[i][0], &config->dt_config), err, TAG, "Setup deadtime failed"); + ESP_GOTO_ON_ERROR(mcpwm_generator_set_dead_time(svpwm_dev->generators[i][0], svpwm_dev->generators[i][1], &config->inv_dt_config), err, TAG, "Setup inv deadtime failed"); + } + + *ret_inverter = svpwm_dev; + return ESP_OK; + +err: + free(svpwm_dev); + return ret; +} + +esp_err_t svpwm_inverter_register_cbs(inverter_handle_t handle, const mcpwm_timer_event_callbacks_t *event, void *user_ctx) +{ + ESP_RETURN_ON_FALSE(handle && event, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_ERROR(mcpwm_timer_register_event_callbacks(handle->timer, event, user_ctx), TAG, "register callbacks failed"); + return ESP_OK; +} + +esp_err_t svpwm_inverter_start(inverter_handle_t handle, mcpwm_timer_start_stop_cmd_t command) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + if ((command != MCPWM_TIMER_STOP_EMPTY) && (command != MCPWM_TIMER_STOP_FULL)) { + ESP_RETURN_ON_ERROR(mcpwm_timer_enable(handle->timer), TAG, "mcpwm timer enable failed"); + } + ESP_RETURN_ON_ERROR(mcpwm_timer_start_stop(handle->timer, command), TAG, "mcpwm timer start failed"); + return ESP_OK; +} + +esp_err_t svpwm_inverter_set_duty(inverter_handle_t handle, uint16_t u, uint16_t v, uint16_t w) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + ESP_RETURN_ON_ERROR(mcpwm_comparator_set_compare_value(handle->comparators[0], u), TAG, "set duty failed"); + ESP_RETURN_ON_ERROR(mcpwm_comparator_set_compare_value(handle->comparators[1], v), TAG, "set duty failed"); + ESP_RETURN_ON_ERROR(mcpwm_comparator_set_compare_value(handle->comparators[2], w), TAG, "set duty failed"); + return ESP_OK; +} + +esp_err_t svpwm_del_inverter(inverter_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + ESP_RETURN_ON_ERROR(mcpwm_timer_disable(handle->timer), TAG, "mcpwm timer disable failed"); + for (int i = 0; i < 3; i++) { + ESP_RETURN_ON_ERROR(mcpwm_del_generator(handle->generators[i][0]), TAG, "free mcpwm positive generator failed"); + ESP_RETURN_ON_ERROR(mcpwm_del_generator(handle->generators[i][1]), TAG, "free mcpwm negative generator failed"); + ESP_RETURN_ON_ERROR(mcpwm_del_comparator(handle->comparators[i]), TAG, "free mcpwm comparator failed"); + ESP_RETURN_ON_ERROR(mcpwm_del_operator(handle->operators[i]), TAG, "free mcpwm operator failed"); + } + ESP_RETURN_ON_ERROR(mcpwm_del_timer(handle->timer), TAG, "free mcpwm timer failed"); + free(handle); + return ESP_OK; +} diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/svpwm/esp_svpwm.h b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/svpwm/esp_svpwm.h new file mode 100644 index 0000000000..c4f50c68f3 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/main/svpwm/esp_svpwm.h @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "driver/mcpwm_prelude.h" + +/** + * @brief svpwm inverter config struct type + */ +typedef struct inverter_config { + mcpwm_timer_config_t timer_config; // pwm timer and timing config + mcpwm_operator_config_t operator_config; // mcpwm operator config + mcpwm_comparator_config_t compare_config; // mcpwm comparator config + int gen_gpios[3][2]; // 6 GPIO pins for generator config + mcpwm_dead_time_config_t dt_config; // dead time config for positive pwm output + mcpwm_dead_time_config_t inv_dt_config; // dead time config for negative pwm output +} inverter_config_t; + +// inverter handler type +typedef struct mcpwm_svpwm_ctx *inverter_handle_t; + +/** + * @brief Config mcpwm as a inverter with corresponding config value + * + * @param config config value for mcpwm peripheral + * @param ret_inverter return handler for corresponding mcpwm + * + * @return - ESP_OK: Create invertor successfully + * - ESP_ERR_INVALID_ARG: NULL arguments + * - ESP_ERR_NO_MEM: no free memory + */ +esp_err_t svpwm_new_inverter(const inverter_config_t *config, inverter_handle_t *ret_inverter); + +/** + * @brief register update callbacks for a mcpwm peripheral + * + * @param handle svpwm invertor handler + * @param event callbacks config + * @param user_ctx pointer to user data to be passed to callbacks + * + * @return - ESP_OK: register callbacks successfully + * - ESP_ERR_INVALID_ARG: NULL arguments + */ +esp_err_t svpwm_inverter_register_cbs(inverter_handle_t handle, const mcpwm_timer_event_callbacks_t *event, void *user_ctx); + +/** + * @brief start/stop a svpwm invertor + * + * @param handle svpwm invertor handler + * @param command see "mcpwm_timer_start_stop_cmd_t" + * + * @return - ESP_OK: start inverter successfully + * - ESP_ERR_INVALID_ARG: NULL arguments + */ +esp_err_t svpwm_inverter_start(inverter_handle_t handle, mcpwm_timer_start_stop_cmd_t command); + +/** + * @brief set 3 channels pwm comparator value for invertor + * + * @param handle svpwm invertor handler + * @param u comparator value for channel UH and UL + * @param v comparator value for channel VH and VL + * @param w comparator value for channel WH and WL + * + * @return - ESP_OK: set compare value successfully + * - ESP_ERR_INVALID_ARG: NULL arguments + */ +esp_err_t svpwm_inverter_set_duty(inverter_handle_t handle, uint16_t u, uint16_t v, uint16_t w); + +/** + * @brief free a svpwm invertor + * + * @param handle svpwm invertor handler + * + * @return - ESP_OK: free inverter successfully + * - ESP_ERR_INVALID_ARG: NULL arguments + */ +esp_err_t svpwm_del_inverter(inverter_handle_t handle); diff --git a/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/pytest_foc_open_loop.py b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/pytest_foc_open_loop.py new file mode 100644 index 0000000000..1b8f901822 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_foc_svpwm_open_loop/pytest_foc_open_loop.py @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +@pytest.mark.esp32s3 +@pytest.mark.esp32c6 +@pytest.mark.esp32h2 +@pytest.mark.generic +def test_open_foc(dut: Dut) -> None: + dut.expect_exact('example_foc: Hello FOC') + dut.expect_exact('example_foc: Inverter init OK') + dut.expect_exact('example_foc: Inverter start OK') + dut.expect_exact('example_foc: Enable MOSFET gate') + dut.expect_exact('example_foc: Start FOC')