VGA项目:联合精简帧+双fifo+sobel算法 实现VGA显示(未完)

news2024/9/25 9:36:05

前言:该项目实际上是在很多基础的小练习上合成起来的,例如涉及到uart(rs232)的数据传输、双fifo流水线操作、VGA图像显示,本次内容在此基础上又增添了sobel算法,能实现图像的边沿监测并VGA显示。

文章目录

  • 1.项目描述
  • 2.sobel算法解析:
  • 3.模块结构示意图:
  • 4.sobel_ctrl模块设计
    • 4.1波形设计
    • 4.2代码
  • 5.VGA_shift模块设计
    • 5.1 显示效果示意图:
    • 5.2 简易波形示意图:
    • 5.3 代码
  • 6.uart_rx模块设计(直接用之前的设计 略)
  • 7.顶层设计
  • 8.最终的显示效果:

在这里插入图片描述

1.项目描述

通过串口助手把 200 行 x200 列数据传入 FPGA,对应三行三列的九个数进行 sobel算法,把边缘检测的结果(198x198的数据)通过 vga 来显示,用两种颜色来区分是否是边界点(边界用白色显示,其他用黑色)。

2.sobel算法解析:

在这里插入图片描述

  1. 把图像每三行三列的数据分别乘上算子中对应位置的值再相加。然后进行如下运算,得到相应方向(x 和 y)的 Dx 和 Dy。
    Dx=(a3-a1)+(b3-b1)*2+c3-c1;
    Dy=(a1-c1)+(a2-c2)*2+a3-c3;

  2. 对上面求得的 Dx 和 Dy 做平方和的平方根,再取近似值 Dx 和 Dy 的绝对值的和得到 Dxy:

在这里插入图片描述

  1. 如果 Dxy 的值大于一个阈值(这个阈值是多次尝试试出来的,本次项目中设置为5),表示该点为边界点,就让 VGA 显示一个白点,否则显示黑点。

  2. 把计算的结果通过 vga 显示,显示器会把是边界点的以白色像素显示,不是边界点的以黑色像素点显示,于是得到了一幅图像的轮廓

在这里插入图片描述

3.模块结构示意图:

在这里插入图片描述
其中:
uart_rx模块在:手写一个uart协议——rs232
sobel_ctrl模块的核心是双fifo的流水线操作(需在此基础上进行改动):双fifo流水线操作
VGA_shift模块(原移动框为200x200 这里变成198x198,且添加了ram方便rgb图像的存储与读取):

4.sobel_ctrl模块设计

4.1波形设计

在这里插入图片描述

4.2代码

module sobel_ctrl(
    input wire clk,
    input wire rst,
    input wire [7:0] rx_data,
    input wire pi_flag,
    output reg [7:0] rgb,
    output reg po_flag
    );
reg[9:0] cnt_col,cnt_row;
reg wr_en1_r,wr_en2_r;
wire wr_en1,wr_en2;

reg [7:0] data_in1_r;
wire [7:0] data_in1;
wire [7:0] dout1,dout2;

reg [7:0] dout1_t,dout1_tt,dout2_t,dout2_tt;
reg [7:0] rx_data_t,rx_data_tt;
reg rd_en_r;
wire rd_en;

reg shift_flag;

reg flag_d;

reg [7:0] Dx,Dy;

reg flag_abs,flag_dxy,flag_rgb;
reg [7:0] abs_dx,abs_dy;
reg [7:0] dxy;

parameter COL_MUX=199;
parameter ROW_MUX=199;
parameter VALUR=5;  //不断调试得到一个合适的阈值

// cnt_col
always @(posedge clk) begin 
    if(rst==1'b1) begin
        cnt_col <= 'd0;
    end 
    else if (pi_flag==1'b1 && cnt_col==COL_MUX) begin
         cnt_col<='d0;
    end
    else if (pi_flag==1'b1) begin
         cnt_col<=cnt_col+1'b1;
    end
end

// cnt_row
always @(posedge clk) begin 
    if(rst==1'b1) begin
        cnt_row <= 'd0;
    end
    else if (cnt_row==ROW_MUX && pi_flag==1'b1 && cnt_col==COL_MUX) begin
        cnt_row<='d0;
    end
    else if (pi_flag==1'b1 && cnt_col==COL_MUX) begin
       cnt_row<=cnt_row+1'b1;
    end
end

// wr_en1_r
assign wr_en1=wr_en1_r;
always @(posedge clk) begin 
    if(rst==1'b1) begin
        wr_en1_r <= 'd0;
    end
    else if (cnt_row=='d0) begin
        wr_en1_r<=pi_flag;
    end
    else if (cnt_row>'d1 && cnt_row<ROW_MUX) begin
        wr_en1_r<=shift_flag;
    end
end

// wr_en2_r
assign wr_en2=wr_en2_r;
always @(posedge clk) begin 
    if(rst==1'b1) begin
        wr_en2_r <= 'd0;
    end 
    else if (cnt_row>'d0 && cnt_row<ROW_MUX) begin
        wr_en2_r<=pi_flag;
    end
    else
        wr_en2_r<='d0;
end

// data_in1_r
assign data_in1=data_in1_r;
always @(posedge clk) begin 
    if(rst==1'b1) begin
         data_in1_r<= 'd0;
    end 
    else if (cnt_row=='d0) begin
        data_in1_r<=rx_data;
    end
    else if (cnt_row>'d1 && cnt_row<ROW_MUX) begin
       data_in1_r<=dout2;
    end
end

// rd_en_r
assign rd_en=rd_en_r;

always @(posedge clk) begin 
    if(rst==1'b1) begin
         rd_en_r<= 'd0;
    end 
    else if (cnt_row>'d1) begin
        rd_en_r<=pi_flag;
    end
    else 
        rd_en_r<='d0;
end

// shift_flag
always @(posedge clk) begin 
    if(rst==1'b1) begin
        shift_flag <= 'd0;
    end 
    else
        shift_flag<=rd_en_r;
end

// dout1_t,dout1_tt,dout2_t,dout2_tt,rx_data_t,rx_data_tt
always @(posedge clk) begin 
	if (shift_flag==1'b1) begin
		{dout1_tt,dout1_t}={dout1,dout1_t};
		{dout2_tt,dout2_t}={dout2,dout2_t};
		{rx_data_tt,rx_data_t}={rx_data_t,rx_data};
	end
end

always @(posedge clk) begin 
	if(rst==1'b1) begin
		flag_d <= 'd0;
	end 
	else if (cnt_row>=2 && cnt_col>2) begin
		flag_d<=rd_en_r;
	end
end

always @(posedge clk) begin 
	if(rst==1'b1) begin
		Dx <= 'd0;
		Dy <= 'd0;
	end 
	else if (flag_d==1'b1) begin
		Dx<=(dout1_tt-dout1)+(dout2_tt-dout2)<<1+(rx_data_tt-rx_data);
		Dy<=(dout1_tt-rx_data_tt)+(dout1_t-rx_data_t)<<1+(dout1-rx_data);
	end
	
end

always @(posedge clk) begin 
	if (rst==1'b1) begin
		flag_abs<='d0;
		flag_dxy<='d0;
		flag_rgb<='d0;
		po_flag<='d0;

	end
	else
		{po_flag,flag_rgb,flag_dxy,flag_abs}<={flag_rgb,flag_dxy,flag_abs,flag_d};
end

// abs_dx
always @(posedge clk) begin 
	if(rst==1'b1) begin
		 abs_dx<='d0;
	end 
	else if (flag_abs==1'b1 ) begin
		if (dx[7]==1'b1) begin
			abs_dx<=(~Dx)+1'b1;
		end
		else
			abs_dx<=Dx;
	end
end

// abs_dy
always @(posedge clk) begin 
	if(rst==1'b1) begin
		abs_dy <= 'd0;
	end 
	else if (flag_abs==1'b1) begin
		if (dy[7]==1'b1) begin
			abs_dy<=(~Dy)+1'b1;
		end
		else 
			abs_dy<=Dy;
	end
end

// dxy
always @(posedge clk) begin 
	if(rst==1'b1) begin
		 dxy<= 'd0;
	end 
	else if (flag_dxy==1'b1) begin
		dxy<=abs_dx+abs_dy;
	end
end

// rgb
always @(posedge clk) begin 
	if(rst==1'b1) begin
		rgb <= 'd0;
	end 
	else if (flag_rgb==1'b1) begin
		if (dxy>VALUR) begin
			rgb<=8'hff;
		end
		else
			rgb<=8'h00;
	end
end


sfifo_8X256 sfifo1_8X256 (
  .clk(clk),      // input wire clk
  .din(data_in1),      // input wire [7 : 0] din
  .wr_en(wr_en1),  // input wire wr_en
  .rd_en(rd_en),  // input wire rd_en
  .dout(dout1),    // output wire [7 : 0] dout
  .full(),    // output wire full
  .empty()  // output wire empty
);


sfifo_8X256 sfifo2_8X256 (
 .clk(clk),      // input wire clk
  .din(rx_data),      // input wire [7 : 0] din
  .wr_en(wr_en2),  // input wire wr_en
  .rd_en(rd_en),  // input wire rd_en_r
  .dout(dout2),    // output wire [7 : 0] dout
  .full(),    // output wire full
  .empty()  // output wire empty
);
endmodule

5.VGA_shift模块设计

5.1 显示效果示意图:

在这里插入图片描述

5.2 简易波形示意图:

在这里插入图片描述

5.3 代码

module vga_shift(
    input wire sclk,//50mhz
    input wire clk_25,
    input wire rst,
    input wire [7:0] rgb_in,
    input wire pi_flag,
    output reg hsync,
    output reg vsync,
    output reg [7:0] rgb
    );

parameter HSYNC_END=95;
parameter CNT_H_END=799;

parameter VSYNC_END=1;
parameter CNT_V_END=524;

parameter RED=8'b11100000;
parameter GREEN=8'b00011100;
parameter BLUE=8'b00000011;
parameter WHITE=8'b11111111;

parameter ADDR_MUX=16'd39203;
reg [9:0] cnt_h;
reg [9:0] cnt_v;

reg [8:0] x;
reg [8:0] y;

reg flag_x;
reg flag_y;

reg [15:0] addra,addrb;
wire [7:0] doutb;

  // cnt_h
  always @(posedge clk_25) begin 
       if(rst==1'b1) begin
            cnt_h<= 'd0;
       end 
       else if (cnt_h==CNT_H_END) begin
           cnt_h<='d0;
       end
       else 
            cnt_h<=cnt_h+1'b1;
   end 

// hsync
   always @(posedge clk_25) begin 
       if(rst==1'b1) begin
            hsync<= 'd1;
       end
       else if (cnt_h==CNT_H_END) begin
           hsync<='d1;
       end 
       else if (cnt_h==HSYNC_END) begin
           hsync<='d0;
       end
   end

// cnt_v
always @(posedge clk_25) begin 
    if(rst==1'b1) begin
        cnt_v <= 'd0;
    end 
    else if (cnt_v==CNT_V_END && cnt_h==CNT_H_END) begin
        cnt_v<='d0;
    end
    else if (cnt_h==CNT_H_END) begin
        cnt_v<=cnt_v+1'b1;
    end
end

// vsync
always @(posedge clk_25) begin
    if(rst==1'b1) begin
        vsync <= 'd1;
    end 
    else if (cnt_v==VSYNC_END && cnt_h==CNT_H_END) begin
        vsync<='d0;
    end
    else if (cnt_v==CNT_V_END && cnt_h==CNT_H_END) begin
        vsync<='d1;
    end
   
end
// x
always @(posedge clk_25) begin 
	if(rst==1'b1) begin
		x <= 'd0;
	end 
	else if (cnt_h==CNT_H_END && cnt_v==CNT_V_END && flag_x=='d0) begin
		x<=x+1'b1;
	end
	else if (cnt_h==CNT_H_END && cnt_v==CNT_V_END && flag_x=='d1) begin
		x<=x-1'b1;
	end
end

// flag_x
always @(posedge clk_25) begin 
	if(rst==1'b1) begin
		 flag_x<= 'd0;
	end 
	else if (cnt_h==CNT_H_END && cnt_v==CNT_V_END && x=='d441 && flag_x=='d0) begin
		flag_x<='d1;
	end
	else if (cnt_h==CNT_H_END && cnt_v==CNT_V_END && x=='d1 && flag_x=='d1) begin
		flag_x<='d0;
	end
end

//y
always @(posedge clk_25) begin 
	if(rst==1'b1) begin
		 y<= 'd0;
	end 
	else if (cnt_h==CNT_H_END && cnt_v==CNT_V_END && flag_y=='d0) begin
		y<=y+1'b1;
	end 
	else if (cnt_h==CNT_H_END && cnt_v==CNT_V_END && flag_y=='d1) begin
		y<=y-1'b1;
	end
end

// flag_y
always @(posedge clk_25) begin 
	if(rst==1'b1) begin
		flag_y <= 'd0;
	end 
	else if (cnt_h==CNT_H_END && cnt_v==CNT_V_END && y=='d281 && flag_y<='d0) begin
		flag_y<='d1;
	end
	else if (cnt_h==CNT_H_END && cnt_v==CNT_V_END && y=='d1 && flag_y<='d1) begin
		flag_y<='d0;
	end
end

// rgb 
always @(posedge clk_25) begin 
    if(rst=='b1) begin
         rgb<= 'd0;
    end
    //注意198x198像素时,白框范围需要改变
    else if (cnt_h>=144+x && cnt_h<=341+x && cnt_v>=35+y && cnt_v<=232+y) begin
    	rgb<=doutb;
    end
    else if (cnt_h>=144 && cnt_h<=783) begin
        if (cnt_v>=35 && cnt_v<=194) begin
            rgb<=RED;
        end
        else if (cnt_v>=195 && cnt_v<=354) begin
            rgb<=GREEN;
        end
        else if (cnt_v>=355 && cnt_v<=514) begin
            rgb<=BLUE;
        end
    end 
    else
        rgb<='d0;
    
end


// addra  这里注意写ram的时钟为50mhz,读ram的时钟为25mhz(VGA的时钟)
always @(posedge sclk) begin 
  if(rst) begin
     addra<= 'd0;
  end
  else if (pi_flag==1'b1 && addra==ADDR_MUX) begin
    addra<='d0;
  end
  else if (pi_flag==1'b1) begin
    addra<=addra+1'b1;
  end 

end

// addrb 注意ram读数据相对于读地址有一拍延迟,所以地址要早一拍给出
always @(posedge clk_25) begin 
  if(rst==1'b1) begin
    addrb <= 'd0;
  end 
  else if (cnt_h>=144+x-1 && cnt_h<=341+x-1 && cnt_v>=35+y && cnt_v<=232+y && addrb==ADDR_MUX) begin
    addrb<='d0;
  end
  else if (cnt_h>=144+x-1 && cnt_h<=341+x-1 && cnt_v>=35+y && cnt_v<=232+y) begin
    addrb<=addrb+1'b1;
  end
end


asblk_mem_8x198x198 your_instance_name (
  .clka(sclk),    // input wire clka
  .wea(pi_flag),      // input wire [0 : 0] wea
  .addra(addra),  // input wire [15 : 0] addra
  .dina(rgb_in),    // input wire [7 : 0] dina
  .clkb(clk_25),    // input wire clkb
  .addrb(addrb),  // input wire [15 : 0] addrb
  .doutb(doutb)  // output wire [7 : 0] doutb
);

endmodule

6.uart_rx模块设计(直接用之前的设计 略)

7.顶层设计

module top_sobel(
	input wire clk,//50mhz
	input wire rst,
	input wire rx,
	output wire vsync,
	output wire hsync,
	output wire [7:0]rgb
    );


wire clk_out25;
wire clk_out50;

wire rx_data;
wire pi_flag_rx_to_sobel;
wire pi_flag_sobel_to_vga;

wire [7:0] rgb_in;

 clk_wiz_gen25 instance_name
   (
    // Clock out ports
    .clk_out50(clk_out50),     // output clk_out50
    .clk_out25(clk_out25),     // output clk_out25
   // Clock in ports
    .clk_in50(clk));      // input clk_in50
	uart_rx inst_uart_rx (
			.clk     (clk_out50),
			.rst     (rst),
			.rx      (rx),
			.po_data (rx_data),
			.po_flag (pi_flag_rx_to_sobel)
		);
	sobel_ctrl  inst_sobel_ctrl (
			.clk     (clk_out50),
			.rst     (rst),
			.rx_data (rx_data),
			.pi_flag (pi_flag_rx_to_sobel),
			.rgb     (rgb_in),
			.po_flag (pi_flag_sobel_to_vga)
		);
	vga_shift inst_vga_shift (
			.sclk    (clk_out50),
			.clk_25  (clk_out25),
			.rst     (rst),
			.rgb_in  (rgb_in),
			.pi_flag (pi_flag_sobel_to_vga),
			.hsync   (hsync),
			.vsync   (vsync),
			.rgb     (rgb)
		);


endmodule

8.最终的显示效果:

上位机通过MATLAB处理,用友善助手下发原图像数据:

在这里插入图片描述
经过一系列图像处理后,最终在vga的显示效果:
在这里插入图片描述

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

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

相关文章

使用第三方接口,查询实时快递单号

开通接口服务 全国快递物流查询-快递查询接口【最新版】_自动识别接口_快递查询_数据API-云市场-阿里云 RestTemplate: RestTemplate是Spring框架中的一个用于发送HTTP请求的客户端工具类。它提供了丰富的方法来发送GET、POST、PUT、DELETE等类型的HTTP请求&#xff0c;并支持…

Java内存区域详解

一、Java内存区域划分 1、分为运行时数据区域和本地内存 运行时数据区域是指 Java 虚拟机在运行 Java 程序时使用的不同内存区域&#xff0c;由虚拟机动态管理。本地内存是指由操作系统分配和管理的内存区域&#xff0c;它与虚拟机无关。在 Java 中&#xff0c;本地内存通常用…

生信网络学院|05月10日《全新DWG/DXF文件编辑工具——DraftSight Enterprise》

课程主题&#xff1a;全新DWG/DXF文件编辑工具——DraftSight Enterprise 课程时间&#xff1a;2024年05月10日 14:00-14:30 主讲人&#xff1a;任宇泽 生信科技 解决方案顾问 DraftSight介绍图形用户界面通用命令自定义块尺寸标注导入、导出和附加图纸集管理器Power Tools机…

大学生租房|基于Springboot+vue的大学生租房平台(源码+数据库+文档)

大学生租房平台 目录 基于Springbootvue的大学生租房平台 一、前言 二、系统设计 三、系统功能设计 1 管理员功能实现 5.1.1 房东管理 2 房东功能实现 5.2.1 信息审批管理 3 用户功能实现 5.3.1 房源信息 四、数据库设计 五、核心代码 六、论文参考 七、最新计…

半导体制冷片厂商正元泰达授权世强硬创代理,产品制冷率高寿命长

近日&#xff0c;为扩充旗下产品创新及供应平台世强硬创的半导体制冷片产品&#xff0c;满足硬科技企业不同层次的选择&#xff0c;世强先进&#xff08;深圳&#xff09;科技股份有限公司&#xff08;下称“世强先进”&#xff09;与正元泰达科技&#xff08;深圳&#xff09;…

PG 全页写

1.什么是全页写 修改一个块的时候&#xff0c;把块读到内存中&#xff0c;commit后,WAL写进程会触发写&#xff0c;把修改的块写到WAL日志文件&#xff0c;如果再往这个块中插入一条数据&#xff0c;数据缓冲区里面的块有两条数据了&#xff0c;再次commit后&#xff0c;PG会把…

最新AI实景自动无人直播软件招商加盟:引领直播行业的智能化未来;商家最新拓客工具

随着直播行业的迅速发展&#xff0c;AI实景自动无人直播软件正日益成为商家推广的新宠。该软件具备智能讲解、一键开播和智能回复功能&#xff0c;同时支持手机拍摄真实场景或自行搭建虚拟场景&#xff0c;给观众带来更好的观赏体验。在不断变化的市场竞争中&#xff0c;AI实景…

js浏览器请求,post请求中的参数形式和form-data提交数据时数据格式问题(2024-05-06)

浏览器几种常见的post请求方式 Content-Type 属性规定在发送到服务器之前应该如何对表单数据进行编码。 默认表单数据会编码为 "application/x-www-form-urlencoded" post请求的参数一般放在Body里。 Content-Type&#xff08;内容类型&#xff09;&#xff0c;一般…

论文精读-存内计算芯片研究进展及应用

文章目录 论文精读-存内计算芯片研究进展及应用概述背景介绍前人工作 存内计算3.1 SRAM存内计算3.2 DRAM存内计算3.3 ReRAM/PCM存内计算3.4 MRAM存内计算3.5 NOR Flash存内计算3.6 基于其他介质的存内计算3.7 存内计算芯片应用场景 总结QA 论文精读-存内计算芯片研究进展及应用…

“A”分心得:我的云计算HCIE学习之路

大家好&#xff0c;我是誉天云计算HCIE周末班梁同学&#xff0c;在誉天老师和同学们的帮助下&#xff0c;我终于在4月24日顺利通过了云计算3.0 HCIE的认证考试&#xff0c;而且获得了A&#xff0c;这是让我特别惊喜的&#xff0c;功夫不负有心人。 我日常的工作是网络运维&…

Merck:mPAGE® Lux SDS-PAGE制胶系统

相对于传统SDS-PAGE制胶工艺&#xff0c;Merck公司的mPAGE Lux制胶系统是一种更快速、简单、安全的替代方法&#xff0c;可实现更具可重现性的结果。mPAGE Lux制胶系统可在90秒内完成小型凝胶固化&#xff0c;而传统方法则需要90分钟以上。该系统采用紫外线照射快速制胶工作流程…

Activity工作流基本知识点

1.概念 工作流(Workflow)&#xff0c;就是“业务过程的部分或整体在计算机应用环境下的自动化”&#xff0c;它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行&#xff0c;从而实现某个预期的业务目标&#xff0c;或者促使此目标的…

java:File类概述和构造方法

一、File类概述和构造方法 1.File类的概述 File&#xff1a;它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的对File而言&#xff0c;其封装并不是一个真正存在的文件&#xff0c;仅仅是一个路径名而已。它可以是存在的&#xff0c;也可以是不存在的。…

flink sql 优化

文章目录 一、参数方面二、资源方面三、总结 提示&#xff1a;实时flink sql 参考很多网上方法与自己实践方法汇总(版本:flink1.13) 一、参数方面 flink sql参数配置 //关闭详细算子链(默认为true),true后job性能会略微有提升。false则可以展示更详细的DAG图方便地位性能结点…

Xshell生成ssh密钥及使用

目录 1. 概述2. 环境3. 步骤3.1 生成密钥3.2 部署密钥3.3 使用密钥 1. 概述 使用Xshell软件生成ssh秘钥&#xff0c;正常连接服务器。 2. 环境 Xshell 6 3. 步骤 3.1 生成密钥 1. 打开Xshell --> 工具 --> 新建用户密钥生成向导 2. 选择密钥类型&#xff0c;建议…

如何更好的使用cpm

nvidia发布了RAFT库&#xff0c;支持向量数据库的底层计算优化&#xff0c;RAFT 也使用CMake Package Manager( CPM )和rapids-cmake管理项目&#xff0c;可以方便快捷的下载到需要的对应版本的thirdparty的依赖库&#xff0c;但是&#xff0c;一般情况下&#xff0c;项目是直接…

c++11 标准模板(STL)本地化库 - 平面类别(std::num_get) - 从输入字符序列中解析数字值

本地化库 本地环境设施包含字符分类和字符串校对、数值、货币及日期/时间格式化和分析&#xff0c;以及消息取得的国际化支持。本地环境设置控制流 I/O 、正则表达式库和 C 标准库的其他组件的行为。 平面类别 从输入字符序列中解析数字值 std::num_get template< cl…

【Python时序预测系列】粒子群算法(PSO)优化LSTM实现单变量时间序列预测(案例+源码)

这是我的第272篇原创文章。 一、引言 粒子群算法&#xff08;Particle Swarm Optimization, PSO&#xff09;是一种启发式优化算法&#xff0c;可以用于优化神经网络模型的参数。在优化长短期记忆网络&#xff08;Long Short-Term Memory, LSTM&#xff09;时&#xff0c;可以结…

JetPack之ViewModel+LiveData

目录 一、概述二、LiveData 使用2.1 创建 LiveData 对象2.2 观察 LiveData 对象2.3 更新 LiveData 对象 三、编写 LiveData Demo3.1 不使用 LiveData3.2 使用 MutableLiveData3.3 使用 MediatorLiveData3.3.1 监听 2 个数据源的变化3.3.2 编写模拟 2 个数据源更新的代码 四、Vi…