文章目录
- 前言
- 一、环境及项目情况
-
- 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