使用Arduino制作一个简单的纯正弦波逆变器

电源/新能源

3399人已加入

描述

  在无法从电网获得交流电源的情况下,通常需要逆变器电路。逆变器电路用于将直流电转换为交流电,可分为纯正弦波逆变器和改进方波逆变器两种类型。这些纯正弦波逆变器非常昂贵,而改进的方波逆变器价格低廉。在此处了解有关 不同类型逆变器的更多信息 。在这篇文章中,我将使用Arduino制作一个简单的纯正弦波逆变器,并解释电路的工作原理。

  什么是 SPWM(正弦脉冲宽度调制)?

  顾名思义,SPWM代表S正弦脉冲宽度调制。_ 您可能已经知道,PWM 信号是一种信号,我们可以在其中改变脉冲的频率以及开启时间和关闭时间,也称为占空比。如果您想了解有关 PWM的更多信息,可以在此处阅读。因此,通过改变占空比,我们改变了脉冲的平均电压。下图显示——

Arduino

  如果我们考虑一个在 0-5V 之间切换且占空比为 100%的PWM 信号,我们将获得 5V 的平均输出电压,同样如果我们考虑占空比为 50% 的相同信号,我们将得到2.5V 的输出电压,对于 25% 的占空比,它是那个的一半。总结了PWM信号的基本原理,接下来我们就可以了解SPWM信号的基本原理了。

  正弦电压主要是一种模拟电压,它会随着时间的推移改变其幅度,我们可以通过不断改变 PWM 波的占空比来重现正弦波的这种行为,下图显示了这一点。

Arduino

  如果您查看下面的示意图,它将看到在变压器的输出端连接了一个电容器。该电容器负责从载波频率中平滑交流信号。

  利用的输入信号将根据输入信号 和负载对电容器进行充电和放电。由于我们使用了一个非常高频的SPWM信号,它会有一个非常小的占空比,大概是1%,这个1%的占空比会给电容充电一点,下一个占空比是5%,这会再次充电电容器再多一点,接下来的脉冲将有 10% 的占空比,电容器将再充电一点,我们将施加信号,直到我们达到100% 的占空比,然后从那里返回到 1%。这将在输出端创建一个非常平滑的曲线,如正弦波。 因此,通过在输入端提供适当的占空比值,我们将在输出端获得非常正弦的波形。

  SPWM 逆变器的工作原理

Arduino

  上图显示了 SPWM 逆变器的主要驱动部分,如您所见,我们使用了两个半桥配置的N 沟道 MOSFET来驱动该电路的变压器,以减少不必要的开关噪声并保护 MOSFET ,我们使用了与 MOSFET 并联的 1N5819 二极管。为了减少栅极部分产生的任何有害尖峰,我们使用了与 1N4148 二极管并联的 4.7 欧姆电阻。最后,BD139 和 BD 140 晶体管配置为推挽式配置 驱动 MOSFET 的栅极,因为该 MOSFET 具有非常高的栅极电容,并且基极需要至少 10V 才能正常开启。在此处了解有关推挽式放大器工作原理的更多信息。

Arduino

  为了更好地理解电路的工作原理,我们将其缩小到 MOSFET 的这一部分为 ON 的程度。当MOSFET导通电流时,先流过变压器,然后被MOSFET接地,这样在电流流动的方向上也会感应出磁通量,变压器的铁芯会通过磁通量在次级绕组中,我们将在输出端获得正弦信号的正半周期。

Arduino

  在下一个循环中,电路的底部在电路的顶部,这就是我移除顶部的原因,现在电流以相反的方向流动并在该方向产生磁通量,从而反转磁芯中磁通量的方向。在此处了解有关MOSFET工作原理的更多信息。

  现在,我们都知道变压器是靠磁通量变化来工作的。因此,打开和关闭 MOSFET,一个倒置到另一个并在一秒钟内执行 50 次,将在变压器铁芯内产生良好的振荡磁通量,而变化的磁通量将在次级线圈中感应出电压,如我们知道法拉第定律。这就是基本逆变器的工作原理。

  该项目中使用的完整SPWM 逆变器电路如下所示。

Arduino

  构建 SPWM 逆变器所需的组件

Arduino

Arduino

Arduino

  SPWM逆变器电路结构

Arduino

  对于这个演示,电路是在 Veroboard 上构建的,借助原理图,在变压器的输出端,会有大量的电流流过连接,因此连接跳线需要尽可能厚。

  SPWM逆变器的Arduino程序

  在我们开始理解代码之前,让我们先弄清楚基础知识。通过以上工作原理,您已经了解了PWM 信号在输出端的样子,现在的问题是我们如何在 Arduino 的输出引脚上产生如此变化的波形。

  为了产生变化的 PWM 信号,我们将使用16 位定时器 1 ,预分频器设置为 1,如果我们考虑正弦波的单个半周期,这将为每个计数提供 1600/16000000 = 0.1ms 时间,在波的半个周期内恰好适合 100 次。简单来说,我们将能够对正弦波进行 200 次采样。

  接下来,我们必须将我们的正弦波分成 200 段,并根据幅度的相关性计算它们的值。接下来,我们必须将这些值与计数器限制相乘,将其转换为定时器计数器值。最后,我们必须将这些值放入查找表中以将其提供给计数器,我们将获得正弦波。

  为了让事情更简单一点,我使用了一个来自GitHub的非常好的 SPWM 代码,它是由Kurt Hutten编写的。

  代码很简单,我们通过添加所需的头文件开始我们的程序

 

#include 
#include 

 

接下来,我们有两个查找表,我们将从中获取计时器计数器值。

 

int lookUp1[] = {50 ,100 ,151 ,201 ,250 ,300 ,349 ,398 ,446 ,494 ,542 ,589 ,635 ,681 ,726 ,771 ,814 ,857 ,899 ,940 ,981 ,1020 , 1058 ,1095 ,1131 ,1166 ,1200 ,1233 ,1264 ,1294 ,1323 ,1351 ,1377 ,1402 ,1426 ,1448 ,1468 ,1488 ,1505 ,1522 ,1536 ,1550 ,1561 ,1572 ,1580 ,1587 ,1593 , 1597 ,1599 ,1600 ,1599 ,1597 ,1593 ,1587 ,1580 ,1572 ,1561 ,1550 ,1536 ,1522 ,1505 ,1488 ,1468 ,1448 ,1426 ,1402 ,1377 ,1351 ,1323 ,1294 ,1264 ,1233 , 1200 ,1166 ,1131 ,1095 ,1058 ,1020 ,981 ,940 ,899 ,857 ,814 ,771 ,726 ,681 ,635 ,589 ,542 ,494 ,446 ,398 ,349 ,300 ,5,250 ,201 , 100 ,50 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0};
int lookUp2[] = {0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,50 ,100 ,151 ,201 ,250 ,300 ,349 ,398 ,446 ,494 ,542 ,589 ,635 ,681 ,726 ,771 ,814 ,857 ,899 ,940 ,981 ,1020 , 1058 ,1095 ,1131 ,1166 ,1200 ,1233 ,1264 ,1294 ,1323 ,1351 ,1377 ,1402 ,1426 ,1448 ,1468 ,1488 ,1505 ,1522 ,1536 ,1550 ,1561 ,1572 ,1580 ,1587 ,1593 , 1597 ,1599 ,1600 ,1599 ,1597 ,1593 ,1587 ,1580 ,1572 ,1561 ,1550 ,1536 ,1522 ,1505 ,1488 ,1468 ,1448 ,1426 ,1402 ,1377 ,1351 ,1323 ,1294 ,1264 ,1233 , 1200 ,1166 ,1131 ,1095 ,1058 ,1020 ,981 ,940 ,899 ,857 ,814 ,771 ,726 ,681 ,635 ,589 ,542 ,494 ,446 ,398 ,349 ,300 ,5,250 ,201 ,100 ,50 ,0};

 

接下来,在设置部分,我们初始化定时器计数器控制寄存器以清除每个寄存器。如需更多信息,您需要查看 atmega328 IC 的数据表。

 

    TCCR1A = 0b10100010;
       /*10 匹配时清除,为 compA 设置为 BOTTOM。
         10 在比赛中清除,在底部设置为 compB。
         00
         10 WGM1 1:0 用于波形 15。
       */
    TCCR1B = 0b00011001;
       /*000
         11 WGM1 3:2 用于波形 15。
         001 计数器上没有预分频器。
       */
    TIMSK1 = 0b00000001;
       /*0000000
         1 TOV1 标志中断使能。
       */

 

之后,我们使用预定义的值 16000 初始化输入捕获寄存器,因为这将帮助我们准确生成 200 个样本。

 

ICR1 = 1600;// 16MHz 晶体的周期,对于每 50Hz 正弦波周期 200 次细分的 100KHz 开关频率。

 

接下来,我们通过调用函数来启用全局中断,

 

sei();

 

最后,我们将 Arduino 引脚 9 和 10 设置为输出

 

DDRB = 0b00000110;// 将 PB1 和 PB2 设置为输出。

 

这标志着 setup 函数的结束。

代码的循环部分保持空白,因为它是一个定时器计数器中断驱动程序。

 

无效循环(){;/*没做什么 。. . . 永远!*/}

 

接下来,我们定义了 timer1 溢出向量,一旦 timer1 溢出并产生中断,这个中断函数就会被调用。

 

ISR(TIMER1_OVF_vect){

 

接下来,我们将一些局部变量声明为静态变量,并开始将这些值提供给捕获和比较电阻器。

 

静态整数;
静态字符触发;
// 每个周期改变占空比。
OCR1A = 查找1[编号];
OCR1B = 查找2[编号];

 

最后,我们预先增加计数器以将下一个值提供给捕获和比较电阻器,这标志着这段代码的结束。

 

if(++num >= 200){ // 预先增加 num 然后检查它是否低于 200。
       数 = 0; // 重置号码
       触发=触发^0b00000001;
       数字写入(13,触发);
     }

 

  测试 TL494 PWM 逆变器电路

Arduino

  为了测试电路,使用以下设置。

  12V铅酸电池。

  具有 6-0-6 抽头和 12-0-12 抽头的变压器

  100W白炽灯泡作为负载

  Meco 108B+TRMS 万用表

  Meco 450B+TRMS 万用表

  来自 Arduino 的输出信号:

  一旦我上传了代码。我测量了Arduino两个引脚的输出 SPWM 信号,如下图所示,

Arduino

  如果我们稍微放大一点,我们可以看到 PWM 波不断变化的占空比。

Arduino

  接下来,下图显示了变压器的输出信号。

Arduino

  理想状态下的 SPWM 逆变器电路:

Arduino

  从上图中可以看出,该电路在理想运行时消耗约 13W

  无负载输出电压:

Arduino

  逆变器电路的输出电压如上图所示,这是在没有任何负载的情况下输出的电压。

  输入功耗:

Arduino

  上图显示了连接 40W 负载时 ic 消耗的输入功率。

  输出功耗:

Arduino

  上图显示了该电路消耗的输出功率,(负载为 40W 白炽灯泡)
 

/*
* sPWMv2.c
*
* 创建时间:2014 年 12 月 31 日下午 3:44:43
* 作者:库尔特·赫顿
arduino Uno 的 atMega328 上的 SPWM,可能与其他 atmel 芯片/arduino 板兼容。
比较输出 A 和 B 输出到 PORTB 引脚 1 和 2,它们分别是 Uno 上的引脚 9 和 10。
了解 Uno 上的 LED 也很有用,它是 PORTB 上的引脚 5。
*/
#include
#include
// 查找每个有 200 个条目的表,标准化为最大值 1600,这是加载到寄存器 ICR1 的 PWM 周期。
int lookUp1[] = {50 ,100 ,151 ,201 ,250 ,300 ,349 ,398 ,446 ,494 ,542 ,589 ,635 ,681 ,726 ,771 ,814 ,857 ,899 ,940 ,981 ,1020 , 1058 ,1095 ,1131 ,1166 ,1200 ,1233 ,1264 ,1294 ,1323 ,1351 ,1377 ,1402 ,1426 ,1448 ,1468 ,1488 ,1505 ,1522 ,1536 ,1550 ,1561 ,1572 ,1580 ,1587 ,1593 , 1597 ,1599 ,1600 ,1599 ,1597 ,1593 ,1587 ,1580 ,1572 ,1561 ,1550 ,1536 ,1522 ,1505 ,1488 ,1468 ,1448 ,1426 ,1402 ,1377 ,1351 ,1323 ,1294 ,1264 ,1233 , 1200 ,1166 ,1131 ,1095 ,1058 ,1020 ,981 ,940 ,899 ,857 ,814 ,771 ,726 ,681 ,635 ,589 ,542 ,494 ,446 ,398 ,349 ,300 ,5,250 ,201 , 100 ,50 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0};
int lookUp2[] = {0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,50 ,100 ,151 ,201 ,250 ,300 ,349 ,398 ,446 ,494 ,542 ,589 ,635 ,681 ,726 ,771 ,814 ,857 ,899 ,940 ,981 ,1020 , 1058 ,1095 ,1131 ,1166 ,1200 ,1233 ,1264 ,1294 ,1323 ,1351 ,1377 ,1402 ,1426 ,1448 ,1468 ,1488 ,1505 ,1522 ,1536 ,1550 ,1561 ,1572 ,1580 ,1587 ,1593 , 1597 ,1599 ,1600 ,1599 ,1597 ,1593 ,1587 ,1580 ,1572 ,1561 ,1550 ,1536 ,1522 ,1505 ,1488 ,1468 ,1448 ,1426 ,1402 ,1377 ,1351 ,1323 ,1294 ,1264 ,1233 , 1200 ,1166 ,1131 ,1095 ,1058 ,1020 ,981 ,940 ,899 ,857 ,814 ,771 ,726 ,681 ,635 ,589 ,542 ,494 ,446 ,398 ,349 ,300 ,5,250 ,201 ,100 ,50 ,0};
无效设置(){
// 寄存器初始化,更多细节参见数据表。
TCCR1A = 0b10100010;
/*10 匹配时清除,为 compA 设置为 BOTTOM。
10 在比赛中清除,在底部设置为 compB。
00
10 WGM1 1:0 用于波形 15。
*/
TCCR1B = 0b00011001;
/*000
11 WGM1 3:2 用于波形 15。
001 计数器上没有预分频器。
*/
TIMSK1 = 0b00000001;
/*0000000
1 TOV1 标志中断使能。
*/
ICR1 = 1600;// 16MHz 晶体的周期,对于每 50Hz 正弦波周期 200 次细分的 100KHz 开关频率。
sei(); // 启用全局中断。
DDRB = 0b00000110;// 将 PB1 和 PB2 设置为输出。
pinMode(13,输出);
}
无效循环(){;/*没做什么 。. . . 永远!*/}
ISR(TIMER1_OVF_vect){
静态整数;
静态字符触发;
// 每个周期改变占空比。
OCR1A = 查找1[编号];
OCR1B = 查找2[编号];
if(++num >= 200){ // 预先增加 num 然后检查它是否低于 200。
数 = 0; // 重置号码
触发=触发^0b00000001;
数字写入(13,触发);
}
}

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分