FOC SVPWM函数PWMC_SetPhaseVoltage解析

本文详细解析了电机控制中如何通过扇区划分和矢量时间计算来实现SVPWM(正弦调制脉宽调制),涉及(V_{alpha}

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、扇区判断

wUAlpha = Valfa_beta.qV_Component1 * ( int32_t )pHandle->hT_Sqrt3;//hT_Sqrt3为2/√3
wUBeta = -( Valfa_beta.qV_Component2 * ( int32_t )( pHandle->hPWMperiod ) ) * 2;
wX = wUBeta;
wY = ( wUBeta + wUAlpha ) / 2;
wZ = ( wUBeta - wUAlpha ) / 2;

ST官方扇区如下所示:

 通过反Park变换得到矢量\(V_{\alpha }\)和\(V_{\beta }>0\),首先要知道二者合成的矢量处于哪个扇区。以待合成的矢量在第一扇区为例,当\(V_{\alpha }>0\)且\(V_{\beta }<0\)时,矢量可能处于I或II扇区,只需求tan角度即可判断合成的矢量在那个扇区。

 最终总结出的规律如下:

由上表可知,判断\(V_{\beta }\)、\(\sqrt{3}V_{\alpha }-V_{\beta}\)、\(-\sqrt{3}V_{\alpha }-V_{\beta}\)与0的大小关系即可,后续还要根据该变量算三相占空比,所以这里乘以了周期T。
            \(\large{wX=-V_{\beta }T}\)   
            \(\large{wY= \frac{\sqrt{3}V_{\alpha }T-V_{\beta }T}{2}}\)
            \(\large{wZ= \frac{-V_{\beta }T-\sqrt{3}V_{\alpha }T}{2}}\)
       1、wY<0:即\(\sqrt{3}V_{\alpha }<V_{\beta}\),目标矢量在3、4、5扇区
                (1)wZ<0,则在扇区5
                (2)wZ≥0:①wX≤0时,在扇区4 ②wX>0时,则在扇区3
       2、wY>0:即\(\sqrt{3}V_{\alpha }>V_{\beta}\),目标矢量在1、2、6扇区
                (1)wZ≥0,则在扇区2
                (2)wZ<0:①wX≤0时,在扇区6 ②wX>0时,则在扇区1

 二、矢量时间计算

这里以第一扇区为例进行解释。

(1)首先需要确保相邻矢量合成的矢量在正六边形边界内,否则逆变器的电压输出波形将要失真。

 基本空间向量的幅值,即相电压的值,大小为\(\large{\frac{2V_{dc}}{3}}\),当非零矢量作用时间为0时,相邻基本空间向量合成矢量大小为\(\large{\frac{2V_{dc}}{3}\times sin60^{\circ}}\)。二者相除,即为归一化处理后的基本空间向量的幅值:\(\large{\frac{1}{sin60^{\circ}}= \frac{2}{\sqrt{3}}}\)。 

(2)接下来就是计算矢量作用时间了

 \(\large{V_{out}\times \cos\theta= |V_{4}|\times\frac{T_{4}}{T}+ |V_{6}|\times \frac{T_{6}}{T} \times\cos60^{\circ} = V_{\alpha }}\)

\(\large{V_{out}\times \sin\theta= |V_{6}|\times \frac{T_{6}}{T} \times\sin60^{\circ} = -V_{\beta }}\)(ST官方以第四象限为基准,所以有负号)

\(\large{|V_{4}|=|V_{6}|= \frac{2}{\sqrt{3}}}\)

最终可求出:

 \(\large{T_{4}= \frac{\sqrt{3}V_{\alpha }T+V_{\beta }T}{2}}\),  \(\large{T_{6}= -V_{\beta }T}\)

发现\(T_{4}= -wZ\),\(T_{6}= wX\),而零矢量作用时间\(T_{0}=T-T_{4}-T_{6}\)。 

 接下来分析一下代码中占空比:

第一扇区的七段式SVPWM的发波顺序为:0-4-6-7-6-4-0

现在我们要求出A、B、C三相的CCR值 wTimePhA、 wTimePhB和wTimePhC,而TIM设置为中央对齐模式,最终可以得出:

\(\large{wTimephA= (T - \frac{T+Z-X}{2})\times \frac{1}{2} = \frac{T-Z+X}{4}}\)

\(\large{wTimephB= wTimephA - T_{4}\times \frac{1}{2} = wTimephA+\frac{Z}{2}}\)

\(\large{wTimephC= wTimephB-T_{6}\times \frac{1}{2}= wTimephB-\frac{X}{2}}\)

  • 同理可得其它扇区的七段式SVPWM的占空比和发波波形:
    • 表中的tA、tB、tC表示整个高电平时间,和CCR值不同。

  • 为何不同扇区A、B、C相的波形不同?

首先来看六个扇区的定义,这里不是按0~6顺序排列的,而是保证相邻两个扇区仅差一个位,这样可以减少MOSFET的开关次数。

 最终可以总结出不同扇区具有如下切换顺序,用二进制画出来就对应前面不同扇区的SVPWM 7段图

扇区                                  七段SVPWM顺序
I                                   0-4-6-7-7-6-4-0
II                                   0-2-6-7-7-6-2-0
III                                   0-2-3-7-7-3-2-0
IV                                   0-1-3-7-7-3-1-0
V                                   0-1-5-7-7-5-1-0
VI                                   0-4-5-7-7-5-4-0

最后再以第一扇区为例,分析相关的配置代码:

static void MX_TIM1_Init(void)
    TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_CENTER_UP;
    TIM_InitStruct.Autoreload = ((PWM_PERIOD_CYCLES) / 2);

uint16_t PWMC_SetPhaseVoltage( PWMC_Handle_t * pHandle, Volt_Components Valfa_beta )
    pHandle->hSector = SECTOR_1;
    wTimePhA = ( int32_t )( pHandle->PWMperiod ) / 4 + ( ( wX - wZ ) / ( int32_t )262144 );
    wTimePhB = wTimePhA + wZ / 131072;
    wTimePhC = wTimePhB - wX / 131072;
    pHandle->hCntPhA = ( uint16_t )wTimePhA;
    pHandle->hCntPhB = ( uint16_t )wTimePhB;
    pHandle->hCntPhC = ( uint16_t )wTimePhC;
    pSetADCSamplingPoint = pHandle->pFctSetADCSampPointSect1;
    return ( pSetADCSamplingPoint( pHandle ) );

static uint16_t R3_1_F30X_WriteTIMRegisters( PWMC_Handle_t * pHdl )
    TIMx->CCR1 = pHandle->_Super.hCntPhA;
    TIMx->CCR2 = pHandle->_Super.hCntPhB;
    TIMx->CCR3 = pHandle->_Super.hCntPhC;

(1)CCR直接等于hCntPhA、hCntPhB、hCntPhC

从上面七段SVPWM的图中可以看出,a、b、c三相的的PWM波形为中央对齐的:

在向上或向下计数模式中,PWM周期等于ARR;而在中央对齐模式中,PWM周期等于2ARR。

这里中央对齐模式的计数顺序为从ARR~0~ARR,而非0~ARR~0:

①从ARR减到CRR:低电平

②从CCR减到0:高电平

③从0增加到CCR:高电平

④从CCR增加到ARR:低电平

(2)hPWMperiod和T

hPWMperiod = T。在中央对齐模式中PWM周期为2倍ARR,所以TIM_ARR设置为了hPWMperiod/2。而且前面我们计算T4和T6的时候,公式中用到的T表示的就是一整个周期的T。

(3)131072和262144

前面我们计算出wTimephA、wTimephB、wTimephC,如下:

\(\large{wTimephA= (T - \frac{T+Z-X}{2})\times \frac{1}{2} = \frac{T-Z+X}{4}}\)

\(\large{wTimephB= wTimephA - T_{4}\times \frac{1}{2} = wTimephA+\frac{Z}{2}}\)

\(\large{wTimephC= wTimephB-T_{6}\times \frac{1}{2} = wTimephB-\frac{X}{2}}\)

由于ADC采集的电流是左对齐的,所以是\(Q_{15}\)格式的,计算PWM比较值时要转为\(Q_{0}\)格式,所以这里计算需要先右移15位,即\(2^{^{15}}=32768\)。再加上wUAlpha和wUBeta定义时多乘了一个2,所以X、Z还要多除以一个2。

另外\(131072 = 2^{17}\)、\(262144 = 2^{18}\),所以我会把代码里的除法改为右移17位和18位,从而加快运算速度。

<think>嗯,用户让我解释这个`st7789v_probe`函数。我需要先仔细看看代码的结构,然后逐步分析每个部分的作用。用户可能对Linux驱动开发不太熟悉,所以需要尽量用简单的语言解释清楚。 首先,这个函数是SPI设备驱动的probe函数,当设备与驱动匹配时会被调用。函数名中的`st7789v`应该是指某个显示控制器,比如ST7789V型号的屏幕。probe函数主要负责初始化设备,分配资源,注册到系统中。 接下来看代码的结构。函数开头声明了一些变量,比如`backlight`设备节点指针、`ctx`结构体指针和返回值`ret`。然后使用`devm_kzalloc`分配内存,这是设备管理的内存分配函数,会自动释放,防止内存泄漏。如果分配失败,返回`-ENOMEM`错误。 接着,`spi_set_drvdata`把`ctx`和`spi`设备关联起来,方便后续使用。然后初始化DRM面板,设置设备和函数指针。这里可能涉及到显示框架的部分,需要提到DRM的作用。 然后是获取电源管理`regulator`,使用`devm_regulator_get`获取名为"power"的调节器。如果出错,打印错误信息并返回错误码。不过注意到代码里有个拼写错误,“gyd get our reset line”应该是“Couldn't get our power line”之类的,可能用户提供的代码有误,但需要指出来。 接下来获取复位GPIO,`devm_gpiod_get`获取名为"reset"的GPIO,设置为低电平输出。如果失败,返回错误。这里用户可能关心GPIO初始化的过程,以及复位信号的作用。 然后解析背光设备节点,`of_parse_phandle`获取"backlight"属性,找到对应的背光设备。如果存在,通过`of_find_backlight_by_node`获取背光设备,并检查是否成功。如果背光设备还没准备好,返回`-EPROBE_DEFER`让内核稍后再试。 之后,调用`drm_panel_add`将面板添加到DRM子系统中。如果失败,跳转到错误处理部分`err_free_backlight`,释放背光设备资源。最后返回0表示成功。 用户可能需要了解每个步骤的作用,尤其是资源申请和错误处理部分。比如为什么使用`devm_`系列函数,错误处理中的`goto`用法,以及`-EPROBE_DEFER`的作用。另外,代码中的拼写错误可能影响理解,需要指出。 需要确保解释清晰,分步骤说明每个关键函数和结构体的作用,帮助用户理解驱动初始化的流程。同时,提醒用户注意代码中的潜在问题,比如拼写错误和可能的资源泄漏,尽管使用了`devm`,但某些情况下仍需手动释放。</think>以下是对 `st7789v_probe` 函数的逐段解释,结合 SPI 设备驱动的核心逻辑和代码功能: --- ### **函数作用** `st7789v_probe` 是 Linux 内核中 **SPI 设备驱动**的探测函数(Probe Function),当内核检测到与驱动匹配的 SPI 设备(例如 ST7789V 显示屏控制器)时,此函数会被调用。它的核心任务是: 1. **初始化设备硬件**(如电源、复位引脚、背光)。 2. **分配设备数据结构**并绑定到 SPI 设备。 3. **注册显示面板**到 DRM(Direct Rendering Manager)子系统,使显示屏能被图形系统控制。 --- ### **逐段代码分析** #### **1. 设备上下文分配** ```c struct st7789v *ctx; ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; ``` - **目的**:为设备分配内存(`struct st7789v` 是自定义的显示屏控制结构体)。 - **关键点**: - `devm_kzalloc`:内核提供的“设备管理”内存分配函数,自动释放内存,避免内存泄漏。 - `GFP_KERNEL`:内存分配标志,表示在进程上下文中可休眠等待内存。 --- #### **2. 绑定 SPI 设备与驱动数据** ```c spi_set_drvdata(spi, ctx); ctx->spi = spi; ``` - **目的**:将设备上下文 `ctx` 与 SPI 设备 `spi` 绑定,便于后续通过 `spi_get_drvdata` 获取上下文。 - **意义**:驱动操作中可通过 `spi` 对象访问设备私有数据。 --- #### **3. 初始化 DRM 面板** ```c drm_panel_init(&ctx->panel); ctx->panel.dev = &spi->dev; ctx->panel.funcs = &st7789v_drm_funcs; ``` - **目的**:初始化 DRM 面板,设置显示屏的底层操作函数(如使能、禁用、显示图像)。 - **关键结构**: - `st7789v_drm_funcs`:自定义的 DRM 面板操作函数集合(例如 `prepare`, `enable`, `disable` 等回调函数)。 --- #### **4. 获取电源管理(Power Regulator)** ```c ctx->power = devm_regulator_get(&spi->dev, "power"); if (IS_ERR(ctx->power)) dev_err(&spi->dev, "gyd get our reset line\n") // 存在拼写错误,应为 "Couldn't get power" return PTR_ERR(ctx->power); ``` - **目的**:通过设备树(Device Tree)获取名为 "power" 的电源调节器(Regulator),用于控制显示屏电源。 - **注意**: - `devm_regulator_get`:自动管理资源的获取函数。 - 代码中存在拼写错误(`gyd` 应为 `Couldn't`),实际开发中需修正。 --- #### **5. 获取复位引脚(Reset GPIO)** ```c ctx->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset)) { dev_err(&spi->dev, "Couldn't get our reset line\n"); return PTR_ERR(ctx->reset); } ``` - **目的**:获取复位引脚 GPIO,并初始化为低电平(`GPIOD_OUT_LOW`)。 - **作用**:通过拉高或拉低复位引脚电平,控制显示屏硬件复位。 --- #### **6. 获取背光设备(Backlight)** ```c backlight = of_parse_phandle(spi->dev.of_node, "backlight", 0); if (backlight) { ctx->backlight = of_find_backlight_by_node(backlight); of_node_put(backlight); if (!ctx->backlight) return -EPROBE_DEFER; } ``` - **目的**:从设备树中解析背光设备节点,并获取背光控制对象。 - **关键点**: - `of_parse_phandle`:解析设备树中的 `backlight` 属性,获取背光设备节点。 - `-EPROBE_DEFER`:若背光设备未初始化,延迟探测(等待背光驱动加载完成)。 --- #### **7. 注册 DRM 面板** ```c ret = drm_panel_add(&ctx->panel); if (ret < 0) goto err_free_backlight; return 0; ``` - **目的**:将 DRM 面板添加到系统中,使图形子系统(如 DRM/KMS)能控制显示屏。 - **错误处理**:若注册失败,跳转到 `err_free_backlight` 标签,释放背光资源。 --- #### **8. 错误处理(释放资源)** ```c err_free_backlight: if (ctx->backlight) put_device(&ctx->backlight->dev); return ret; ``` - **作用**:若注册面板失败,释放已获取的背光设备资源,避免内存泄漏。 --- ### **关键问题与改进建议** 1. **拼写错误修正**: - `dev_err(&spi->dev, "gyd get our reset line\n")` → 应为 `"Couldn't get power regulator"`。 2. **电源与复位顺序**: - 实际驱动中,通常需要先启用电源(`regulator_enable`),再操作复位引脚。 3. **延迟探测处理**: - 若背光设备未就绪,返回 `-EPROBE_DEFER` 是正确做法,确保依赖关系。 --- ### **代码流程图** ```plaintext 开始 ├─ 分配设备内存 (ctx) ├─ 绑定 SPI 设备和 ctx ├─ 初始化 DRM 面板 ├─ 获取电源 (power) ├─ 获取复位引脚 (reset) ├─ 获取背光设备 (backlight) ├─ 注册 DRM 面板 │ ├─ 成功 → 返回 0 │ └─ 失败 → 释放背光资源 └─ 结束 ``` --- ### **总结** 此函数是 ST7789V 显示屏驱动的核心初始化逻辑,完成了硬件资源获取、DRM 面板注册等关键步骤。实际开发中需结合设备树配置(如定义 `reset-gpios`, `power-supply`, `backlight` 等属性),并确保时序和错误处理的正确性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tilblackout

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值