• STC89C51基础及项目第12天:IIC协议、OLED显示字符和图片


    1. IIC协议初识(246.92)

    概述

    • 全称 Inter-Integrated Circuit (集成电路总线)
    • 是由 PHILIPS 公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备
    • 属于半双工同步通信方式

    特点(*10KB/s会在面试中问到)

    • 简单性和有效性
      • 由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本
      • 总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件(*10KB/s会在面试中问到)
    • 多主控(multimastering)
      • 其中任何能够进行发送和接收的设备都可以成为主总线
      • 一个主控能够控制信号的传输和时钟频率
      • 当然,在任何时间点上只能有一个主控

    构成

    • IIC 串行总线一般有两根信号线,一根是双向的数据线 SDA,另一根是时钟线 SCL,其时钟信号是由主控器件产生
    • 所有接到 IIC 总线设备上的串行数据 SDA 都接到总线的 SDA 上,各设备的时钟线 SCL 接到总线的 SCL 上
    • 对于并联在一条总线上的每个 IC 都有唯一的地址
      在这里插入图片描述

    2. IIC协议起始和终止信号(247.93)

    • IIC 总线在传输数据的过程中一共有三种类型信号:开始信号、结束信号和应答信号。(多机通信要点:起始位,停止位,数据位,速度(波特率))
    • 这些信号中,起始信号和结束信号是必需的,应答信号则非必需
    • 起始信号、终止信号:
      在这里插入图片描述
      在这里插入图片描述
    • 代码(19./01_IIC协议基本函数封装)
    #include "reg52.h"
    #include "intrins.h"
    
    sbit scl = P0^1;
    sbit sda = P0^3;
    
    void IIC_start(){
    	sda = 1;
    	scl = 1;
    	_nop_();//5us
    	sda = 0;
    	_nop_();
    }
    
    void IIC_stop(){
    	sda = 0;
    	scl = 1;
    	_nop_();
    	sda = 1;
    	_nop_();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3. IIC协议ACK函数封装(248.94)

    • 应答信号
      • 发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号;
      • 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
      • 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
        在这里插入图片描述
    • 代码(19./01_IIC协议基本函数封装)
    char IIC_ACK(){//Answer back
    	char flag;
    	
    	sda = 1;//在时钟脉冲9期间释放数据线
    	_nop_();
    	scl = 1;//拉高
    	_nop_();
    	flag = sda;//读数据
    	_nop_();
    	scl = 0;//拉低
    	_nop_();
    	
    	return flag;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4. IIC协议发送一个字节的函数封装(249.95)

    数据发送的时序

    在这里插入图片描述

    • 代码(19./01_IIC协议基本函数封装)
    void IIC_Send_Byte(char dataSend){
    	int i;
    	
    	for(i = 0;i < 8;i++){
    		scl = 0;//将scl拉低,让sda做好数据准备
    		sda = dataSend & 0x80;//1000 0000获得dataSend的最高位(最左边的位),给sda
    		_nop_();//发送数据建立时间
    		scl = 1;//将scl拉高开始发送数据位
    		_nop_();//数据发送时间
    		scl = 0;//发送完毕拉低准备发送下一位
    		_nop_();
    		dataSend = dataSend << 1;//将要发送的数据左移一位,准备发送下一位
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5. OLED写入指令和数据(250.96)

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    OLED 写命令/数据:

    1. start()
    2. 写入从机地址 b0111 1000 0x78
    3. ACK
    4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
    5. ACK
    6. 写入指令/数据
    7. ACK
    8. STOP

    代码(19./02_OLED写数据写命令函数封装)

    void Oled_Write_Cmd(char dataCmd){
    	//1. start()
    	IIC_start();
    	//2. 写入从机地址 b0111 1000 0x78
    	IIC_Send_Byte(0x78);
    	//3. ACK
    	IIC_ACK();
    	//4. cotrol byte: (0)(0)000000 写入命令
    	IIC_Send_Byte(0x00);
    	//5. ACK
    	IIC_ACK();
    	//6. 写入指令
    	IIC_Send_Byte(dataCmd);
    	//7. ACK
    	IIC_ACK();
    	//8. STOP	
    	IIC_stop();
    }
    
    void Oled_Write_Data(char dataData){
    	//1. start()
    	IIC_start();
    	//2. 写入从机地址 b0111 1000 0x78
    	IIC_Send_Byte(0x78);
    	//3. ACK
    	IIC_ACK();
    	//4. cotrol byte: (0)(1)000000写入数据
    	IIC_Send_Byte(0x40);
    	//5. ACK
    	IIC_ACK();
    	//6. 写入指令
    	IIC_Send_Byte(dataData);
    	//7. ACK
    	IIC_ACK();
    	//8. STOP	
    	IIC_stop();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    6. OLED显示一个点的思路(251.97)

    • Resolution: 128 x 64 dot matrix panel
    • 内存管理
      在这里插入图片描述
    • 如何显示一个点?
      • 有三种寻址模式:页地址模式、水平地址模式和垂直地址模式,可通过以下表格进行配置
    • 设置页寻址模式:(默认即页模式)
      a. 发送 cmd:0x20
      b. 发送 cmd:0x02
      在这里插入图片描述

    页地址模式

    在这里插入图片描述

    水平地址模式

    在这里插入图片描述

    垂直地址模式

    在这里插入图片描述

    • PAGE地址选择

      • 0xB0–0xB7
        在这里插入图片描述
    • 如果写入0x08(b00001000)会显示什么呢

      • 一个字节负责一个Page的一列显示(1处点亮其他灭)
        在这里插入图片描述

    7. OLED显示一个点代码实现(252.98)

    • 代码(19./03_OLED显示一个点)(显示雪花,需要清屏操作:清雪花的函数)
    void Oled_Init(){
    	Oled_Write_Cmd(0xAE);//--display off
    	Oled_Write_Cmd(0x00);//---set low column address
    	Oled_Write_Cmd(0x10);//---set high column address
    	Oled_Write_Cmd(0x40);//--set start line address  
    	Oled_Write_Cmd(0xB0);//--set page address
    	Oled_Write_Cmd(0x81); // contract control
    	Oled_Write_Cmd(0xFF);//--128   
    	Oled_Write_Cmd(0xA1);//set segment remap 
    	Oled_Write_Cmd(0xA6);//--normal / reverse
    	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
    	Oled_Write_Cmd(0x3F);//--1/32 duty
    	Oled_Write_Cmd(0xC8);//Com scan direction
    	Oled_Write_Cmd(0xD3);//-set display offset
    	Oled_Write_Cmd(0x00);//
    	Oled_Write_Cmd(0xD5);//set osc division
    	Oled_Write_Cmd(0x80);//
    	Oled_Write_Cmd(0xD8);//set area color mode off
    	Oled_Write_Cmd(0x05);//
    	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
    	Oled_Write_Cmd(0xF1);//
    	Oled_Write_Cmd(0xDA);//set com pin configuartion
    	Oled_Write_Cmd(0x12);//	
    	Oled_Write_Cmd(0xDB);//set Vcomh
    	Oled_Write_Cmd(0x30);//
    	Oled_Write_Cmd(0x8D);//set charge pump enable
    	Oled_Write_Cmd(0x14);//	
    	Oled_Write_Cmd(0xAF);//--turn on oled panel		
    }
    void main(){
    	int a = 10;
    	//1. OLED初始化
    	Oled_Init();
    	//2. 选择一个位置
    	//2.1 确认页寻址模式
    	Oled_Write_Cmd(0x20);
    	Oled_Write_Cmd(0x02);
    	//2.2 选择PAGE0 1011 0000 即 0xB0
    	Oled_Write_Cmd(0xB0);
    	//3. 显示一个点
    	Oled_Write_Data(0x80);
    	Oled_Write_Data(0x80);
    	Oled_Write_Data(0x80);
    	Oled_Write_Data(0x80);
    	Oled_Write_Data(0x80);
    	Oled_Write_Data(0x80);
    	Oled_Write_Data(0x80);
    	Oled_Write_Data(0x80);
    	
    	while(1);//不让程序结束
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    8. OLED列地址和雪花BUG解决(253.99)

    • 列地址选择
      在这里插入图片描述
    • 代码(19./04_OLED选择列地址显示)
    void main(){
    	int a = 10;
    	//1. OLED初始化
    	Oled_Init();
    	//2. 选择一个位置
    	//2.1 确认页寻址模式
    	Oled_Write_Cmd(0x20);
    	Oled_Write_Cmd(0x02);
    	//2.2 选择PAGE0 1011 0000 即 0xB0
    	Oled_Write_Cmd(0xB0);
    	Oled_Write_Cmd(0x00);//列地址选择要用两个字节
    	Oled_Write_Cmd(0x10);//定位到首列
    	//3. 显示一个点*8
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	
    	Oled_Write_Cmd(0xB5);//在0xB5行显示........
    	Oled_Write_Cmd(0x00);
    	Oled_Write_Cmd(0x10);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	
    	Oled_Write_Cmd(0xB6);//列是递增的
    	Oled_Write_Cmd(0x00);
    	Oled_Write_Cmd(0x10);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	Oled_Write_Data(0x08);
    	
    	Oled_Write_Cmd(0x0f);
    	Oled_Write_Cmd(0x17);//在0xB6行的最后一列显示一个点
    	Oled_Write_Data(0x08);
    	
    	while(1);//不让程序结束
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    9. OLED清屏添加清屏函数(254.100)

    • 代码(19./05_OLED添加清屏函数)

    10. OLED显示字母A(255.101)

    1. 打开“字模提取”app,点击“文字输入区字体选择”
      在这里插入图片描述
    2. 做字体设置
      在这里插入图片描述
    3. 其他选项
      在这里插入图片描述
    4. 输入“A”,Ctrl+Enter
      在这里插入图片描述
    5. 点击“取模方式”,选择“C51 格式”,即可复制生成的点阵数据
      在这里插入图片描述
    • 代码(19./06_OLED显示字符A)
    /*--  文字:  A  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
    char A1[8] = {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00};
    char A2[8] = {0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20};
    
    void main(){
    	int i = 0;
    	//1. OLED初始化
    	Oled_Init();
    	//2. 选择一个位置
    	//2.1 确认页寻址模式
    	Oled_Write_Cmd(0x20);
    	Oled_Write_Cmd(0x02);
    	Oled_Clear();				 //清屏函数
    	//2.2 选择PAGE0 1011 0000 即 0xB0
    	Oled_Write_Cmd(0xB0);
    	Oled_Write_Cmd(0x00);//列地址选择要用两个字节
    	Oled_Write_Cmd(0x10);//定位到首列
    	for(i = 0;i < 8;i++){//显示‘A’的上半部分
    		Oled_Write_Data(A1[i]);
    	}
    	
    	Oled_Write_Cmd(0xB1);
    	Oled_Write_Cmd(0x00);
    	Oled_Write_Cmd(0x10);//定位到首列
    	for(i = 0;i < 8;i++){//显示‘A’的下半部分
    		Oled_Write_Data(A2[i]);
    	}
    	
    	while(1);//不让程序结束
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    11. OLED显示上官可编程(256.102)

    • 代码(19./07_OLED显示上官可编程 - 我记得–赵雷)
    /*--  文字:  我  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char w1[16] = {0x20,0x24,0x24,0x24,0xFE,0x23,0x22,0x20,0x20,0xFF,0x20,0x22,0x2C,0xA0,0x20,0x00};
    code char w2[16] = {0x00,0x08,0x48,0x84,0x7F,0x02,0x41,0x40,0x20,0x13,0x0C,0x14,0x22,0x41,0xF8,0x00};
    
    /*--  文字:  记  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char j1[16] = {0x40,0x40,0x42,0xCC,0x00,0x00,0x00,0x84,0x84,0x84,0x84,0x84,0xFC,0x00,0x00,0x00};
    code char j2[16] = {0x00,0x00,0x00,0x7F,0x20,0x10,0x00,0x3F,0x40,0x40,0x40,0x40,0x41,0x40,0x70,0x00};
    
    /*--  文字:  得  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char d1[16] = {0x00,0x10,0x88,0xC4,0x33,0x00,0xBE,0xAA,0xAA,0xAA,0xAA,0xAA,0xBE,0x80,0x00,0x00};
    code char d2[16] = {0x02,0x01,0x00,0xFF,0x00,0x02,0x0A,0x12,0x02,0x42,0x82,0x7F,0x02,0x02,0x02,0x00};
    
    /*--  文字:  -  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char h1[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    code char h2[16] = {0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    
    /*--  文字:  赵  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char z1[16] = {0x40,0x48,0x48,0x48,0xFF,0x48,0x48,0x00,0x04,0x08,0x30,0xC0,0x30,0x0E,0x00,0x00};
    code char z2[16] = {0x80,0x60,0x1F,0x20,0x7F,0x44,0x44,0x40,0x48,0x44,0x43,0x40,0x43,0x4C,0x40,0x00};
    
    /*--  文字:  雷  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    code char l1[16] = {0x20,0x18,0x0A,0xAA,0xAA,0xAA,0x0A,0xFE,0x0A,0xAA,0xAA,0xAA,0x0A,0x28,0x18,0x00};
    code char l2[16] = {0x00,0x00,0xFE,0x92,0x92,0x92,0x92,0xFE,0x92,0x92,0x92,0x92,0xFE,0x00,0x00,0x00};
    
    void main(){
    	int i = 0;
    	//1. OLED初始化
    	Oled_Init();
    	//2. 选择一个位置
    	//2.1 确认页寻址模式
    	Oled_Write_Cmd(0x20);
    	Oled_Write_Cmd(0x02);
    	Oled_Clear();				 //清屏函数
    	//2.2 选择PAGE0 1011 0000 即 0xB0
    	Oled_Write_Cmd(0xB0);
    	Oled_Write_Cmd(0x00);//列地址选择要用两个字节
    	Oled_Write_Cmd(0x10);//定位到首列
    	for(i = 0;i < 16;i++){//显示‘xxxxx’的上半部分
    		Oled_Write_Data(w1[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(j1[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(d1[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(h1[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(h1[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(z1[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(l1[i]);
    	}
    	
    	Oled_Write_Cmd(0xB1);
    	Oled_Write_Cmd(0x00);
    	Oled_Write_Cmd(0x10);//定位到首列
    	for(i = 0;i < 16;i++){//显示‘xxxxx’的下半部分
    		Oled_Write_Data(w2[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(j2[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(d2[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(h2[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(h2[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(z2[i]);
    	}
    	for(i = 0;i < 16;i++){
    		Oled_Write_Data(l2[i]);
    	}
    	
    	while(1);//不让程序结束
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    12. OLED显示图片(257.103)

    1. 打开画图app,画简单的图像
      在这里插入图片描述

    2. 保存为bmp格式(未经压缩,可轻松导入取模软件而无需解压(相比于会压缩的jpg))
      在这里插入图片描述

    3. 打开“字模提取”app,“基本操作”、“打开图像图标”
      在这里插入图片描述

    4. “取模方式”、“C51 格式”
      在这里插入图片描述

    • 代码(19./08_OLED显示一张图片)
    code unsigned char bmpImage[] = {....128*8....
    /*--  调入了一幅图像:E:\学习\嵌入式学习\Jessie\code\Keil\19. OLED编程案例\我记得.bmp  --*/
    /*--  宽度x高度=128x64  128x8(x8位)--*/};
    void Oled_Show_Image(unsigned char *image){
    	unsigned char i;//0 到 255,char: -128 到 127,So不能用char
    	unsigned int j;
    	
    	for(i = 0;i < 8;i++){
    		//PAGE0--PAGE7
    		Oled_Write_Cmd(0xB0 + i);
    		//每个PAGE都从其0列开始
    		Oled_Write_Cmd(0x00);
    		Oled_Write_Cmd(0x10);
    		//0到127列,依次写入0(每写入数据后 列地址自动偏移至下一列)
    		for(j = 128 * i;j < 128 *(i+1);j++){
    			Oled_Write_Data(image[j]);
    		}
    	}
    }
    
    void main(){
    	//1. OLED初始化
    	Oled_Init();
    	//2. 选择一个位置
    	//2.1 确认页寻址模式
    	Oled_Write_Cmd(0x20);
    	Oled_Write_Cmd(0x02);
    	Oled_Clear();				 //清屏函数
    	Oled_Show_Image(bmpImage);
    	
    	while(1);//不让程序结束
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
  • 相关阅读:
    NTT 的各类优化
    3D建模基础教程:石墨工具介绍
    dll文件找不到,微软官方地址
    【招聘岗位】基础设施软件开发人员
    热门Java开发工具IDEA入门指南——从Eclipse迁移到IntelliJ IDEA(一)
    I2C3挂载wm8960音频芯片
    基于Java+SpringBoot+Thymeleaf+Mysql疫情疫苗预约系统学习系统设计与实现
    HTTP协议数字报错(详细说明)
    MySQL高级SQL语句(终章)
    790.多米诺和托米诺平铺(DP)
  • 原文地址:https://blog.csdn.net/Jaci133/article/details/133577100