资讯详情

从零开始做单相逆变电源(软件)

文章目录

  • 前言
  • 一、环境及项目情况
    • 1.1 CCS配置
    • 1.2 Quartus 配置
    • 1.3 系统总体框图
  • 二、软件设计
    • 2.1 单片机,FPGA通信示意图
    • 2.2 MSP430主程序 `main.c`
    • 2.3 Verilog 顶层文件
    • 2.4 注意点:
    • 2.5 其他代码
  • 三、固化
  • 四、总结


前言

主题:单相逆变电源 在这里插入图片描述

提示:,单片机控制FPGA输出波,控制SPWM波正弦调制波控制电压。


一、环境及项目情况

使用硬件:

  • Tiva TM4C123GH6PM 作为烧录器
  • FPGA板:Cyclone IV E Tiva TM4C123H6PZ

使用软件:

  • Code Composer Studio
  • Quartus Prime 17

1.1 CCS配置

大致同MSP附加步骤如下:

注意Tiva TM4C为ARM结构,性能强

1.2 Quartus 配置

无用管脚按以下设置配置,否则会输出奇怪的电平

1.3 系统总体框图

二、软件设计

2.1 单片机,FPGA通信示意图

2.2 MSP430主程序 main.c

#include "common.h" #include "bus_fpga.h" #include "LCD12864_rom_enable.h"  #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include "inc/tm4c123gh6pm.h" #include "inc/hw_memmap.h" #include "driverlib/debug.h" #include "driverlib/fpu.h" #include "driverlib/gpio.h" #include "driverlib/sysctl.h" #include "lib/blue.h" #define MAXLEN 20 //多读AD取平均值,数组大小 /** * main.c */  double get_DC_voltage(unsigned char base, unsigned char L16_addr);
double get_AC_voltage(unsigned char base, unsigned char H16_addr);
double PID(double V); //调制度ma
void updateKey(void);
void responseKey(void);
void globalParamConstraints(void);
void update_AC_VI_Measured(void);

void initADint(void);
void Timer0IntHandler(void);

char keydat; //从FPGA按键读值
int key; //key = updateKey(keydat),具体的操作数

double V0 = 0, I0 = 0;
double Kp = 66, Ki = 27, Kd = 5;
double TarV = 10.1; //目标电压
int TarFreq = 50; //目标频率
double ma = 9880; // 调制度,精度万分之一
int SysFreq = 12500;
int VoI = 0;
int isPID = 1;

double ratioV = 1, ratioI = 1;
double biasV = 0, biasI = 0;

double MeasVsum[MAXLEN] = { 
         0 }, MeasIsum[MAXLEN] = { 
         0 };
double MeasV, MeasI;
int MeasCnt = 0, MeasIcnt = 0;
double deltV[3] = { 
         0 };

// 固化后一定要重启FPGA

// 初始化AD中断
void initADint(void)
{ 
        
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    //使能TIMER0
    //TimerConfigure(TIMER0_BASE, TIMER_CFG_ONE_SHOT);//单次计数模式
    TimerConfigure(TIMER0_BASE, TIMER_CFG_A_PERIODIC);    //周期性计数模式
// TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
    TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet() / 20 / 3);    //计数频率10HZ
    IntEnable(INT_TIMER0A);    //NVIC
    //使能TIMER0A
    TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    //TIMEOUT标志位触发中断
    IntMasterEnable();
    //为定时器指定中断处理函数
    TimerIntRegister(TIMER0_BASE, TIMER_A, Timer0IntHandler);
    //master interrupt enable API for all interrupts
    TimerEnable(TIMER0_BASE, TIMER_A);
    //TIMER0A开始计数,当计数值等于TimerLoadSet,触发中断
}

// (AD)TimerB中断服务函数
void Timer0IntHandler(void)
{ 
        
    TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);    //清除标志位
    update_AC_VI_Measured();
}

int main(void)
{ 
        
    SysCtlClockSet(
    SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN); //Main OSC = using pll = 40MHz = 2.5 * 16MHz

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //解F、D、C口
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);

    //解锁
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY; //该地址赋值GPIO_LOCK_KEY,解锁,p684
    HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= GPIO_PIN_0; //允许写入,GPIOAFSEL, GPIOPUR, GPIOPDR, or GPIODEN
    HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;    //Lock and commit
    HWREG(GPIO_PORTD_BASE + GPIO_O_CR) |= GPIO_PIN_7;               //F, D

    lcd12864_init(); // 显示屏初始化
    DispClear(); // 清屏
    BUS_Init(); // 初始化单片机<=>FPGA
    initADint();

    float V0f = V0, I0f = I0;
    initialize_uart();
    blue1(&V0f); //double -> float 绑定蓝牙传输数值
    blue2(&I0f);

    IOWR(CS6, 0, 0); //不过流
    while (1)
    { 
        
        //读取键盘
        keydat = IORD(CS0, 0);

        //更新AD采样结果
        V0 = MeasV;
        I0 = MeasI;            //Next: A5

        //Update Ratio
        if (TarFreq > 50)
        { 
        
            V0 = V0 * (14.40184197 + biasV);            // - 0.658;
            I0 = I0 * (1.5358854 + biasI);            // - 0.0425;
        }
        else
        { 
        
            V0 = V0 * (14.09751 + biasV);
            I0 = I0 * (1.19746 + biasI);
// V0 = V0 * (14.13+biasV - 0.1324);
// I0 = I0 * (1.363856+biasI - 0.0437);
        }
        V0f = V0;
        I0f = I0;

        //PID
        if (isPID)
        { 
        
            double deltM = PID(V0);
            ma += deltM;
        }

        SysCtlDelay(SysCtlClockGet() * 0.1 / 3);     //delay 0.2 s
        //键盘事件响应
        sentdata(); //蓝牙发送数据
        updateKey();
        responseKey();

        //全局参数限制
        globalParamConstraints();

        //过流保护
        if (I0 >= 1.5)   //过流*A
        { 
        
            IOWR(CS6, 0, 1); //触发过流
            SysCtlDelay(SysCtlClockGet() * 2 / 3); //延迟两秒后取消过流保护
            IOWR(CS6, 0, 0); //触发过流
        }
        else
        { 
        
        }

        //更新主要参数
        IOWR(CS1, 0, TarFreq);
        IOWR(CS1, 1, SysFreq);
        IOWR(CS1, 2, (int) ma);

        //屏幕显示
        DispString5x8(1, 1 * 8, "Freq:", 1);
        DispNumber5x8(1, 6 * 8, TarFreq, 3, 1); //Page 行,column列
        DispString5x8(1, 10 * 8, "PID:", 1);
        DispNumber5x8(1, 14 * 8, isPID, 1, 1); //Page 行,column列
        DispString5x8(2, 1 * 8, "TarV:", 1);
        DispNumber5x8(2, 6 * 8, TarV, 2, 1); //Page 行,column列
        DispString5x8(3, 1 * 8, "V,Io:", 1);
        DispFloat5x8(3, 6 * 8, V0, 1, 2, 4); //Page 行,column列
        DispFloat5x8(4, 6 * 8, I0, 1, 2, 4); //Page 行,column列
        DispString5x8(5, 1 * 8, "SysF:", 1);
        DispNumber5x8(5, 6 * 8, SysFreq, 5, 1); //Page 行,column列

        DispString5x8(6, 1 * 8, "MA:", 1);
        DispNumber5x8(6, 6 * 8, (int) ma, 4, 1);
//
// DispString5x8(7, 1 * 8, "V:", 1);
        DispFloat5x8(7, 12 * 8, biasV > 0 ? biasV : -biasV, 1, 2, 2);
// DispString5x8(8, 1 * 8, "I:", 1);
        DispFloat5x8(8, 12 * 8, biasI > 0 ? biasI : -biasI, 1, 2, 2);

        DispString5x8(7, 1 * 8, "P:", 1);
        DispFloat5x8(7, 6 * 8, V0 * I0, 1, 2, 2); //Page 行,column列

// DispString5x8(6, 1 * 8, Kp > 0 ? "Kp:" : "-Kp:", 1);
// DispFloat5x8(6, 6 * 8, Kp > 0 ? Kp : -Kp, 1, 2, 4);
// DispString5x8(7, 1 * 8, Ki > 0 ? "Ki:" : "-Ki:", 1);
// DispFloat5x8(7, 6 * 8, Ki > 0 ? Ki : -Ki, 1, 2, 4);
        DispString5x8(8, 1 * 8, Kd > 0 ? "Kd:" : "-Kd:", 1);
        DispFloat5x8(8, 6 * 8, Kd > 0 ? Kd : -Kd, 1, 2, 4);
    }
    return 0;
}

void globalParamConstraints(void)
{ 
        
    ma = ma > 9940 ? 9940 : ma;
    ma = ma < 9400 ? 9400 : ma;
    TarFreq = TarFreq > 110 ? 110 : TarFreq;
    TarFreq = TarFreq < 15 ? 15 : TarFreq;
}

//将获得的采样值转换为double类型
double get_DC_voltage(unsigned char base, unsigned char L16_addr)
{ 
        
    double DC_voltage;
    DC_voltage = IORD(base, L16_addr);
    DC_voltage = (DC_voltage - 0x8000) * 10.24 / 0x7FFF;
    return DC_voltage;
}

double get_AC_voltage(unsigned char base, unsigned char H16_addr)
{ 
        
    double AC_voltage, cnt;
    unsigned int AC_voltage_H16, AC_voltage_M16, AC_voltage_L16;
    AC_voltage_H16 = IORD(base, H16_addr);
    AC_voltage_M16 = IORD(base, H16_addr + 1);
    AC_voltage_L16 = IORD(base, H16_addr + 2);
    cnt = IORD(base, 6);  // Locked at 6

    AC_voltage = AC_voltage_H16;
    AC_voltage = AC_voltage * 65536;
    AC_voltage = AC_voltage + AC_voltage_M16;
    AC_voltage = AC_voltage * 65536;
    AC_voltage = AC_voltage + AC_voltage_L16;

    AC_voltage = AC_voltage / cnt;
    AC_voltage = sqrt(AC_voltage);
    AC_voltage = (AC_voltage) * 10.24 / 0x7FFF;

// DispString5x8(6, 1 * 8, "cnt:", 1);
// DispNumber5x8(6, 6 * 8, cnt, 5, 1);
//
// DispString5x8(7, 1 * 8, "H,M,L:", 1);
// DispNumber5x8(7, 6 * 8, AC_voltage_H16, 5, 1);
// DispNumber5x8(7, 12 * 8, AC_voltage_M16, 5, 1);
// DispNumber5x8(8, 12 * 8, AC_voltage_L16, 5, 1);
    if (AC_voltage > 5)
        AC_voltage = 0.1;
    return AC_voltage;
}

void update_AC_VI_Measured(void)
{ 
        
    double V, I, Vsum = 0, Isum = 0;
    I = get_AC_voltage(CS5, 7);            //A6
    V = get_AC_voltage(CS4, 0);            //A0
    MeasVsum[MeasCnt] = V;
    MeasIsum[MeasCnt] = I;
    MeasCnt++;
    MeasCnt = MeasCnt >= MAXLEN ? 0 : MeasCnt;
    int i;
    for (i = 0; i < MAXLEN; i++)
    { 
        
        Vsum += MeasVsum[i];
        Isum += MeasIsum[i];
    }
    MeasV = Vsum / (double) MAXLEN;
    MeasI = Isum / (double) MAXLEN;
// MeasV = V;
// MeasI = I;
}

//上一时刻电压,此时电压,目标电压
double PID(double V)
{ 
        
    double deltM;     //调制度增量
    deltV[2] = TarV - V;
    deltM = Kp * (deltV[2] - deltV[1]) + Ki * deltV[2]
            + Kd * (deltV[2] - deltV[1] * 2 + deltV[0]);
    deltV[0] = deltV[1];
    deltV[1] = deltV[2];
    return deltM;
}

void responseKey(void)
{ 
        
    //1,4: 步进电压/频率
    if (key == 1)
    { 
        
        TarFreq = TarFreq + 1;
    }
    if (key == 4)
    { 
        
        TarFreq = TarFreq - 1;
    }
    //2,5调制度
    if (key == 2)  // 2022.06.27, max at 9800
    { 
        
        if (ma >= 9900)
            ma += 1;
        else
            ma += 10;
    }
    if (key == 5)
    { 
        
        if (ma >= 9900)
            ma -= 1;
        else
            ma -= 10;
    }
    //3,6 系统频率
    if (key == 3)
    { 
        
        SysFreq = SysFreq + 100;
    }
    if (key == 6)
    { 
        
        SysFreq = SysFreq - 100;
    }

    //*:14, #:15, 0:0, A:10, B:11, C:12, D:13
    //7,* Kp
    if (key == 7)
    { 
        
// Kp += 1;
        biasV += 0.01;
    }
    if (key == 14)
    { 
        
// Kp -= 1;
        biasV -= 0.01;
    }
    //8,0 Ki
    if (key == 8)
    { 
        
// Ki += 1;
        biasI += 0.01;
    }
    if (key == 0)
    { 
        
// Ki -= 1;
        biasI -= 0.01;
    }
    //9,15 Kd
    if (key == 9)
    { 
        
        Kd += 1;
    }
    if (key == 15)
    { 
        
        Kd -= 1;
    }
    //Enable PID
    if (key == 10)
    { 
        
        isPID = isPID > 0 ? 0 : 1;
    }
}

void updateKey(void)
{ 
        
    switch (keydat)
    { 
        
    case 0x11:
        key

标签: 1500f过流继电器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

 锐单商城 - 一站式电子元器件采购平台  

 深圳锐单电子有限公司