[原创] 【ST NUCLEO-U5A5ZJ-Q开发板测评】05 使用SPWM,产生正弦波

怀揣少年梦   2024-2-4 09:16 楼主

一、SPWM是什么?

        SPWM的全称是(Sinusoidal PWM),称为正弦脉冲宽度调制是一种非常成熟,使用非常广泛的技术。简单来讲其实就是PWM的占空比按照正弦波的规律进行变化的PWM。

二、SPWM的作用是什么?

        SPWM在多个领域中具有广泛的应用:

        1、产生交流电

        SPWM技术可用于逆变器,将直流电源转化为高质量的交流电源。通过调整脉冲宽度,可生成与正弦波等效的脉冲序列,从而产生理想的交流电波形。

        2、电机控制

        在电机驱动系统中,SPWM用于精确控制电机的转速和转矩。通过改变脉冲的宽度和频率,可实现电机的平滑调速和精确运行

        3、功率变换

        SPWM技术还应用于电力电子变换器,如整流器和变频器,以高效地进行电能转换和调节。

        4、减少谐波

        SPWM技术能够显著降低输出波形中的谐波含量,提高电能质量,对维护电网和用电设备的稳定性至关重要。

        5、改善功率因数

        SPWM有助于优化系统的功率因数,减少无功功率,提高电能利用率。

        6、声音合成

        在音频领域,SPWM通过调制技术合成不同频率的声音波形,实现声音的再现。

        7、通信系统

        在通信领域,SPWM用于信号的调制和解调,提升信号传输的效率和品质。

三、SPWM的原理是什么?

        基本原理就是,面积等效,即利用幅值相同,脉冲宽度不一样的PWM波,在一小段时间内的积分等价于正弦波一小段时间的面积。具体可以参看下面一篇文章,对于原理的描述写的还是很清楚的。

SPWM基本原理详解(图文并茂+公式推导+C程序实现)-CSDN博客

        具体实现可以参考下面一篇文章

真硬核!从零开始一文教你快速实现数字化SPWM纯正弦波逆变器 (baidu.com)

四、产生正弦波采样宽度

        产生SPWM的关键在于获取一系列等价于正弦波的脉冲宽度表;

        使用python实现如下:

/*产生正弦波脉冲宽度表*/
"""
Created on Mon Jan 25 15:06:04 2021
单极性
@author: liuxingguo
"""

import numpy as np
import matplotlib.pyplot as plt
import math 


fig =plt.figure(figsize = (15,15))
ax= fig.add_subplot(1,1,1)

"正弦波分段数"
seg_num =64

"调制深度--调制波幅值与载波幅值的比值"
n = 0.5    //这个调制深度可以调整正弦波的峰值
"定时器的计数值最大值"
arr =1000

"占空比序列"
plus=[]
"占空比寄存器序列"
pluse_arr=[]
"半个周期分段的每段的弧度"
x=np.linspace(0,math.pi,seg_num+1)   
sin_y = np.sin(x)
 
for i in range(seg_num):
    "正弦波对应的面积,式子是利用积分计算得到的"
    sin_area = (np.cos(x[i])-np.cos(x[i+1]))
    "占空比=正弦波面积占一个周期内多少"
    duty = sin_area*n/x[1]
    pwm= x[i]+duty*x[1]
    "小数部分的占空比"
    plus.append(duty)  
    "对应计数器arr值的占空比"
    pluse_arr.append(round(duty*arr))
    
    rect1 = plt.Rectangle((x[i],0,0), x[1]*duty,1,color ='Green')
    ax.add_patch(rect1)
    
    
print(plus)
print('\r')
print(pluse_arr)

pic1 =plt.plot(x,sin_y)

使用jupyter notebook运行上述程序

输出SPWM数组如下:

小数
[0.012269382343518818, 0.036778589013820015, 0.0611991928417635, 0.08547236245373524, 0.10953962165805997, 0.13334299031927357, 0.15682512403734036,
 0.17992945229534474, 0.20260031474280907, 0.22478309528636706, 0.24642435366470614, 0.2674719541908437, 0.28787519135155765, 0.3075849119614181, 0.3265536335771058, 
0.3447356588867888, 0.36208718579893473, 0.378566412965397, 0.3941336404845251, 0.408751365541699, 0.42238437275689056, 0.43499981902160484, 0.4465673126208184, 0.45705898644924836, 
0.4664495651456836, 0.4747164259835257, 0.481839653370924, 0.4878020868291859, 0.4925893623338585, 0.49618994691893364, 0.4985951664607794, 0.49979922657484005, 0.4997992265748401, 0.49859516646077934, 
0.49618994691893376, 0.49258936233385825, 0.4878020868291862, 0.48183965337092344, 0.47471642598352626, 0.4664495651456836, 0.4570589864492461, 0.44656731262082067, 0.43499981902160484, 0.4223843727568883, 
0.4087513655417013, 0.3941336404845251, 0.3785664129653992, 0.36208718579893245, 0.34473565888678653, 0.3265536335771092, 0.30758491196141696, 0.28787519135155987, 0.2674719541908414, 0.24642435366470614, 0.2247830952863682, 
0.20260031474280907,0.1799294522953436, 0.1568251240373415, 0.13334299031927357, 0.10953962165805997, 0.08547236245373524, 0.06119919284176237, 0.036778589013821146, 0.012269382343518818]
整数
[12, 37, 61, 85, 110, 133, 157, 180, 203, 225, 246, 267, 288, 308, 327, 345, 362, 379, 394, 409, 422, 435, 447, 457, 466, 475, 
482, 488, 493, 496, 499, 500, 500, 499, 496, 493, 488, 482, 475, 466, 457, 447, 435, 422, 409, 394, 379, 362, 345, 327, 308, 288, 
267, 246, 225, 203, 180, 157, 133, 110, 85, 61, 37, 12]

输出的波形如图:

SPWM波形.png

五、程序编写

1、新家工程以及配置时钟,具体就不一一赘述;

2、使能定时器

1)使能定时供1互补通道;定时器8一样操作即可。

image.png  

2)使能定时器3,用于更新PWM的脉冲宽度

image.png  

3、配置定时器参数

主要配置:输出极性、死时间、PWM mode

image.png  

4、对定时器的分配、周期和死时间进行定义

/* USER CODE BEGIN Private defines */
#define SIN_Freq_25Hz      239
#define SIN_Freq_50Hz      119
#define SIN_Freq_100Hz    59
#define SIN_Freq_200Hz    29
#define SIN_Freq_400Hz    14
#define DEAD_TIME 10
#define TIM1_PRE  3
#define TIM1_ARR  999    //这里更新PWM的频率为40K
#define TIM8_PRE  3
#define TIM8_ARR  999
#define TIM3_PRE  SIN_Freq_50Hz
#define TIM3_ARR  624

/* USER CODE END Private defines */

5、复制之前生成的脉冲宽度表

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "tim.h"

/* USER CODE BEGIN 0 */
#define Point_Max 256

uint16_t pluse_cnt=0;
uint8_t  Voltage_Level = 2;

uint32_t Sin_Data_32[][32]={

/*32个点,调制深度为0.2    定时器为1000,单极性SPWM ------0 */
    {10, 29, 49, 67, 85, 103, 119, 134,
    148, 161, 171, 181, 188, 194, 198, 200,
    200, 198, 194, 188, 181, 171, 161, 148,
    134, 119, 103, 85, 67, 49, 29, 10},

/*32个点,调制深度为0.35    定时器为1000,单极性SPWM ------1 */
    {17, 51, 85, 118, 150, 180, 208, 235,
    259, 281, 300, 316, 329, 339, 346, 349,
    349, 346, 339, 329, 316, 300, 281, 259,
    235, 208, 180, 150, 118, 85, 51, 17},

/*32个点,调制深度为0.4    定时器为1000,单极性SPWM ------2 */
    {20, 59, 97, 135, 171, 206, 238, 269,
    296, 321, 343, 361, 376, 388, 396, 399,
    399, 396, 388, 376, 361, 343, 321, 296,
    269, 238, 206, 171, 135, 97, 59, 20},

/*32个点,调制深度为0.45    定时器为1000,单极性SPWM ------3 */
    {22, 66, 109, 152, 192, 231, 268, 302,
    333, 361, 386, 407, 424, 436, 445, 449,
    449, 445, 436, 424, 407, 386, 361, 333,
    302, 268, 231, 192, 152, 109, 66, 22},

/*32个点,调制深度为0.5    定时器为1000,单极性SPWM------4 */
    {25, 73, 121, 168, 214, 257, 298, 336,
    370, 401, 429, 452, 471, 485, 494, 499,
    499, 494, 485, 471, 452, 429, 401, 370,
    336, 298, 257, 214, 168, 121, 73, 25},

/*32个点,调制深度为0.6    定时器为1000,单极性SPWM------5 */
    {29, 88, 146, 202, 256, 308, 357, 403,
    444, 482, 514, 542, 565, 582, 593, 599,
    599, 593, 582, 565, 542, 514, 482, 444,
    403, 357, 308, 256, 202, 146, 88, 29},

/*32个点,调制深度为0.85    定时器为1000,单极性SPWM ------6 */
    {42, 125, 206, 286, 363, 437, 506, 571,
    630, 682, 729, 768, 800, 824, 840, 849,
    849, 840, 824, 800, 768, 729, 682, 630,
    571, 506, 437, 363, 286, 206, 125, 42},

/*32个点,调制深度为0.9    定时器为1000,单极性SPWM------7 */
    {44, 132, 219, 303, 385, 463, 536, 604,
    667, 723, 772, 813, 847, 873, 890, 899,
    899, 890, 873, 847, 813, 772, 723, 667,
    604, 536, 463, 385, 303, 219, 132, 44},

/*32个点,调制深度为0.95    定时器为1000,单极性SPWM------8 */
    {47, 139, 231, 320, 406, 488, 566, 638,
    704, 763, 815, 858, 894, 921, 939, 948,
    948, 939, 921, 894, 858, 815, 763, 704,
    638, 566, 488, 406, 320, 231, 139, 47}

};
/* USER CODE END 0 */

6、启动定时器

  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);    //启动定时器CH1输出
  HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1); //启动定时器CH1N输出
  HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1);
  HAL_TIMEx_PWMN_Start(&htim8,TIM_CHANNEL_1);
  __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);
  HAL_TIM_Base_Start_IT(&htim3); //启动定时器3
  /* USER CODE END 2 */

7、编译下载验证

1)使用示波器查看定时器1CH1和CH1N的波形;

可以看到,CH1和CH1N是互补的,当CH1为高电平时,CH1N为低电平;频率为40KHz;

image-20240204085938-5.png  

放大一点看看

image-20240204085938-6.png  

2)在CH1的PWM引脚上接一个10uF的电容,就可以看到正弦波的波形了,不过不是很纯正,可以使用两路SPWM驱动H桥,然后加上一个电容和电感,就可以看到纯正的正弦波。

image-20240204085938-7.png  

3)看一下CH1和CH1N的脉宽动态变化。

可以看到PWM的脉宽是持续根据脉宽表变化。

4023a110551ff134be9a1e5180873533

 

工程如下:

SPWM.zip (16.39 MB)
(下载次数: 0, 2024-2-4 09:09 上传)

 

回复评论 (11)

这个作品挺好的,感谢分享,期待后续精彩呀!
点赞  2024-2-4 10:17
引用: lugl4313820 发表于 2024-2-4 10:17 这个作品挺好的,感谢分享,期待后续精彩呀!

多谢大佬,可惜没有现成的H桥,不然就有纯正的正弦波出来了

点赞  2024-2-4 11:10

不知道你是怎么修改占空比的,我打算使用DMA,但是STM32Cube不生产DMA的初始化代码

点赞  2024-2-4 12:32
引用: bigbat 发表于 2024-2-4 12:32 不知道你是怎么修改占空比的,我打算使用DMA,但是STM32Cube不生产DMA的初始化代码

使用定时器中断来修改占空比。STM32U5的DMA还没用过,CubeMX不产生吗?

点赞  2024-2-4 14:11
引用: 怀揣少年梦 发表于 2024-2-4 14:11 使用定时器中断来修改占空比。STM32U5的DMA还没用过,CubeMX不产生吗?

是的我这个版本6.10.0,应该是最新版了,hal库是1.4.0,dma的代码就两行

/**
  * [url=home.php?mod=space&uid=159083]@brief[/url] GPDMA1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPDMA1_Init(void)
{

  /* USER CODE BEGIN GPDMA1_Init 0 */

  /* USER CODE END GPDMA1_Init 0 */

  /* Peripheral clock enable */
  __HAL_RCC_GPDMA1_CLK_ENABLE();

  /* GPDMA1 interrupt Init */
    HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);

  /* USER CODE BEGIN GPDMA1_Init 1 */
  
  /* USER CODE END GPDMA1_Init 1 */
  /* USER CODE BEGIN GPDMA1_Init 2 */

  /* USER CODE END GPDMA1_Init 2 */

}

image.png  

点赞  2024-2-4 14:17
引用: bigbat 发表于 2024-2-4 14:17 是的我这个版本6.10.0,应该是最新版了,hal库是1.4.0,dma的代码就两行 /** * @brief GPDMA1 Ini ...

那应该是工具问题,需要自己定义

点赞  2024-2-4 14:44

GPDMA有个链表要设计的,有点小复杂呀。

点赞  2024-2-4 18:22
引用: lugl4313820 发表于 2024-2-4 18:22 GPDMA有个链表要设计的,有点小复杂呀。

大佬,有什么参考资料,学习学习吗?

点赞  2024-2-4 20:08
引用: 怀揣少年梦 发表于 2024-2-4 20:08 大佬,有什么参考资料,学习学习吗?

我在其他论坛有试用,好象写过这方面的帖子,我找找看。

点赞  2024-2-4 20:10
引用: lugl4313820 发表于 2024-2-4 20:11 【STM32U599J-DK】基于touchGFX的ADC_GPDMA数字电压表 - stm32/stm8 - 电子工程世界-论坛 (eeworld.com.cn) ...

好的,多谢大佬,我看一下

点赞  2024-2-4 20:57
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复