本文介绍了对OLED的几种驱动方式,8080并口,IIC,SPI三种驱动方式,采用的单片机是STM32F407.

文章目录

  • 一.OLED驱动原理介绍
  • 二.8080并口驱动方式
  • 三.IIC驱动方式
  • 四.SPI驱动方式

一.OLED驱动原理介绍

OLED模块的驱动芯片为SSD1306,其显存大小总共为 12864bit 大小,SSD1306 将
这些显存分为了 8 页,其对应关系如表 17.1.3 所示:

可以看出,SSD1306 的每页包含了 128 个字节,总共 8 页,这样刚好是 128
64 的点阵大小。因为每次写入都是按字节写入的,这就存在一个问题,如果我们使用只写方式操作模块,那么,每次要写 8 个点,这样,我们在画点的时候,就必须把要设置的点所在的字节的每个位都搞清楚当前的状态(0/1?),否则写入的数据就会覆盖掉之前的状态,结果就是有些不需要显示的点,显示出来了,或者该显示的没有显示了。这个问题在能读的模式下,我们可以先读出来要写入的那个字节,得到当前状况,在修改了要改写的位之后再写进 GRAM,这样就不会影响到之前的状况了。但是这样需要能读 GRAM,对于 4 线 SPI 模式/IIC 模式,模块是不支持读的,而且读->改->写的方式速度也比较慢。
所以我们采用的办法是在 STM32F4 的内部建立一个 OLED 的 GRAM(共 128*8 个字节),在每次修改的时候,只是修改 STM32F4 上的 GRAM(实际上就是 SRAM),在修改完了之后,一次性把 STM32F4 上的 GRAM 写入到 OLED 的 GRAM。当然这个方法也有坏处,就是对于那些 SRAM 很小的单片机(比如 51 系列)就比较麻烦了。
SSD1306 的命令比较多,这里我们仅介绍几个比较常用的命令,这些命令如表 17.1.4 所示:

我们再来介绍一下 OLED 模块的初始化过程,SSD1306 的典型初始化框图如下图:

二.8080并口驱动方式

介绍一下模块的 8080 并行接口,8080 并行接口的发明者是 INTEL,该总线也被广泛应用于各类液晶显示器,使得 MCU 可以快速的访问 OLED。ALIENTEK OLED 模块的 8080 接口方式需要如下一些信号线:
CS:OLED 片选信号。
WR:向 OLED 写入数据。
RD:从 OLED 读取数据。
D[7:0]:8 位双向数据线。
RST(RES):硬复位 OLED。
DC:命令/数据标志(0,读写命令;1,读写数据)。
模块的 8080 并口读/写的过程为:先根据要写入/读取的数据的类型,设置 DC 为高(数据)/低(命令),然后拉低片选,选中 SSD1306,接着我们根据是读数据,还是要写数据置 RD/WR为低,然后:
在 RD 的上升沿, 使数据锁存到数据线(D[7:0])上;
在 WR 的上升沿,使数据写入到 SSD1306 里面;
SSD1306 的 8080 并口写时序图如图 17.1.3 所示:


OLED配置及驱动程序:

/*******************OLED.c代码*************************/
//OLED显存
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127
u8 OLED_GRAM[128][8];//定义SRAM缓存区//更新显存到LCD
void OLED_Refresh_Gram(void)
{u8 i, n;for (i = 0; i < 8; i++){OLED_WR_Byte(0xb0 + i, OLED_CMD);    //设置页地址(0-7)OLED_WR_Byte(0x00, OLED_CMD);      //设置显示位置-列低地址OLED_WR_Byte(0x10, OLED_CMD);      //设置显示位置-列高地址   for (n = 0; n < 128; n++)OLED_WR_Byte(OLED_GRAM[n][i], OLED_DATA); //更新到OLED}
}
//通过拼凑的方法向OLED输出一个8位数据
//data:输出的数据
void OLED_Data_Out(u8 data)
{u16 dat = data & 0X0F;GPIOC->ODR &= ~(0XF << 6);        //清空6~9GPIOC->ODR |= dat << 6;               //D[3:0]-->PC[9:6]GPIO_Write(GPIOC, dat << 6);PCout(11) = (data >> 4) & 0X01;    //D4PBout(6) = (data >> 5) & 0X01;   //D5PEout(5) = (data >> 6) & 0X01;   //D6PEout(6) = (data >> 7) & 0X01;   //D7
}
//向SSD1306写入一个字节
//dat:写入的数据/命令
//cmd:0:命令,1:数据
void OLED_WR_Byte(u8 dat, u8 cmd)
{OLED_Data_Out(dat);OLED_RS = cmd;OLED_CS = 0;OLED_WR = 0;OLED_WR = 1;OLED_CS = 1;OLED_RS = 1;
}
//开启OLED显示
void OLED_Display_On(void)
{OLED_WR_Byte(0X8D, OLED_CMD);  //SET DCDCÃüÁîOLED_WR_Byte(0X14, OLED_CMD);  //DCDC ONOLED_WR_Byte(0XAF, OLED_CMD);  //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{OLED_WR_Byte(0X8D, OLED_CMD);  //SET DCDCÃüÁîOLED_WR_Byte(0X10, OLED_CMD);  //DCDC OFFOLED_WR_Byte(0XAE, OLED_CMD);  //DISPLAY OFF
}
// OLED清屏
void OLED_Clear(void)
{u8 i, n;for (i = 0; i < 8; i++)for (n = 0; n < 128; n++)OLED_GRAM[n][i] = 0X00;OLED_Refresh_Gram();//¸更新显存到LCD
}
//画点
//x:0~127
//y:0~63
//t:1填充,0清空
void OLED_DrawPoint(u8 x, u8 y, u8 t)
{u8 pos, bx, temp = 0;if (x > 127 || y > 63)return;//超出范围pos = 7 - y / 8;bx = y % 8;temp = 1 << (7 - bx);if (t)OLED_GRAM[x][pos] |= temp;else OLED_GRAM[x][pos] &= ~temp;
}
//按区域填充
void OLED_Fill(u8 x1, u8 y1, u8 x2, u8 y2, u8 dot)
{u8 x, y;for (x = x1; x <= x2; x++){for (y = y1; y <= y2; y++)OLED_DrawPoint(x, y, dot);}OLED_Refresh_Gram();//更新显存到OLED
}
//在指定位置显示一个字符(需要调用字符显示字库数组,自行建立)
//x:0~127
//y:0~63
//mode:0反白显示 1正常显示
//size:字体 12/16/24
void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 size, u8 mode)
{u8 temp, t, t1;u8 y0 = y;u8 csize = (size / 8 + ((size % 8) ? 1 : 0))*(size / 2);       //得到一个字符所占字节数chr = chr - ' ';                                    //得到偏移后的值        for (t = 0; t < csize; t++){if (size == 12)temp = asc2_1206[chr][t];      //调用1206字体else if (size == 16)temp = asc2_1608[chr][t];  //调用1608字体else if (size == 24)temp = asc2_2412[chr][t];  //调用2412字体else return;                              //没有的字库for (t1 = 0; t1 < 8; t1++){if (temp & 0x80)OLED_DrawPoint(x, y, mode);else OLED_DrawPoint(x, y, !mode);temp <<= 1;y++;if ((y - y0) == size){y = y0;x++;break;}}}
}
//字符串显示函数
void OLED_ShowString(u8 x, u8 y, const u8 *p, u8 size)
{while ((*p <= '~') && (*p >= ' '))       //判断是不是非法字符{if (x > (128 - (size / 2))) { x = 0; y += size; }if (y > (64 - size)) { y = x = 0; OLED_Clear(); }OLED_ShowChar(x, y, *p, size, 1);x += size / 2;p++;}
}
//m^n函数
u32 mypow(u8 m, u8 n)
{u32 result = 1;while (n--)result *= m;return result;
}
//数字显示函数
void OLED_ShowNum(u8 x, u8 y, u32 num, u8 len, u8 size)
{u8 t, temp;u8 enshow = 0;for (t = 0; t < len; t++){temp = (num / mypow(10, len - t - 1)) % 10;if (enshow == 0 && t < (len - 1)){if (temp == 0){OLED_ShowChar(x + (size / 2)*t, y, ' ', size, 1);continue;}else enshow = 1;}OLED_ShowChar(x + (size / 2)*t, y, temp + '0', size, 1);}
}
//显示数字,高位是0,还是显示
//x,y:起点坐标
//num:数值(0~999999999);
//len:长度(即要显示的位数)
//size:字体大小
//mode:
//[7]:0,不填充;1,填充0.
//[6:1]:保留
//[0]:0,非叠加显示;1,叠加显示.
void OLED_ShowxNum(u16 x, u16 y, u32 num, u8 len, u8 size, u8 mode)
{u8 t, temp;u8 enshow = 0;for (t = 0; t < len; t++){temp = (num / OLED_Pow(10, len - t - 1)) % 10;if (enshow == 0 && t < (len - 1)){if (temp == 0){if (mode & 0X80)OLED_ShowChar(x + (size / 2)*t, y, '0', size, mode & 0X01);else OLED_ShowChar(x + (size / 2)*t, y, ' ', size, mode & 0X01);continue;}else enshow = 1;}OLED_ShowChar(x + (size / 2)*t, y, temp + '0', size, mode & 0X01);}
}
//计算整数位数(为下面显示float型数据用)
int DataNum(int num)
{int i;if ((int)num == 0)i = 1;elsefor (i = 0; num != 0; i++)num = num / 10;return i;
}
//显示float型数据
//num:输入的float型数据
void OLED_ShowFloatNum(u16 x, u16 y, double num, u8 size, u8 mode)
{int a = DataNum((int)num);if ((int)num == 0)                                             //如果输入的float型数据整数部分是0,则加1(便于调用ShowxNum函数){OLED_ShowxNum(x + size / 2, y, 10000 * (num + 1), 5, size, mode);        //显示电压的小数部分,如果是3.0011的话,这里显示30011OLED_ShowxNum(x, y, (int)num, DataNum((int)num), size, mode);    //显示电压的整数部分,如果是3.0011的话,这里显示3OLED_ShowChar(x + a * size / 2, y, '.', size, mode);                 //显示小数点}else                                                         //如果输入的float型数据整数部分是不是0,则正常显示{if (num - (int)num == 0)                                        //如果是正整数{OLED_ShowxNum(x, y, (int)num, DataNum((int)num), size, mode);      //显示整数OLED_ShowChar(x + a * size / 2, y, '.', size, mode);                  //显示小数点OLED_ShowChar(x + (a + 1)*size / 2, y, '0', size, mode);              //显示小数点后一个0}else                                                        //如果不是正整数{OLED_ShowxNum(x + size / 2, y, 10000 * num, DataNum((int)num) + 4, size, mode);    //显示电压的小数部分,如果是3.0011的话,这里显示30011OLED_ShowChar(x + a * size / 2, y, '.', size, mode);                           //显示小数点OLED_ShowxNum(x, y, (int)num, DataNum((int)num), size, mode);//显示电压的整数部分,如果是3.0011的话,这里显示3}}
}//初始化SSD1306
void OLED_Init(void)
{GPIO_InitTypeDef  GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOG, ENABLE);//IO口时钟使能//GPIO初始化设置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_Init(GPIOB, &GPIO_InitStructure);//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_11;GPIO_Init(GPIOC, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_Init(GPIOD, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_5;GPIO_Init(GPIOE, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;GPIO_Init(GPIOG, &GPIO_InitStructure);OLED_WR = 1;OLED_RD = 1;OLED_CS = 1;OLED_RS = 1;OLED_RST = 0;delay_ms(100);OLED_RST = 1;//以下为SSD1306设置初始化代码,基本不用改动                 OLED_WR_Byte(0xAE, OLED_CMD); //关闭显示OLED_WR_Byte(0xD5, OLED_CMD); //设置时钟分频因子,振荡频率OLED_WR_Byte(80, OLED_CMD);   //[3:0],分频因子;[7:4],振荡频率OLED_WR_Byte(0xA8, OLED_CMD); //设置驱动路数OLED_WR_Byte(0X3F, OLED_CMD); //默认0X3F(1/64) OLED_WR_Byte(0xD3, OLED_CMD); //设置显示偏移OLED_WR_Byte(0X00, OLED_CMD); //默认为0OLED_WR_Byte(0x40, OLED_CMD); //设置显示开始行 [5:0],行数.OLED_WR_Byte(0x8D, OLED_CMD); //电荷泵设置OLED_WR_Byte(0x14, OLED_CMD); //bit2 开启/关闭OLED_WR_Byte(0x20, OLED_CMD); //设置内存地址模式OLED_WR_Byte(0x02, OLED_CMD); //[1:0],00列地址模式;01行地址模式;10,页地址模式;默认10;OLED_WR_Byte(0xA1, OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127;OLED_WR_Byte(0xC0, OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数OLED_WR_Byte(0xDA, OLED_CMD); //设置COM口硬件引脚配置OLED_WR_Byte(0x12, OLED_CMD); //[5:4]配置OLED_WR_Byte(0x81, OLED_CMD); //对比度设置OLED_WR_Byte(0xEF, OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮)OLED_WR_Byte(0xD9, OLED_CMD); //设置预充电周期OLED_WR_Byte(0xf1, OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2;OLED_WR_Byte(0xDB, OLED_CMD); //设置VCOMH 电压倍率OLED_WR_Byte(0x30, OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;OLED_WR_Byte(0xA4, OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)OLED_WR_Byte(0xA6, OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示                                  OLED_WR_Byte(0xAF, OLED_CMD); //开启显示  OLED_Clear();//OLED清屏
}
/*******************OLED.h代码*************************/
//-----------------OLED端口定义----------------
#define OLED_CS     PBout(7)
#define OLED_RST    PGout(15)
#define OLED_RS     PDout(6)
#define OLED_WR     PAout(4)
#define OLED_RD     PDout(7)#define OLED_CMD    0       //写命令
#define OLED_DATA   1       //写数据
/*******************main()函数代码*************************/
int main(void)
{OLED_Init();               //初始化OLEDwhile (1){OLED_ShowChar(100, 30, 'K', 16, 1);//显示字符OLED_ShowString(64, 52, "CODE:", 12);//显示字符串OLED_ShowFloatNum(10, 30, 0.1213, 16, 1);//显示小数OLED_ShowChinese(26, 10, 3, 1);//显示汉字OLED_Refresh_Gram(2);//更新显示到OLED,此句非常重要,将数据写到SSD1306的GRAM区域delay_ms(1000);}
}

三.IIC驱动方式

一些驱动方式与显示代码均与上面一致,因此这里只介绍IIC写数据/命令驱动代码,也即重写OLED_WR_Byte(u8 dat,u8 cmd)函数,其他均不变!

//向SSD1306写入一个字节
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据
void WriteCmd(u8 command)    //写命令函数
{IIC_Start();IIC_Send_Byte(0x78);//OLED地址IIC_Wait_Ack(t);IIC_Send_Byte(0x00);//写命令寄存器地址IIC_Wait_Ack();IIC_Send_Byte(command);IIC_Wait_Ack();IIC_Stop();
}
void WriteData(u8 data)      //写数据函数
{IIC_Start();IIC_Send_Byte(0x78);//OLED地址IIC_Wait_Ack();IIC_Send_Byte(0x40);//写数据寄存器地址IIC_Wait_Ack();IIC_Send_Byte(data);IIC_Wait_Ack();IIC_Stop();
}
void OLED_WR_Byte(u8 dat,u8 cmd)  //为了直接替换上面,做一个封装函数
{    if(cmd)WriteData(dat);elseWriteCmd(dat);
}

这里采用的是模拟IIC通信方式,在main函数中要先配置IIC初始化

四.SPI驱动方式

SPI模式使用的信号线有如下几条:
CS:OLED 片选信号。
RST(RES):硬复位 OLED。
DC:命令/数据标志(0,读写命令;1,读写数据)。
SCLK:串行时钟线。在 4 线串行模式下,D0 信号线作为串行时钟线 SCLK。
SDIN:串行数据线。在 4 线串行模式下,D1 信号线作为串行数据线 SDIN。
在 4 线串行模式下,只能往模块写数据而不能读数据。
在 4 线 SPI 模式下,每个数据长度均为 8 位,在 SCLK 的上升沿,数据从 SDIN 移入到
SSD1306,并且是高位在前的。DC 线还是用作命令/数据的标志线。在 4 线 SPI 模式下,写操作的时序如图 17.1.6 所示:

一些驱动方式与显示代码均与上面一致,因此这里只介绍SPI写数据/命令驱动代码,也即重写OLED_WR_Byte(u8 dat,u8 cmd)函数,其他均不变!

//向SSD1306写入一个字节
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据//STM32F407采用硬件SPI发送数据(也可采用模拟SPI方式发送数据,在后续的博客中会有介绍)
void OLED_Write_Byte(uint8_t dat)
{  SPI1_ReadWriteByte(dat);      //调用硬件SPI写数据函数
}
//写数据
void OLED_Write_Data(uint8_t dat)
{CS=0;DC=1;OLED_Write_Byte(dat);
}
//写命令
void OLED_Write_Cmd(uint8_t cmd)
{ CS=0;DC=0;OLED_Write_Byte(cmd);
}
//为了直接替换上面,做一个封装函数
void OLED_WR_Byte(u8 dat,u8 cmd)
{    if(cmd)OLED_Write_Data(dat);elseOLED_Write_Cmd(dat);
}

小结:OLED的驱动方式非常简单,应用起来也非常的方便,分辨率也较高,作为平时辅助开发的小工具也是极好的。在上文中对于各种字符的显示均给出了驱动程序,可以非常方便的调用,另外对于字符取模也有很多可用的小软件,大家可自行应用。
至此,OLED显示屏的几种驱动方式均已介绍完,也预示着第一篇博客的完结。作为刚入行半年的嵌入式小白,小心翼翼的编辑每一行文字。但由于技术水平有限,难免会有诸多错误之处,还希望得到诸位大神的批评指教,也希望借此平台可以和大家畅所欲言,共同进步!

OLED显示屏驱动:8080并口,IIC,SPI三种驱动方式相关推荐

  1. [步进电机] 步进电机的三种驱动方式

    步进电机的三种驱动方式:转自http://www.ing10bbs.com/forum.php?mod=viewthread&tid=1002&extra=page%3D2

  2. 锐捷睿易:路由器WAN口三种上网方式的选择

    一.WAN口三种上网方式 拨号上网:有些路由器也叫宽带上网.ADSL拨号.PPPOE拨号等 拨号上网方式是向运营商办宽带的时候,运营商会给你一个账号.密码 在路由器上WAN口设置里选择连接了光猫的口, ...

  3. 浅析步进电机常见的三种驱动方式

    步进电机是一种作为控制用的特种电机, 它的旋转是以固定的角度(称为"步距角")一步一步运行的, 其特点是没有积累误差(  为100%), 所以广泛应用于各种开环控制. 步进电机的运 ...

  4. 直流电机H桥的三种驱动方式:受限单机模式,单极模式,双极模式

    一.引出 1.简单的开关串联电机只能控制有刷直流电机的启停,要同时控制正反转和转速就要引入H桥式控制电路. 2.对4个开关管的控制采用PWM,调节占空比可以实现调速.PWM频率一般在10KHz到20K ...

  5. 单片机IO口模拟SPI四种模式的程序

    2013年09月21日 10:00:02 zyboy2000 阅读数:23924 #include "iom8535v.h" #define _CPOL 1 #define _CP ...

  6. STM32 IO口三种配置方式

    浮空,顾名思义就是浮在空中,上面用绳子一拉就上去了,下面用绳子一拉就沉下去了. 开漏,就等于输出口接了个NPN三极管,并且只接了e,b. c极 是开路的,你可以接一个电阻到3.3V,也可以接一个电阻到 ...

  7. java 加载驱动3种方法_加载jdbc驱动程序的三种不同方式

    1.比较常用 try{ Class.forName("com.mysql.jdbc.Driver");//加载数据库驱动 String url="jdbc:mysql:/ ...

  8. android专有驱动和设备驱动,针对Android驱动程序问题的三种全合一解决方案

    过去,无论何时要在设备上使用ADB或FastBoot,都需要为每个设备安装特定的驱动程序.对于经常刷新几种设备或在众多不同手机和平板电脑上测试过的开发人员的人来说,这可能会带来不便,特别是在首次设置并 ...

  9. STM32-使用IIC接线的OLED显示屏

    一.IIC协议的简单介绍 在物理层上:IIC协议就是通过一根数据线(SDA)与一根时钟线(SCL)将一个主机与一个或多个从机相连接,并且为了不同设备之间进行访问,每一个设备都有不同的地址. 在协议层上 ...

最新文章

  1. 打开word2007弹出未设置对象变量_【跟我学LabVIEW】什么是全局变量?如何创建及使用全局变量?...
  2. python3的socket_python2 与 python3 socket编程的一点小区别
  3. ahp层次分析法matlab代码_Matlab数据分析实战,基于AHP高校食堂满意度调查分析
  4. TabHost选项卡的实现(一):使用TabActivity实现
  5. Python快速学习07:文本文件的操作
  6. TextView设置缩略显示
  7. VMware为全球数字化基础架构提供原生安全
  8. FPGA、DSP、ARM能力值对比
  9. 惠斯通电桥信号调理芯片_变频器通电后无反应,如何检查维修?
  10. CCF推荐国际学术会议和期刊目录2019年
  11. 本地上传文件至Linux虚拟机报错“复制时发生出错“
  12. 合成孔径 saft matlab,合成孔径聚焦超声成像系统的计算机仿真
  13. 现代大学英语精读第二版(第四册)学习笔记(原文及全文翻译)——5B - Could You Live with Less(你可以简朴地生活吗)
  14. 只需3步把VSCode打造成Markdown编辑器
  15. 丰富的层次感,打造190㎡现代简约居住空间
  16. 电子商务订单处理及发货流程
  17. css预处理全部知识点一文讲清楚
  18. 全国程序员薪酬大曝光!看完我酸了,33% 程序员月薪达到.....
  19. target is null for setProperty(null, quot;xquot;, [Ljava.lang.String;@b0c40e)错误异常
  20. F5 iAPP 配置自动备份

热门文章

  1. 西电上机题——P385-报数游戏
  2. 中文论文翻译成英文投稿国外期刊的小tips
  3. Linux常识与基操
  4. 基于第一行代码CoolWeather的小白天气开发
  5. C51单片机——红外遥控 驱动程序
  6. 获取SQLServer连接字符串的方法
  7. java新零售平台_java最新阿里新零售面试题分享(一面)
  8. python 期权量化交易系统_Python期权日内程序化交易课程 - 优量在线 - 学量化投资,到优量在线 - Powered By EduSoho...
  9. Unity之人物骨骼动画
  10. CISP-ICSSE 工控安全工程师知识体系结构、知识图谱介绍