SPI通讯协议示例

news2024/11/23 5:04:55

目录

  • 0x01 SPI通讯特点
  • 0x01 硬件SPI示例
  • 0x02 软件SPI示例

0x01 SPI通讯特点

SPI在接线方面会拥有片选引脚、时钟引脚、数据引脚,其中数据引脚又分为 MISO和MOSI,分别对应的是 “Master IN Slave Out”(主机输入从机输出)“Master Out Slave IN”(主机输出从机输出),在一些只需要使用MOSI引脚的设备例如OLED显示屏上,通常会将其标注为SDA引脚,因为无需读取数据,所以这个引脚自然也就成了唯一的数据引脚。

而使用SPI的方式进行通信,只需要CS引脚来区分所选择的设备。例如我要读取sd卡中的内容,并将其传输至OLED屏幕上进行显示,那么我可以先为OLED屏幕分配SPI1,随后再为sd卡增选一只spi引脚。CS引脚在使能后(有的设备是高电平选中,有的设备是低电平选中),设备会认为被选中,从而可以向其发送或读取数据。

在使用SPI进行通讯时,具体配置与设备的时钟极性和时钟相位有关。

CPHA(时钟相位):决定第一次电平跳变采样或第二次电平跳变采样;
CPOL(时钟极性):决定SCLK在空闲时的电平状态。

0x01 硬件SPI示例

在实际使用中,数据手册一般都不会告知你使用的是SPI协议通信,需要自行去体会,下面随便找一个数据手册为例。
在这里插入图片描述
如何判断其使用的是SPI协议进行通信的呢?

可以看到图中有一个CS引脚,又有一只SCLK引脚,所以基本可以判断其使用的是SPI通讯协议了。

通过对这个时序图进行分析,我们马上能知道: CS管脚是低电平选中,所以在你仅有一个SPI设备的情况下,你可以直接把SPI引脚进行接地处理。

随后对SCLK这根线进行分析,我们可以看到时钟总线,在没有进行信号传输的时候,是处在一个低电平的状态,依此可以得到 时钟极性应配置为0

继续观察 SCLK时序,可以看到在一个下降的箭头处有一个小箭头,这说明在与此设备的通信中,数据是在 SCLK低电平是时有效,并且也可以知道,此设备允许 一次传输16位数据

根据这些,可以完成对SPI的配置,以下是GD32的SPI配置示例。

	/* SPI parameter config */
	spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; //全双工模式
	spi_init_struct.device_mode = SPI_MASTER;//本机为主机模式
	spi_init_struct.frame_size = SPI_FRAMESIZE_16BIT;//传输16位数据
	spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;//空闲为低电平,第1个下降沿有效(从0开始编号)
	spi_init_struct.nss = SPI_NSS_SOFT;//CS引脚由软件控制
	spi_init_struct.prescale = SPI_PSC_16;//分频速率
	spi_init_struct.endian = SPI_ENDIAN_MSB;//大端模式传输
	spi_init(SPI_TC, &spi_init_struct);

如此一来,即便是拿到了一个完全陌生的设备,只要拿到了时序图,便也可以快速上手了。

0x02 软件SPI示例

例如GD32的硬件SPI,是比较稳定的,可以使用DMA进行传输。但是某些单片机的SPI并不稳定例如STM32。

现在我们来随便找一个常用的OLED显示屏,ssd1306芯片的数据手册,使用软件SPI的方式来模拟SPI时序。
在这里插入图片描述
如图,可以获知,CS低电平有效,SCLK空闲状态为高电平,并且在第二个跳变沿生效,一次传输8位数据,D/C引脚为区分数据与命令的引脚,此项为区分数据与命令的作用。网上很多OLED屏的驱动例程,通常都会封装一个写命令函数和写数据函数。

可以自己编写一个使用STM32驱动 ssd1306屏幕的驱动库,看看能否达到想要的效果。

以下为网上广为流传的驱动代码,可以提供参考

#include "bsp_oled.h"
#include "bsp_oled.h"
#include "bsp_delay.h"
#include "stdlib.h"
#include "oledfont.h"  	 

u8 OLED_GRAM[144][8];


//反显函数
void OLED_ColorTurn(u8 i)
{
	if(i==0)
		{
			OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
		}
	if(i==1)
		{
			OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
		}
}

//屏幕旋转180度
void OLED_DisplayTurn(u8 i)
{
	if(i==0)
	{
		OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
		OLED_WR_Byte(0xA1,OLED_CMD);
	}
	if(i==1)
	{
		OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
		OLED_WR_Byte(0xA0,OLED_CMD);
	}
}


//开启OLED显示 
void OLED_DisPlay_On(void)
{
	OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
	OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
	OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
}

//关闭OLED显示 
void OLED_DisPlay_Off(void)
{
	OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
	OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
	OLED_WR_Byte(0xAE,OLED_CMD);//关闭屏幕
}

//设置OLED亮度
void OLED_SetBrightness(uint8_t xNUM)
{
	OLED_WR_Byte(0x81,OLED_CMD);//设置亮度命令
	OLED_WR_Byte(0x81,OLED_CMD);//亮度0x00-0xFF
}

//更新显存到OLED	
void OLED_Refresh(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
	   OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
	   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);
  }
}
//清屏函数
void OLED_Clear(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
	   for(n=0;n<128;n++)
			{
			 OLED_GRAM[n][i]=0;//清除所有数据
			}
  }
	OLED_Refresh();//更新显示
}

//画点 
//x:0~127
//y:0~63
//t:1 填充 0,清空	
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
	u8 i,m,n;
	i=y/8;
	m=y%8;
	n=1<<m;
	if(t){OLED_GRAM[x][i]|=n;}
	else
	{
		OLED_GRAM[x][i]=~OLED_GRAM[x][i];
		OLED_GRAM[x][i]|=n;
		OLED_GRAM[x][i]=~OLED_GRAM[x][i];
	}
}

//画线
//x1,y1:起点坐标
//x2,y2:结束坐标
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode)
{
	u16 t; 
	int xerr=0,yerr=0,delta_x,delta_y,distance;
	int incx,incy,uRow,uCol;
	delta_x=x2-x1; //计算坐标增量 
	delta_y=y2-y1;
	uRow=x1;//画线起点坐标
	uCol=y1;
	if(delta_x>0)incx=1; //设置单步方向 
	else if (delta_x==0)incx=0;//垂直线 
	else {incx=-1;delta_x=-delta_x;}
	if(delta_y>0)incy=1;
	else if (delta_y==0)incy=0;//水平线 
	else {incy=-1;delta_y=-delta_x;}
	if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
	else distance=delta_y;
	for(t=0;t<distance+1;t++)
	{
		OLED_DrawPoint(uRow,uCol,mode);//画点
		xerr+=delta_x;
		yerr+=delta_y;
		if(xerr>distance)
		{
			xerr-=distance;
			uRow+=incx;
		}
		if(yerr>distance)
		{
			yerr-=distance;
			uCol+=incy;
		}
	}
}
//x,y:圆心坐标
//r:圆的半径
void OLED_DrawCircle(u8 x,u8 y,u8 r)
{
	int a, b,num;
    a = 0;
    b = r;
    while(2 * b * b >= r * r)      
    {
        OLED_DrawPoint(x + a, y - b,1);
        OLED_DrawPoint(x - a, y - b,1);
        OLED_DrawPoint(x - a, y + b,1);
        OLED_DrawPoint(x + a, y + b,1);
 
        OLED_DrawPoint(x + b, y + a,1);
        OLED_DrawPoint(x + b, y - a,1);
        OLED_DrawPoint(x - b, y - a,1);
        OLED_DrawPoint(x - b, y + a,1);
        
        a++;
        num = (a * a + b * b) - r*r;//计算画的点离圆心的距离
        if(num > 0)
        {
            b--;
            a--;
        }
    }
}


//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size1:选择字体 6x8/6x12/8x16/12x24
//mode:0,反色显示;1,正常显示
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode)
{
	u8 i,m,temp,size2,chr1;
	u8 x0=x,y0=y;
	if(size1==8)size2=6;
	else size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数
	chr1=chr-' ';  //计算偏移后的值
	for(i=0;i<size2;i++)
	{
		if(size1==8)
			  {temp=asc2_0806[chr1][i];} //调用0806字体
		else if(size1==12)
        {temp=asc2_1206[chr1][i];} //调用1206字体
		else if(size1==16)
        {temp=asc2_1608[chr1][i];} //调用1608字体
		else if(size1==24)
        {temp=asc2_2412[chr1][i];} //调用2412字体
		else return;
		for(m=0;m<8;m++)
		{
			if(temp&0x01)OLED_DrawPoint(x,y,mode);
			else OLED_DrawPoint(x,y,!mode);
			temp>>=1;
			y++;
		}
		x++;
		if((size1!=8)&&((x-x0)==size1/2))
		{x=x0;y0=y0+8;}
		y=y0;
  }
}


//显示字符串
//x,y:起点坐标  
//size1:字体大小 
//*chr:字符串起始地址 
//mode:0,反色显示;1,正常显示
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode)
{
	while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
	{
		OLED_ShowChar(x,y,*chr,size1,mode);
		if(size1==8)x+=6;
		else x+=size1/2;
		chr++;
  }
}

//m^n
u32 OLED_Pow(u8 m,u8 n)
{
	u32 result=1;
	while(n--)
	{
	  result*=m;
	}
	return result;
}

//显示数字
//x,y :起点坐标
//num :要显示的数字
//len :数字的位数
//size:字体大小
//mode:0,反色显示;1,正常显示
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode)
{
	u8 t,temp,m=0;
	if(size1==8)m=2;
	for(t=0;t<len;t++)
	{
		temp=(num/OLED_Pow(10,len-t-1))%10;
			if(temp==0)
			{
				OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode);
      }
			else 
			{
			  OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode);
			}
  }
}

//显示汉字
//x,y:起点坐标
//num:汉字对应的序号
//mode:0,反色显示;1,正常显示
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode)
{
	u8 m,temp;
	u8 x0=x,y0=y;
	u16 i,size3=(size1/8+((size1%8)?1:0))*size1;  //得到字体一个字符对应点阵集所占的字节数
	for(i=0;i<size3;i++)
	{
		if(size1==16)
				{temp=Hzk1[num][i];}//调用16*16字体
		else if(size1==24)
				{temp=Hzk2[num][i];}//调用24*24字体
		else if(size1==32)       
				{temp=Hzk3[num][i];}//调用32*32字体
		else if(size1==64)
				{temp=Hzk4[num][i];}//调用64*64字体
		else return;
		for(m=0;m<8;m++)
		{
			if(temp&0x01)OLED_DrawPoint(x,y,mode);
			else OLED_DrawPoint(x,y,!mode);
			temp>>=1;
			y++;
		}
		x++;
		if((x-x0)==size1)
		{x=x0;y0=y0+8;}
		y=y0;
	}
}

//num 显示汉字的个数
//space 每一遍显示的间隔
//mode:0,反色显示;1,正常显示
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode)
{
	u8 i,n,t=0,m=0,r;
	while(1)
	{
		if(m==0)
		{
	    OLED_ShowChinese(128,24,t,16,mode); //写入一个汉字保存在OLED_GRAM[][]数组中
			t++;
		}
		if(t==num)
			{
				for(r=0;r<16*space;r++)      //显示间隔
				 {
					for(i=1;i<144;i++)
						{
							for(n=0;n<8;n++)
							{
								OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
							}
						}
           OLED_Refresh();
				 }
        t=0;
      }
		m++;
		if(m==16){m=0;}
		for(i=1;i<144;i++)   //实现左移
		{
			for(n=0;n<8;n++)
			{
				OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
			}
		}
		OLED_Refresh();
	}
}

//x,y:起点坐标
//sizex,sizey,图片长宽
//BMP[]:要写入的图片数组
//mode:0,反色显示;1,正常显示
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode)
{
	u16 j=0;
	u8 i,n,temp,m;
	u8 x0=x,y0=y;
	sizey=sizey/8+((sizey%8)?1:0);
	for(n=0;n<sizey;n++)
	{
		 for(i=0;i<sizex;i++)
		 {
				temp=BMP[j];
				j++;
				for(m=0;m<8;m++)
				{
					if(temp&0x01)OLED_DrawPoint(x,y,mode);
					else OLED_DrawPoint(x,y,!mode);
					temp>>=1;
					y++;
				}
				x++;
				if((x-x0)==sizex)
				{
					x=x0;
					y0=y0+8;
				}
				y=y0;
		}
	 }
}

#if OLED_COMMUNICATION_MODE == IIC
void OLED_WR_Byte(u8 dat,u8 cmd)
{	
	u8 i;			  
	if(cmd)
	  OLED_DC_Set();
	else 
	  OLED_DC_Clr();		  
	OLED_CS_Clr();
	for(i=0;i<8;i++)
	{			  
		OLED_SCL_Clr();
		if(dat&0x80)
		   OLED_SDA_Set();
		else 
		   OLED_SDA_Clr();
		OLED_SCL_Set();
		dat<<=1;   
	}				 		  
	OLED_CS_Set();
	OLED_DC_Set();   	  
}

//OLED的初始化
void OLED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);	 //使能A端口时钟
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_15;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOA, &GPIO_InitStructure);	  //初始化GPIOA
 	GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_15);
	
	OLED_RES_Clr();
	delay_ms(200);
	OLED_RES_Set();
	
	OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
	OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
	OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
	OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
	OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//-not offset
	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
	OLED_WR_Byte(0x12,OLED_CMD);
	OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
	OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
	OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WR_Byte(0x02,OLED_CMD);//
	OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
	OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
	OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
	OLED_Clear();
	OLED_WR_Byte(0xAF,OLED_CMD);
}

#elif OLED_COMMUNICATION_MODE == SPI
void OLED_WR_Byte(u8 dat,u8 cmd)
{	
	u8 i;			  
	if(cmd)
	  OLED_DC_Set();
	else 
	  OLED_DC_Clr();		  
	OLED_CS_Clr();
	for(i=0;i<8;i++)
	{			  
		OLED_SCL_Clr();
		if(dat&0x80)
		   OLED_SDA_Set();
		else 
		   OLED_SDA_Clr();
		OLED_SCL_Set();
		dat<<=1;   
	}				 		  
	OLED_CS_Set();
	OLED_DC_Set();   	  
}

//OLED的初始化
void OLED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);	 //使能A端口时钟
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_15;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOA, &GPIO_InitStructure);	  //初始化GPIOA
 	GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_15);
	
	OLED_RES_Clr();
	delay_ms(200);
	OLED_RES_Set();
	
	OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel 		关闭OLED面板
	OLED_WR_Byte(0x00,OLED_CMD);//--set low column address 		设置低列地址
	OLED_WR_Byte(0x10,OLED_CMD);//--set high column address		设置高列地址
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  	Set Mapping RAM Display Start Line (0x00~0x3F) 设置起始行地址 设置映射 RAM 显示起始行(0x00~0x3F)
	OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register    设置对比度控制寄存器
	OLED_WR_Byte(0xCF,OLED_CMD);//--Set SEG Output Current Brightness 设置 SEG 输出电流亮度
	OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WR_Byte(0xC8,OLED_CMD);//--Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display	设置正常显示
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64) 设置多路复用比(1 至 64)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
	OLED_WR_Byte(0xD3,OLED_CMD);//--set display offset	Shift Mapping RAM Counter (0x00~0x3F) 设置显示偏移移映射 RAM 计数器 (0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//--not offset 不偏移
	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency 设置显示时钟分频比/振荡器频率
	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec 设置分频比,将时钟设置为 100 帧/秒
	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period 设置预充电周期
	OLED_WR_Byte(0xF1,OLED_CMD);//--Set Pre-Charge as 15 Clocks & Discharge as 1 Clock 将预充电设置为15个时钟,将放电设置为1个时钟
	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration 设置 COM 引脚硬件配置
	OLED_WR_Byte(0x12,OLED_CMD);
	OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh 设置 VCOMH
	OLED_WR_Byte(0x40,OLED_CMD);//--Set VCOM Deselect Level 设置 VCOM 取消选择级别
	OLED_WR_Byte(0x20,OLED_CMD);//--Set Page Addressing Mode (0x00/0x01/0x02) 设置页面寻址模式 (0x00/0x01/0x02)
	OLED_WR_Byte(0x02,OLED_CMD);//
	OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable 设置电荷泵启用(0X14)/禁用(0X10)
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable 
	OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5) 禁用整个显示屏开启 (0xa4/0xa5)
	OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 禁用反向显示开启 (0xa6/a7)
	OLED_Clear();
	OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel 		开启OLED面板
}
#else
	#error "OLED_COMMUNICATION_MODE" is no define, Please define OLED_COMMUNICATION_MODE to IIC or SPI!
#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2036599.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机械学习—零基础学习日志(如何理解线性代数)

零基础为了学人工智能&#xff0c;正在快乐学习&#xff0c;每天都长脑子 如何理解线性代数&#xff1f; 线性代数的本质是代数——代替数字。有时数学里有很多的规律&#xff0c;不以数字形式存在&#xff0c;可以用字幕替代。用一个通用的等式替代我们发现的规律。 代数研…

在VB.net中,CDbl、Double.Parse与Double.TryParse有什么区别

标题 在VB.net中&#xff0c;CDbl、Double.Parse与Double.TryParse有什么区别 正文 在VB.NET中&#xff0c;CDbl、Double.Parse和Double.TryParse都是用于将不同类型的值&#xff08;主要是字符串&#xff09;转换为Double类型的方法&#xff0c;但它们之间在用法、性能、错误处…

django学习入门系列之第七点《案例 添加页面》

文章目录 7.6 前端整合标准引入格式案例 添加页面 往期回顾 7.6 前端整合 HTMLCSSjavaScript、jQueryBootStrap&#xff08;动态效果依赖于jQuery&#xff09; 标准引入格式 css在上面js动态效果放下面bootstrap依赖于jQuery&#xff0c;所以先要有jQuery&#xff0c;再有bo…

汽车精密设计、无人机外形优化总是遇难题?CFD参数优化详解2来袭

数值仿真的参数优化 在上期文章中&#xff0c;我们给大家带来了机翼多学科优化、拟合试验曲线、一维CFD模型参数的DOE和回归分析三个参数优化案例&#xff0c;本期文章将继续为各位讲解多个 Altair CFD 参数优化案例&#xff0c;一起来看看吧。 案例&#xff1a;汽车排气管形状…

Jenkins链接Gitlab(HttpSSH方式)

文章目录 前言一、安装必要插件1、安装git2、安装Jenkins插件 二、配置git1、http方式&#xff08;1&#xff09;基础配置&#xff08;http方式配置凭证&#xff09;&#xff08;2&#xff09;测试 2、SSH方式配置凭证 总结 前言 为避免汉化导致的显示差异&#xff0c;以下操作…

通过Go示例理解函数式编程思维

一个孩子要尝试10次、20次才肯接受一种新的食物&#xff0c;我们接受一种新的范式&#xff0c;大概不会比这个简单。-- 郭晓刚 《函数式编程思维》译者 函数式编程(Functional Programming, 简称fp)是一种编程范式&#xff0c;与命令式编程(Imperative Programming)、面向对象编…

xlua使用

1. 安装 到 github 移动三个文件夹过去即可 Assets -》Plugins Assets -》Xlua Tools 移动到 unity里面的Assets目录即可 会在工具栏出现Xlua即安装成功 2. 引入基础类 ABMgr.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using Un…

生成式人工智能(大语言模型)上线备案材料

材料总体一览 生成式人工智能&#xff08;大语言模型&#xff09;上线备案&#xff0c;除申请表外还需要提交五份材料&#xff1a; 《生成式人工智能 &#xff08;大语言模型&#xff09;上线备案申请表》 《附件1&#xff1a;安全自评估报告》 《附件2&#xff1a;模型服务协议…

django学习入门系列之第七点《案例 点击删除文本》

文章目录 前置回顾案例 点击删除文本总结往期回顾 前置回顾 HTML结构&#xff1a; 页面使用<!DOCTYPE html>声明为HTML5文档。<html>标签定义了页面的根元素&#xff0c;并且设置了lang"en"属性&#xff0c;表示页面内容使用英语。<head>部分包含…

统计回归与Matlab软件实现上(一元多元线性回归模型)

引言 关于数学建模的基本方法 机理驱动 由于客观事物内部规律的复杂及人们认识程度的限制&#xff0c;无法得到内在因果关系&#xff0c;建立合乎机理规律的数学模型数据驱动 直接从数据出发&#xff0c;找到隐含在数据背后的最佳模型&#xff0c;是数学模型建立的另一大思路…

数据结构 - 位图 | 布隆过滤器

文章目录 一、位图1、位图概念2、实现一个简略的位3、位图的优缺点4、位图的应用场景 二、布隆过滤器1、提出2、概念3、布隆过滤器的实现 三、海量数据处理1、哈希切割2、面试题 一、位图 1、位图概念 位图&#xff08;Bitmap&#xff09;是一种非常高效的数据结构&#xff0c…

【ocr识别003】flask+paddleocr+bootstrap搭建OCR文本推理WEB服务

1.欢迎点赞、关注、批评、指正&#xff0c;互三走起来&#xff0c;小手动起来&#xff01; 2.了解、学习OCR相关技术知识领域&#xff0c;结合日常的场景进行测试、总结。如本文总结的flaskpaddleocrbootstrap搭建OCR文本推理WEB服务应用示例场景。 文章目录 1.代码结构2.效果演…

【算法】梯度下降

一、引言 梯度下降算法&#xff08;Gradient Descent&#xff09;是一种一阶迭代优化算法&#xff0c;用于求解最小化目标函数的问题&#xff0c;广泛应用于机器学习和人工智能中的参数优化。 用于优化问题的迭代算法&#xff0c;尤其在机器学习和深度学习中广泛用于最小化损失…

EMQX Platform Snowflake:构建可再生分布式能源的智慧未来

引言 可再生能源如风力和太阳能发电&#xff0c;具有低成本和环保的特性&#xff0c;是未来能源供应的主要方向。然而&#xff0c;这类发电方式存在供应分散、设备数量多、地区分布广等特点。再加上不同地区的季节和天气变化&#xff0c;不确定性极大。 随着社会用电需求的持…

11 Radiobutton组件

11 Radiobutton组件 Tkinter 是 Python 的标准图形用户界面库&#xff0c;它提供了一个 Radiobutton 控件&#xff0c;用于在一组选项中让用户选择一个选项。Radiobutton 通常用于提供一组互斥的选项&#xff0c;用户只能选择其中一个。 Radiobutton 组件基础 Radiobutton 控…

CMake详解-捡重要的讲

CMake 通常我们使用cmake构建C++项目,其实就是编写CMakeLists.txt文件,过程如下 首先在创建项目名称,我这里是CMake文件夹,在路径下创建CMakeLists.txt文件,也就是在工作空间的目录下创建,具体有几个要素要设置 CMake最低版本要求项目名称-自定义即可编译方法:Debug或…

html+css+js网页制作 自定义电商10个页面

htmlcssjs网页制作 自定义电商10个页面 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#…

前端面试题整理-Javascript

JS组成&#xff1a; JS是运行在浏览器的一门编程语言 函数类型&#xff1a; 1. 说说 js 都有哪些数据类型&#xff0c;他们在内存存储上有什么不同 基本数据类型&#xff1a;number、boolean、string、null&#xff08;null就是特殊的object&#xff09;、undefined、Symbo…

循环神经网络三

一.介绍 在普通的神经网络中&#xff0c;信息的传递是单向的&#xff0c;这种限制虽然使得网络变得更容易学习&#xff0c;单在一定程度上也减弱了神经网络模型的能力。特别是在现实生活中&#xff0c;网络的输出不仅和当前时刻的输入相关&#xff0c;也过去一段时间的输出相关…

keepalived搭建与基础配置

目录 1 keepalived部署与环境准备 1.1 Keepalived 实验环境准备 1.2 Keepalived 相关文件 1.3 Keepalived 安装 1.4 KeepAlived 配置说明 1.5 配置语法说明 2 企业应用示例与配置 2.1 主从架构 2.1.1 启用keepalived日志功能 2.1.2 vrrp_iptables 参数 2.1.3 实现独立子配置文件…