当前位置: 首页 > news >正文

【星闪】Hi2821 | SPI串行外设接口 + OLED显示屏驱动例程

1. 简介

        SPI(Serial Peripheral interface)全称串行外围设备接口,是一种高速、全双工、同步通信总线。

        SPI 采用主从控制模式(Master-Slave)架构,一般有一个主设备一个或多个从设备,使得主设备可以与多个从设备之间实现片间通信。SPI 通信需要 4 根信号线——时钟线(SCLK)、主出从入线(MOSI)、主入从出线(MISO)和片选线(CS)

        每增加一个从机那么就要增加一条片选线,理论上片选线可以无限增加,如果总线上的外设通信效率不需要很高的话。

1.1 工作模式

        SPI 的通讯方式比较简单,主机主动发起通讯,先拉低对应从机的片选线(CS)通知开始通讯,接着时钟线发送脉冲,在时钟脉冲的上升沿或下降沿,两者接收或者发送数据。

        在 SPI 的通讯过程中,既可以上升沿采集数据,也可以下降沿采集数据,这取决于 SPI 的工作模式,工作模式由时钟极性(CPOL)和相位(CPHA)决定。

  • CPOL=0:时钟空闲时为低电平;
  • CPOL=1:时钟空闲时为高电平;
  • CPHA=0:在第一个时钟边沿采集数据;
  • CPHA=1:在第二个时钟边沿采集数据;

        根据上面的描述可以知道,当 CPOL=0、CPHA=0 或 CPOL=1、CPHA=1 时,通信时在上升沿采集数据;当 CPOL=0、CPHA=1 或 CPOL=0、CPHA=1 时,通信时在下降沿采集数据

2. 例程

        这个例程中会使用 SPI 协议去驱动一块 OLED 显示屏,屏幕使用 SSD1306 驱动芯片,要注意只有 7 个引脚的版本才支持 SPI 协议通讯,如下图:

        SSD1306 驱动芯片数据手册:下载链接

2.1 Kconfig

        以下是比较常用的配置

  • SPI support MASTER:使能主机功能;
  • SPI support SLAVE:使能从机功能;
  • SPI support DMA transfer:使能 DMA 传输;
  • SPI support interrupt transfer:使能中断传输(不能与 DMA 传输同时打开);
  • SPI support concurrency:使能并发传输;

        另外在芯片配置这里可以调整 SPI 总线的数量,可以根据使用的外设数量来调整。

2.2 代码

#include <stdbool.h>#include "oled.h"#include "spi.h"
#include "pinctrl.h"
#include "gpio.h"
#include "dma.h"#define LOG_TAG "oled"
#include "debug.h"#define SPI_BUS SPI_BUS_1#define CS_PIN(x) uapi_gpio_set_val(11, x)
#define DC_PIN(x) uapi_gpio_set_val(12, x)typedef struct {uint8_t p1;  // 起始页uint8_t p2;  // 结尾页uint8_t y1_off;  // y距页起始的偏移uint8_t y2_off;  // y距页结尾的偏移
} window_t;static uint8_t g_gram[(CONFIG_SCREEN_HEIGHT >> 3) * (CONFIG_SCREEN_WIDTH)] = {0};static int oled_bsp_init(void)
{int ret = 0;/* SCLK */uapi_pin_set_mode(15, HAL_PIO_SPI1_CLK);/* MOSI */uapi_pin_set_mode(14, HAL_PIO_SPI1_TXD);/* RST */uapi_pin_set_mode(13, HAL_PIO_FUNC_GPIO);uapi_gpio_set_dir(13, GPIO_DIRECTION_OUTPUT);uapi_gpio_set_val(13, GPIO_LEVEL_HIGH);/* DC */uapi_pin_set_mode(12, HAL_PIO_FUNC_GPIO);uapi_gpio_set_dir(12, GPIO_DIRECTION_OUTPUT);/* CS */uapi_pin_set_mode(11, HAL_PIO_FUNC_GPIO);uapi_gpio_set_dir(11, GPIO_DIRECTION_OUTPUT);uapi_gpio_set_val(11, GPIO_LEVEL_HIGH);/* 初始化SPI */spi_attr_t config = { 0 };config.freq_mhz = 8;                                        /* 工作频率 */config.is_slave = false;                                    /* 主机模式 */config.frame_size = HAL_SPI_FRAME_SIZE_8;                   /* 帧大小,8位 */config.slave_num = 1;                                       /* 片选 0 */config.spi_frame_format = HAL_SPI_FRAME_FORMAT_STANDARD;    /* 传输模式:标准 */config.bus_clk = SPI_CLK_FREQ;                              /* 总线速率 32MHz */config.frame_format = SPI_CFG_FRAME_FORMAT_MOTOROLA_SPI;    /* 协议格式:摩托罗拉SPI协议格式 */config.tmod = HAL_SPI_TRANS_MODE_TXRX;                      /* 传输模式:收发模式 */config.clk_phase = SPI_CFG_CLK_CPHA_0;                      /* 相位:空闲低电平 */config.clk_polarity = SPI_CFG_CLK_CPOL_0;                   /* 极性:第一个时钟沿采集 */spi_extra_attr_t ext_config = { 0 };ext_config.sspi_param.wait_cycles = 0x10;                   /* 等待周期 */ret = uapi_spi_init(SPI_BUS, &config, &ext_config);if (ret) {return ret;}return 0;
}static int oled_write_bytes(bool is_data, uint8_t* bytes, size_t len)
{spi_xfer_data_t data = {.tx_buff = bytes,.tx_bytes = len,};DC_PIN(is_data);CS_PIN(0);int ret = uapi_spi_master_write(SPI_BUS, &data, CONFIG_SPI_MAX_TIMEOUT);CS_PIN(1);return ret;
}static int oled_write_cmd(uint8_t cmd)
{return oled_write_bytes(false, &cmd, 1);
}static void oled_reg_init(void)
{/* 进入睡眠 */oled_write_cmd(0xAE);/* 起始行地址 */oled_write_cmd(0x40);/* 对比度 */oled_write_cmd(0x81);oled_write_cmd(0xCF);  // A[7:0]/* 段重映射 */oled_write_cmd(0xA1);  // 0xA0:左右反置,0xA1:正常/* COM输出扫描方向 */oled_write_cmd(0xC8);  // 0xC0:上下反置,0xC8:正常/* Multiplex Ratio */oled_write_cmd(0xA8);oled_write_cmd(0x3f);  // 1/64 duty/* 屏幕偏移 */oled_write_cmd(0xD3);oled_write_cmd(0x00);  // A[5:0]/* 时钟频率 */oled_write_cmd(0xD5);oled_write_cmd(0x80);  // A[3:0]+1:分频系数;A[7:4]:晶振频率/* 预充电周期 */oled_write_cmd(0xD9);oled_write_cmd(0xF1);  // A[3:0]:第一阶段;A[7:4]:第二阶段/* COM配置 */oled_write_cmd(0xDA);oled_write_cmd(0x12);  // A[4]=0:序列;A[4]=1:另类;A[5]:COM左右重映射/* Vcomh */oled_write_cmd(0xDB);oled_write_cmd(0x40);  // A[6:4]/* 内存取址模式 */oled_write_cmd(0x20);oled_write_cmd(0x00);  // A[1:0]:0x00:行取址;0x01:列取址;0x02:页取址oled_write_cmd(0x8D);//--set Charge Pump enable/disableoled_write_cmd(0x14);//--set(0x10) disable/* 屏幕使能 */oled_write_cmd(0xA4);  // 0xA4:使用RAM内容;0xA5:忽略RAM内容/* 屏幕反转 */oled_write_cmd(0xA6);  // 0xA6:正常;0xA7:反转/* 唤醒屏幕 */oled_write_cmd(0xAF);/* 清屏 */oled_clear();
}static int oled_open_window(uint8_t x, uint8_t y, uint8_t w, uint8_t h, window_t* window)
{if (window == NULL ||x >= CONFIG_SCREEN_WIDTH ||y >= CONFIG_SCREEN_HEIGHT ||w + x > CONFIG_SCREEN_WIDTH ||h + y > CONFIG_SCREEN_HEIGHT ||w == 0 || h == 0) {LOG("invalid param");return -1;}window->y1_off = y % 8;window->y2_off = (y + h + window->y1_off) % 8 ? 8 - ((y + h + window->y1_off) % 8) : 0;window->p1 = y >> 3;window->p2 = ((y + h + window->y1_off + window->y2_off) >> 3) - 1;/* 设置列区间 */oled_write_cmd(0x21);oled_write_cmd(x);oled_write_cmd(x + w - 1);/* 设置页区间 */oled_write_cmd(0x22);oled_write_cmd(window->p1);oled_write_cmd(window->p2);return 0;
}int oled_init(void)
{int ret = 0;/* 初始化板级驱动 */ret = oled_bsp_init();if (ret) {LOG("oled bsp init failed, err: %d", ret);return ret;}/* 初始化寄存器 */oled_reg_init();return 0;
}int oled_set_data(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* data)
{int ret = 0;/* 开窗 */window_t window = {0};ret = oled_open_window(x, y, w, h, &window);if (ret) {LOG("open window failed");return ret;}// LOG("p1=%d, p2=%d, y1_off=%d, y2_off=%d", window.p1, window.p2, window.y1_off, window.y2_off);/* 缓存数据 */for (uint8_t i = window.p1, *ptr = data; i <= window.p2; i++, ptr += w) {memcpy(g_gram + i * CONFIG_SCREEN_WIDTH + x, ptr, w);}/* 写数据 */return oled_write_bytes(true, g_gram + window.p1 * CONFIG_SCREEN_WIDTH + x, (window.p2 - window.p1 + 1) * w);
}void oled_clear(void)
{memset(g_gram, 0, sizeof(g_gram));/* 设置列区间 */oled_write_cmd(0x21);oled_write_cmd(0);oled_write_cmd(CONFIG_SCREEN_WIDTH - 1);/* 设置页区间 */oled_write_cmd(0x22);oled_write_cmd(0);oled_write_cmd((CONFIG_SCREEN_HEIGHT >> 3) - 1);/* 写数据 */oled_write_bytes(true, g_gram, sizeof(g_gram));
}

1. 初始化管脚。

        SPI 的相关管脚需要调用 uapi_pin_set_mode 来配置复用,但片选线比较特殊,像我这块 OLED 屏幕的显示芯片使用的是 3 线 SPI,即没有 MISO 线,那么片选线就不需要复用,配置成普通 GPIO 即可;如果使用的是标准 SPI,那么片选线就要配置复用

        除了 SPI 相关的管脚还有一根 DC 线用于控制命令发送或数据发送和一根 RES 线用于复位芯片(不需要可以直接接3.3V电源)。

2. 初始化 SPI 接口。

        调用 uapi_spi_init 函数进行初始化,参数一为总线号;参数二为初始化结构体,定义如下:

typedef struct hal_spi_attr {bool is_slave;                      /*!< @if Eng Indicates if SPI work in slave mode or not.@else   SPI工作在Master/Slave模式。 @endif */uint32_t slave_num;                 /*!< @if Eng Index when selecting a slave.- 0: Not select.- 1: slave index 0.- 2: slave index 1.- ...@else   选择从机时的索引- 0:不选择。- 1:从机索引0。- 2:从机索引1。- ...@endif */uint32_t bus_clk;                   /*!< @if Eng Provide ssi_clk for clock freq division calculation.@else   用于计算SPI的时钟分频系数。 @endif */uint32_t freq_mhz;                  /*!< @if Eng Indicates the frequency of SPI.@else   SPI的工作频率。 @endif */uint32_t clk_polarity;              /*!< @if Eng Indicates the clock polarity of SPI.For details, see @ref hal_spi_cfg_clk_cpol_t@else   SPI的时钟极性。参考 @ref hal_spi_cfg_clk_cpol_t @endif */uint32_t clk_phase;                 /*!< @if Eng Indicates the clock phase of SPI.For details, see @ref hal_spi_cfg_clk_cpha_t@else   SPI的时钟相位。参考 @ref hal_spi_cfg_clk_cpha_t @endif */uint32_t frame_format;              /*!< @if Eng Indicates the which serial protocol transfers the data.For details, see @ref hal_spi_cfg_frame_format_t@else   选择串行传输的协议。参考 @ref hal_spi_cfg_frame_format_t @endif */uint32_t spi_frame_format;          /*!< @if Eng Indicates the frame format of SPI.For details, see @ref hal_spi_frame_format_t@else   SPI的帧格式。参考 @ref hal_spi_frame_format_t @endif */uint32_t frame_size;                /*!< @if Eng Indicates the frame size of SPI.For details, see @ref hal_spi_frame_size_t@else   SPI的帧长度。参考 @ref hal_spi_frame_size_t @endif */uint32_t tmod;                      /*!< @if Eng Indicates the transfer mode.For details, see @ref hal_spi_trans_mode_t@else   SPI的传输模式。参考 @ref hal_spi_trans_mode_t @endif */uint32_t ndf;                       /*!< @if Eng Indicates the number of data frames.@else   SPI的数据帧数。 @endif */uint32_t sste;                      /*!< @if Eng Indicates if SPI slave select toggle enable or not.When disable, master should reed all data in slave tx_queueat ONE time when reading data from slave device. Otherwise,data loss occurs.For details, see @ref hal_spi_cfg_sste_t@else   SPI从机选择切换使能/不使能。当此配置不使能,主机从从机读取数据时,需要一次性将从机发送队列中的数据读完,否则会出现丢失数据问题。参考 @ref hal_spi_cfg_sste_t @endif */
} hal_spi_attr_t;
  • is_slave:是否为从机;
  • slave_num:从机数量;
  • bus_clk:总线时钟频率,单位:Hz,SPI 默认使用 32 MHz 时钟。
  • freq_mhz:工作时钟频率,单位:MHz;

        工作时钟频率可以通过数据手册查看,t_{cycle}是最小的时钟周期,为100ns,因此可以计算出最高时钟频率为 10MHz。

  • clk_polarity:时钟极性(CPOL);
  • clk_phase:时钟相位(CPHA);

        根据时序图,芯片支持 CPOL=0、CPHA=0 和 CPOL=1、CPHA=1,两种工作方式。

  • frame_format:帧传输协议,选项如下:
typedef enum hal_spi_cfg_frame_format {SPI_CFG_FRAME_FORMAT_MOTOROLA_SPI,      /*!< @if Eng Motorolla SPI Frame Format.@else   摩托罗拉SPI帧格式。 @endif */SPI_CFG_FRAME_FORMAT_TEXAS_SSP,         /*!< @if Eng Texas Instruments SSP Frame Format.@else   德州仪器SSP帧格式。 @endif */SPI_CFG_FRAME_FORMAT_NS_MICROWIRE,      /*!< @if Eng National Microwire Frame Format.@else   国家微线帧格式。 @endif */SPI_CFG_FRAME_FORMAT_MAX
} hal_spi_cfg_frame_format_t;
  • spi_frame_format:帧格式,选项如下:
typedef enum hal_spi_frame_format {HAL_SPI_FRAME_FORMAT_STANDARD = 0,      /*!< @if Eng SPI Standard frame format.@else   标准的单线SPI帧格式。 @endif */HAL_SPI_FRAME_FORMAT_DUAL,              /*!< @if Eng SPI Dual frame format.@else   双线SPI帧格式。 @endif */HAL_SPI_FRAME_FORMAT_QUAD,              /*!< @if Eng SPI Quad frame format.@else   4线SPI帧格式。 @endif */HAL_SPI_FRAME_FORMAT_OCTAL,             /*!< @if Eng SPI Octal frame format.@else   8线SPI帧格式。 @endif */HAL_SPI_FRAME_FORMAT_DOUBLE_OCTAL,      /*!< @if Eng SPI Double Octal frame format.@else   16线SPI帧格式。 @endif */HAL_SPI_FRAME_FORMAT_SIXT,HAL_SPI_FRAME_FORMAT_MAX_NUM,HAL_SPI_FRAME_FORMAT_NONE = HAL_SPI_FRAME_FORMAT_MAX_NUM
} hal_spi_frame_format_t;
  • frame_size:数据帧大小,选项如下:
typedef enum hal_spi_frame_size {HAL_SPI_FRAME_SIZE_8    = 0x07,         /*!< @if Eng 8-bit serial data transfer.@else   8-位串行数据传输。 @endif */HAL_SPI_FRAME_SIZE_16   = 0x0F,         /*!< @if Eng 16-bit serial data transfer(Not supported now).@else   16-位串行数据传输(暂不支持)。 @endif */HAL_SPI_FRAME_SIZE_24   = 0x17,         /*!< @if Eng 24-bit serial data transfer(Not supported now).@else   24-位串行数据传输(暂不支持)。 @endif */HAL_SPI_FRAME_SIZE_32   = 0x1F          /*!< @if Eng 32-bit serial data transfer.@else   32-位串行数据传输。 @endif */
} hal_spi_frame_size_t;
  • tmod:传输模式,选项如下:
typedef enum hal_spi_trans_mode {HAL_SPI_TRANS_MODE_TXRX = 0,            /*!< @if Eng Transmit and receive mode.@else   收发模式。 @endif */HAL_SPI_TRANS_MODE_TX,                  /*!< @if Eng Transmit only / Transmit mode.@else   发送模式。 @endif */HAL_SPI_TRANS_MODE_RX,                  /*!< @if Eng Receive only / Receive mode.@else   接收模式。 @endif */HAL_SPI_TRANS_MODE_EEPROM,              /*!< @if Eng EEPROM read mode.@else   EEPROM模式。 @endif */HAL_SPI_TRANS_MODE_MAX
} hal_spi_trans_mode_t;
  • ndf:数据帧数量;
  • sste:从机片选切换使能,即软件片选或硬件片选。

        参数三为拓展的 SPI 配置参数,主要用于一些特殊的模式,如 QSPI、EEPROM、DMA 等等,初始化结构体定义如下:

typedef struct hal_spi_extra_attr {bool tx_use_dma;                        /*!< @if Eng Indicates if SPI use dma or not in TX.@else   SPI是否使用DMA发送数据。 @endif */bool rx_use_dma;                        /*!< @if Eng Indicates if SPI use dma or not in RX.@else   SPI是否使用DMA接收数据。 @endif */hal_spi_xfer_qspi_param_t qspi_param;   /*!< @if Eng Indicates the qspi parameters.@else   QSPI参数。 @endif */hal_spi_xfer_sspi_param_t sspi_param;   /*!< @if Eng Indicates the single spi parameters.@else   Single SPI参数。 @endif */
} hal_spi_extra_attr_t;typedef struct hal_spi_xfer_qspi_param {hal_spi_trans_type_t trans_type;  /*!< @if Eng SPI frame format for instruction and address.@else   传输类型,用于指定指令和地址的长度。 @endif */hal_spi_inst_len_t   inst_len;    /*!< @if Eng Instruction length, support 0, 4, 8, 16bits.@else   指令长度,支持0、4、8、16位。 @endif */hal_spi_addr_len_t   addr_len;    /*!< @if Eng Address length, support 0, 8, 16, 24, 32bits.@else   地址长度,支持0、8、16、24、32位。 @endif */uint32_t             wait_cycles; /*!< @if Eng Indicates the wait cycles.@else   等待的周期数。 @endif */
} hal_spi_xfer_qspi_param_t;typedef struct hal_spi_xfer_sspi_param {uint32_t             wait_cycles; /*!< @if Eng Indicates the wait cycles.@else   等待的周期数。 @endif */
} hal_spi_xfer_sspi_param_t;

3. 初始化寄存器。

        驱动芯片的寄存器初始化可以参考数据手册说明。

4. 写数据。

        参考时序图,通讯前先拉低片选;接着,如果发送数据就拉高 DC 脚,发送命令就拉低 DC 脚;然后就可以调用 uapi_spi_master_write 函数发送数据,参数一为总线号,参数二为数据包,定义如下:

typedef struct hal_spi_xfer_data {uint8_t *tx_buff;       /*!< @if Eng Buff to send data through tx fifo.@else   通过tx fifo发送数据的Buff。 @endif */uint32_t tx_bytes;      /*!< @if Eng Bytes of data need to send. For details, see @ref hal_spi_attr_t.frame_size.when frame_size is HAL_SPI_FRAME_SIZE_8, The value must be a multiple of 1.when frame_size is HAL_SPI_FRAME_SIZE_16, The value must be a multiple of 2.when frame_size is HAL_SPI_FRAME_SIZE_24, The value must be a multiple of 3.when frame_size is HAL_SPI_FRAME_SIZE_32, The value must be a multiple of 4.@else   发送数据的个数。参考 @ref hal_spi_attr_t.frame_size.如果frame_size为HAL_SPI_FRAME_SIZE_8,则需设定为1的倍数如果frame_size为HAL_SPI_FRAME_SIZE_16,则需设定为2的倍数如果frame_size为HAL_SPI_FRAME_SIZE_24,则需设定为3的倍数如果frame_size为HAL_SPI_FRAME_SIZE_32,则需设定为4的倍数@endif */uint8_t *rx_buff;       /*!< @if Eng Buff to receive data from rx fifo.@else   通过rx fifo接收数据的Buff。 @endif */uint32_t rx_bytes;      /*!< @if Eng Bytes of data need to receive, For details, see @ref hal_spi_attr_t.frame_size.when frame_size is HAL_SPI_FRAME_SIZE_8, The value must be a multiple of 1.when frame_size is HAL_SPI_FRAME_SIZE_16, The value must be a multiple of 2.when frame_size is HAL_SPI_FRAME_SIZE_24, The value must be a multiple of 3.when frame_size is HAL_SPI_FRAME_SIZE_32, The value must be a multiple of 4.@else   接收数据的个数。参考 @ref hal_spi_attr_t.frame_size.如果frame_size为HAL_SPI_FRAME_SIZE_8,则需设定为1的倍数如果frame_size为HAL_SPI_FRAME_SIZE_16,则需设定为2的倍数如果frame_size为HAL_SPI_FRAME_SIZE_24,则需设定为3的倍数如果frame_size为HAL_SPI_FRAME_SIZE_32,则需设定为4的倍数@endif */uint8_t cmd;            /*!< @if Eng Command for QSPI mode.@else   QSPI模式下的命令。 @endif */uint8_t reserved[3];    /*!< @if Eng Reserved.@else   保留。 @endif */uint32_t addr;          /*!< @if Eng Address for QSPI mode.@else   QSPI模式下的地址。 @endif */
} hal_spi_xfer_data_t;
  • tx_buff:发送缓冲区;
  • tx_bytes:发送字节数;
  • rx_buff:接收缓冲区;
  • rx_bytes:接收字节数;
  • cmd:命令(仅 QSPI);
  • addr:地址(仅 QSPI)。

        参数三为超时等待时间,单位:毫秒;传输完成后需要拉高片选,示意传输结束。

        SSD1306 芯片有 3 种写显存方式——页优先、行优先和列优先,例程中使用的是行优先,示意图如下:

        写显存时要先通过命令确定起始列、结束列、起始页和结束页。然后发送显示数据,芯片会按行依次填充数据,当发送完最后一个数据,指针会重新跳回起始位置。

        行方向上的数据是通过页来储存的,相当于用一个字节来储存同一列的 8 行数据,所以写数据的时候要做一些额外的操作来实现非 8 比特对齐的写显存。

5. 主函数。

#include <string.h>#include "soc_osal.h"
#include "app_os_init.h"
#include "common_def.h"#include "oled.h"static uint8_t data[2 * 128];void app_main(void *unused)
{(void)(unused);/* 初始化OLED */oled_init();memset(data, 0xFF, sizeof(data));oled_set_data(0, 0, 128, 16, data);while (1) {osal_msleep(10);}
}

        主函数里面先初始化 OLED,然后填充一块区域,测试工作是否正常。

2.3 效果

http://www.dtcms.com/a/360423.html

相关文章:

  • 语音芯片3W输出唯创知音WTN6040FP、WT588F02BP-14S、WT588F04AP-14S
  • [回溯+堆优化]37. 解数独
  • Q1 Top IF 18.7 | 基于泛基因组揭示植物NLR进化
  • 高校心理教育辅导系统的设计与实现|基于SpringBoot高校心理教育辅导系统的设计与实现
  • 网格图--Day02--网格图DFS--面试题 16.19. 水域大小,LCS 03. 主题空间,463. 岛屿的周长
  • 技术总体方案设计思路
  • SAP报工与收货的区别(来自deepseek)
  • c++ 二维码、条形码开发实例
  • FFMPEG学习任务
  • 为什么计算机使用补码存储整数:补码的本质
  • 自定义AXI_PWM_v1.0——ZYNQ学习笔记15
  • Ultra Low Power Transceiver for Wireless Body Area Networks中文版
  • Makefile语句解析:头文件目录自动发现与包含标志生成
  • Day 01(01): Hadoop与大数据基石
  • RPC个人笔记(包含动态代理)
  • Qwen2.5-VL代码初步解读
  • 一个从7zip中分离出来的高压缩比文本压缩工具ppmd
  • 使用PowerShell监听本地端口
  • 多线程案例、线程池
  • QT6(QStandardItemModel和QTableView及自定义代理)
  • 第3章 乱码的前世今生-字符集和比较规则
  • 部署在windows的docker中的dify知识库存储位置
  • 常见线程池的创建方式及应用场景
  • Cookie、Session 和 JWT
  • 【K8s-Day 22】深入解析 Kubernetes Deployment:现代应用部署的基石与滚动更新的艺术
  • 服装管理软件与工厂计件系统精选
  • 【OpenGL】LearnOpenGL学习笔记18 - Uniform缓冲对象UBO
  • [每周一更]-(第158期):构建高性能数据库:MySQL 与 PostgreSQL 系统化问题管理与优化指南
  • XPlayer播放器APP:安卓平台上的全能视频播放器
  • 网络代理协议深度对比