SPI协议简介树莓派4B+1.3寸OLED(SPI协议)
1.3寸OLED简介接线图开启树莓派SPI接口wiringPiSPI库简介C语言实现(汉字、字符、数字、图片显示)Python实现 总结
SPI协议简介SPI(Serial Peripheral interface):是由Motorola公司开发的串行外围设备接口,是一种高速的,全双工,同步的通信总线。主要应用在FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器等器件。
它是串行、全双工、 同步(使用时钟)、板级通信,一般是高位先发(也可以设置为低位先发)。
标准四线SPI引脚定义如下:
| 引脚定义 | 功能 |
|---|---|
| SCLK | 时钟线,用于主从时序的同步 |
| CS | 片选信号线,用于选择跟哪个芯片进行通信 |
| MOSI | 数据线,主出从入 |
| MISO | 数据线,master input slave output 主入从出 |
有时也会根据通信需求设计时只采取三线模式,毕竟不是所有的系统都需要主从之间实现全双工的通信,大多是只需要主机发送数据控制从机即可,所以硬件设计时会采用舍去MISO。我们今天使用的1.3寸OLED就是使用的3线模式(关于SPI这个三线四线的定义emmm有好几个说法,还有种说法是根据有没有单独的片选线而定,这里不纠结,毕竟也没有官方的定义。)。
一主一从接线方式:
一个主机对多个从机,总线挂载多个SPI从机,这里的挂在数量是根据你的片选信号个数而定的。
SPI的传输速度会比前面使用的I2C快,其传输速度可达几Mb/s,设置SPI的通信速度时,一定不能大于从机的最大通信速度 。如果大于从机的通信速度,会导致通信失败。
SPI的通讯流程:
主机:我要给从机1传命令(主发送从接收),先把从机1给叫到办公室(CS片选选中从机1),然后开始传送命令,传输完毕后从机1离开办公室(CS恢复),主机可以重新选择传输对象。
有关SPI的四种模式等详细介绍,参考此文。
使用的是中景原子的1.3寸OLED,这种屏幕的好处在于它兼容I2C和SPI,自己改一下元器件的焊接位置即可,以下来自厂家描述:
7针和6针两种屏均支持SPI/IIC,默认为SPI接口出货。将电阻SPI拆掉焊接到到IIC位置;R4两端用锡短接,DC,CS管脚接地;RES管脚通过一个IO去控制完成复位;复位方法先将RES管脚保持低电平200ms;然后将RES管脚操持高平完成复位操作,然后对OLED进行初始化操作;这样就可以用iic通信了; 时钟=SCL; 数据线=SDA。
这里特别说明一下DC管脚,在用IIC通信的时候DC管脚的高低电平是用来设置IIC设备地址的;一般常用是将DC直接接地了,如果IIC总线上有两个OLED屏,就需要一个将DC接地,另一个DC接高电平;这样两个OLED屏的IIC地址就会不同了,可以分时操作两个屏6针与7针的不同是将CS管脚直接接地了;这样用户可以节省一个IO,不过这样的话6针的屏在用SPI接口的时候,就不能与其它SPI复用了,7脚的可以。
在终端命令行输入 gpio readall 回车可以找到树莓派的板载硬件SPI接口。
我们对照上面的引脚和实物接线就可以了,这里我也是参考的是此博文,如果是用的0.96寸屏幕直接参考链接里面接线即可。
打开终端命令,输入ls /dev 回车
发现没有SPI节点,这是因为系统默认是关闭了SPI接口的,需要我们自己打开。
跟昨天I2C的步骤一样,终端界面就是输入sudo raspi-config,参考上一篇将SPI enable即可,不清楚的可以去看上一篇,这里不再一步一步贴图了,开启后还是重启一下树莓派。
打开终端命令输入 ls /dev回车
可以看见有spidev0.0和spidev0.1两个节点说明SPI已经开启。
打开wiringPiSPI.h,我们可以看见里面外部声明了四个函数,也就是说,我们通过这四个函数即可实现spi的使用。
有关这四个函数的详细介绍参考此文。
打开Geany输入以下代码(移植的中景园电子的源代码)
// An highlighted block #include#include #include #define LCD_DC 3 // 模式选择 1:写数据 0:写命令 #define LCD_DIN 12 // 串行数据线 #define LCD_CLK 14 // 串行时钟线 #define LCD_RST 0 // 复位信号 低电平有效 #define LCD_CE 10 // 芯片使能 低电平有效 #define OLED_CMD 0 //写命令 #define OLED_DATA 1 //写数据 #define OLED_MODE 0 #define SIZE 16 #define XLevelL 0x00 #define XLevelH 0x10 #define Max_Column 128 #define Max_Row 64 #define Brightness 0xFF #define X_WIDTH 128 #define Y_WIDTH 64 #define u16 unsigned int #define u8 unsigned char const unsigned char F6x8[][6]; const unsigned char F8X16[]; unsigned char BMP2[]; unsigned char BMP1[]; unsigned char picture6[]; unsigned char picture7[]; unsigned char picture8[]; unsigned char picture9[]; char Hzk[][32]; #define X_Col_addr 0x80 // 定位列 地址 0-83 #define Y_Row_addr 0x40 // 定位行 地址 0-5 // 初始化GPIO 端口 void gpioInit(){ pinMode(LCD_DC,OUTPUT); pinMode(LCD_RST,OUTPUT); pinMode(LCD_CE,OUTPUT); } void OLED_Set_Pos(unsigned char x, unsigned char y) { lcd_writeByte(0xb0+y,OLED_CMD); lcd_writeByte((((x+2)&0xf0)>>4)|0x10,OLED_CMD); lcd_writeByte(((x+2)&0x0f),OLED_CMD); } //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!! void lcd_clear(void) { u8 i,n; for(i=0;i<8;i++) { lcd_writeByte (0xb0+i,OLED_CMD); //设置页地址(0~7) lcd_writeByte (0x02,OLED_CMD); //设置显示位置—列低地址 lcd_writeByte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++)lcd_writeByte(0,OLED_DATA); } //更新显示 } //显示汉字 void OLED_ShowCHinese(u8 x,u8 y,u8 no) { u8 t,adder=0; OLED_Set_Pos(x,y); for(t=0;t<16;t++) { lcd_writeByte(Hzk[2*no][t],OLED_DATA); adder+=1; } OLED_Set_Pos(x,y+1); for(t=0;t<16;t++) { lcd_writeByte(Hzk[2*no+1][t],OLED_DATA); adder+=1; } } void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]) { unsigned int j=0; unsigned char x,y; if(y1%8==0) y=y1/8; else y=y1/8+1; for(y=y0;y Max_Column-1){x=0;y=y+2;} if(SIZE ==16) { OLED_Set_Pos(x,y); for(i=0;i<8;i++) lcd_writeByte(F8X16[c*16+i],OLED_DATA); OLED_Set_Pos(x,y+1); for(i=0;i<8;i++) lcd_writeByte(F8X16[c*16+i+8],OLED_DATA); } else { OLED_Set_Pos(x,y+1); for(i=0;i<6;i++) lcd_writeByte(F6x8[c][i],OLED_DATA); } } //m^n函数 unsigned int oled_pow(u8 m,u8 n) { unsigned int result=1; while(n--)result*=m; return result; } //显示2个数字 //x,y :起点坐标 //len :数字的位数 //size:字体大小 //mode:模式 0,填充模式;1,叠加模式 //num:数值(0~4294967295); void OLED_ShowNum(u8 x,u8 y,unsigned int num,u8 len,u8 size) { u8 t,temp; u8 enshow=0; for(t=0;t


