SPI通信——FPGA学习笔记14

news2024/10/9 10:26:37

一、简介

        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.coloradmin.cn/o/2198769.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【GESP】C++一级练习BCQM3033,略微复杂的计算,国庆七天乐

应该算第一道对小学生来说&#xff0c;计算逻辑稍微复杂一点的题目。多定义几个变量可能对解题过程更有帮助。 题解详见&#xff1a;https://www.coderli.com/gesp-1-bcqm3033/ 【GESP】C一级练习BCQM3033&#xff0c;略微复杂的计算&#xff0c;国庆七天乐 | OneCoder应该算第…

(11)(2.1.4) DroneCAN ESCs

文章目录 前言 1 DroneCAN ESC列表 2 连接到飞行控制器 3 自动驾驶仪设置 4 记录和报告 5 附加资料 前言 Copter、Plane 和 Rover 支持 DroneCAN 电子速度控制器&#xff08;ESC&#xff09;&#xff0c;该控制器允许与自动驾驶仪进行双向通信&#xff0c;从而可能更容易…

Python中的数据可视化艺术:用Matplotlib和Seaborn讲故事

Python中的数据可视化艺术&#xff1a;用Matplotlib和Seaborn讲故事 数据可视化不仅仅是图表的绘制&#xff0c;更是通过视觉形式传达复杂信息的一种艺术。使用Python中的两个强大的库——Matplotlib和Seaborn&#xff0c;可以将数据转化为清晰、优美的图表&#xff0c;帮助我…

理解计算机系统_程序的机器级表示(二):寄存器,操作数,数据传送,程序栈

前言 以<深入理解计算机系统>(以下称“本书”)内容为基础&#xff0c;对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定 引入 本书第三章&#xff1a;程序的机器级表示内容的理解,这一章内容以汇…

智能电子价签:助力零售效率升级的关键

在竞争日益激烈的零售市场&#xff0c;如何优化运营、提升效率&#xff0c;是每个零售商都在关注的问题。电子价签作为一项创新技术&#xff0c;提供了蒿效的解决方案。今天&#xff0c;我们就来聊聊电子价签如何帮助零售商轻松管理信息、减少人工误差&#xff0c;并展示它在门…

【AI绘画】Midjourney进阶:对称构图详解

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AI绘画 | Midjourney 文章目录 &#x1f4af;前言&#x1f4af;什么是构图为什么Midjourney要使用构图 &#x1f4af;对称构图特点使用场景提示词书写技巧测试 &#x1f4af;小结 &#x1f4af;前言 通常来学习AI绘画的人可以分为…

遥感影像-语义分割数据集:多源多模态地物多要素数据集详细介绍及训练样本处理流程

原始数据集详情 简介&#xff1a;该数据集由WHU-OPT-SAR数据集整理而来&#xff0c;覆盖面积51448.56公里&#xff0c;分辨率为5米。据我们所知&#xff0c;WHU-OPT-SAR是第一个也是最大的土地利用分类数据集&#xff0c;它融合了高分辨率光学和SAR图像&#xff0c;并进行了充…

非酒精性脂肪性肝炎NASH临床赛道的百米冲刺,谁将成为胜者?

前 言 非酒精性脂肪性肝炎&#xff08;NASH&#xff09;是一种与肥胖、血脂异常、2型糖尿病和代谢综合征密切相关的疾病&#xff0c;可能会发展为肝硬化、终末期肝病甚至肝癌。据美国肝脏基金会统计数据显示&#xff0c;截至2023年8月&#xff0c;美国成年人中有5%的NASH患者…

使用 YOLOv 11 模型实现实时手语检测 可同时识别多个手语手势

项目&#xff1a;Yolo11 - Roboflow - OpenCV 手语是聋哑人之间以及他们与外界沟通的重要工具&#xff0c;然而&#xff0c;许多不会手语的人无法与他们有效交流。这个项目的目标是通过自动检测手语手势&#xff0c;构建一个可以帮助聋哑人和普通人之间沟通的桥梁&#xff0c;…

立体扬声器棒球帽专利TRO维权,速查避免踩坑

案件基本情况起诉时间&#xff1a;2024-9-18案件号&#xff1a;24-cv-08626原告&#xff1a;Audiowear Technology Corporation原告律所&#xff1a;Loza & Loza, LLP起诉地&#xff1a;伊利诺伊州北部法院品牌介绍Audiowear Technology Corporation&#xff0c;一家位于特…

麒麟V10系统下的调试工具(网络和串口调试助手)

麒麟V10系统下的调试工具&#xff08;网络和串口调试助手&#xff09; 1.安装网络调试助手mnetassist arm64-main ①在linux下新建一个文件夹 mkdir /home/${USER}/NetAssist②将mnetassist arm64-main.zip拷贝到上面文件夹中&#xff0c;并解压给权限 cd /home/${USER}/Ne…

(23)DBPSK信号在Rayleigh衰落信道条件下的传输性能仿真

文章目录 前言一、MATLAB仿真代码二、仿真结果画图 前言 此示例创建了一个【频率平坦的瑞利衰落信道】对象&#xff0c;并使用该对象来对DBPSK信号进行衰落处理&#xff0c;衰落之后增加了不同信噪比的AWGN&#xff0c;计算出不同的信噪比值计算误码率&#xff0c;并和理论误码…

八、索引的创建与设计原则

文章目录 1. 索引的声明与使用1.1 索引的分类1.2 创建索引1.2.1 在创建表时创建索引1.2.2 在已经存在的表上创建索引1.3 删除索引2. MySQL8.0索引新特性2.1 支持降序索引2.2 隐藏索引3. 索引的设计原则3.1 数据准备3.2 哪些情况适合创建索引3.2.1 字段的数值有唯一性的限制3.2.…

Cisco Catalyst 9000 交换产品系列 IOS XE 17.15.1 发布下载,新增功能概览

Cisco Catalyst 9000 Series Switches, IOS XE Release 17.15.1 ED 思科 Catalyst 9000 交换产品系列 IOS XE 系统软件 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-catalyst-9000/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&…

Google:敏感信息访问权限和 API 政策更新

目录 公布时间公布内容内容有关 Google Play 照片和视频权限政策的详细信息截止时间相关问题公布时间 公布日期:2023-10-25 公布内容 内容 为向用户提供更注重隐私保护的体验,我们将推出“照片和视频访问权限”政策,以减少获准针对照片/视频请求广泛权限(READ_MEDIA_IM…

探索 ACM:计算机领域的卓越组织

《探索 ACM&#xff1a;计算机领域的卓越组织》 在计算机科学的广袤星空中&#xff0c;ACM&#xff08;Association for Computing Machinery&#xff0c;美国计算机协会&#xff09;犹如一颗璀璨的巨星&#xff0c;散发着耀眼的光芒。 ACM 是世界上最大的计算机领域专业性学…

从给定的序列中随机抽取一个元素secrets.choice()

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 从给定的序列中随机抽取一个元素 secrets.choice() [太阳]选择题 根据给定的Python代码&#xff0c;哪个选项是不可能输出的&#xff1f; import secrets sequence ["red", &qu…

PCL 法线空间下采样滤波

目录 一、概述二、代码三、结果 一、概述 对于原始点云&#xff0c;通过其点云法向量进行下采样&#xff0c;在法向量变化大的地方采样密度大&#xff0c;在法向量变化小的地方&#xff0c;采样密度小。 计算点云的空间法向量。依次计算相邻点之间的法向量夹角&#xff0c;以此…

Node.js管理工具NVM

nvm&#xff08;Node Version Manager&#xff09;是一个用于管理多个 Node.js 版本的工具。以下是 nvm 的使用方法和一些常见命令&#xff1a; 一、安装 nvm 下载 nvm&#xff1a; 地址&#xff1a;https://github.com/coreybutler/nvm-windows/releases访问 nvm 的 GitHub 仓…

Autodesk Flame 2025:视觉特效制作解决方案

Autodesk Flame 2025是一款功能强大的视觉特效制作解决方案&#xff0c;由Autodesk公司开发。它提供了出色的性能&#xff0c;为视觉特效艺术家成功完成制作项目提供了所需的交互性和灵活性。 以下是Autodesk Flame 2025的一些主要特点和功能&#xff1a; 高效的三维合成环境&…