【精选】STM32单片机初学6

您所在的位置:网站首页 spi转并口芯片 【精选】STM32单片机初学6

【精选】STM32单片机初学6

2023-11-16 04:59| 来源: 网络整理| 查看: 265

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线。

和IIC一样,是单片机开发中最常用的通信方式之一。对比IIC,其特点表现在:

1.通信速率快,以STM32F103C8T6为例,其SPI通信速率可达18Mbps,即2.25MB/s,而IIC高速模式也才3.4Mbps;

2.全双工通信,SPI有两根数据线,一根主出从入,一根主入从出,在同一时刻既可以发送数据,也可以接受数据,而IIC是半双工,发送、接受数据不能同时进行;

主要应用场景有SD卡数据读写、TFT彩屏控制等。

本文将详细讲解利用STM32的硬件SPI接口来驱动IPS彩屏显示图片(显示文字就不详细介绍了,与OLED显示文字类似)。

先来看看SPI物理接口定义:  

MISO– Master Input Slave Output,主设备数据输入,从设备数据输出;

MOSI– Master Output Slave Input,主设备数据输出,从设备数据输入;

SCLK – Serial Clock,时钟信号,由主设备产生;

CS – Chip Select,从设备使能信号,由主设备控制,每个从设备对应主设备的一个CS接口,若要与某个从设备进行通讯,则通过CS使能对应的从设备。在STM32中为SPI_NSS。

在我关于IIC软件模拟的文章中详细解析了IIC的软件模拟实现方法,同样的,SPI也能用软件模拟实现,只要按照其时序控制GPIO电平高低。这里暂不使用软件模拟实现,直接配置STM32内部的硬件SPI,再调用库函数。以STM32F103C8T6为例,该型号共有两个SPI。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_20,color_FFFFFF,t_70,g_se,x_16

这里我使用的屏幕是1.3寸的IPS彩屏,分辨率240*240,最高支持16位真彩(RGB565),驱动芯片ST7789。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_20,color_FFFFFF,t_70,g_se,x_16

 16位真彩即每个像素点有65535(2^16)种颜色。

三原色原理中说道:大多数的颜色可以通过红、绿、蓝三色按照不同的比例合成产生。同样,绝大多数单色光也可以分解成红、绿、蓝三种色光。

IPS屏幕上的每个像素点都由一组红绿蓝遮光片组成,白色的背光透过遮光片就呈现出红绿蓝三种颜色。改变对应遮光片的角度就能改变该颜色的强度,不同强度的红绿蓝三色混合在一起就组成了屏幕上显示的65535种颜色。

在这16位中,前面5位表示红色的强度,中间6位表示绿色的强度,后面5位表示蓝色的强度。也就是红绿蓝分别占5、6、5位,这就是RGB565得名原因。所以,红绿蓝三色的强度分别有32、64、32级。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_20,color_FFFFFF,t_70,g_se,x_16

一个像素点需要占用16个bit(即2Byte)的数据,整个屏幕就需要240x240x2=115200Byte(即115.2KB)数据。

-------针脚定义:

该屏幕有7个针脚,接口定义及与单片机连接方式如下:

GND:供电电源地

VCC:  供电电源正(3-5V)

SCL:SPI时钟线。接单片机SPI_SCK

SDA:SPI数据线。对该屏幕来说。只有数据输出而不需要输出,即主出从入,所以接单片机MOSI。单片机的MISO悬空不用。

RES:屏幕复位。单片机与屏幕进行通讯前,需将屏幕复位一次,即将该引脚置低短暂时间后再置高(习惯100ms)。

DC:数据/命令选择端口。与我之前介绍的OLED屏幕不同,OLED是通过向不同的寄存器里写数据来区分是命令还是显示SRAM数据,而该屏幕是通过该引脚电平状态来区分当前收到的数据是命令还是显示数据。DC端口为低时,写入的是命令;为高时,写入的是数据。

BLK:屏幕背光。与像素点自发光的OLED屏幕不同,IPS屏幕是需要有背光才能显示图像的。该屏幕默认是开启背光的,当BLK端口被置低,背光关闭。这里可以用一个GPIO来控制,或者直接悬空,或者接高电平。

具体与STM32的连接方式如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_20,color_FFFFFF,t_70,g_se,x_16

 因为这里只有TFT屏幕一个从站,所以SPI_NSS不接。

------------------------------------------

接下来,一边看程序,一边讲解

1.头文件及GPIO置复位定义

将用到的外设头文件包含到.c文件中:GPIO、RCC、SPI

定义PA1、PA2、PA3的高低电平。这与51单片机不一样,51单片机可以使用sbit将变量映射到GPIO口,但是STM32没有sbit。

#include #include #include #include #define SPI_DC_H() GPIO_SetBits(GPIOA,GPIO_Pin_2) //数据/命令选择 #define SPI_DC_L() GPIO_ResetBits(GPIOA,GPIO_Pin_2) #define LCD_RES_H() GPIO_SetBits(GPIOA,GPIO_Pin_3) //LCD复位(PA3) #define LCD_RES_L() GPIO_ResetBits(GPIOA,GPIO_Pin_3) #define LCD_BLK_H() GPIO_SetBits(GPIOA,GPIO_Pin_1) //LCD背光(PA1) #define LCD_BLK_L() GPIO_ResetBits(GPIOA,GPIO_Pin_1)

2.将图片转成十六进制数据

要在OLED显示字符,是通过让屏幕像素点按照字模点亮或者灭来实现。显示图片也是同理,对于彩屏,只是每个像素点不止0或者1两种状态,它有65535种状态,只要让每个像素点按照我想要的方式处于各自的方式即可得到一张图片。我们可以把它放在一个数组里,里面的元素就是每个像素点的状态值。然后通过SPI按顺序将数组的元素传输至TFT屏幕的显示缓存寄存器,就能在屏幕显示想要的图片了。

这里需要用到的小工具是Img2Lcd。这是一个可以将图片转成数组的工具。可在网上下载该工具。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_20,color_FFFFFF,t_70,g_se,x_16

 现在来演示如何将下面这张图片转成C程序数组。由于我这里使用的240x240的正方形屏幕,所以为了能填满屏屏幕,特意将该图片裁切成了正方形。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_18,color_FFFFFF,t_70,g_se,x_16

 需要注意,Img2Lcd只能转换jpg格式的图片,如果是其他格式的图片,需要先转换成jpg格式。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_14,color_FFFFFF,t_70,g_se,x_16

 然后在小工具中打开该文件

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_20,color_FFFFFF,t_70,g_se,x_16

 打开之后还需要设置相关参数。输出灰度选择16位真彩色。

最大宽度与与高度设置需要注意,我这里使用的STM32F103C8T6,其Flash只有64KB,所以该数组的大小绝不能超过64KB,否则会因为存储空间不足而通不过编译。这里我留一定的Flash余量给其他变量及程序占用,给数组分配60KB的空间。而该屏幕一个像素点需要2B的空间,也就是一共能存储60K÷2=30000个像素点的状态。再对30000开根号,约等于173。所以这里将最大宽度与高度设置成173x173。(如果使用的单片机是128K或者以上的flash,则可以全屏显示,设置成240x240)

取消勾选“包含图像头数据”,勾选“高位在前”(这是因为该屏幕是先发送高位再发送低位,如果不勾选,则会出现反色效果)。最后,还可以稍微将对比度拉高一点(因为IPS屏色彩不够鲜艳,最后的画面会有点暗淡)。

设置完成之后,点击“最大宽度和高度”右边的小按钮,即可生成预览图如下。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_20,color_FFFFFF,t_70,g_se,x_16

 然后点击“保存”,选择路径后确认即可得到一个.c的文件,这里我们直接用记事本打开。可以发现里面是一个数组,元素个数为59859,正好等于173x173x2。

这里每个元素是一个2位十六进制的数,每两个元素代表一个像素点。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_20,color_FFFFFF,t_70,g_se,x_16

 然后,我们将该数组复制粘贴到程序中(如下)。数组很长,可将其折叠。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAI2xpdWZlbmdlcyM=,size_20,color_FFFFFF,t_70,g_se,x_16

 这样,这步就算完成了。

3.SPI初始化

因为使用的是硬件SPI1,所以需要对SPI1进行初始化。

首先是GPIO设置,SPI1占用PA4-PA7共四个IO,这四个IO设置成复用推挽输出。另外还使用了PA1,PA2,PA3三个IO口分别做为背光、数据/命令选择、复位的控制端口。所以这里也需要给这三个IO初始化,设为推挽输出即可。然后开启对应的时钟。 然后是SPI的配置,与GPIO一样,在SPI的外设库中也有一个结构体,里面的成员是SPI的各项参数。在外设库中可以查看该结构体具体内容。8f7c84e0e5104c33a422f4e6ccc2fa00.png

下面的代码可供参考

void SPI_UserInit(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //PA4为SPI_NSS,PA5为SPI_SCK,PA6为SPI_MISO,PA7为SPI_MOSI,对SPI这4个端口进行初始化 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //PA1为BLK背光,PA2位DC选择,PA3为屏幕复位 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //使能时钟 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//设置为双向双线全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置为SPI主站。控制屏幕,单片机需做主站 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置数据大小为8位,即每次发送8位数据 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //设置串行时钟的稳态为时钟高 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //设置位捕捉的时钟活动沿为第一个上升沿 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //设置NSS为软件控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//设置波特率分频(该值越大,波特率越慢) SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //设置先发送高位 SPI_InitStructure.SPI_CRCPolynomial = 7; //设置CRC校验 SPI_Init(SPI1, &SPI_InitStructure); //初始化 SPI_Cmd(SPI1, ENABLE); //使能SPI1 }

4.SPI写数据与写命令

与OLED屏幕一样,需要分别声明写数据和写命令的子函数。

前面讲过,该屏幕是通过DC端口来区分命令与数据,所以写数据与写命令的区别就是DC端口分别为高电平与低电平。由于写数据还分写8位数据与写16位数据,所以还得分两个写数据的函数。

需要注意,SPI本身是支持16数据帧的(通过 SPI_InitStructure.SPI_DataSize 设置),但是为了方便发送8位的数据帧,所以在SPI初始化中把DataSize设置成了8。而发送16位数据是通过先发送低8位,然后数据右移8位,再发送一次8位数据实现的。

void SPI_WriteCmd(u8 Data) //写命令 { SPI_DC_L(); SPI_I2S_SendData(SPI1,Data); } void LCD_WriteData8(u8 Data) //写8位数据 { SPI_DC_H(); SPI_I2S_SendData(SPI1,Data); } void LCD_WriteData16(u16 Data) //写16位数据 { SPI_DC_H(); SPI_I2S_SendData(SPI1,(Data>>8) ) ; //Date右移8位 SPI_I2S_SendData(SPI1,Data); }

5.屏幕初始化

与OLED屏幕一样,该IPS上电之后需要设置参数进行初始化。

具体需要设置的参数可查阅ST7789使用书册。可参考我以下参数。

void LCD_Init(void) { u16 x; LCD_RES_L(); Delay_ms(50); LCD_RES_H(); Delay_ms(50); //复位屏幕 SPI_WriteCmd(0x3A); //设置颜色格式为16位真彩 LCD_WriteData8(0x05); //03 4K;05 65K;06 262K SPI_WriteCmd(0x36); //设置屏幕方向为从上到下,从左到右 LCD_WriteData8(0x00); //----------- ST7789S Frame rate setting ---------// SPI_WriteCmd(0xB2); LCD_WriteData8(0x0C); LCD_WriteData8(0x0C); LCD_WriteData8(0x00); LCD_WriteData8(0x33); LCD_WriteData8(0x33); SPI_WriteCmd(0xB7); LCD_WriteData8(0x35); //----------- ST7789S Power setting ---------// SPI_WriteCmd(0xBB); LCD_WriteData8(0x19); SPI_WriteCmd(0xC0); LCD_WriteData8(0x2C); SPI_WriteCmd(0xC2); LCD_WriteData8(0x01); SPI_WriteCmd(0xC3); LCD_WriteData8(0x12); SPI_WriteCmd(0xC4); LCD_WriteData8(0x20); SPI_WriteCmd(0xC6); LCD_WriteData8(0x0F); SPI_WriteCmd(0xD0); LCD_WriteData8(0xA4); LCD_WriteData8(0xA1); //----------- Posistive Voltage Gamma Control ---------// SPI_WriteCmd(0xE0); LCD_WriteData8(0xD0); LCD_WriteData8(0x04); LCD_WriteData8(0x0D); LCD_WriteData8(0x11); LCD_WriteData8(0x13); LCD_WriteData8(0x2B); LCD_WriteData8(0x3F); LCD_WriteData8(0x54); LCD_WriteData8(0x4C); LCD_WriteData8(0x18); LCD_WriteData8(0x0D); LCD_WriteData8(0x0B); LCD_WriteData8(0x1F); LCD_WriteData8(0x23); SPI_WriteCmd(0xE1); LCD_WriteData8(0xD0); LCD_WriteData8(0x04); LCD_WriteData8(0x0C); LCD_WriteData8(0x11); LCD_WriteData8(0x13); LCD_WriteData8(0x2C); LCD_WriteData8(0x3F); LCD_WriteData8(0x44); LCD_WriteData8(0x51); LCD_WriteData8(0x2F); LCD_WriteData8(0x1F); LCD_WriteData8(0x1F); LCD_WriteData8(0x20); LCD_WriteData8(0x23); //----------- Setting ---------// SPI_WriteCmd(0x21); SPI_WriteCmd(0x11) SPI_WriteCmd(0x29); LCD_SetRegion(0,0,239,239); SPI_DC_H(); for(x=0;x


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3