一、SPI
1.1 简介
SPI英文全称Serial Peripheral Interface,即串行外围设备接口,是一种高速、全双工、同步的串行通信总线。
1.2 信号线
- SCK(Serial Clock):串行时钟线,由主设备产生,用于同步数据传输。
- MOSI(Master Output Slave Input):主机输出从机输入线,主设备通过这条线发送数据给从设备。
- MISO(Master Input Slave Output):主机输入从机输出线,主设备通过这条线接收从设备发送的数据。
- SS(Slave Select):从机选择线(每个从机一根选择线),用于选择与主设备进行通信的从设备。通常情况下,SS线为低电平有效,即当SS线为低电平时,选中对应的从设备进行通信。

1.3 开始与结束
当SS从高电平拉低到低电平,这个算是起始的一个时序。而SS从低电平拉高到高电平,就算是结束的时序。

1.4 发送和接收字节
发送接收字节看似是两个时序,但是在SPI中却是同一个时序,因为SPI的机制是==我们发送一个字节,并且接收一个字节==(哪怕我们并不需要接收数据)。反过来看也可以是我们接收一个字节,并且发送一个字节(哪怕这个字节是无用的数据)。


交换bit

在SCK上升沿的时候,移出MOSI的数据,在SCK下降沿的时候读取MISO的数据。
需要在SCK上升沿之前把需要发送的数据位放置在MOSI线上
SCK下降沿的时候马上读取MISO线上的数据位
(实际上下降沿和读取应该是同时的,但是我们软件模拟没法同时,但是效果是一样的)
1.6 不同模式下的通信







二、TFT-LCD

引脚 |
功能 |
GND |
接地 |
VCC |
供电(3.3V-5V) |
TP_INT |
接3v3上拉, |
TP_SDA |
TP触摸芯片数据引脚 |
TP_SCL |
TP触摸芯片时钟引脚 |
LCD_RST |
低电平TFT复位 |
LCD_MOSI |
数据输入引脚 |
LCD_CLK |
LCD时钟引脚 |
LCD_CS |
片选信号,低电平使能 |
LCD_DC |
区分接受的是数据还是命令(低电平命令,高电平数据) |
LCD_BLK |
背光(可常接3.3V) |
HAL_SPI_Transmit()
1
| HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
|
**SPI_HandleTypeDef \*hspi
**:
- 指向 SPI 句柄 的指针。这个句柄包含了关于 SPI 外设的配置信息,必须在使用之前通过
HAL_SPI_Init()
初始化。
**uint8_t \*pData
**:
- 指向要发送的数据的指针。
pData
是一个数据缓冲区,包含要通过 SPI 发送的数据。数据以字节的形式存储。
**uint16_t Size
**:
- 发送数据的大小,以字节为单位。
Size
表示要发送的字节数。
**uint32_t Timeout
**:
- 传输操作的超时时间,以毫秒为单位。如果在指定的时间内传输未完成,函数将返回超时错误。
LCD_GPIO_Init()
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
| void LCD_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStructure.Pin = BLK_PIN; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); HAL_GPIO_WritePin(GPIOA, BLK_PIN, GPIO_PIN_SET); GPIO_InitStructure.Pin = RES_PIN; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); HAL_GPIO_WritePin(GPIOB, RES_PIN, GPIO_PIN_SET);
GPIO_InitStructure.Pin = DC_PIN; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); HAL_GPIO_WritePin(GPIOC, DC_PIN, GPIO_PIN_SET);
GPIO_InitStructure.Pin = CS_PIN; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOD, &GPIO_InitStructure); HAL_GPIO_WritePin(GPIOD, CS_PIN, GPIO_PIN_SET);
}
|
写一个字节数据 LCD_Writ_Bus()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void LCD_Writ_Bus(u8 dat) { HAL_SPI_Transmit(&hspi1,&dat,1,1);
}
|
写两个字节数据 LCD_WR_DATA()
1 2 3 4 5 6 7 8 9 10
| void LCD_WR_DATA(u16 dat) {
uint8_t temp[2]; temp[0]=(dat>>8)&0xff; temp[1]=dat&0xff; HAL_SPI_Transmit(&hspi1,temp,2,1); }
|
写命令 LCD_WR_REG()
1 2 3 4 5 6
| void LCD_WR_REG(u8 dat) { LCD_DC_Clr(); LCD_Writ_Bus(dat); LCD_DC_Set(); }
|
设置起始和初始地址 LCD_Address_Set()
1 2 3 4 5 6 7 8 9 10
| void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2) { LCD_WR_REG(0x2a); LCD_WR_DATA(x1); LCD_WR_DATA(x2); LCD_WR_REG(0x2b); LCD_WR_DATA(y1); LCD_WR_DATA(y2); LCD_WR_REG(0x2c); }
|
设置显示区域:该函数通过设置列地址和行地址,定义了一个矩形区域 (x1, y1)
到 (x2, y2)
,然后准备向该区域写入图像数据或颜色。
显示区域绘制:在调用 LCD_Address_Set()
后,LCD 屏幕的指定区域就被设置好了,之后可以通过其他函数(例如 LCD_WriteColor()
或类似的函数)向该区域写入具体的颜色或图像数据,从而实现绘制操作。
LCD常用函数
1.LCD_Fill()区域颜色填充
1 2 3 4 5 6 7 8 9 10 11 12
| void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color) { u16 i,j; LCD_Address_Set(xsta+OFFSET_X,ysta+OFFSET_Y,xend+OFFSET_X-1,yend-1+OFFSET_Y); for(i=ysta;i<yend;i++) { for(j=xsta;j<xend;j++) { LCD_WR_DATA(color); } } }
|
2.指定位置画点LCD_DrawPoint()
1 2 3 4 5
| void LCD_DrawPoint(u16 x,u16 y,u16 color) { LCD_Address_Set(x,y,x,y); LCD_WR_DATA(color); }
|
3.画线LCD_DrawLine()
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
| void LCD_DrawLine(u16 x1,u16 y1,u16 x2,u16 y2,u16 color) { 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_y;} if(delta_x>delta_y)distance=delta_x; else distance=delta_y; for(t=0;t<distance+1;t++) { LCD_DrawPoint(uRow,uCol,color); xerr+=delta_x; yerr+=delta_y; if(xerr>distance) { xerr-=distance; uRow+=incx; } if(yerr>distance) { yerr-=distance; uCol+=incy; } } }
|
4.画矩形LCD_DrawRectangle()
1 2 3 4 5 6 7
| void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2,u16 color) { LCD_DrawLine(x1,y1,x2,y1,color); LCD_DrawLine(x1,y1,x1,y2,color); LCD_DrawLine(x1,y2,x2,y2,color); LCD_DrawLine(x2,y1,x2,y2,color); }
|
5.画圆Draw_Circle()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void Draw_Circle(u16 x0,u16 y0,u8 r,u16 color) { int a,b; a=0;b=r; while(a<=b) { LCD_DrawPoint(x0-b,y0-a,color); LCD_DrawPoint(x0+b,y0-a,color); LCD_DrawPoint(x0-a,y0+b,color); LCD_DrawPoint(x0-a,y0-b,color); LCD_DrawPoint(x0+b,y0+a,color); LCD_DrawPoint(x0+a,y0-b,color); LCD_DrawPoint(x0+a,y0+b,color); LCD_DrawPoint(x0-b,y0+a,color); a++; if((a*a+b*b)>(r*r)) { b--; } } }
|
6.写汉字LCD_ShowChinese()
1 2 3 4 5 6 7 8 9 10 11 12 13
| void LCD_ShowChinese(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode) { while(*s!=0) { if(sizey==12) LCD_ShowChinese12x12(x,y,s,fc,bc,sizey,mode); else if(sizey==16) LCD_ShowChinese16x16(x,y,s,fc,bc,sizey,mode); else if(sizey==24) LCD_ShowChinese24x24(x,y,s,fc,bc,sizey,mode); else if(sizey==32) LCD_ShowChinese32x32(x,y,s,fc,bc,sizey,mode); else return; s+=2; x+=sizey; } }
|
入口数据:x,y显示坐标
*s 要显示的汉字串
fc 字的颜色
bc 字的背景色
sizey 字号 可选 16 24 32
mode: 0非叠加模式 1叠加模式
7.显示单个字符LCD_ShowChar()
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
| void LCD_ShowChar(u16 x,u16 y,u8 num,u16 fc,u16 bc,u8 sizey,u8 mode) { u8 temp,sizex,t,m=0; u16 i,TypefaceNum; u16 x0=x; sizex=sizey/2; TypefaceNum=(sizex/8+((sizex%8)?1:0))*sizey; num=num-' '; LCD_Address_Set(x,y,x+sizex-1,y+sizey-1); for(i=0;i<TypefaceNum;i++) { if(sizey==12)temp=ascii_1206[num][i]; else if(sizey==16)temp=ascii_1608[num][i]; else if(sizey==24)temp=ascii_2412[num][i]; else if(sizey==32)temp=ascii_3216[num][i]; else return; for(t=0;t<8;t++) { if(!mode) { if(temp&(0x01<<t))LCD_WR_DATA(fc); else LCD_WR_DATA(bc); m++; if(m%sizex==0) { m=0; break; } } else { if(temp&(0x01<<t))LCD_DrawPoint(x,y,fc); x++; if((x-x0)==sizex) { x=x0; y++; break; } } } } }
|
8.显示字符串LCD_ShowString()
1 2 3 4 5 6 7 8 9
| void LCD_ShowString(u16 x,u16 y,const u8 *p,u16 fc,u16 bc,u8 sizey,u8 mode) { while(*p!='\0') { LCD_ShowChar(x,y,*p,fc,bc,sizey,mode); x+=sizey/2; p++; } }
|
9.显示数字mypow()
1 2 3 4 5 6
| u32 mypow(u8 m,u8 n) { u32 result=1; while(n--)result*=m; return result; }
|
10.显示整数变量LCD_ShowIntNum()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void LCD_ShowIntNum(u16 x,u16 y,u16 num,u8 len,u16 fc,u16 bc,u8 sizey) { u8 t,temp; u8 enshow=0; u8 sizex=sizey/2; for(t=0;t<len;t++) { temp=(num/mypow(10,len-t-1))%10; if(enshow==0&&t<(len-1)) { if(temp==0) { LCD_ShowChar(x+t*sizex,y,' ',fc,bc,sizey,0); continue; }else enshow=1; } LCD_ShowChar(x+t*sizex,y,temp+48,fc,bc,sizey,0); } }
|
11.显示两位小数变量LCD_ShowFloatNum1()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void LCD_ShowFloatNum1(u16 x,u16 y,float num,u8 len,u16 fc,u16 bc,u8 sizey) { u8 t,temp,sizex; u16 num1; sizex=sizey/2; num1=num*100; for(t=0;t<len;t++) { temp=(num1/mypow(10,len-t-1))%10; if(t==(len-2)) { LCD_ShowChar(x+(len-2)*sizex,y,'.',fc,bc,sizey,0); t++; len+=1; } LCD_ShowChar(x+t*sizex,y,temp+48,fc,bc,sizey,0); } }
|
12.显示图片LCD_ShowPicture()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void LCD_ShowPicture(u16 x,u16 y,u16 length,u16 width,const u8 pic[]) { u16 i,j; u32 k=0; LCD_Address_Set(x,y,x+length-1,y+width-1); for(i=0;i<length;i++) { for(j=0;j<width;j++) { LCD_WR_DATA8(pic[k*2]); LCD_WR_DATA8(pic[k*2+1]); k++; } } }
|