基于Lattice XO2-4000HC FPGA核心板的SSD1306 OLED12832驱动芯片指令及工作方式详述(Verilog)

本文主要是介绍基于Lattice XO2-4000HC FPGA核心板的SSD1306 OLED12832驱动芯片指令及工作方式详述(Verilog),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

    • :pushpin: 前言
    • :gift: oled_driver_adc驱动顶层模块代码
    • :umbrella: OLED驱动原理简述
      • :paperclip: 结构及引脚分配
      • :game_die: MCU总线接口:4线SPI
    • :microscope: OLED驱动模块工作分析(结合代码)
      • :art: 图形显示数据RAM(GDDRAM)
        • 寻址模式
          • 页寻址模式
          • 水平模式寻址
          • 垂直模式寻址
        • 重映射及多路复用
      • :fish_cake: 代码中使用的工作方式
        • 单个字符编码
        • 字符串写入顺序
        • 多行字符串写入顺序

📌 前言

本篇文章为《基于Lattice XO2-4000HC FPGA核心板及电子森林综合训练底板的ADC数字电压表及OLED显示设计(Verilog)》一文的延伸,针对该项目中的SSD1306 OLED显示屏驱动的指令及工作(扫描)方式进行较为详细的说明,并结合原文中oled_driver_adc驱动模块的部分代码进行详细分析。

👉 注意事项及源代码参见原文。

🎁 oled_driver_adc驱动顶层模块代码

为便于后续对照代码进行说明,在此先贴出源代码,子模块的代码在原文中给出,简要而言为两个只读ROM,分别读取命令编码及字符编码。

module oled_driver_adc #(parameter CMD_WIDTH = 8,   		// LCD命令宽度parameter CMD_DEPTH = 5'd25, 	// LCD初始化的命令的数量parameter CHAR_WIDTH = 40,		// 一个文字的数据宽度parameter CHAR_DEPTH = 7'd123	// 文字库数量
)(input sys_clk,input rst_n,input [7:0] oled_display_digital, 	// 两位ADC数据(一位小数)output reg oled_csn,				//OLCD液晶屏使能output reg oled_rst,				//OLCD液晶屏复位output reg oled_dcn,				//OLCD数据指令控制output reg oled_clk,				//OLCD时钟信号output reg oled_data				//OLCD数据信号
);localparam IDLE = 3'b0, MAIN = 3'b1, INIT = 3'b10;localparam SCAN = 3'b11, WRITE = 3'b100, DELAY = 3'b101;localparam HIGH	= 1'b1, LOW = 1'b0;localparam DATA	= 1'b1, CMD = 1'b0;wire [CMD_WIDTH-1:0]  cmd_out;		// cmd_RAM输出的8位命令wire [CHAR_WIDTH-1:0] char_out;   	// data_RAM输出的40位文字reg [7:0] wr_reg;reg	[7:0] ypage, xpage_high, xpage_low;reg	[(8*21-1):0] char;				// 字符串reg	[4:0] char_num;   				// 文字个数 最多16reg [4:0] cmd_addr;reg [7:0] char_addr;reg	[2:0] cnt_main;reg [2:0] cnt_init;reg [3:0] cnt_scan;reg	[4:0] cnt_write;reg	[14:0]num_delay, cnt_delay;reg	[2:0] state, state_last;oled_cmd_RAM #(.RAM_WIDTH(CMD_WIDTH),.RAM_DEPTH(CMD_DEPTH),.ADDR_WIDTH(5)) CMD_RAM(.clk(sys_clk),.rst_n(rst_n),.re(oled_dcn),.addr(cmd_addr),.data(cmd_out));oled_char_RAM #(.RAM_WIDTH(CHAR_WIDTH),.RAM_DEPTH(CHAR_DEPTH),.ADDR_WIDTH(8)) CHAR_RAM(.clk(sys_clk),.rst_n(rst_n),.re(oled_dcn),.addr(char_addr),.data(char_out));always @(posedge sys_clk or negedge rst_n) beginif(!rst_n) begincnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;wr_reg <= 1'b0;ypage <= 1'b0;xpage_high <= 1'b0; xpage_low <= 1'b0;char <= 1'b0; char_num <= 1'b0;cmd_addr <= 1'b0;char_addr <= 1'b0;num_delay <= 15'd5; cnt_delay <= 1'b0; oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_data <= LOW;state <= IDLE; state_last <= IDLE;end else begincase(state)IDLE: begincnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;wr_reg <= 1'b0;ypage <= 1'b0;xpage_high <= 1'b0; xpage_low <= 1'b0;char <= 1'b0; char_num <= 1'b0; cmd_addr <= 1'b0;char_addr <= 1'b0;num_delay <= 15'd5; cnt_delay <= 1'b0; oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_data <= LOW;state <= MAIN; state_last <= MAIN;endMAIN: beginif(cnt_main >= 3'd6)cnt_main <= 3'd5;else cnt_main <= cnt_main + 1'b1;case(cnt_main)	//MAIN状态3'd0: begin state <= INIT; end3'd1: begin ypage <= 8'hb0; xpage_high <= 8'h10; xpage_low <= 8'h00; char_num <= 5'd16; char <= "ADC DATA DISPLAY";state <= SCAN; end3'd2: begin ypage <= 8'hb1; xpage_high <= 8'h10; xpage_low <= 8'h00; char_num <= 5'd16; char <= "VOLTAGE:   . V  ";state <= SCAN; end3'd3: begin ypage <= 8'hb2; xpage_high <= 8'h10; xpage_low <= 8'h00; char_num <= 5'd16; char <= "AUTHOR: ZIRU PAN";state <= SCAN; end3'd4: begin ypage <= 8'hb3; xpage_high <= 8'h10; xpage_low <= 8'h00; char_num <= 5'd16; char <= "                ";state <= SCAN; end3'd5: begin ypage <= 8'hb1; xpage_high <= 8'h15; xpage_low <= 8'h00; char_num <= 5'd1 ; char <= oled_display_digital[7:4]; state <= SCAN; end3'd6: begin ypage <= 8'hb1; xpage_high <= 8'h16; xpage_low <= 8'h00; char_num <= 5'd1 ; char <= oled_display_digital[3:0]; state <= SCAN; enddefault: state <= IDLE;endcaseendINIT: begin	//初始化状态case(cnt_init)5'd0: begin oled_rst <= LOW; cnt_init <= cnt_init + 1'b1; end	//复位有效5'd1: begin num_delay <= 15'd25000; state <= DELAY; state_last <= INIT; cnt_init <= cnt_init + 1'b1; end	//延时大于3us5'd2: begin oled_rst <= HIGH; cnt_init <= cnt_init + 1'b1; end	//复位恢复5'd3: begin num_delay <= 15'd25000; state <= DELAY; state_last <= INIT; cnt_init <= cnt_init + 1'b1; end	//延时大于220us5'd4: begin if(cmd_addr >= CMD_DEPTH) begincmd_addr <= 1'b0;cnt_init <= cnt_init + 1'b1;end else begin	cmd_addr <= cmd_addr + 1'b1; num_delay <= 15'd5;oled_dcn <= CMD; wr_reg <= cmd_out;state <= WRITE; state_last <= INIT;endend5'd5: begin cnt_init <= 1'b0; state <= MAIN; enddefault: state <= IDLE;endcaseendSCAN: beginif(cnt_scan == 4'd12) beginif(char_num) cnt_scan <= 4'd3;else cnt_scan <= cnt_scan + 1'b1;end else if(cnt_scan == 4'd13) cnt_scan <= 4'd0;else cnt_scan <= cnt_scan + 1'b1;case(cnt_scan)4'd0: begin oled_dcn <= CMD; wr_reg <= ypage; state <= WRITE; state_last <= SCAN; end    	//定位列页地址4'd1: begin oled_dcn <= CMD; wr_reg <= xpage_low; state <= WRITE; state_last <= SCAN; end	//定位行地址低位4'd2: begin oled_dcn <= CMD; wr_reg <= xpage_high; state <= WRITE; state_last <= SCAN; end	//定位行地址高位4'd3: begin char_num <= char_num - 1'b1; end4'd4: begin char_addr <= char[(char_num*8)+:8]; end4'd5: begin oled_dcn <= DATA; wr_reg <= 8'h00; state <= WRITE; state_last <= SCAN; end	//5*8点阵变成8*84'd6: begin oled_dcn <= DATA; wr_reg <= 8'h00; state <= WRITE; state_last <= SCAN; end	//5*8点阵变成8*84'd7: begin oled_dcn <= DATA; wr_reg <= 8'h00; state <= WRITE; state_last <= SCAN; end	//5*8点阵变成8*84'd8: begin oled_dcn <= DATA; wr_reg <= char_out[39:32]; state <= WRITE; state_last <= SCAN; end4'd9: begin oled_dcn <= DATA; wr_reg <= char_out[31:24]; state <= WRITE; state_last <= SCAN; end4'd10:begin oled_dcn <= DATA; wr_reg <= char_out[23:16]; state <= WRITE; state_last <= SCAN; end4'd11:begin oled_dcn <= DATA; wr_reg <= char_out[15:8] ; state <= WRITE; state_last <= SCAN; end4'd12:begin oled_dcn <= DATA; wr_reg <= char_out[7:0]  ; state <= WRITE; state_last <= SCAN; end4'd13:begin state <= MAIN; enddefault: state <= IDLE;endcaseendWRITE: begin	//WRITE状态,将数据按照SPI时序发送给屏幕if(cnt_write >= 5'd17) cnt_write <= 5'd0;else cnt_write <= cnt_write + 1'b1;case(cnt_write)5'd0: begin oled_csn <= LOW; end	//9位数据最高位为命令数据控制位5'd1: begin oled_clk <= LOW; oled_data <= wr_reg[7]; end	//先发高位数据5'd2: begin oled_clk <= HIGH; end5'd3: begin oled_clk <= LOW; oled_data <= wr_reg[6]; end5'd4: begin oled_clk <= HIGH; end5'd5: begin oled_clk <= LOW; oled_data <= wr_reg[5]; end5'd6: begin oled_clk <= HIGH; end5'd7: begin oled_clk <= LOW; oled_data <= wr_reg[4]; end5'd8: begin oled_clk <= HIGH; end5'd9: begin oled_clk <= LOW; oled_data <= wr_reg[3]; end5'd10:begin oled_clk <= HIGH; end5'd11:begin oled_clk <= LOW; oled_data <= wr_reg[2]; end5'd12:begin oled_clk <= HIGH; end5'd13:begin oled_clk <= LOW; oled_data <= wr_reg[1]; end5'd14:begin oled_clk <= HIGH; end5'd15:begin oled_clk <= LOW; oled_data <= wr_reg[0]; end	//后发低位数据5'd16:begin oled_clk <= HIGH; end5'd17:begin oled_csn <= HIGH; state <= DELAY; enddefault: state <= IDLE;endcaseendDELAY: beginif(cnt_delay >= num_delay) begincnt_delay <= 15'd0; state <= state_last; end else cnt_delay <= cnt_delay + 1'b1;enddefault: state <= IDLE;endcaseendend
endmodule

☔️ OLED驱动原理简述

📎 结构及引脚分配

该部分参看SSD1306 OLED驱动芯片 详细介绍、百度云:SSD1306 英文手册(提取码csdn,由于CSDN存在相同资源但是不免费)及百度文库:SSD1306 中文手册,在此不做详述。

🎲 MCU总线接口:4线SPI

关于SSD1306的总线接口,在上述参考链接中均有说明,在此对原文FPGA核心板中所使用的4线SPI接口做详细说明。

FPGA GPIO与OLED(其实是与SSD1306,该驱动集成在显示屏背部)结构连接电路图如下图所示,包括VCC、串行时钟(SCLK)、串行数据(SDIN)、数据/命令控制(D/C)、片选(CS#)及一复位(RES)。
FPGA GPIO与OLED连接电路原理图
在SCLK每个上升沿,SDIN上的数据按MSB~LSB顺序移位到一个8位移位寄存器中。每八个时钟对D/C进行一次采样,移位寄存器中的8位数据根据D/C的采样结果决定写入到图形显示数据RAM(GDDRAM)或命令寄存器。其写时序如下图所示:
4线SPI下的写时序
根据上述4线SPI接口的工作原理,即:片选有效后,命令解码模块根据D/C#确定输入数据是被解释为数据还是命令。D/C#引脚为HIGH,SDIN输入的D[7:0]则被解释为写入图形显示数据RAM (Graphic Display Data RAM, GDDRAM)的显示数据。如果是LOW,则D[7:0]被解释为命令,然后被解码并写入相应的命令寄存器。

代码中,在INIT状态下5’d4处cmd_addr <= cmd_addr + 1'b1; oled_dcn <= CMD; wr_reg <= cmd_out;即指定D/C#为低电平,而后输入的cmd_out则被解释为命令编码,并存入相应寄存器中。

SCAN状态中,oled_dcn <= DATA则指定D/C#为高电平,而后输入wr_reg的数据为字符编码的一个八位,连续输入5次,则代表一个字母的全部显示编码,这杯解释为图形显示数据,则存入GDDRAM中。

🔬 OLED驱动模块工作分析(结合代码)

🎨 图形显示数据RAM(GDDRAM)

GDDRAM是一个位映射静态RAM,保存要显示的位模式。内存大小为128×64位,分成8页,从PAGE0到PAGE7,用于黑白128×64点阵显示,如下图所示。
SSD1306 GDDRAM的页结构
注:重映射在后文说明。

当一个数据字节被写入GDDRAM时,当前列同页的所有行图像数据被填充(即列地址指针指向的整个列(8位)被填充)。数据位D0被写入顶部行。数据位D7写入底部一行。
数据填充方式

寻址模式

在SSD1306中有3种不同的内存寻址模式:页寻址模式、水平寻址模式和垂直寻址模式。设置寻址模式需要两个字节的命令:

  1. 20H,00H:水平寻址模式;
  2. 20H,01H,垂直寻址模式;
  3. 20H,02H,页寻址模式(复位);
  4. 20H,03H,无效。
页寻址模式

页面寻址模式(A[1:0]-10xb)页寻址模式。当GDDRAM被读写后,列地址指针自动增加1。如果列地址指针到达列结束地址,列地址指针将重置为列起始地址,页地址指针不变。用户必须设置新的页面和列地址,以便访问下一页RAM内容。页寻址模式的页和列地址的移动顺序如下图所示。
地址指针移动页面寻址模式
在正常GDDRAM读写和页寻址模式下,需要定义启动RAM访问指针的位置:

  1. 通过命令B0H~B7H设置目标显示位置的页面起始地址;
  2. 通过命令00H~0FH设置指针的较低列地址,高4位恒定为00H,低4位为要设置的起始列地址的低4位;
  3. 通过命令10H~1FH设置指针的较高列地址,高4位恒定为01H,低4位为要设置的起始列地址的高4位;
  4. *例如:设定B0H、00H、15H,则指针在PAGE1,COL80(50H = 80D)的COM0处。

例如,页面地址设置为B2H,较低列地址为03H,较高列地址为10H。这意味着起始列是PAGE2的SEG3。内存访问指针的位置如下图所示,输入数据字节将被写入RAM第3列的位置。
在页寻址模式下设置GDDRAM访问指针的示例
代码中,MIAN状态下设置了要显示的字符串,其中ypage指定了页,xpage_lowxpage_high指定了低与高列地址。由于该OLED屏幕为128×32,因此分为4页(page)和SEG0~SEG127,则ypage = 8'hb0~8'hb3

由于每个字符为5×8,保持恰当的字间距使之扩充为8×8,因此一行可以显示16个字符,对于代码中需要不断动态刷新的一行字符串 “VOLTAGE:空空空.空V空空”,两个不断更新的数字需要在第三和第四个“空”字处,因此单独设置这两个字符的访问指针为:

ypage <= 8'hb1; xpage_high <= 8'h15; xpage_low <= 8'h00; // 50H = 80(COL)
ypage <= 8'hb1; xpage_high <= 8'h16; xpage_low <= 8'h00; // 60H = 96(COL)

对于满满一行字符串,则设置高低列地址均为00H,则指针在该页COL0处。

水平模式寻址

在水平寻址模式下,当GDDRAM被读写后,列地址指针自动增加1。如果列地址指针到达列结束地址。列地址指针被重置为列起始地址,页地址指针增加1。

水平寻址模式下,页面和列地址的移动顺序如下图所示。当列地址指针和页地址指针都到达结束地址时,指针被重置为列起始地址和页起始地址(图中虚线)。
水平寻址模式下地址指针的移动

垂直模式寻址

在垂直寻址模式下,当GDDRAM被读写后,页面地址指针自动增加1。如果页面地址指针到达页面结束地址,页面地址指针被重置为页面开始地址,列地址指针加1。

垂直寻址方式下,页面和列地址的移动顺序如下图所示。当列地址指针和页地址指针都到达结束地址时,指针被重置为列起始地址和页起始地址(图中虚线)。
垂直寻址模式下地址指针的移动

重映射及多路复用
  1. A0/A1,设置段映射:
  • A0H:列地址0映射到SEG0(复位状态);
  • A1H:列地址127映射到SEG0,即原本:SEG0对应COL0→SEG127对应COL127,现在映射为:SEG0对应COL127→SEG127对应COL0。
  1. A8 + A[5:0] (00H~3FH),设置多路复用率(MUX Ratio)为N+1:A[5:0]从0到14的取值都是无效的。代码中,命令RAM中8'ha8后接8'h1F,则指定复用率(MUX Ratio) = 1FH + 1 = 20H,则复用率为20H。

  2. C0/C8,设置COM输出扫描方向:

  • C0:正常模式(复位状态),从COM0→COM[N-1];
  • C1:重映射模式,扫描从COM[N-1]→COM0,其中N为复用率。上述复用率设置为20H,代码中指定为C0,则从COM0→COM31进行扫描。
  1. DA + A[5:4] (02H/12H/22H/32H):设置COM引脚的硬件配置(以复用率为64为例):
  • A[5:4] = 00H:顺序COM引脚配置,禁止COM左右重映射,当扫描方向为COM0→COM63,COM的配置如下图;当扫描方向为COM63→COM0,为第二幅图。
    A[5:4] = 00H 正向扫描
    A[5:4] = 00H 反向扫描

  • A[5:4] = 10H:顺序COM引脚配置,允许COM左右重映射,当扫描方向为COM0→COM63,COM配置如下图,当扫描方向为COM63→COM0,为第二幅图。
    A[5:4] = 10H 正向扫描
    A[5:4] = 10H 反向扫描

  • A[5:4] = 01H:备用COM引脚配置(奇偶间隔),禁止COM左右重映射,当扫描方向为COM0→COM63,COM配置如下图,当扫描方向为COM63→COM0,为第二幅图。
    A[5:4] = 01H 正向扫描
    A[5:4] = 01H 反向扫描

  • A[5:4] = 11H:备用COM引脚配置(奇偶间隔),允许COM左右重映射,当扫描方向为COM0→COM63,COM配置如下图,当扫描方向为COM63→COM0,为第二幅图。
    A[5:4] = 11H 正向扫描
    A[5:4] = 11H 反向扫描
    代码中,命令RAM中8'hda后接8'h02,则指定A[5:4] = 00H,顺序配置COM引脚,禁止引脚左右重映射,扫描方向为COM31→COM0。

🍥 代码中使用的工作方式

OLED屏幕上显示出字符的方式不唯一,这与OLED屏幕加载的命令、输入字符编码的顺序等均有关系。

⚠️ 注意:经作者实验,OLED屏幕正面看右上角可能为每一页的COL0、ROW0处;从上至下依次为PAGE3/2/1/0。

单个字符编码

首先考虑代码中,oled_char_RAM中储存的字符编码是怎么一回事。例如:

Mem[ 50] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46};   // 50  2

无段映射(SEG与COL一致,SEG0)、扫描方向为正向(COM0→COM7),写入GDDRAM时先写入字符编码高位WRITE状态下先写入一个字节编码的高位时表示字符“2”的字符编码为如下图所示:
字符2的编码显示-1
为正常显示,需要改变扫描方向和SEG重映射:
字符2的编码显示-2

字符串写入顺序

字符串由多个字符组成,因此相较于单个字符存在字符扫描顺序问题。该问题取决于页选址模式小节中GDDRAM访问指针的位置。

无段映射(SEG与COL一致,A0H)、扫描方向为正向(COM0→COM7,C0H),写入GDDRAM时先写入字符编码高位正序读入字符串时字符串“27”显示的效果为:
字符串27编码显示-1
指针从右上方红色箭头处开始朝下扫描。

而原文所提供的代码其字符串扫描顺序如下图所示,具体方式说明见后:
字符串27编码显示-2
指针从左下方红色箭头处开始朝上扫描。

oled_cmd_RAM中设置了段重映射,因此SEG0对应COL127→SEG127对应COL0;设置反向扫描,因此每一页(page)从下往上填充一个八位编码。

在读入显示数据(D/C = 1)时,先需要在D/C = 0下输入3个字节的命令以决定RAM访问指针起始位置:ypage(设置页起始地址)、xpage_lowxpage_high(共同设置COL地址)。

SCAN状态下,记录字符个数的计数器char_num是倒序的,由于char_addr <= char[(char_num*8)+:8],字符串从最左侧(高位)开始取字符编码(示例中先扫描“2”)。程序中,由于字符编码为5×8,需要扩充至8×8才可在每个字符间保持恰当的间距,因此先输入3列8'h00(空白),而后再读入字符编码。读入字符编码时,程序中先读入字符“2”的高位(8’h42)→低位(8’h46),并且写入COM中的一个八位编码(8’h42)顺序为:01000010。

多行字符串写入顺序

多行字符串写入需要增加PAGE的控制。具体体现在代码中,由于是页寻址模式,扫描每一行字符串需要设置不同的ypagexpage_lowxpage_high

这篇关于基于Lattice XO2-4000HC FPGA核心板的SSD1306 OLED12832驱动芯片指令及工作方式详述(Verilog)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/262257

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

【即时通讯】轮询方式实现

技术栈 LayUI、jQuery实现前端效果。django4.2、django-ninja实现后端接口。 代码仓 - 后端 代码仓 - 前端 实现功能 首次访问页面并发送消息时需要设置昵称发送内容为空时要提示用户不能发送空消息前端定时获取消息,然后展示在页面上。 效果展示 首次发送需要设置昵称 发送消息与消息展示 提示用户不能发送空消息 后端接口 发送消息 DB = []@ro

工作常用指令与快捷键

Git提交代码 git fetch  git add .  git commit -m “desc”  git pull  git push Git查看当前分支 git symbolic-ref --short -q HEAD Git创建新的分支并切换 git checkout -b XXXXXXXXXXXXXX git push origin XXXXXXXXXXXXXX

脏页的标记方式详解

脏页的标记方式 一、引言 在数据库系统中,脏页是指那些被修改过但还未写入磁盘的数据页。为了有效地管理这些脏页并确保数据的一致性,数据库需要对脏页进行标记。了解脏页的标记方式对于理解数据库的内部工作机制和优化性能至关重要。 二、脏页产生的过程 当数据库中的数据被修改时,这些修改首先会在内存中的缓冲池(Buffer Pool)中进行。例如,执行一条 UPDATE 语句修改了某一行数据,对应的缓

嵌入式方向的毕业生,找工作很迷茫

一个应届硕士生的问题: 虽然我明白想成为技术大牛需要日积月累的磨练,但我总感觉自己学习方法或者哪些方面有问题,时间一天天过去,自己也每天不停学习,但总感觉自己没有想象中那样进步,总感觉找不到一个很清晰的学习规划……眼看 9 月份就要参加秋招了,我想毕业了去大城市磨练几年,涨涨见识,拓开眼界多学点东西。但是感觉自己的实力还是很不够,内心慌得不行,总怕浪费了这人生唯一的校招机会,当然我也明白,毕业