树莓派4B学习笔记——IO通信篇(SPI)

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的四种模式等详细介绍,参考此文

树莓派4B+1.3寸OLED(SPI协议)

1.3寸OLED简介

使用的是中景原子的1.3寸OLED,这种屏幕的好处在于它兼容I2CSPI,自己改一下元器件的焊接位置即可,以下来自厂家描述:
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寸屏幕直接参考链接里面接线即可。
在这里插入图片描述

开启树莓派SPI接口

打开终端命令,输入ls /dev 回车
在这里插入图片描述
发现没有SPI节点,这是因为系统默认是关闭了SPI接口的,需要我们自己打开。
跟昨天I2C的步骤一样,终端界面就是输入sudo raspi-config,参考上一篇将SPI enable即可,不清楚的可以去看上一篇,这里不再一步一步贴图了,开启后还是重启一下树莓派。
在这里插入图片描述
打开终端命令输入 ls /dev回车
在这里插入图片描述
可以看见有spidev0.0和spidev0.1两个节点说明SPI已经开启。

wiringPiSPI库简介

打开wiringPiSPI.h,我们可以看见里面外部声明了四个函数,也就是说,我们通过这四个函数即可实现spi的使用。
在这里插入图片描述
有关这四个函数的详细介绍参考此文

C语言实现(汉字、字符、数字、图片显示)

打开Geany输入以下代码(移植的中景园电子的源代码)

// An highlighted block
#include <wiringPi.h>
#include <stdio.h>  
#include <wiringPiSPI.h>

#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;
      }					
}

/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
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<y1;y++)
	{
		OLED_Set_Pos(x0,y);
    for(x=x0;x<x1;x++)
	    {      
	    	lcd_writeByte(BMP[j++],OLED_DATA);	    	
	    }
	}
} 


//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示				 
//size:选择字体 16/12 
void OLED_ShowChar(u8 x,u8 y,u8 chr)
{      	
	unsigned char c=0,i=0;	
		c=chr-' ';//得到偏移后的值			
		if(x>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<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size/2)*t,y,' ');
				continue;
			}else enshow=1; 
		 	 
		}
	 	OLED_ShowChar(x+(size/2)*t,y,temp+'0'); 
	}
} 

//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr)
{
	unsigned char j=0;
	while (chr[j]!='\0')
	{		OLED_ShowChar(x,y,chr[j]);
			x+=8;
		if(x>120){x=0;y+=2;}
			j++;
	}
}



// 初始化液晶
void lcd_init(){
    gpioInit();
    int isOK = wiringPiSPISetup(0, 1000000);
    if (isOK == -1) {
        printf("SPI设置失败\n");
    }
    else {
        printf("SPI设置OK == %d\n",isOK);
    }
    digitalWrite(LCD_RST,LOW); // 液晶复位
    delay(200);
    digitalWrite(LCD_RST,HIGH);
    delay(20);
    	lcd_writeByte(0xAE,OLED_CMD);//--turn off oled panel
	lcd_writeByte(0x02,OLED_CMD);//---set low column address
	lcd_writeByte(0x10,OLED_CMD);//---set high column address
	lcd_writeByte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	lcd_writeByte(0x81,OLED_CMD);//--set contrast control register
	lcd_writeByte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
	lcd_writeByte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	lcd_writeByte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	lcd_writeByte(0xA6,OLED_CMD);//--set normal display
	lcd_writeByte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	lcd_writeByte(0x3f,OLED_CMD);//--1/64 duty
	lcd_writeByte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	lcd_writeByte(0x00,OLED_CMD);//-not offset
	lcd_writeByte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
	lcd_writeByte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
	lcd_writeByte(0xD9,OLED_CMD);//--set pre-charge period
	lcd_writeByte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	lcd_writeByte(0xDA,OLED_CMD);//--set com pins hardware configuration
	lcd_writeByte(0x12,OLED_CMD);
	lcd_writeByte(0xDB,OLED_CMD);//--set vcomh
	lcd_writeByte(0x40,OLED_CMD);//Set VCOM Deselect Level
	lcd_writeByte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
	lcd_writeByte(0x02,OLED_CMD);//
	lcd_writeByte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
	lcd_writeByte(0x14,OLED_CMD);//--set(0x10) disable
	lcd_writeByte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
	lcd_writeByte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
	lcd_writeByte(0xAF,OLED_CMD);//--turn on oled panel
	
	lcd_writeByte(0xAF,OLED_CMD); /*display ON*/ 
    lcd_clear(); // 清屏
    OLED_Set_Pos(0,0); 
}

int main (void)
{
	unsigned char t;
    wiringPiSetup();
    lcd_init();
    lcd_clear(); // 清屏
    t=' ';
    while(1)
    {
		lcd_clear();
	//	LED_ON;
		OLED_ShowCHinese(0,0,0);//中
		OLED_ShowCHinese(18,0,1);//景
		OLED_ShowCHinese(36,0,2);//园
		OLED_ShowCHinese(54,0,3);//电
		OLED_ShowCHinese(72,0,4);//子
		OLED_ShowCHinese(90,0,5);//科
		OLED_ShowCHinese(108,0,6);//技
		OLED_ShowString(0,3,"1.3' OLED TEST");
		//OLED_ShowString(8,2,"ZHONGJINGYUAN");  
	 //	OLED_ShowString(20,4,"2014/05/01");  
		OLED_ShowString(0,6,"ASCII:");  
		OLED_ShowString(63,6,"CODE:");  
		OLED_ShowChar(48,6,t);//显示ASCII字符	   
		t++;
		if(t>'~')t=' ';
		OLED_ShowNum(103,6,t,3,16);//显示ASCII字符的码值 	
		delay(2000);
		lcd_clear();
		delay(100);
	//		LED_OFF;
		OLED_DrawBMP(0,0,128,8,BMP1);  //图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)
		delay(1000);
		OLED_DrawBMP(0,0,128,8,picture6);  //图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)
		delay(200);
		OLED_DrawBMP(0,0,128,8,picture7);
		delay(200);
		OLED_DrawBMP(0,0,128,8,picture8);  //图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)
		delay(200);
		OLED_DrawBMP(0,0,128,8,picture9);
		delay(200);
		OLED_DrawBMP(0,0,128,8,picture6);  //图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)
		delay(200);
		OLED_DrawBMP(0,0,128,8,BMP2);
		delay(1000);
	}
}
//取模部分代码太长了,需要的去资源下载或者点赞收藏私信博主获取。

然后保存,编译,效果如下:
在这里插入图片描述

Python实现

此处参考此博文即可,Python部分主要是调用API接口,比C语言的取模要方便的多。
最终效果如下所示:
请添加图片描述

总结

有关SPI部分的使用就到这,欢迎大家提出指正。

树莓派4B入门学习笔记汇总

树莓派4B学习笔记——系统烧录及初次开机
树莓派4B学习笔记——点亮你的LED
树莓派4B学习笔记——IO输入检测
树莓派4B学习笔记——IO通信篇(I2C)
树莓派4B学习笔记——IO通信篇(SPI)
树莓派4B学习笔记——IO通信篇(1-Wire)
树莓派4B学习笔记——IO通信篇(UART)

版权声明:本文为CSDN博主「小向是个Der」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41954556/article/details/122921230

生成海报
点赞 0

小向是个Der

我还没有学会写个人说明!

暂无评论

相关推荐

树莓派4B学习笔记——IO通信篇(UART)

UART简介 UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设计中,UART用来主机与辅助设备通信,如汽车音响与外接AP之间的通信&#x

树莓派控制sg90舵机 驱动 Python详细教程|徐奥雯

灰色线为GND接地、红色线为VCC接5V供电、黄色线为脉冲输入 直接复制以下代码运行即可 运行前 请将脉冲输入所连接的树莓派GPIO引脚号 填写入下面的gpio_pin中 您可以直接运行此文件来测试他是否正常工作,你的舵机应该会开