开发一个RISC-V上的操作系统(三)—— 串口驱动程序(UART)

news2024/10/6 6:47:22

目录

文章传送门

一、什么是串口

二、本项目串口的FPGA实现

三、串口驱动程序的编写

四、上板测试


文章传送门

开发一个RISC-V上的操作系统(一)—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客

开发一个RISC-V上的操作系统(二)—— 系统引导程序(Bootloader)_Patarw_Li的博客-CSDN博客

开发一个RISC-V上的操作系统(三)—— 串口驱动程序(UART)_Patarw_Li的博客-CSDN博客

一、什么是串口

串口(UART)又名异步收发传输器(Universal Asynchronous Receiver/Transmitter),是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将串行数据转换成并行数据。SPI和I2C为同步通信接口,双方时钟频率相同,而UART属于异步通信接口,没有统一时钟,靠起始位和终止位来接收数据。

串口包括RS232、RS499、RS423等接口标准规范,我们这里使用的是RS232:

上图为串口的通信方式,可以同时收发(全双工通信)。其中rx负责接收,tx负责发送,每次发送10bit数据(起始位+8bit数据+停止位),从最低位开始发送。 

二、本项目串口的FPGA实现

在写串口的驱动程序之前,我们首先要知道如何与开发板上的串口进行交互,所以我们要先看看我们riscv cpu项目是怎么实现串口模块的。

项目仓库:

cpu_prj: 一个基于RISC-V指令集的CPU实现

串口模块的实现在 FPGA/rtl/perips/目录下的uart.v文件中,它作为一个外设挂载在rib总线上

在总线模块 FPGA/rtl/core/rib.v中可以看到, uart.v外设的地址范围为0x1000_0000 ~ 0x1fff_ffff,用于访问uart模块中的寄存器(实际uart模块只有三个寄存器,不需要这么大空间,但是影响不大):

下面是uart.v的代码。其中uart_rx和uart_tx引脚为串口接收和发送引脚;最下面五个信号则用于读写串口寄存器。

// 串口模块,默认波特率为9600
module uart(

    input   wire                        clk                 ,
    input   wire                        rst_n               ,
    
    input   wire                        uart_rx             , // uart接收引脚
    output  reg                         uart_tx             , // uart发送引脚
    
    input   wire                        wr_en_i             , // uart寄存器写使能信号
    input   wire[`INST_ADDR_BUS]        wr_addr_i           , // uart寄存器写地址
    input   wire[`INST_DATA_BUS]        wr_data_i           , // uart写数据
    input   wire[`INST_ADDR_BUS]        rd_addr_i           , // uart寄存器读地址
    output  reg [`INST_DATA_BUS]        rd_data_o             // uart读数据

    );
    
    // 寄存器地址定义
    parameter   UART_CTRL = 4'd0,
                UART_TX_DATA_BUF= 4'd4,
                UART_RX_DATA_BUF= 4'd8;
    // addr: 0x0
    // 低两位(1:0)为TI和RI
    // TI:发送完成位,该位在数据发送完成时被设置为高电平
    // RI:接收完成位,该位在数据接收完成时被设置为高电平
    reg[31:0]   uart_ctrl;
    
    // addr: 0x4
    // 发送数据寄存器
    reg[31:0]   uart_tx_data_buf;
    
    // addr: 0x8
    // 接收数据寄存器
    reg[31:0]   uart_rx_data_buf;
    
    parameter   BAUD_CNT_MAX = `CLK_FREQ / `UART_BPS;
    parameter   IDLE = 4'd0,
                BEGIN= 4'd1,
                RX_BYTE = 4'd2,
                TX_BYTE = 4'd3,
                END  = 4'd4;
    
    wire                        uart_rx_temp;
    reg                         uart_rx_delay; // rx延迟后的输入
    reg[3:0]                    uart_rx_state; // rx状态机
    reg[12:0]                   rx_baud_cnt;   // rx计数器
    reg[3:0]                    rx_bit_cnt;    // rx比特计数
    
    reg[3:0]                    uart_tx_state; // rx状态机
    reg[12:0]                   tx_baud_cnt;   // rx计数器
    reg[3:0]                    tx_bit_cnt;    // rx比特计数
    reg                         tx_data_rd;    // 发送数据就绪信号
    
    
    // 读写寄存器,write before read
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            uart_ctrl <= `ZERO_WORD;
            uart_tx_data_buf <= `ZERO_WORD;
        end
        else begin
            if(wr_en_i == 1'b1) begin
                case(wr_addr_i[3:0])
                    UART_CTRL: begin
                        uart_ctrl = wr_data_i;
                    end
                    UART_TX_DATA_BUF: begin
                        uart_tx_data_buf = wr_data_i;
                    end
                    default: begin
                    end
                endcase
            end
            if(uart_tx_state == END && tx_baud_cnt == 1) begin
                uart_ctrl[1] = 1'b1; // TI置1,代表发送完毕,需要软件置0
            end
            if(uart_rx_state == END && rx_baud_cnt == 1) begin
                uart_ctrl[0] = 1'b1; // RI置1,代表接收完毕,需要软件置0
            end
            case(rd_addr_i[3:0])
                UART_CTRL: begin
                    rd_data_o = uart_ctrl;
                end
                UART_TX_DATA_BUF: begin
                    rd_data_o = uart_tx_data_buf;
                end
                UART_RX_DATA_BUF: begin
                    rd_data_o = uart_rx_data_buf;
                end
                default: begin
                    rd_data_o = `ZERO_WORD;
                end
            endcase
        end
    end
    
    /* TX发送模块 */
    
    // tx数据就绪信号 tx_data_rd
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            tx_data_rd <= 1'b0;  
        end
        else if(wr_en_i == 1'b1 && wr_addr_i[3:0] == UART_TX_DATA_BUF) begin
            tx_data_rd <= 1'b1;
        end
        else if(uart_tx_state == END && tx_baud_cnt == 1) begin
            tx_data_rd <= 1'b0;
        end
        else begin
            tx_data_rd <= tx_data_rd;
        end
    end
    
    // tx_baud_cnt计数
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            tx_baud_cnt <= 13'd0;
        end
        else if(uart_tx_state == IDLE || tx_baud_cnt == BAUD_CNT_MAX - 1) begin
            tx_baud_cnt <= 13'd0;
        end
        else begin
            tx_baud_cnt <= tx_baud_cnt + 1'b1;
        end
    end
    
    // TX发送模块
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            uart_tx_state <= IDLE;
            tx_bit_cnt <= 4'd0;
            uart_tx <= 1'b1;
        end
        else begin
            case(uart_tx_state)
                IDLE: begin
                    uart_tx <= 1'b1;
                    if(tx_data_rd == 1'b1) begin
                        uart_tx_state <= BEGIN; 
                    end
                    else begin
                        uart_tx_state <= uart_tx_state;
                    end
                end
                BEGIN: begin
                    uart_tx <= 1'b0;
                    if(tx_baud_cnt == BAUD_CNT_MAX - 1) begin
                        uart_tx_state <= TX_BYTE; 
                    end
                    else begin
                        uart_tx_state <= uart_tx_state;
                    end
                end
                TX_BYTE: begin
                    if(tx_bit_cnt == 4'd7 && tx_baud_cnt == BAUD_CNT_MAX - 1) begin
                        tx_bit_cnt <= 4'd0;
                        uart_tx_state <= END; 
                    end
                    else if(tx_baud_cnt == BAUD_CNT_MAX - 1) begin
                        tx_bit_cnt <= tx_bit_cnt + 1'b1; 
                    end
                    else begin
                        uart_tx <= uart_tx_data_buf[tx_bit_cnt];
                    end
                end
                END: begin
                    uart_tx <= 1'b1;
                    if(tx_baud_cnt == BAUD_CNT_MAX - 1) begin
                        uart_tx_state <= IDLE; 
                    end
                    else begin
                        uart_tx_state <= uart_tx_state;
                    end
                end
                default: begin
                    uart_tx_state <= IDLE;
                    tx_bit_cnt <= 4'd0;
                    uart_tx <= 1'b1;
                end
            endcase
        end
    end
    
    
    /* RX接收模块 */
    
    // 将输入rx延迟4个时钟周期,减少亚稳态的影响
    delay_buffer #(
        .DEPTH(4),
        .DATA_WIDTH(1)
    ) u_delay_buffer(
        .clk           (clk),   //  Master Clock
        .data_i        (uart_rx),   //  Data Input
        .data_o        (uart_rx_temp)    //  Data Output
    );
    
    always @ (posedge clk) begin
        uart_rx_delay <= uart_rx_temp;
    end
    
    // rx_baud_cnt计数
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            rx_baud_cnt <= 13'd0;
        end
        else if(uart_rx_state == IDLE || rx_baud_cnt == BAUD_CNT_MAX - 1) begin
            rx_baud_cnt <= 13'd0;
        end
        else begin
            rx_baud_cnt <= rx_baud_cnt + 1'b1;
        end
    end
    
    // RX接收模块
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            uart_rx_state <= IDLE;
            rx_bit_cnt <= 4'd0;
            uart_rx_data_buf <= `ZERO_WORD;
        end
        else begin
            case(uart_rx_state)
                IDLE: begin
                    if(uart_rx_temp == 1'b0 && uart_rx_delay == 1'b1) begin
                        uart_rx_state <= BEGIN; 
                    end
                    else begin
                        uart_rx_state <= uart_rx_state;
                    end
                end
                BEGIN: begin
                    if(rx_baud_cnt == BAUD_CNT_MAX - 1) begin
                        uart_rx_state <= RX_BYTE; 
                    end
                    else begin
                        uart_rx_state <= uart_rx_state;
                    end
                end
                RX_BYTE: begin
                    if(rx_bit_cnt == 4'd7 && rx_baud_cnt == BAUD_CNT_MAX - 1) begin
                        rx_bit_cnt <= 4'd0;
                        uart_rx_state <= END; 
                    end
                    else if(rx_baud_cnt == BAUD_CNT_MAX / 2 - 1) begin
                        uart_rx_data_buf[rx_bit_cnt] <= uart_rx_delay;
                    end
                    else if(rx_baud_cnt == BAUD_CNT_MAX - 1) begin
                        rx_bit_cnt <= rx_bit_cnt + 1'b1; 
                    end
                    else begin
                        uart_rx_state <= uart_rx_state;
                    end
                end
                END: begin
                    if(rx_baud_cnt == 1) begin
                        uart_rx_state <= IDLE; 
                    end
                    else begin
                        uart_rx_state <= uart_rx_state;
                    end
                end
                default: begin
                    uart_rx_state <= IDLE;
                    rx_bit_cnt <= 4'd0;
                    uart_rx_data_buf <= `ZERO_WORD;
                end
            endcase
        end
    end
    
endmodule

在串口模块uart.v中定义了三个寄存器,分别为 串口控制寄存器 uart_ctrl串口发送数据缓存寄存器 uart_tx_data_buf、 串口接收数据缓存寄存器 uart_rx_data_buf,它们的作用分别是:

  •  uart_ctrl串口控制寄存器,只有最低两位有效,分别为TI和RI。TI,发送完成标志位,该位在数据发送完成时被设置为1RI,接收完成标志位,该位在数据接收完成时被设置为高电平。在数据发送/接收完成后由uart模块将TI/RI置1,这样驱动程序就可以通过读取该寄存器的TI/RI位来确定数据是否发送/接收完成,这两位需要软件复位
  • uart_tx_data_buf串口发送数据缓存寄存器只要uart_tx_data_buf寄存器有数据写入,就开始发送数据,发送完毕将TI置1
  • uart_rx_data_buf串口接收数据缓存寄存器用于接收用户通过rx端口发送的数据,接收完就将RI置1

它们的地址偏移分别是0,4,8,实际的物理地址为0x10000000,0x10000004,0x10000008:

在了解上述内容后,我们就可以开始编写串口驱动程序了。

三、串口驱动程序的编写

源码放在我的gitee仓库,欢迎star:

riscv_os: 一个RISC-V上的简易操作系统

代码在 01_UART 目录下,目录结构为:

inc目录存放头文件,其中platform.h里定义了串口设备首地址UART:

uart.c即为我们的串口驱动程序,代码内容如下: 

#define UART_REG_ADDRESS(reg) ((uint8_t *) (UART + reg))

/*
 * UART registers map
 */
#define UART_CTRL 	 0
#define UART_TX_DATA_BUF 4
#define UART_RX_DATA_BUF 8

#define uart_read_reg(reg) (*(UART_REG_ADDRESS(reg)))
#define uart_write_reg(reg, data) (*(UART_REG_ADDRESS(reg)) = (data))

void uart_init()
{
	// init uart_ctrl reg to 0
	uart_write_reg(UART_CTRL, 0x00);
}

void uart_putc(char ch)
{    
	// fill send buf
	uart_write_reg(UART_TX_DATA_BUF, ch);
    // wait send over
    while((uart_read_reg(UART_CTRL) & (1 << 1)) != (1 << 1)){}
    // set TI to 0
	uart_write_reg(UART_CTRL, (uart_read_reg(UART_CTRL) & ~(1 << 1)));
}

void uart_puts(char *s)
{
	while(*s){
		uart_putc(*s++);
	}
}

char uart_getc()
{
	// wait RI to 1
	while((uart_read_reg(UART_CTRL) & (1 << 0)) != (1 << 0)){}
	// set RI to 0
	uart_write_reg(UART_CTRL, (uart_read_reg(UART_CTRL) & ~(1 << 0)));
	// read receive buf
	return uart_read_reg(UART_RX_DATA_BUF);
}

void uart_gets(char *s, uint8_t len){
	uint8_t i = 0;
	while(i < len - 1 && (*s = uart_getc()) != '\n'){
		++i;
		++s;
	}
	*(s + 1) = '\0';
}

其中第一行定义的宏UART_REG_ADDRESS(reg)用于取得对应寄存器reg的地址,下面三个宏UART_CTRL,UART_TX_DATA_BUF,UART_RX_DATA_BUF 即为寄存器的偏移地址,例如uart_ctrl寄存器的地址为 (UART + UART_CTRL ) = 0x10000000 + 0。

宏函数uart_read_reg(reg)用于读取对应reg的内容,uart_write_reg(reg, data)则用于将data写入对应reg。

uart_init()函数很简单,用于初始化uart_ctrl寄存器。

uart_putc(char ch)函数用于发送一个字符给上位机。首先会把要发的内容写入uart_tx_data_buf;写入后串口模块会自动发送数据,发送完成后TI会置1,在此期间该函数会一直轮询查看TI是否置1,若TI置1则代表发送完成。发送完成后需要将TI置0。

void uart_putc(char ch)
{    
	// fill send buf
	uart_write_reg(UART_TX_DATA_BUF, ch);
    // wait send over
    while((uart_read_reg(UART_CTRL) & (1 << 1)) != (1 << 1)){}
    // set TI to 0
	uart_write_reg(UART_CTRL, (uart_read_reg(UART_CTRL) & ~(1 << 1)));
}

uart_getc()函数用于接收上位机发过来的一个字符。因为只有RI为1才代表有一个字节数据需要接收,所以在RI为0的时候需要一直循环等待;待RI为1后,先把RI复位(置0),然后再读取uart_rx_data_buf的内容,得到上位机发过来的一个字符数据

char uart_getc()
{
	// wait RI to 1
	while((uart_read_reg(UART_CTRL) & (1 << 0)) != (1 << 0)){}
	// set RI to 0
	uart_write_reg(UART_CTRL, (uart_read_reg(UART_CTRL) & ~(1 << 0)));
	// read receive buf
	return uart_read_reg(UART_RX_DATA_BUF);
}

uart_puts()和uart_gets()则用于连续获取一组字符得到一个字符串,是通过连续调用上面两个函数实现的。

至此,uart驱动程序的内容已经讲解完毕,接下来调用试试看。

四、上板测试

首先我们看看主程序里面的内容:

void start_kernel(void){
        /* User code begin */

        printf("Hello %d", 110);
        printf("World!\n");

        char msg[20] = "";
        while(1){
                uart_gets(msg, 20);
                uart_puts(msg);
        }

        /* User code end */

        while(1){}; // stop here!
}

可以看到这是一个串口回环,会把我们上位机发送的msg又回传到上位机。接下来我们烧录到板子上看看,在烧录前确保你的板子已经把我的riscv cpu跑起来了,可以看我前面的文章。

首先执行make得到os.bin文件,然后通过python烧录程序把os.bin烧录到处理器的memory中,烧录完后我们需要用串口工具来调试,可以下载我上传到cpu_prj仓库里面的串口工具:

打开串口调试工具,设置好参数后点击右下角的打开按钮:

打开串口后,按下复位键即可看到串口输出内容 ,输出的是printf函数打印的内容(pirntf函数也调用了uart_puts函数,这里就不细讲了,感兴趣的话可以去看printf.c文件的内容):

在下面的输入框内输入内容,最后加一个回车(内容最后一定要加一个回车,uart_gets()函数是通过回车符号来判断内容的结束的),然后点击发送即可看到上面显示你发送的内容:

至此,串口驱动程序实验结束,实现了串口,我们之后开发调试都会方便很多~

遇到问题欢迎加群 892873718 交流~

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

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

相关文章

Linux-定时清除日志No space left on device

由于开发环境上一般机器资源较少&#xff0c;很容易导致因日志文件过大而导致系统宕机&#xff0c;报错No space left on device等问题&#xff0c;我们可以通过添加定时任务&#xff0c;自动删除日志从而达到节省空间的目的 操作步骤&#xff1a; 云服务器进入救援模式(若服…

目前主流的几个Web前端框架

启动项目时&#xff0c;请查看 2023 年最好的 Web 前端框架。为什么选择合适的工具很重要? 前端开发人员使用前端框架来简化工作。这些软件包通常提供可重用的代码模块、系统化的前端技术和预构建的接口块。这使团队可以更快、更轻松地创建可持续的 Web 应用程序和用户界面&am…

[linux]VI编辑器常用命令

VI编辑器常用命令 命令用法含义dd删除游标所在一整行d1G删除光标所在到第一行的所有数据dG删除光标所在到最后一行的所有数据d$删除光标所在处&#xff0c;到该行的最后一个字符d0那个是数字0,删除光标所在到该行的最前面的一个字符x,Xx向后删除一个字符(相当于[del]按键),X向…

深入浅出多种开发语言对接淘宝京东1688阿里巴巴等电商平台,获取实时商品详情数据API接口介绍

api接口详解大全?优秀的设计是产品变得卓越的原因设计API意味着提供有效的接口&#xff0c;可以帮助API使用者更好地了解、使用和集成&#xff0c;同时帮助人们有效地维护它每个产品都需要使用手册&#xff0c;API也不例外在API领域&#xff0c;可以将设计视为服务器和客户端之…

Oracle中varchar2、clob字段类型中特殊字符会显示为问号解决方法

项目中遇到varchar2、clob字段存储数据&#xff0c;内容中存在特殊字符导致显示问号&#xff0c;以下说明解决此问题的办法 首先我们查询下数据库编码、客户端编码、查询用户操作系统字符集 --查看oracle数据库编码 select * from nls_database_parameters where parameter NL…

MySQL数据库第十一课---------SQl语句的拔高-------水平提升

作者前言 个人主页::小小页面 gitee页面:秦大大 一个爱分享的小博主 欢迎小可爱们前来借鉴 ______________________________________________________ 目录 SQL提高 日期函数 length round reverse substring ifnull case when cast grouping sets 排序函数 开窗函…

从零到一,激活GPU的力量:使用TensorRT量化和CUDA并行编程

TensorRT学习笔记 前情提要&#xff1a;TensorRT 模型优化与推理&#xff1a;从零到一&#xff0c;激活GPU的力量&#xff1a;使用TensorRT优化和执行深度学习模型&#xff0c;你的TensorRT入门指南 本篇将会介绍TensorRT下的模型量化与CUDA并行计算编程的介绍。 TensorRT模型…

【雕爷学编程】Arduino动手做(170)---LGT8F328P 开发板

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

如何在3ds max中创建可用于真人场景的巨型机器人:第 2 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建主体 步骤 1 打开 3ds Max。选择机器人头部后&#xff0c;二次单击鼠标并选择隐藏未选中。机器人的其他部分 除了头部之外&#xff0c;将被隐藏。 打开 3ds Max 步骤 2 在人脸选择模式下&#x…

自动化测试项目实战

目录 1.熟悉项目 2.针对核心流程设计手工测试用例 3.手工测试用例转换为自动化测试用例 前置工作 测试工作 登陆界面 博客列表页数量 博客详情页检验 写博客并发布 校验标题&#xff0c;时间 删除博客 注销博客 针对博客系统进行自动化测试 1.熟悉项目 2.针对核…

2023年9月北京/广州/深圳CDGA/CDGP认证考试报名开启

据DAMA中国官方网站消息&#xff0c;2023年度第三期DAMA中国CDGA和CDGP认证考试定于2023年9月23日举行。 报名通道现已开启&#xff0c;相关事宜通知如下&#xff1a; 考试科目: 数据治理工程师(CertifiedDataGovernanceAssociate,CDGA) 数据治理专家(CertifiedDataGovernanc…

AlSD 系列智能安全配电装置是安科瑞电气有限公司专门为低压配电侧开发的一款智能安全用电产 品-安科瑞黄安南

一、应用背景 电力作为一种清洁能源&#xff0c;给人们带来了舒适、便捷的电气化生活。与此同时&#xff0c;由于使用不当&#xff0c;维护 不及时等原因引发的漏电触电和电气火灾事故&#xff0c;也给人们的生命和财产带来了巨大的威胁和损失。 为了防止低压配电系统发生漏…

数据结构和算法——表排序(算法概述、物理排序、复杂度分析,包含详细清晰图示过程)

目录 算法概述 物理排序 复杂度分析 算法概述 表排序用于 待排元素都为一个庞大的结构&#xff0c;而不是一个简单的数字&#xff0c;例如&#xff1a;一本书&#xff0c;一部电影等等。 如果这些待排元素都用之前的排序方法&#xff0c;元素需要频繁互换&#xff0c;那么…

uniapp 即时通讯开发流程详解

今天我将为您详细介绍UniApp开发中的即时通讯流程。本文将向您展示如何在UniApp中实现即时通讯功能&#xff0c;为您的应用程序增添交互性和实时性。 1. 准备工作 在开始开发之前&#xff0c;确保您已完成以下准备工作&#xff1a; 确保您已经安装好UniApp开发环境&#xff…

实现简单Spring基于XML的配置程序

定义一个容器&#xff0c;使用ConcurrentHashMap 做为单例对象的容器 先解析beans.xml得到第一个bean对象的信息&#xff0c;id&#xff0c;class&#xff0c;属性和属性值使用反射生成对象&#xff0c;并赋值将创建好的bean对象放入到singletonObjects集合中提供getBean(id)方…

【Nodejs】Node.js开发环境安装

1.版本介绍 在命令窗口中输入 node -v 可以查看版本 0.x 完全不技术 ES64.x 部分支持 ES6 特性5.x 部分支持ES6特性&#xff08;比4.x多些&#xff09;&#xff0c;属于过渡产品&#xff0c;现在来说应该没有什么理由去用这个了6.x 支持98%的 ES6 特性8.x 支持 ES6 特性 2.No…

下载python模块包

离线安装 在PyPI The Python Package Index 查找自己需要的安装包&#xff0c;直接下载对应操作系统的安装包即可 拿numpy举个例子&#xff0c;上面有好多类型的安装包&#xff0c;针对不同的操作系统&#xff0c;其中在文件名中可以看出有linus和macos等等&#xff0c;此外还…

论文笔记--Distilling the Knowledge in a Neural Network

论文笔记--Distilling the Knowledge in a Neural Network 1. 文章简介2. 文章概括3 文章重点技术3.1 Soft Target3.2 蒸馏Distillation 4. 文章亮点5. 原文传送门 1. 文章简介 标题&#xff1a;Distilling the Knowledge in a Neural Network作者&#xff1a;Hinton, Geoffre…

Macbook M1编译安装Java OpenCV

OpenCV-4.8.0编辑安装 查询编译依赖 brew info opencv确保所有需要模块都打上了✔&#xff0c;未打✔的需要使用brew进行安装 下载OpenCV源码 在此处下载OpenCV源代码&#xff0c;选择Source&#xff0c;点击此处下载opencv_contrib-4.8.0 或者使用如下命令&#xff0c;通…

gerrit 从安装到出坑

一般公司在做代码审核的时候选择codereview gerrit来处理代码的入库的问题。 它是通过提交的时候产生Change-Id: If4e0107f3bd7c5df9e2dc72ee4beb187b07151b9 来决定是不是入库&#xff0c;一般如果不是通过这个管理&#xff0c;那么就是我们通常的操作 git add . git comm…