SPI通信——FPGA学习笔记14

一、简介

        SPI(Serial Periphera Interface,串行外围设备接口)通讯协议,是 Motorola 公司提出的一种同步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输,广泛用于 EEPROM、FIash、RTC(实时时钟)、ADC(数模转换器)、DSP(数字信号处理器)以及数字信号解码器上,是常用的、重要的低速通讯协议之一。        

多从机

常规寻址:

通信过程:

SPI协议

时钟极性(CPOL):空闲时时钟的电平极性

时钟相位(CPHA):控制数据更新时刻和采样时刻

CPOL=0,CPHA=0

Sendstrobe:内部发送模块发送的触发信号;Capstrobe:内部接收数据模块采样的触发信号

CPOL=0,CPHA=1

CPOL=1,CPHA=0

 CPOL=1,CPHA=1

 

驱动基本模块

FPGA时序要求

二、程序设计

1、本实验设计一个SPI发送驱动,包含SPI四种工作模式

2、握手信号

        只有当req请求信号拉高并低电平时才会开启一次数据传输,数据传输过程中busy信号拉高表示设备忙。

3、SPI发送状态机控制器设计

`timescale 1ns / 1ps

module spi_tx#(
    parameter CLK_DIV   = 'd100 ,   //     
    parameter CPOL      = 1'b0  ,   //时钟极性
    parameter CPHA      = 1'b0      //时钟相位
)
(   
    input           I_clk           ,   //系统时钟
    input           I_rstn          ,   //系统复位
    input           I_spi_tx_req    ,   //发送数据请求
    input   [7:0]   I_spi_tx_data   ,   //发送数据

    output          O_spi_mosi      ,   //输出SPI数据
    output          O_spi_sclk      ,   //输出SPI时钟
    output          O_spi_busy          //输出忙信号
    
);

localparam  [9:0]   SPI_DIV     =   CLK_DIV     ;   //第二时钟边沿计数器
localparam  [9:0]   SPI_DIV1    =   SPI_DIV / 2 ;   //第一时钟边沿计数器    //分频系数一半

reg [9:0]   clk_div         ;
reg         spi_en          ;       //发送使能
reg [3:0]   tx_cnt          ;
reg         spi_clk         ;
reg [7:0]   I_spi_tx_data_r ;  
reg         spi_strobe_en   ; 


wire        clk_end         ;   //
wire        clk_en1         ;   //第一内部时钟使能
wire        clk_en2         ;   //第二内部时钟使能
wire        spi_strobe      ;



//计数器发送第一个时钟0-7次,当计数达到8时,不发送时钟
assign      clk_en1     = (clk_div == SPI_DIV1);    //第一内部时钟边沿使能      一半
assign      clk_en2     = (clk_div == SPI_DIV );    //第二内部时钟边沿使能      记满
assign      clk_end     = ((clk_div == SPI_DIV1)&&(tx_cnt == 4'd8));    //时钟结束信号    计数半个周期


//当CPHA=0时,数据的第一个SCLK转换边缘被采样,因此数据更新在第二个转换边缘上
//当CPHA=1时,数据的第二个SCLK转换边缘被采样,因此数据更新在第一个转换边缘上
assign      spi_strobe = CPHA ?  clk_en1 & spi_strobe_en : clk_en2 & spi_strobe_en;
assign      O_spi_sclk  = (CPOL == 1'b1) ? !spi_clk : spi_clk ;         //设置SPI初始时钟
assign      O_spi_mosi  = I_spi_tx_data_r[7] ;                          //SPI输出数据
assign      O_spi_busy  = spi_en;



//SPI时钟计数器
always @(posedge I_clk ) begin
    if (spi_en == 1'b0) begin
        clk_div <= 10'd0;
    end 
    else if(clk_div < SPI_DIV)begin
        clk_div <= clk_div + 1'b1;
    end
    else begin
        clk_div <= 10'd0;
    end
end

//SPI时钟生成
always @(posedge I_clk ) begin
    if (spi_en == 1'b0) begin
        spi_clk <= 1'b0;    
    end 
    else if(clk_en2 == 1'b1)begin
        spi_clk <= 1'b0;
    end
    else if ((clk_en1 == 1'b1)&&(tx_cnt < 4'd8)) begin
        spi_clk <= 1'b1;
    end 
    else begin
        spi_clk <= spi_clk;    
    end
end

//SPI bit计数器
always @(posedge I_clk ) begin
    if ((!I_rstn)||(spi_en == 1'b0)) begin
        tx_cnt <= 4'd0;
    end 
    else if(clk_en1 == 1'b1)begin
        tx_cnt <= tx_cnt + 1'b1;
    end
end

//
always @(posedge I_clk ) begin
    if (!I_rstn) begin
        spi_strobe_en <= 1'b0;
    end 
    else if(tx_cnt < 4'd8)begin
        if (clk_en1 == 1'b1) begin
            spi_strobe_en <= 1'b1;
        end 
        else begin
            spi_strobe_en <= spi_strobe_en; 
        end    
    end
    else begin
        spi_strobe_en <= 1'b0;
    end
end

//SPI发送模块
always @(posedge I_clk ) begin
    if ((!I_rstn)||(clk_end == 1'b1)) begin
        spi_en <= 1'b0;
        I_spi_tx_data_r <= 8'b0;
    end 
    else if((I_spi_tx_req == 1'b1)&&(spi_en == 1'b0))begin      //启动传输
        spi_en <= 1'b1;
        I_spi_tx_data_r <=  I_spi_tx_data;
    end
    else if (spi_en == 1'b1) begin
        I_spi_tx_data_r[7:0] <= (spi_strobe)?{I_spi_tx_data_r[6:0],1'b1} : I_spi_tx_data_r;
    end
end


endmodule
`timescale 1ns / 1ps

module spi_master_tx#(
    parameter   CLK_DIV = 100
)
(
    input   I_clk       ,   //
    input   I_rstn      ,   //
    output  O_spi_sclk  ,   //
    output  O_spi_mosi      //
);

wire        spi_busy    ;   //spi忙的标志
reg         spi_tx_req  ;   //spi请求发送
reg [7:0]   spi_tx_data ;   //spi待发送数据
reg [1:0]   M_S         ;   //状态机


always @(posedge I_clk ) begin
    if (!I_rstn) begin
        spi_tx_req  <=  1'b0;
        spi_tx_data <=  8'd0;
        M_S         <=  2'd0;
    end 
    else begin
        case (M_S)
            0:begin
                if (spi_busy == 1'b0) begin
                    spi_tx_req   <= 1'b1;       //请求发送
                    spi_tx_data  <= spi_tx_data + 1'b1;     //测试累加数据
                    M_S          <= 2'd1; 
                end 
            end 
            1:begin
                if (spi_busy == 1'b1) begin     //清楚请求信号
                    spi_tx_req <= 1'b0;
                    M_S        <= 2'd0;
                end
            end
            default:M_S        <= 2'd0;
        endcase    
    end
end


spi_tx#(
    .CLK_DIV   (CLK_DIV),   //     
    .CPOL      (1'b0   ),   //时钟极性
    .CPHA      (1'b0   )    //时钟相位
)
u_spi_tx(   
    .I_clk           (I_clk         ),   //系统时钟
    .I_rstn          (I_rstn        ),   //系统复位
    .I_spi_tx_req    (spi_tx_req    ),   //发送数据请求
    .I_spi_tx_data   (spi_tx_data   ),   //发送数据
    .O_spi_mosi      (O_spi_mosi    ),   //输出SPI数据
    .O_spi_sclk      (O_spi_sclk    ),   //输出SPI时钟
    .O_spi_busy      (spi_busy      )    //输出忙信号
);


endmodule

 

`timescale 1ns / 1ps


module tb( );

localparam  SYS_TIME = 4'd10;

reg I_clk;
reg I_rstn;

wire    O_spi_mosi;
wire    O_spi_sclk;

spi_master_tx#(
    .CLK_DIV (100)
)
u_spi_master_tx(
    .I_clk       (I_clk         ),   //
    .I_rstn      (I_rstn        ),   //
    .O_spi_sclk  (O_spi_sclk    ),   //
    .O_spi_mosi  (O_spi_mosi    )    //
);

initial begin
    I_clk = 0;
    I_rstn = 0;
    #100;
    I_rstn = 1;
end

always #(SYS_TIME / 2) I_clk = !I_clk;

endmodule

 00

4、SPI接收模块

 

`timescale 1ns / 1ps

module spi_rx#
(
    parameter   BITS_LEM    =   8       ,//bit数量
    parameter   CPOL        =   1'b0    ,
    parameter   CPHA        =   1'b0     
)
(   
    input                       I_clk       ,   //系统时钟
    input                       I_rstn      ,   //系统复位
    input                       I_spi_clk   ,   //SPI时钟
    input                       I_spi_rx    ,   //SPI rx数据总线
    input                       I_spi_ss    ,   //SPI片选信号

    output                      O_spi_rvalid,   //SPI rx接收数据有效信号  1:rdata有效     0:无效
    output [BITS_LEM - 1'b1:0]  O_spi_rdata     //SPI rx接收到的数据输出

);

reg [3:0]               spi_clk_r   ;       //时钟打拍
reg [3:0]               spi_ss_r    ;       //片选打拍
reg                     spi_cap     ;       //采样信号
reg [3:0]               spi_bit_cnt ;       //bit计数器
reg [BITS_LEM - 1'b1:0] spi_rx_r1   ;       //接收缓存

wire                    spi_rx_en   ;       //接收使能
wire                    spi_clkp    ;       //spi上升沿
wire                    spi_clkn    ;       //spi下升沿


assign  spi_clkp        =   (spi_clk_r[3:2] == 2'b01)   ;        //spi上升沿
assign  spi_clkn        =   (spi_clk_r[3:2] == 2'b10)   ;        //spi下升沿
assign  spi_rx_en       =   (!spi_ss_r[3])              ;

assign  O_spi_rdata     =   spi_rx_r1                   ;
assign  O_spi_rvalid    =   (spi_bit_cnt == BITS_LEM)   ;


//I_spi_clk去毛刺
always @(posedge I_clk or negedge I_rstn ) begin
    if (!I_rstn) begin
        spi_clk_r <= 1'b0;
    end 
    else begin
       spi_clk_r <= {spi_clk_r[2:0],I_spi_clk};     
    end
end

//I_spi_ss去毛刺
always @(posedge I_clk or negedge I_rstn ) begin
    if (!I_rstn) begin
        spi_ss_r <= 1'b0;
    end 
    else begin
       spi_ss_r <= {spi_ss_r[2:0],I_spi_ss};     
    end
end

//cap信号生成    何时采样
always @( * ) begin
    if (CPHA == 1'b1) begin
        if (CPOL == 1'b1) begin
            spi_cap = spi_clkn;        //CPHA = 1   CPOL = 1
        end 
        else begin
            spi_cap = spi_clkp;        //CPHA = 1   CPOL = 0   
        end
    end 
    else begin
        if (CPOL == 1'b1) begin
            spi_cap = spi_clkn;         //CPHA = 0   CPOL = 1 
        end 
        else begin
            spi_cap = spi_clkp;         //CPHA = 0   CPOL = 0 
        end
    end
end

//bit计数器
always @(posedge I_clk ) begin
    if ((spi_rx_en == 1'b1)&&(spi_bit_cnt < BITS_LEM)&&(spi_cap == 1'b1)) begin    //开启接收,并且未达到计数最大值
        spi_bit_cnt <= spi_bit_cnt + 1'b1;
    end 
    else if((spi_rx_en == 1'b0)||(spi_bit_cnt == BITS_LEM))begin    //未开启接收    或计数到最大追
        spi_bit_cnt <= 4'd0;
    end
end

//bit移位  串转并
always @(posedge I_clk ) begin
    if ((spi_rx_en == 1'b1)&&(spi_cap == 1'b1)) begin
        spi_rx_r1 <= {spi_rx_r1[BITS_LEM - 2:0],I_spi_rx};          //移位寄存   高位在前
    end 
    else if(spi_rx_en == 1'b0)begin
        spi_rx_r1 <= 'd0;
    end
end







endmodule
`timescale 1ns / 1ps


module tb( );

localparam   BYTES       =   8       ;
localparam   CPOL        =   1'b0    ;
localparam   CPHA        =   1'b0    ;

localparam   TCNT        =   BYTES * 8 * 2 - 1;

reg             I_clk               ;       //系统时钟
reg     [7:0]   i                   ;       //计数器,产生SPI时钟           
reg             I_rstn              ;       //系统复位
reg             I_spi_clk           ;       //SPI时钟
reg             I_spi_ss            ;       //SPI片选
reg     [3:0]   bit_cnt             ;       //bit计数器           
reg     [7:0]   spi_tx_buf          ;       //发送缓冲(移位寄存器)
reg     [7:0]   spi_tx_buf_r        ;       //发送化缓冲,用于生成测试文件
reg             first_data_flag     ;       //是否是一个时钟该变数据           

wire            O_spi_rvalid        ;       //SPI数据接收有效
wire    [7:0]   O_spi_rdata         ;       
wire            I_spi_rx            ;  


assign  I_spi_rx = spi_tx_buf[7];




spi_rx#
(
    .BITS_LEM    (8      ) ,//bit数量
    .CPOL        (1'b0   ) ,
    .CPHA        (1'b0   )  
)
u_spi_rx(   
    .I_clk       (I_clk         ),   //系统时钟
    .I_rstn      (I_rstn        ),   //系统复位
    .I_spi_clk   (I_spi_clk     ),   //SPI时钟
    .I_spi_rx    (I_spi_rx      ),   //SPI rx数据总线
    .I_spi_ss    (I_spi_ss      ),   //SPI片选信号

    .O_spi_rvalid(O_spi_rvalid  ),   //SPI rx接收数据有效信号  1:rdata有效     0:无效
    .O_spi_rdata (O_spi_rdata   )    //SPI rx接收到的数据输出
);

initial begin
    I_clk = 0;
    I_rstn = 0;
    #100;
    I_rstn = 1;
end

always #5 I_clk = !I_clk;

initial begin
    #100
    i = 0;

    forever begin
        I_spi_clk = CPOL;
        I_spi_ss = 1;
        #2000
        I_spi_ss = 0;
        for (i = 0;i<TCNT ;i = i + 1 ) begin
            #1000
            I_spi_clk = !I_spi_clk;
        end
        #2000
        I_spi_ss = 1;
    end
end

initial begin
    #100
    bit_cnt = 0;
    first_data_flag = 0;
    spi_tx_buf[7:0] = 8'ha0;
    spi_tx_buf_r[7:0] = 5'ha0;
    forever begin
        wait(I_spi_ss);
        bit_cnt = 0;
        spi_tx_buf[7:0] =   8'ha0;
        spi_tx_buf_r[7:0] = 8'ha0; 
        if ((CPHA == 1 && CPOL == 0)||(CPHA == 1 && CPOL == 1)) begin
            first_data_flag = 1;
        end

        wait(!I_spi_ss);
        
        while (!I_spi_ss) begin
            if (CPHA == 0 && CPOL == 0) begin
                @(negedge I_spi_clk)begin
                    if (bit_cnt == 7) begin
                        bit_cnt = 0;
                        spi_tx_buf_r = spi_tx_buf_r + 1'b1;
                        spi_tx_buf = spi_tx_buf_r;
                    end 
                    else begin
                        spi_tx_buf = {spi_tx_buf[6:0],1'b0};
                        bit_cnt = bit_cnt + 1'b1;
                    end
                end
            end 

            if (CPHA == 0 && CPOL == 1) begin
                @(posedge I_spi_clk)begin
                    if (bit_cnt == 7) begin
                        bit_cnt = 0;
                        spi_tx_buf_r = spi_tx_buf_r + 1'b1;
                        spi_tx_buf = spi_tx_buf_r;
                    end 
                    else begin
                        spi_tx_buf = {spi_tx_buf[6:0],1'b0};
                        bit_cnt = bit_cnt + 1'b1;
                    end
                end
            end

            if (CPHA == 1 && CPOL == 0) begin
                @(posedge I_spi_clk)begin
                    if (first_data_flag == 1'b1) begin
                        first_data_flag = 0;
                    end
                    else begin
                        if (bit_cnt == 7) begin
                            bit_cnt = 0;
                            spi_tx_buf_r = spi_tx_buf_r + 1'b1;
                            spi_tx_buf = spi_tx_buf_r;
                        end 
                        else begin
                            spi_tx_buf = {spi_tx_buf[6:0],1'b0};
                            bit_cnt = bit_cnt + 1'b1;
                        end
                    end
                end
            end  
            
            if (CPHA == 1 && CPOL == 1) begin
                @(negedge I_spi_clk)begin
                    if (first_data_flag == 1'b1) begin
                        first_data_flag = 0;
                    end
                    else begin
                        if (bit_cnt == 7) begin
                            bit_cnt = 0;
                            spi_tx_buf_r = spi_tx_buf_r + 1'b1;
                            spi_tx_buf = spi_tx_buf_r;
                        end 
                        else begin
                            spi_tx_buf = {spi_tx_buf[6:0],1'b0};
                            bit_cnt = bit_cnt + 1'b1;
                        end
                    end
                end
            end  
        end
    end
end





endmodule

三、仿真验证

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/886485.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于STM32的智能家居灯光控制系统设计

引言 本项目将使用STM32微控制器实现一个智能家居灯光控制系统&#xff0c;能够通过按键、遥控器或无线模块远程控制家庭照明。该项目展示了如何结合STM32的外设功能&#xff0c;实现对灯光的智能化控制&#xff0c;提升家居生活的便利性和节能效果。 环境准备 1. 硬件设备 …

C--编译和链接见解

欢迎各位看官&#xff01;如果您觉得这篇文章对您有帮助的话 欢迎您分享给更多人哦 感谢大家的点赞收藏评论 感谢各位看官的支持&#xff01;&#xff01;&#xff01; 一&#xff1a;翻译环境和运行环境 在ANSIIC的任何一种实现中&#xff0c;存在两个不同的环境1&#xff0c;…

戴尔电脑怎么开启vt虚拟化_戴尔电脑新旧机型开启vt虚拟化教程

最近使用戴尔电脑的小伙伴们问我&#xff0c;戴尔电脑怎么开启vt虚拟。大多数可以在Bios中开启vt虚拟化技术&#xff0c;当CPU支持VT-x虚拟化技术&#xff0c;有些电脑会自动开启VT-x虚拟化技术功能。而大部分的电脑则需要在Bios Setup界面中&#xff0c;手动进行设置&#xff…

SpringCloud入门(九)Feign实战应用和性能优化

一、Feign实战应用 Feign的客户端与服务提供者的controller代码非常相似&#xff1a; 有没有一种办法简化这种重复的代码编写呢&#xff1f; 方式一&#xff1a;继承 优点&#xff1a; 简单。实现了代码共享。 缺点&#xff1a;服务提供方、服务消费方紧耦合。参数列表中的注解…

vscode安装及c++配置编译

1、VScode下载 VS Code官网下载地址&#xff1a;Visual Studio Code - Code Editing. Redefined。 2、安装中文插件 搜索chinese&#xff0c;点击install下载安装中文插件。 3、VS Code配置C/C开发环境 3.1、MinGW-w64下载 VS Code是一个高级的编辑器&#xff0c;只能用来写代…

Coggle数据科学 | Kaggle赛题解析:CMI 体育损伤指数预测

本文来源公众号“Coggle数据科学”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;Kaggle赛题解析&#xff1a;CMI 体育损伤指数预测 赛题名称&#xff1a;Child Mind Institute — Problematic Internet Use 赛题类型&#xff1a…

Windows 环境上安装 NASM 和 YASM 教程

NASM 和 YASM NASM NASM&#xff08;Netwide Assembler&#xff09;是一个开源的、可移植的汇编器&#xff0c;它支持多种平台和操作系统。它可以用来编写16位、32位以及64位的代码&#xff0c;并且支持多种输出格式&#xff0c;包括ELF、COFF、OMF、a.out、Mach-O等。NASM使用…

GS-SLAM论文阅读笔记--GEVO

前言 这篇文章看着就让人好奇。众所周知&#xff0c;高斯是一个很不错的建图方法&#xff0c;但是本文的题目居然是只用高斯进行单目VO&#xff0c;咱也不知道这是怎么个流程&#xff0c;看了一下作者来自于MIT&#xff0c;说不定是个不错的工作&#xff0c;那就具体看看吧&am…

LeetCode从入门到超凡(五)深入浅出---位运算

引言 大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年9月学习赛的LeetCode学习总结文档&#xff1b;本文主要讲解 位运算算法。&#x1f495;&#x1f495;&#x1f60a; 一、 位运算简介 1.什么是位…

简易CPU设计入门:取指令(三),ip_buf与rd_en的非阻塞赋值

在开篇&#xff0c;还是请大家首先准备好本项目所用的源代码。如果已经下载了&#xff0c;那就不用重复下载了。如果还没有下载&#xff0c;那么&#xff0c;请大家点击下方链接&#xff0c;来了解下载本项目的CPU源代码的方法。 下载本项目代码 准备好了项目源代码以后&…

【重学 MySQL】五十一、更新和删除数据

【重学 MySQL】五十一、更新和删除数据 更新数据删除数据注意事项 在MySQL中&#xff0c;更新和删除数据是数据库管理的基本操作。 更新数据 为了更新&#xff08;修改&#xff09;表中的数据&#xff0c;可使用UPDATE语句。UPDATE语句的基本语法如下&#xff1a; UPDATE ta…

【ADC】噪声(1)噪声分类

概述 本文学习于TI 高精度实验室课程&#xff0c;总结 ADC 的噪声分类&#xff0c;并简要介绍量化噪声和热噪声。 文章目录 概述一、ADC 中的噪声类型二、量化噪声三、热噪声四、量化噪声与热噪声对比 一、ADC 中的噪声类型 ADC 固有噪声由两部分组成&#xff1a;第一部分是量…

【重学 MySQL】四十六、创建表的方式

【重学 MySQL】四十六、创建表的方式 使用CREATE TABLE语句创建表使用CREATE TABLE LIKE语句创建表使用CREATE TABLE AS SELECT语句创建表使用CREATE TABLE SELECT语句创建表并从另一个表中选取数据&#xff08;与CREATE TABLE AS SELECT类似&#xff09;使用CREATE TEMPORARY …

【JVM】垃圾释放方式:标记-清除、复制算法、标记-整理、分代回收

文章目录 1. 标记-清除2. 复制算法4. 标记-整理4. 分代回收 把标记为垃圾的对象的内存空间进行释放。主要有三种释放方式 1. 标记-清除 把标记为垃圾的对象&#xff0c;直接释放掉&#xff08;最朴素的做法&#xff09; 此时就是把标记为垃圾的对象所对应的内存空间直接释放。…

本地化测试对游戏漏洞修复的影响

本地化测试在游戏开发的质量保证过程中起着至关重要的作用&#xff0c;尤其是在修复bug方面。当游戏为全球市场做准备时&#xff0c;它们通常会被翻译和改编成各种语言和文化背景。这种本地化带来了新的挑战&#xff0c;例如潜在的语言错误、文化误解&#xff0c;甚至是不同地区…

k8s中,ingress的实现原理,及其架构。

图片来源&#xff1a;自己画的 图片来源&#xff1a;k8s官网 首先&#xff0c;什么是ingress? 是服务还是控制器&#xff1f; 都不精确 ingress是一个api资源 service和deployment也是api资源。 这几个相互协作&#xff0c;组建成一个对外提供服务的架构。 ingress提供的…

MDM监管锁系统上锁流程

上锁与解锁 上锁设备 完整的上锁流程可参考: https://b23.tv/UvM35sU 上锁需要已经注册了一个普通用户 并使用管理员分配了台数 且有可用的MDM证书和ABM证书(公有和私有的都可以 只要有可用的就可以) 一部用来上锁的手机 链接wifi wifi必须要是2.4g频段 不要使用5gwifi 上锁…

HTTPS协议详解:从原理到流程,全面解析安全传输的奥秘

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

(done) 声音信号处理基础知识(11) (Complex Numbers for Audio Signal Processing)

参考&#xff1a;https://www.youtube.com/watch?vDgF4m0AWCgA&t1047s 似乎是因为信号处理需要使用复数&#xff0c;作者花了一节课介绍复数 据油管主所说&#xff0c;声学信号处理中引入复数的原因是&#xff1a;快速完成部分计算 这里的例子是&#xff0c;当我们做傅里…

行为型模式-命令-迭代-观察者-策略

命令模式 是什么 将一个请求封装成为一个对象, 从而可以使用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及可以撤销的操作 实例 请求封装成为对象 //用来声明执行操作的接口 public abstract class Command { protected Receiver receiver; public Comma…