diff --git a/FPGA/foc.qpf b/FPGA/foc.qpf deleted file mode 100644 index 9794919..0000000 --- a/FPGA/foc.qpf +++ /dev/null @@ -1,30 +0,0 @@ -# -------------------------------------------------------------------------- # -# -# Copyright (C) 1991-2013 Altera Corporation -# Your use of Altera Corporation's design tools, logic functions -# and other software and tools, and its AMPP partner logic -# functions, and any output files from any of the foregoing -# (including device programming or simulation files), and any -# associated documentation or information are expressly subject -# to the terms and conditions of the Altera Program License -# Subscription Agreement, Altera MegaCore Function License -# Agreement, or other applicable license agreement, including, -# without limitation, that your use is for the sole purpose of -# programming logic devices manufactured by Altera and sold by -# Altera or its authorized distributors. Please refer to the -# applicable agreement for further details. -# -# -------------------------------------------------------------------------- # -# -# Quartus II 64-Bit -# Version 13.1.0 Build 162 10/23/2013 SJ Full Version -# Date created = 17:37:36 February 05, 2021 -# -# -------------------------------------------------------------------------- # - -QUARTUS_VERSION = "13.1" -DATE = "17:37:36 February 05, 2021" - -# Revisions - -PROJECT_REVISION = "foc" diff --git a/FPGA/foc.qsf b/FPGA/foc.qsf deleted file mode 100644 index ddb58b6..0000000 --- a/FPGA/foc.qsf +++ /dev/null @@ -1,91 +0,0 @@ -# -------------------------------------------------------------------------- # -# -# Copyright (C) 1991-2013 Altera Corporation -# Your use of Altera Corporation's design tools, logic functions -# and other software and tools, and its AMPP partner logic -# functions, and any output files from any of the foregoing -# (including device programming or simulation files), and any -# associated documentation or information are expressly subject -# to the terms and conditions of the Altera Program License -# Subscription Agreement, Altera MegaCore Function License -# Agreement, or other applicable license agreement, including, -# without limitation, that your use is for the sole purpose of -# programming logic devices manufactured by Altera and sold by -# Altera or its authorized distributors. Please refer to the -# applicable agreement for further details. -# -# -------------------------------------------------------------------------- # -# -# Quartus II 64-Bit -# Version 13.1.0 Build 162 10/23/2013 SJ Full Version -# Date created = 17:37:36 February 05, 2021 -# -# -------------------------------------------------------------------------- # -# -# Notes: -# -# 1) The default values for assignments are stored in the file: -# foc_assignment_defaults.qdf -# If this file doesn't exist, see file: -# assignment_defaults.qdf -# -# 2) Altera recommends that you do not modify this file. This -# file is updated automatically by the Quartus II software -# and any changes you make may be lost or overwritten. -# -# -------------------------------------------------------------------------- # - - -set_location_assignment PIN_128 -to clk_50m - - -set_location_assignment PIN_142 -to pwm_en -set_location_assignment PIN_143 -to pwm_a -set_location_assignment PIN_137 -to pwm_b -set_location_assignment PIN_136 -to pwm_c - - -set_location_assignment PIN_144 -to spi_ss -set_location_assignment PIN_7 -to spi_mosi -set_location_assignment PIN_10 -to spi_miso -set_location_assignment PIN_11 -to spi_sck - - -set_location_assignment PIN_125 -to i2c_scl -set_location_assignment PIN_121 -to i2c_sda - - -set_location_assignment PIN_105 -to uart_tx - - -set_global_assignment -name FAMILY "Cyclone IV E" -set_global_assignment -name DEVICE EP4CE15E22I7 -set_global_assignment -name TOP_LEVEL_ENTITY top -set_global_assignment -name ORIGINAL_QUARTUS_VERSION 13.1 -set_global_assignment -name PROJECT_CREATION_TIME_DATE "17:37:36 FEBRUARY 05, 2021" -set_global_assignment -name LAST_QUARTUS_VERSION "18.1.0 Standard Edition" -set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files -set_global_assignment -name MIN_CORE_JUNCTION_TEMP "-40" -set_global_assignment -name MAX_CORE_JUNCTION_TEMP 100 -set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1 -set_global_assignment -name NOMINAL_CORE_SUPPLY_VOLTAGE 1.2V -set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top -set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top -set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top -set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V" - -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/top.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/uart_monitor.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/sensors/as5600_read.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/sensors/i2c_register_read.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/sensors/adc_ad7928.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/foc/foc_top.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/foc/clark_tr.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/foc/park_tr.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/foc/sincos.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/foc/pi_controller.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/foc/cartesian2polar.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/foc/svpwm.sv -set_global_assignment -name SYSTEMVERILOG_FILE ../RTL/foc/hold_detect.sv - -set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/README.md b/README.md index c98f604..c7a9e96 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,9 @@ FpOC 该示例的所有代码都在 RTL 目录内。工程在 FPGA 目录内(这是一个 Quartus 工程,但由于本库是可移植性设计,也可以很容易地部署到 VIvado 上,只需要把不可移植的 PLL 核替换掉)。 -## 准备硬件 + + +## 搭建硬件 运行本库的电机驱动需要的硬件包括: @@ -76,22 +78,33 @@ FpOC -## 硬件连接与引脚分配 +## 建立 FPGA 工程 -FPGA 工程的顶层文件是 [RTL/top.sv](./RTL/top.sv) ,它的 IO 连接方法如下: +你需要建立 FPGA 工程,把 RTL 目录(包括其子目录)里的所有 .sv 源文件加入工程,请以 [top.sv](./RTL/top.sv) 作为顶层文件。 -* **clk_50m** : 连接在 FPGA 开发板自带的 **50MHz** 晶振上。 - * 若你的开发板的晶振不是 **50MHz** 也没关系,读 top.sv 就会发现,它用 PLL (altpll 原语) 把 **50MHz** 转换成 **36.864MHz** 去用,所以你只需要修改 PLL 的配置,把输入频率改成板上晶振的实际频率,输出频率仍保持 **36.864MHz** 即可。 +### 时钟配置 + +top.sv 中有一处调用了 altpll 原语,用来把开发板晶振输入的 50MHz 时钟(clk\_50m 信号)变成 36.864MHz 的主时钟(clk 信号),altpll 原语只适用于 Altera Cyclone IV FPGA,如果你用的是其它系列的 FPGA,需要使用它们各自的 IP 核或原语(例如 Xilinx 的 clock wizard)替换它。 + +若你的开发板的晶振不是 50MHz ,你需要修改 PLL 的配置,保证主时钟 clk 信号的频率是 36.864MHz 即可。 + +实际上,主时钟 clk 的频率可以取小于 40MHz 的任意值。clk 是 FOC 系统的驱动时钟,clk 的频率会决定 SVPWM 的频率(SVPWM频率=clk 频率/2048),我选 36.864MHz 是因为可以让 SVPWM 频率 = 36864/2048 = 18kHz,只是为了凑个整数。 + +clk 的频率不能超过 40MHz 的原因是 adc_ad7928.sv 会通过二分频来产生 SPI 时钟(spi_sck),而 ADC7928 芯片要求 SPI 时钟不能超过 20MHz。 + +### 引脚约束 + +top.sv 的 IO 连接方法如下: + +* **clk_50m** : 连接在 FPGA 开发板的晶振上。 * **i2c_scl, i2c_sda** : 连接 AS5600 (磁编码器) 的 I2C 接口。 * **spi_ss, spi_sck, spi_mosi, spi_miso** : 连接 AD7928 (ADC芯片) 的 SPI 接口。 * **pwm_a, pwm_b, pwm_c** : 连接电机驱动板的 3 相 PWM 信号。 * **pwm_en** : 连接电机驱动板的 EN (使能) 信号。 * 如果电机驱动板没有 EN 输入,则不接。 - * 如果电机驱动板有 3 路 EN 输入,每个对应 1 相,则应该进行 1 对 3 连接。 + * 如果电机驱动板有 3 路 EN 输入,每路对应 1 相,则应该进行一对三连接。 * **uart_tx** : 连接 UART 转 USB 模块,插入计算机的 USB 口,用于监测电流环的跟随曲线,**可不接**。 -连接好后别忘了根据实际情况在 Quartus 中选择正确的 FPGA 芯片型号、进行引脚约束。 - ## 调参 @@ -105,8 +118,8 @@ FPGA 工程的顶层文件是 [RTL/top.sv](./RTL/top.sv) ,它的 IO 连接方 - POLE_PAIR:电机极对数,取值范围1~255,根据电机型号决定(注意:电角度ψ = 极对数N * 机械角度φ) - MAX_AMP:SVPWM 的最大振幅,取值范围为1~511,该值越小,电机能达到的最大力矩越小;但考虑到使用3相下桥臂电阻采样法来采样电流,该值也不能太大,以保证3个下桥臂有足够的持续导通时间来供ADC进行采样。在本例中,使用默认值 9'd384 即可。 - SAMPLE_DELAY:采样延时,取值范围0~511,考虑到3相的驱动 MOS 管从开始导通到电流稳定需要一定的时间,所以从3个下桥臂都导通,到 ADC 采样时刻之间需要一定的延时。该参数决定了该延时是多少个时钟周期,当延时结束时,该模块在 sn_adc 信号上产生一个高电平脉冲,指示外部 ADC “可以采样了”。在本例中,使用默认值 9'd120 即可。 -- Kp:PID 的 P 参数 -- Ki:PID 的 I 参数 +- Kp:PID 的 P 参数。 +- Ki:PID 的 I 参数。 你需要根据你所选的电机型号等实际情况,通过修改 [top.sv](./RTL/top.sv) 的 97~103 行来调整这些参数。 @@ -154,8 +167,7 @@ FPGA 工程的顶层文件是 [RTL/top.sv](./RTL/top.sv) ,它的 IO 连接方 | :-- | :-- | :-- | | top.sv | FPGA工程的顶层模块 | | | uart_monitor.sv | UART 发送器,用于数据监测 | 不需要的话可以移除 | -| as5600_read.sv | AS5600 磁编码器读取器 | | -| i2c_register_read.sv | I2C 读取器,被 as5600_read.sv 调用 | | +| i2c_register_read.sv | I2C 读取器,用于读取 AS5600 磁编码器 | | | adc_ad7928.sv | AD7928 读取器 | | | foc_top.sv | FOC+SVPWM (即**图1**中的蓝色部分的顶层) | 固定功能,一般不需要改动 | | clark_tr.sv | Clark 变换 | 固定功能,一般不需要改动 | @@ -295,18 +307,6 @@ AD7928 只有一个T/H,所以采样窗口内要做 3 次采样,这个过程 虽然不同步,但3相的采样毕竟都是在同一个控制周期的采样窗口(几微秒)内。而相电流的变化通常以控制周期(即几十微秒)为尺度变化,例如 3000r/min(50r/s)的转子,若极对数=7,其相电流是350Hz(周期28ms)的正弦波,其变化在几微秒内是可以忽略的。 -### 关于时钟周期和控制周期 - -* **问**: **时钟频率为什么选择36.864MHz?有什么特殊含义嘛?我如果直接用FPGA的50MHz(不经过PLL变成36.864MHz)会出现什么问题呢?** -* **答**: 本库的 SVPWM 的频率被设计为 clk 频率/2048 ,所以选 36.864MHz 只是因为可以让控制周期为 36864/2048 = 18kHz,凑个整。 -foc_top.sv 和 as5600_read.sv 对时钟频率是没有要求的,多高都可以(唯一的限制是频率太高时可能会导致 FPGA 时序不收敛) -但 adc_ad7928.sv 限制了频率不能大于 40MHz,因为 ADC7928 的 SPI 时钟不能超过 20.48MHz,而 adc_ad7928.sv 内部用驱动时钟2分频作为SPI时钟。 -综上,时钟 clk 的频率是可调的,但要小于 40MHz。 - -* **问**: **代码里 控制频率= 时钟频率 / 2048,为什么? 我觉得控制频率和时钟频率应该是没有必然的联系,时钟频率是FPGA的时钟频率,而控制频率是pwm的频率,一般是几十khz,你是根据控制频率来确定的时钟频率嘛?** -* **答**: 从原理上讲,FPGA时钟频率和控制周期确实没有关系,但要考虑实际,因为PWM是由FPGA的时钟驱动的,所以必然有个分频系数(控制频率 = 时钟频率 / 分频系数),别人实现的FOC可能具有可调的分频系数,但FpOC里为了简单,分频系数=2048。这意味着 FpOC 如果要调节控制频率,就只能调时钟频率。 - - # 参考资料 diff --git a/RTL/sensors/as5600_read.sv b/RTL/sensors/as5600_read.sv deleted file mode 100644 index c6348ab..0000000 --- a/RTL/sensors/as5600_read.sv +++ /dev/null @@ -1,37 +0,0 @@ - -// 模块: as5600_read -// Type : synthesizable -// Standard: SystemVerilog 2005 (IEEE1800-2005) -// 功能:通过 I2C 接口从 AS5600 磁编码器中读出转子机械角度 φ -// 参数:详见下方注释 -// 输入输出:详见下方注释 - -module as5600_read #( - parameter [15:0] CLK_DIV = 16'd10 // I2C SCL 时钟信号分频系数,SCL 时钟频率 = clk频率 / (4*CLK_DIV) ,例如若 clk 为 40MHz,CLK_DIV=10,则 SCL 频率为 40/(4*10) = 1MHz。注,AS5600 芯片要求 SCL 频率不超过 1MHz -)( - input wire rstn, - input wire clk, - output wire scl, // I2C SCL 信号 - inout sda, // I2C SDA 信号 - output reg o_en, // 不断读取转子机械角度 φ,每读出一个新值,o_en 产生一个高电平脉冲 - output reg [11:0] o_phi // o_en 产生一个高电平脉冲的同时,o_phi 上产生最新的转自角度,取值范围0~4095。0对应0°;1024对应90°;2048对应180°;3072对应270°。 -); - -wire [15:0] regout; - -assign o_phi = regout[11:0]; - -i2c_register_read #( - .CLK_DIV ( CLK_DIV ) -) i2c_register_read_i ( - .rstn ( rstn ), - .clk ( clk ), - .scl ( scl ), - .sda ( sda ), - .start ( 1'b1 ), - .ready ( ), - .done ( o_en ), - .regout ( regout ) -); - -endmodule diff --git a/RTL/top.sv b/RTL/top.sv index c638859..7a7f8ee 100644 --- a/RTL/top.sv +++ b/RTL/top.sv @@ -7,7 +7,7 @@ // 输入输出:详见下方注释 module top( - input wire clk_50m, // 50MHz 时钟 + input wire clk_50m, // 连接 50MHz 晶振 // ------- 3相 PWM 信号,(包含使能信号) ----------------------------------------------------------------------------------------------------- output wire pwm_en, // 3相共用的使能信号,当 pwm_en=0 时,6个MOS管全部关断。 output wire pwm_a, // A相PWM信号。当 =0 时。下桥臂导通;当 =1 时,上桥臂导通 @@ -44,7 +44,7 @@ reg signed [15:0] iq_aim; // 转子 q 轴(交轴)的目标电流值, -// PLL,用 50MHz 时钟产生 36.864 MHz 时钟 +// PLL,用 50MHz 时钟(clk_50m)产生 36.864 MHz 时钟(clk) // 注:该模块仅适用于 Altera Cyclone IV FPGA ,对于其他厂家或系列的FPGA,请使用各自相同效果的IP核/原语(例如Xilinx的clock wizard)代替该模块。 wire [3:0] subwire0; altpll altpll_i ( .inclk ( {1'b0, clk_50m} ), .clk ( {subwire0, clk} ), .locked ( rstn ), .activeclock (), .areset (1'b0), .clkbad (), .clkena ({6{1'b1}}), .clkloss (), .clkswitch (1'b0), .configupdate (1'b0), .enable0 (), .enable1 (), .extclk (), .extclkena ({4{1'b1}}), .fbin (1'b1), .fbmimicbidir (), .fbout (), .fref (), .icdrclk (), .pfdena (1'b1), .phasecounterselect ({4{1'b1}}), .phasedone (), .phasestep (1'b1), .phaseupdown (1'b1), .pllena (1'b1), .scanaclr (1'b0), .scanclk (1'b0), .scanclkena (1'b1), .scandata (1'b0), .scandataout (), .scandone (), .scanread (1'b0), .scanwrite (1'b0), .sclkout0 (), .sclkout1 (), .vcooverrange (), .vcounderrange ()); @@ -52,16 +52,21 @@ defparam altpll_i.bandwidth_type = "AUTO", altpll_i.clk0_divide_by = 99, altpll -// AS5600 磁编码器读取器,内含简易 I2C 控制器,通过 I2C 接口读取当前转子机械角度 φ -as5600_read #( - .CLK_DIV ( 16'd10 ) // i2c_scl 时钟信号分频系数,scl频率 = clk频率 / (4*CLK_DIV) ,例如在本例中 clk 为 36.864MHz,CLK_DIV=10,则 SCL 频率为 36864/(4*10) = 922kHz 。注,AS5600 芯片要求 SCL 频率不超过 1MHz -) as5600_i ( +// 简易 I2C 读取控制器,实现 AS5600 磁编码器读取,读出当前转子机械角度 φ +wire [3:0] i2c_trash; // 丢弃的高4位 +i2c_register_read #( + .CLK_DIV ( 16'd10 ), // i2c_scl 时钟信号分频系数,scl频率 = clk频率 / (4*CLK_DIV) ,例如在本例中 clk 为 36.864MHz,CLK_DIV=10,则 SCL 频率为 36864/(4*10) = 922kHz 。注,AS5600 芯片要求 SCL 频率不超过 1MHz + .SLAVE_ADDR ( 7'h36 ), // AS5600's I2C slave address + .REGISTER_ADDR( 8'h0E ) // the register address to read +) as5600_read_i ( .rstn ( rstn ), .clk ( clk ), .scl ( i2c_scl ), // I2C 接口: SCL .sda ( i2c_sda ), // I2C 接口: SDA - .o_en ( ), // output: 每成功读取一次 φ,o_en就产生一个高电平脉冲,这里我们用不到该信号 - .o_phi ( phi ) // output: 转子机械角度 φ + .start ( 1'b1 ), // 持续进行 I2C 读操作 + .ready ( ), + .done ( ), + .regout ( {i2c_trash, phi} ) ); diff --git a/figures/diagram.png b/figures/diagram.png index c1b02df..480fe5f 100644 Binary files a/figures/diagram.png and b/figures/diagram.png differ