FPGA实现直流电机转速、电压、电流测量系统(基于EP4CE6F17C8 + INA226)

一、项目背景与目标

在电机控制系统中,实时监测电机的运行状态(如转速、电压、电流)对于系统稳定性、效率优化和故障诊断至关重要。本项目基于FPGA平台,利用Verilog HDL语言,设计一套完整的直流电机运行参数采集与显示系统,目标如下:

  1. Verilog HDL语言设计,Quartus18.1软件开发,FPGA型号EP4CE10,实现对直流电机的转速、工作电压、工作电流的测量;
  2. FPGA控制电机速度,电机驱动采用20kHz PWM信号,支持按键加减占空比控制速度;
  3. 测量电机转速、电机工作电压和电流,数码管 默认显示转速,通过按键切换显示电压或电流;
  4. 超过设定速度,蜂鸣器报警;
  5. 其他功能可定制;

二、系统整体架构

系统由以下几个模块组成:

  • PWM生成模块:输出20kHz、占空比可调的PWM信号,用于驱动直流电机;
  • 转速测量模块:通过霍尔传感器或编码器输出的脉冲信号,利用FPGA计数器测量转速(单位:RPM);
  • INA226通信模块:通过I²C总线读取INA226传感器的电压、电流数据;
  • 数码管驱动模块:动态扫描6位共阴数码管,显示当前参数;
  • 按键控制模块:检测按键状态,切换显示内容(转速 → 电压 → 电流 → 转速);
  • 蜂鸣器模块:检测转速/电压/电流超限时,警告提示用户;
  • 顶层模块:整合上述子模块,完成系统协同工作。

三、关键模块设计详解

1. PWM生成模块(20kHz可调)

FPGA系统时钟为50MHz,要生成20kHz PWM,周期为50μs,对应2500个时钟周期(FREQ_DIV_TIME = 50MHz / 20kHz = 2500),同时可手动给定占空比控制PWM脉宽。

cpp 复制代码
module pwm_controller #(
    parameter SYS_FREQ = 'd50_000_000,
    parameter PWM_FREQ = 'd20_000
)(
    input wire clk,         // 系统时钟
    input wire reset_n,     // 复位信号
    input wire [7:0] duty,  // 占空比 (0-100)
    output reg pwm_out      // PWM输出信号
);

    localparam FREQ_DIV_TIME = SYS_FREQ / PWM_FREQ;

    reg [15:0] counter;         // 计数器 用于生成PWM周期
    reg [7:0] duty_cycle_reg;   // 内部寄存器存储占空比

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            counter <= 16'd0;       // 复位时清零计数器
            pwm_out <= 1'b0;        // 复位时PWM输出为低电平
            duty_cycle_reg <= 8'd0; // 复位时占空比为0
        end else begin
            if (counter >= FREQ_DIV_TIME - 1) begin
                counter <= 16'd0;   // 计数器达到最大值后重置
            end else begin
                counter <= counter + 1; // 计数器递增
            end

            // 更新占空比寄存器
            duty_cycle_reg <= duty;

            // 根据占空比和计数器生成PWM信号
            if (counter < (FREQ_DIV_TIME * duty_cycle_reg) / 100) begin
                pwm_out <= 1'b1;    // 高电平
            end else begin
                pwm_out <= 1'b0;    // 低电平
            end
        end
    end

endmodule

说明 :通过修改duty值(如50 = 50%占空比),可调节电机转速。


2. 转速测量模块

假设电机每转输出N个脉冲(如霍尔传感器N=1,编码器N=12等),读取编码器数值并通过"频率法+编码倍频"实现转速测量:

  • 在100毫秒定时窗口内计数脉冲数;
  • 转速 RPM = (count / N) * 60 * (1000 / 100)。

其中关于AB相输出倍频的代码片段如下:

cpp 复制代码
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            A_prev <= 1'b0;
            B_prev <= 1'b0;
        end else begin
            A_prev <= A_debounced;
            B_prev <= B_debounced;
        end
    end

    // 边沿检测
    assign A_rising = ~A_prev & A_debounced;  // A相上升沿
    assign A_falling = A_prev & ~A_debounced; // A相下降沿
    assign B_rising = ~B_prev & B_debounced;  // B相上升沿
    assign B_falling = B_prev & ~B_debounced; // B相下降沿

代码中给定TIME_WINDOW计数值为100毫秒的时间,计数时间到后,转速输出的计算公式如下:

cpp 复制代码
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            timer_counter <= 0;
        end else begin
            /* 定时器与转速计算 */
            if (timer_counter < TIME_WINDOW) begin
                timer_counter <= timer_counter + 1;
            end else begin
                rpm <= (count * 60000 / time_period) / TOTAL_PULSES_PER_REV; // RPM = 转/min
                timer_counter <= 0;
            end
        end
    end

3. INA226 I²C通信模块

INA226是一款高精度电流/电压监测芯片,支持I²C接口。FPGA需实现I²C主控逻辑,读取寄存器:

  • **Config Register(0x00):**配置寄存器
  • Bus Voltage Register (0x02):总线电压(mV)
  • Current Register (0x04):电流(mA,需根据校准值计算)
  • Power Register(0x03): 功率寄存器
  • Calib Register(0x05): 校准寄存器(写入值用于配置最大采样电流)

该部分的I2C配置代码通用,关于配置寄存器的配置为:0x4527,转换为2进制为:0100_010_100_100_111,代表设备采用16次的采样平均值,其中电压采样周期为1.1ms,电流采样周期为1.1ms,并使用连续测量的工作模式去采集数据。

校准寄存器的配置计算如下:

其中分母为固定值,分子中SetCurrent为需要设置的采样电流大小,默认单位为安培(A),0.1是INA226的默认采样电阻阻值0.1欧姆。贴出部分代码:

cpp 复制代码
module ina226_measure (
    input               clk,        // FPGA时钟输入
    input               rst_n,      // 复位信号
    output reg          start,      // I2C触发执行信号
    output reg          wr_flag,    // I2C读写控制信号
    output reg [ 7:0]   reg_addr,   // I2C器件内地址
    output reg [15:0]   wdata,      // I2C要写的数据
    input      [15:0]   rdata,      // I2C读出的数据
    input               done,       // I2C一次操作完成
    // 输出传感器数据
    output reg [19:0]   voltage,    // 输出电压值 单位 mV
    output reg [19:0]   current,    // 输出电流值 单位 mA
    output reg [19:0]   power       // 输出功率值 单位 mW
);

    // INA226 寄存器地址和初始值定义(均为8位地址 寄存器数据16位)
    parameter CONFIG_REG  = 8'h00;  // 配置寄存器地址
    parameter VOLTAGE_REG = 8'h02;  // 电压寄存器地址
    parameter CURRENT_REG = 8'h04;  // 电流寄存器地址
    parameter POWER_REG   = 8'h03;  // 功率寄存器地址
    parameter CALIB_REG   = 8'h05;  // 校准寄存器地址
    
    parameter CONFIG_VAL  = 16'h4527;   // 配置寄存器写入值
    parameter CALIB_VAL   = 16'h00A8;   // 校准寄存器写入值 最大10A
                                        // 计算公式 CAL = 0.00512 / (10/32768) / 0.1Ω
    

4. 按键与数码管显示模块

按键消抖,使用20ms延时消抖:

cpp 复制代码
    // 参数计算
    localparam CNT_MAX = CLK_FREQ / 1000 * DEBOUNCE_TIME; // 计数器最大值

    // 内部寄存器定义
    integer i;
    reg [KEY_NUM-1:0]  key_reg;     // 按键输入寄存器
    reg [31:0] delay_cnt [7:0];     // 每个按键独立的计数器

    // 按键输入同步与计数器控制逻辑
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n) begin
            key_reg <= 1;       // 初始化按键寄存器为高电平
            for (i = 0; i < KEY_NUM; i = i + 1)
                delay_cnt[i] <= 32'd0;    // 初始化计数器
        end else begin
            key_reg <= key_in;            // 同步按键输入
            for (i = 0; i < KEY_NUM; i = i + 1) begin
                if (key_reg[i] != key_in[i])    // 如果按键状态发生变化
                    delay_cnt[i] <= CNT_MAX;    // 重置计数器
                else if (delay_cnt[i] > 0)      // 如果按键状态稳定且计数器未归零
                    delay_cnt[i] <= delay_cnt[i] - 1; // 计数器递减
            end
        end
    end

数码管动态扫描:6位数码管,每位显示0~9,支持显示rpm(0~999999)、电压(0~36V → 显示为36000表示36.0V)、电流(0~10000mA)。

cpp 复制代码
//cnt_sel从0计数到5,用于选择当前处于显示状态的数码管
always @ (posedge dri_clk or negedge rst_n) begin
    if (rst_n == 1'b0)
        cnt_sel <= 3'b0;
    else if(flag) begin
        if(cnt_sel < 3'd5)
            cnt_sel <= cnt_sel + 1'b1;
        else
            cnt_sel <= 3'b0;
    end
    else
        cnt_sel <= cnt_sel;
end

再配合7段译码器输出到数码管。


四、顶层模块整合

cpp 复制代码
module motor_measure_top (
    input           sys_clk,        /* 系统时钟输入 */
    input           sys_rst_n,      /* 系统复位输入 */
    
    /* 蜂鸣器相关 */
    output          beep,           /* 蜂鸣器引脚 */
    
    /* 编码器相关 */
    input           A,              /* 编码器A相 */
    input           B,              /* 编码器B相 */
    
    /* 电机相关 */
    output          IN1,            /* IN1 */
    output          IN2,            /* IN2 */
    output          pwm_out,        /* PWM占空比输出 */
    
    /* 按键相关 */
    input   [4:0]   key,            /* 5个按键输入 */

    /* INA226电压电流采样模块 */
    inout           sda,            /* INA226 SDA */
    output          scl,            /* INA226 SCL */

    /* 数码管相关 */
    output  [5:0]   smg_bit,        /* 数码管位选 */
    output  [7:0]   smg_seg         /* 数码管段选 */
);

// 内部信号声明...
// 实例化各子模块...

endmodule

五、硬件连接说明

5.1 蜂鸣器及按键

FPGA引脚 外设功能
M7 蜂鸣器输出
M15 按键输入K5
M16 按键输入K4
L10 按键输入K3
K10 按键输入K2
M2 按键输入K1

5.2 6位数码管

FPGA引脚 外设功能
A2 数码管位选6
A3 数码管位选5
A4 数码管位选4
B5 数码管位选3
A5 数码管位选2
E6 数码管位选1
C8 数码管段选.
A10 数码管段选G
B9 数码管段选F
E7 数码管段选E
A7 数码管段选D
D8 数码管段选C
K8 数码管段选B
A9 数码管段选A

5.3 TB6612驱动+带编码器的直流电机

FPGA引脚 外设功能
C9 TB6612电机驱动的通道A_IN1输入
E10 TB6612电机驱动的通道A_IN2输入
E9 TB6612电机驱动的通道PWM输入
P15 带编码功能的直流电机编码器A相
P16 带编码功能的直流电机编码器B相

5.4 INA226采样模块

FPGA引脚 外设功能
B8 INA226 SCL
F3 INA226 SDA

注意:INA226的Vbus接电机电源正极,Shunt电阻串联在GND回路中。


六、测试与效果

  • 上电后数码管默认显示电机转速;
  • 按下按键,依次切换为电压(如 10.345表示10.345V)、电流(如2.875 表示2.875A);
  • 调节PWM占空比,可观察转速与电流同步变化;
  • 系统响应快,无明显延迟,满足实时监测需求。

源码不开源,需要的友友可以私聊,如需课程设计定制也可私聊。


相关推荐
清风6666663 小时前
基于单片机的元胞自动机仿真系统设计
单片机·嵌入式硬件·毕业设计·课程设计
测试专家4 小时前
HKM9000视频处理卡
fpga开发
点灯小铭4 小时前
基于单片机的N型热电偶PID锅炉温度控制系统
单片机·嵌入式硬件·毕业设计·课程设计
li星野9 小时前
打工人日报#20251009
fpga开发
cycf10 小时前
Vivado 时序约束的完整作战地图(二)
fpga开发
cycf10 小时前
时钟周期约束(三)
fpga开发
szxinmai主板定制专家10 小时前
RK3588+AI算力卡替代英伟达jetson方案,大算力,支持FPGA自定义扩展
arm开发·人工智能·分布式·fpga开发
ARM+FPGA+AI工业主板定制专家12 小时前
基于NVIDIA ORIN+FPGA+AI自动驾驶硬件在环注入测试
人工智能·fpga开发·机器人·自动驾驶
bnsarocket12 小时前
Verilog和FPGA的自学笔记4——多路选择器(always语句)
笔记·fpga开发·编程·verilog·自学·硬件编程