STM32 SPI屏幕刷图总结:GPIO模拟、硬件SPI和DMA硬件SPI对比分析

文章目录

  • GPIO模拟SPI
  • 硬件SPI外设
  • DMA+硬件SPI外设
  • 总结
  • 代码工程:https://github.com/liefyuan/stm32-spi-st7789-tft.git

    前言

    我的屏幕的分辨率是:240*320
    驱动是:ST7789V
    线驱动方式:四线SPI(CS,DC,SDA,SCL)
    以下分别使用了三种方式来实现刷图。

    头文件:

    #ifndef __ST7789_DRIVER_H__
    #define __ST7789_DRIVER_H__
    
    #include "stdint.h"
    #include "stm32f4xx.h"
    #include "system_stm32f4xx.h"
    
    #define ST7789_LCD_WIDTH             240
    #define ST7789_LCD_HEIGHT            320
    
    // System Function Command Table 1
    #define ST7789_CMD_NOP               0x00 // No operation
    #define ST7789_CMD_SWRESET           0x01 // Software reset
    #define ST7789_CMD_RDDID             0x04 // Read display ID
    #define ST7789_CMD_RDDST             0x09 // Read display status
    #define ST7789_CMD_RDDPM             0x0a // Read display power
    #define ST7789_CMD_RDDMADCTL         0x0b // Read display
    #define ST7789_CMD_RDDCOLMOD         0x0c // Read display pixel
    #define ST7789_CMD_RDDIM             0x0d // Read display image
    #define ST7789_CMD_RDDSM             0x0e // Read display signal
    #define ST7789_CMD_RDDSDR            0x0f // Read display self-diagnostic result
    #define ST7789_CMD_SLPIN             0x10 // Sleep in
    #define ST7789_CMD_SLPOUT            0x11 // Sleep out
    #define ST7789_CMD_PTLON             0x12 // Partial mode on
    #define ST7789_CMD_NORON             0x13 // Partial off (Normal)
    #define ST7789_CMD_INVOFF            0x20 // Display inversion off
    #define ST7789_CMD_INVON             0x21 // Display inversion on
    #define ST7789_CMD_GAMSET            0x26 // Gamma set
    #define ST7789_CMD_DISPOFF           0x28 // Display off
    #define ST7789_CMD_DISPON            0x29 // Display on
    #define ST7789_CMD_CASET             0x2a // Column address set
    #define ST7789_CMD_RASET             0x2b // Row address set
    #define ST7789_CMD_RAMWR             0x2c // Memory write
    #define ST7789_CMD_RAMRD             0x2e // Memory read
    #define ST7789_CMD_PTLAR             0x30 // Partial start/end address set
    #define ST7789_CMD_VSCRDEF           0x33 // Vertical scrolling definition
    #define ST7789_CMD_TEOFF             0x34 // Tearing line effect off
    #define ST7789_CMD_TEON              0x35 // Tearing line effect on
    #define ST7789_CMD_MADCTL            0x36 // Memory data access control
    #define ST7789_CMD_VSCRSADD          0x37 // Vertical address scrolling
    #define ST7789_CMD_IDMOFF            0x38 // Idle mode off
    #define ST7789_CMD_IDMON             0x39 // Idle mode on
    #define ST7789_CMD_COLMOD            0x3a // Interface pixel format
    #define ST7789_CMD_RAMWRC            0x3c // Memory write continue
    #define ST7789_CMD_RAMRDC            0x3e // Memory read continue
    #define ST7789_CMD_TESCAN            0x44 // Set tear scanline
    #define ST7789_CMD_RDTESCAN          0x45 // Get scanline
    #define ST7789_CMD_WRDISBV           0x51 // Write display brightness
    #define ST7789_CMD_RDDISBV           0x52 // Read display brightness value
    #define ST7789_CMD_WRCTRLD           0x53 // Write CTRL display
    #define ST7789_CMD_RDCTRLD           0x54 // Read CTRL value display
    #define ST7789_CMD_WRCACE            0x55 // Write content adaptive brightness control and Color enhancemnet
    #define ST7789_CMD_RDCABC            0x56 // Read content adaptive brightness control
    #define ST7789_CMD_WRCABCMB          0x5e // Write CABC minimum brightness
    #define ST7789_CMD_RDCABCMB          0x5f // Read CABC minimum brightness
    #define ST7789_CMD_RDABCSDR          0x68 // Read Automatic Brightness Control Self-Diagnostic Result
    #define ST7789_CMD_RDID1             0xda // Read ID1
    #define ST7789_CMD_RDID2             0xdb // Read ID2
    #define ST7789_CMD_RDID3             0xdc // Read ID3
    
    // System Function Command Table 2
    #define ST7789_CMD_RAMCTRL           0xb0 // RAM Control
    #define ST7789_CMD_RGBCTRL           0xb1 // RGB Control
    #define ST7789_CMD_PORCTRL           0xb2 // Porch control
    #define ST7789_CMD_FRCTRL1           0xb3 // Frame Rate Control 1
    #define ST7789_CMD_GCTRL             0xb7 // Gate control
    #define ST7789_CMD_DGMEN             0xba // Digital Gamma Enable
    #define ST7789_CMD_VCOMS             0xbb // VCOM Setting
    #define ST7789_CMD_LCMCTRL           0xc0 // LCM Control
    #define ST7789_CMD_IDSET             0xc1 // ID Setting
    #define ST7789_CMD_VDVVRHEN          0xc2 // VDV and VRH Command enable
    #define ST7789_CMD_VRHS              0xc3 // VRH Set
    #define ST7789_CMD_VDVSET            0xc4 // VDV Setting
    #define ST7789_CMD_VCMOFSET          0xc5 // VCOM Offset Set
    #define ST7789_CMD_FRCTR2            0xc6 // FR Control 2
    #define ST7789_CMD_CABCCTRL          0xc7 // CABC Control
    #define ST7789_CMD_REGSEL1           0xc8 // Register value selection 1
    #define ST7789_CMD_REGSEL2           0xca // Register value selection 2
    #define ST7789_CMD_PWMFRSEL          0xcc // PWM Frequency Selection
    #define ST7789_CMD_PWCTRL1           0xd0 // Power Control 1
    #define ST7789_CMD_VAPVANEN          0xd2 // Enable VAP/VAN signal output
    #define ST7789_CMD_CMD2EN            0xdf // Command 2 Enable
    #define ST7789_CMD_PVGAMCTRL         0xe0 // Positive Voltage Gamma Control
    #define ST7789_CMD_NVGAMCTRL         0xe1 // Negative voltage Gamma Control
    #define ST7789_CMD_DGMLUTR           0xe2 // Digital Gamma Look-up Table for Red
    #define ST7789_CMD_DGMLUTB           0xe3 // Digital Gamma Look-up Table for Blue
    #define ST7789_CMD_GATECTRL          0xe4 // Gate control
    #define ST7789_CMD_PWCTRL2           0xe8 // Power Control 2
    #define ST7789_CMD_EQCTRL            0xe9 // Equalize Time Control
    #define ST7789_CMD_PROMCTRL          0xec // Program Control
    #define ST7789_CMD_PROMEN            0xfa // Program Mode Enable
    #define ST7789_CMD_NVMSET            0xfc // NVM Setting
    #define ST7789_CMD_PROMACT           0xfe // Program Action
    
    #define ST7789_CMDLIST_END           0xff // End command (used for command list)
    
    #define RED  	0xf800
    #define GREEN	0x07e0
    #define BLUE 	0x001f
    #define WHITE	0xffff
    #define BLACK	0x0000
    #define YELLOW  0xFFE0
    #define GRAY0   0xEF7D   	    //灰色0 3165 00110 001011 00101
    #define GRAY1   0x8410      	//灰色1      00000 000000 00000
    #define GRAY2   0x4208      	//灰色2  1111111111011111
    
    //#define LCD_SDA        	//PB15    	// SPI 数据引脚
    //#define LCD_SCL        	//PB13    	// SPI 时钟引脚
    //#define LCD_CS        	//PB12    	// SPI片选引脚
    //#define LCD_DC         	//PB11    	// SPI 数据/命令引脚
    //#define LCD_RST           //PB10    	// 屏幕复位引脚 
    //#define LCD_BLK           //PB1    	// 背光控制引脚
    
    //液晶控制口置1操作语句宏定义
    #define	LCD_SDA_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_15)
    #define	LCD_SCL_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_13)
    
    #define	LCD_CS_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_12)   
    #define	LCD_DC_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_11)
    
    #define LCD_RST_SET     GPIO_SetBits(GPIOB, GPIO_Pin_10)
    
    #define	LCD_BLK_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_1)   
    
    //液晶控制口置0操作语句宏定义
    #define	LCD_SDA_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_15)    
    #define	LCD_SCL_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_13)  
    
    #define	LCD_CS_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_12)  
    #define	LCD_DC_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_11) 
    
    #define LCD_RST_CLR     GPIO_ResetBits(GPIOB, GPIO_Pin_10)
    
    #define	LCD_BLK_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_1)
    
    
    void LCD_GPIO_Init(void);
    void Lcd_Init(void);
    void Lcd_Test(void);
    void Lcd_Full(uint16_t color);
    void Lcd_Dma_Full(uint16_t color);
    
    #endif // __ST7789_DRIVER_H__
    
    

    GPIO模拟SPI

    //液晶控制口置1操作语句宏定义
    #define	LCD_SDA_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_15)
    #define	LCD_SCL_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_13)
    
    #define	LCD_CS_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_12)   
    #define	LCD_DC_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_11)
    
    #define LCD_RST_SET     GPIO_SetBits(GPIOB, GPIO_Pin_10)
    
    #define	LCD_BLK_SET  	GPIO_SetBits(GPIOB, GPIO_Pin_1)   
    
    //液晶控制口置0操作语句宏定义
    #define	LCD_SDA_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_15)    
    #define	LCD_SCL_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_13)  
    
    #define	LCD_CS_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_12)  
    #define	LCD_DC_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_11) 
    
    #define LCD_RST_CLR     GPIO_ResetBits(GPIOB, GPIO_Pin_10)
    
    #define	LCD_BLK_CLR  	GPIO_ResetBits(GPIOB, GPIO_Pin_1)
    
    //液晶IO初始化配置
    void LCD_GPIO_Init(void)
    {
        GPIO_InitTypeDef  GPIO_InitStructure;
    
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    
        GPIO_InitStructure.GPIO_Pin =   GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_15;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;     //普通输出模式
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;    //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      //上拉
        GPIO_Init(GPIOB, &GPIO_InitStructure);
    }
    //向SPI总线传输一个8位数据
    void  SPI_WriteData(uint8_t Data)
    {
        uint8_t i;
    
        for(i = 0; i < 8; i++)    // 295 ms 1 frame
        {
            if((Data&0x80) != 0)
            {
                LCD_SDA_SET;
            }
            else
                LCD_SDA_CLR;
    
            LCD_SCL_CLR;
            LCD_SCL_SET;
            Data <<= 1;
        }
    }
    
    //向液晶屏写一个8位指令
    void Lcd_WriteIndex(uint8_t Index)
    {
        //SPI 写命令时序开始
        LCD_CS_CLR;
        LCD_DC_CLR;
    
        SPI_WriteData(Index);
        LCD_CS_SET;
    }
    
    //向液晶屏写一个8位数据
    void Lcd_WriteData(uint8_t Data)
    {
        LCD_CS_CLR;
        LCD_DC_SET;
    
        SPI_WriteData(Data);
        LCD_CS_SET;
    }
    //向液晶屏写一个16位数据
    void LCD_WriteData_16Bit(uint16_t Data)
    {
    //    LCD_CS_CLR;
    //    LCD_DC_SET;
        SPI_WriteData(Data>>8); 	//写入高8位数据
        SPI_WriteData(Data); 		//写入低8位数据
    //    LCD_CS_SET;
    }
    
    void Lcd_WriteReg(uint8_t Index,uint8_t Data)
    {
        Lcd_WriteIndex(Index);
        Lcd_WriteData(Data);
    }
    
    void Lcd_Reset(void)
    {
        LCD_RST_CLR;//RST引脚输出为低
        delay_ms(100);
        LCD_RST_SET;//RST引脚输出为高
        delay_ms(100);
    }
    
    /***********************************************************************/
    
    void Lcd_Init(void)
    {
        LCD_GPIO_Init();
    
        LCD_RST_CLR;
        delay_ms(1000);
        LCD_RST_SET;
        /********************************************************/
        delay_ms(1000);
        Lcd_WriteIndex(ST7789_CMD_SLPOUT); 	//Sleep Out
        delay_ms(120);
        //-----------------------ST7789V Frame rate setting-----------------//
        //************************************************
        Lcd_WriteIndex(ST7789_CMD_COLMOD);       //65k mode
        Lcd_WriteData(0x05);
    
        Lcd_WriteIndex(ST7789_CMD_VCMOFSET); 	//VCOM
        Lcd_WriteData(0x1A);
    
        Lcd_WriteIndex(ST7789_CMD_MADCTL);       // 屏幕显示方向设置
        Lcd_WriteData(0x00);
        //-------------ST7789V Frame rate setting-----------//
        Lcd_WriteIndex(ST7789_CMD_PORCTRL);		//Porch Setting
        Lcd_WriteData(0x05);
        Lcd_WriteData(0x05);
        Lcd_WriteData(0x00);
        Lcd_WriteData(0x33);
        Lcd_WriteData(0x33);
    
        Lcd_WriteIndex(ST7789_CMD_GCTRL);		//Gate Control
        Lcd_WriteData(0x05);			//12.2v   -10.43v
        //--------------ST7789V Power setting---------------//
        Lcd_WriteIndex(ST7789_CMD_VCOMS);//VCOM
        Lcd_WriteData(0x3F);
    
        Lcd_WriteIndex(ST7789_CMD_LCMCTRL); //Power control
        Lcd_WriteData(0x2c);
    
        Lcd_WriteIndex(ST7789_CMD_VDVVRHEN);	//VDV and VRH Command Enable
        Lcd_WriteData(0x01);
    
        Lcd_WriteIndex(ST7789_CMD_VRHS);		//VRH Set
        Lcd_WriteData(0x0F);		//4.3+( vcom+vcom offset+vdv)
    
        Lcd_WriteIndex(ST7789_CMD_VDVSET);		//VDV Set
        Lcd_WriteData(0x20);				//0v
    
        Lcd_WriteIndex(ST7789_CMD_FRCTR2);		//Frame Rate Control in Normal Mode
        Lcd_WriteData(0X01);			     //111Hz
    
        Lcd_WriteIndex(ST7789_CMD_PWCTRL1);		//Power Control 1
        Lcd_WriteData(0xa4);
        Lcd_WriteData(0xa1);
    
        Lcd_WriteIndex(ST7789_CMD_PWCTRL2);		//Power Control 1
        Lcd_WriteData(0x03);
    
        Lcd_WriteIndex(ST7789_CMD_EQCTRL);		//Equalize time control
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x08);
        //---------------ST7789V gamma setting-------------//
        Lcd_WriteIndex(ST7789_CMD_PVGAMCTRL); //Set Gamma
        Lcd_WriteData(0xD0);
        Lcd_WriteData(0x05);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x08);
        Lcd_WriteData(0x14);
        Lcd_WriteData(0x28);
        Lcd_WriteData(0x33);
        Lcd_WriteData(0x3F);
        Lcd_WriteData(0x07);
        Lcd_WriteData(0x13);
        Lcd_WriteData(0x14);
        Lcd_WriteData(0x28);
        Lcd_WriteData(0x30);
    
        Lcd_WriteIndex(ST7789_CMD_NVGAMCTRL); //Set Gamma
        Lcd_WriteData(0xD0);
        Lcd_WriteData(0x05);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x08);
        Lcd_WriteData(0x03);
        Lcd_WriteData(0x24);
        Lcd_WriteData(0x32);
        Lcd_WriteData(0x32);
        Lcd_WriteData(0x3B);
        Lcd_WriteData(0x14);
        Lcd_WriteData(0x13);
        Lcd_WriteData(0x28);
        Lcd_WriteData(0x2F);
    
        Lcd_WriteIndex(ST7789_CMD_INVON);		//反显
    
        Lcd_WriteIndex(ST7789_CMD_DISPON);		//开启显示
    }
    
    /*************************************************
    函数名:LCD_Set_Region
    功能:设置lcd显示区域,在此区域写点数据自动换行
    入口参数:xy起点和终点,Y_IncMode表示先自增y再自增x
    返回值:无
    *************************************************/
    void Lcd_SetRegion(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end)
    {
        Lcd_WriteIndex(0x2a);
        Lcd_WriteData(0x00);
        Lcd_WriteData(x_start);
        Lcd_WriteData(0x00);
        Lcd_WriteData(x_end);
    
        Lcd_WriteIndex(0x2b);
        Lcd_WriteData(0x00);
        Lcd_WriteData(y_start);
        Lcd_WriteData(y_end>>8);
        Lcd_WriteData(y_end);
    
        Lcd_WriteIndex(0x2c); //开始写入GRAM
    }
    
    void Lcd_Full(uint16_t color)
    {
    	uint16_t row,column;
    
    	Lcd_SetRegion(0, 0, ST7789_LCD_WIDTH, ST7789_LCD_HEIGHT);
    	
        LCD_CS_CLR;
        LCD_DC_SET;
    	for(row = 0;row < ST7789_LCD_WIDTH; row++)             //ROW loop
    	{
    		for(column = 0;column < ST7789_LCD_HEIGHT; column++) //column loop
    		{
    			LCD_WriteData_16Bit(color); // 180ms 1 frame
    		}
    	}
    	LCD_CS_SET;
    }
    

    硬件SPI外设

    SPI_InitTypeDef  SPI_InitStructure;
    
    void SPI2_Init(void)
    {
        GPIO_InitTypeDef  GPIO_InitStructure;
    
        //SPI2的时钟来源是APB1 42MHz
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);	//使能GPIOB、SPI2时钟
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);	//使能GPIOB、SPI2时钟
    
        //PB13--SCL ,PB15--SDI初始化设置,
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;//PB13,PB15复用功能输出
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
        GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    
        GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2); //PB13复用为 SPI2
    
        GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2); //PB15复用为 SPI2
    
        //这里只针对SPI口初始化
        RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,ENABLE);//复位SPI2
        RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,DISABLE);//停止复位SPI2
    
        SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
        SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
        SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
        SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		//串行同步时钟的空闲状态为低电平
        SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//串行同步时钟的第一个跳变沿(上升或下降)数据被采样
        SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;		//定义波特率预分频的值:波特率预分频值为
        SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
        SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
        SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    
        SPI_Cmd(SPI2, ENABLE); //使能SPI外设
        SPI2_ReadWriteByte(0xff);//启动传输
    }
    
    //液晶IO初始化配置
    void LCD_Hal_Spi_Init(void)
    {
        GPIO_InitTypeDef  GPIO_InitStructure;
    
        RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC,ENABLE);
    
        GPIO_InitStructure.GPIO_Pin =   GPIO_Pin_10  | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_14;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
        GPIO_Init(GPIOB, &GPIO_InitStructure);
    
        GPIO_SetBits(GPIOB,GPIO_Pin_14);//MISO引脚输出为高
    
        SPI2_Init();//初始化硬件SPI2
    }
    
    //SPIx 读写一个字节
    //TxData:要写入的字节
    //返回值:读取到的字节
    uint8_t SPI2_ReadWriteByte(uint16_t TxData)
    {
    //    uint8_t retry=0;
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
        {
    //        retry++;
    //        if(retry>200)return 0;
        }
        SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
    //    retry=0;
    
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位
        {
    //        retry++;
    //        if(retry>200)return 0;
        }
        return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
    }
    
    //向SPI总线传输一个8位数据
    void  SPI_WriteData(uint8_t Data)
    {
        SPI2_ReadWriteByte(Data);
    }
    
    //向液晶屏写一个8位指令
    void Lcd_WriteIndex(uint8_t Index)
    {
        //SPI 写命令时序开始
        LCD_CS_CLR;
        LCD_DC_CLR;
    
        SPI_WriteData(Index);
        LCD_CS_SET;
    }
    
    //向液晶屏写一个8位数据
    void Lcd_WriteData(uint8_t Data)
    {
        LCD_CS_CLR;
        LCD_DC_SET;
    
        SPI_WriteData(Data);
        LCD_CS_SET;
    }
    //向液晶屏写一个16位数据
    void LCD_WriteData_16Bit(uint16_t Data)
    {
    //    LCD_CS_CLR;
    //    LCD_DC_SET;
        SPI_WriteData(Data>>8); 	//写入高8位数据
        SPI_WriteData(Data); 		//写入低8位数据
    //    LCD_CS_SET;
    }
    
    void Lcd_WriteReg(uint8_t Index,uint8_t Data)
    {
        Lcd_WriteIndex(Index);
        Lcd_WriteData(Data);
    }
    
    void Lcd_Reset(void)
    {
        LCD_RST_CLR;//RST引脚输出为低
        delay_ms(100);
        LCD_RST_SET;//RST引脚输出为高
        delay_ms(100);
    }
    
    /***********************************************************************/
    
    void Lcd_Init(void)
    {
        LCD_Hal_Spi_Init();
    
        Lcd_Reset();
        /********************************************************/
        delay_ms(1000);
        Lcd_WriteIndex(ST7789_CMD_SLPOUT); 	//Sleep Out
        delay_ms(120);
        //-----------------------ST7789V Frame rate setting-----------------//
        //************************************************
        Lcd_WriteIndex(ST7789_CMD_COLMOD);       //65k mode
        Lcd_WriteData(0x05);
    
        Lcd_WriteIndex(ST7789_CMD_VCMOFSET); 	//VCOM
        Lcd_WriteData(0x1A);
    
        Lcd_WriteIndex(ST7789_CMD_MADCTL);       // 屏幕显示方向设置
        Lcd_WriteData(0x00);
        //-------------ST7789V Frame rate setting-----------//
        Lcd_WriteIndex(ST7789_CMD_PORCTRL);		//Porch Setting
        Lcd_WriteData(0x05);
        Lcd_WriteData(0x05);
        Lcd_WriteData(0x00);
        Lcd_WriteData(0x33);
        Lcd_WriteData(0x33);
    
        Lcd_WriteIndex(ST7789_CMD_GCTRL);		//Gate Control
        Lcd_WriteData(0x05);			//12.2v   -10.43v
        //--------------ST7789V Power setting---------------//
        Lcd_WriteIndex(ST7789_CMD_VCOMS);//VCOM
        Lcd_WriteData(0x3F);
    
        Lcd_WriteIndex(ST7789_CMD_LCMCTRL); //Power control
        Lcd_WriteData(0x2c);
    
        Lcd_WriteIndex(ST7789_CMD_VDVVRHEN);	//VDV and VRH Command Enable
        Lcd_WriteData(0x01);
    
        Lcd_WriteIndex(ST7789_CMD_VRHS);		//VRH Set
        Lcd_WriteData(0x0F);		//4.3+( vcom+vcom offset+vdv)
    
        Lcd_WriteIndex(ST7789_CMD_VDVSET);		//VDV Set
        Lcd_WriteData(0x20);				//0v
    
        Lcd_WriteIndex(ST7789_CMD_FRCTR2);		//Frame Rate Control in Normal Mode
        Lcd_WriteData(0X01);			     //111Hz
    
        Lcd_WriteIndex(ST7789_CMD_PWCTRL1);		//Power Control 1
        Lcd_WriteData(0xa4);
        Lcd_WriteData(0xa1);
    
        Lcd_WriteIndex(ST7789_CMD_PWCTRL2);		//Power Control 1
        Lcd_WriteData(0x03);
    
        Lcd_WriteIndex(ST7789_CMD_EQCTRL);		//Equalize time control
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x08);
        //---------------ST7789V gamma setting-------------//
        Lcd_WriteIndex(ST7789_CMD_PVGAMCTRL); //Set Gamma
        Lcd_WriteData(0xD0);
        Lcd_WriteData(0x05);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x08);
        Lcd_WriteData(0x14);
        Lcd_WriteData(0x28);
        Lcd_WriteData(0x33);
        Lcd_WriteData(0x3F);
        Lcd_WriteData(0x07);
        Lcd_WriteData(0x13);
        Lcd_WriteData(0x14);
        Lcd_WriteData(0x28);
        Lcd_WriteData(0x30);
    
        Lcd_WriteIndex(ST7789_CMD_NVGAMCTRL); //Set Gamma
        Lcd_WriteData(0xD0);
        Lcd_WriteData(0x05);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x09);
        Lcd_WriteData(0x08);
        Lcd_WriteData(0x03);
        Lcd_WriteData(0x24);
        Lcd_WriteData(0x32);
        Lcd_WriteData(0x32);
        Lcd_WriteData(0x3B);
        Lcd_WriteData(0x14);
        Lcd_WriteData(0x13);
        Lcd_WriteData(0x28);
        Lcd_WriteData(0x2F);
    
        Lcd_WriteIndex(ST7789_CMD_INVON);		//反显
    
        Lcd_WriteIndex(ST7789_CMD_DISPON);		//开启显示
    }
    
    /*************************************************
    函数名:LCD_Set_Region
    功能:设置lcd显示区域,在此区域写点数据自动换行
    入口参数:xy起点和终点,Y_IncMode表示先自增y再自增x
    返回值:无
    *************************************************/
    void Lcd_SetRegion(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end)
    {
        Lcd_WriteIndex(0x2a);
        Lcd_WriteData(0x00);
        Lcd_WriteData(x_start);
        Lcd_WriteData(0x00);
        Lcd_WriteData(x_end);
    
        Lcd_WriteIndex(0x2b);
        Lcd_WriteData(0x00);
        Lcd_WriteData(y_start);
        Lcd_WriteData(y_end>>8);
        Lcd_WriteData(y_end);
    
        Lcd_WriteIndex(0x2c); //开始写入GRAM
    }
    
    void Lcd_Full(uint16_t color)
    {
    	uint16_t row,column;
    
    	Lcd_SetRegion(0, 0, ST7789_LCD_WIDTH, ST7789_LCD_HEIGHT);
    	
        LCD_CS_CLR;
        LCD_DC_SET;
    	for(row = 0;row < ST7789_LCD_WIDTH; row++)             //ROW loop
    	{
    		for(column = 0;column < ST7789_LCD_HEIGHT; column++) //column loop
    		{
    			LCD_WriteData_16Bit(color); // 180ms 1 frame
    		}
    	}
    	LCD_CS_SET;
    }
    

    DMA+硬件SPI外设


    我使用的是SPI2,所以对应的DMA信息如上表。

    void DMA_Config(u32 TX_Buff,u32 SENDBUFF_SIZE)
    {
        // 中断结构体
        NVIC_InitTypeDef NVIC_InitStructure;		
        // DMA结构体
        DMA_InitTypeDef DMA_InitStructure;  		
        /* 使能DMA时钟 */  		
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);	
        /* 复位初始化DMA数据流 */  
        DMA_DeInit(DMA1_Stream4);								
        /* 确保DMA数据流复位完成 */  
        while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE);	
    
        /* 配置 DMA Stream */
        /* 通道0,数据流4 */	  
        DMA_InitStructure.DMA_Channel = DMA_Channel_0;
        /* 外设地址 */  
        DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR;	
        /* 内存地址(要传输的变量的指针) ,DMA存储器0地址*/  	
        DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TX_Buff;	
        /* 方向:存储器到外设 */			
        DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
        /* 数据传输量 ,可设置为0, 实际发送时会重新设置*/	    
        DMA_InitStructure.DMA_BufferSize = (uint32_t)SENDBUFF_SIZE;		
        /* 外设非增量模式 */		
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
        /* 存储器增量模式 */  	
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
        /* 外设数据长度:16位 */	 
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
        /* 内存数据长度:16位 */
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
        /* DMA模式:正常模式 */  		
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
        /* 优先级:高 */	 		
        DMA_InitStructure.DMA_Priority = DMA_Priority_High;
        /* 禁用FIFO */
        DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;        		
        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;   
        /* 外设突发单次传输 */  
        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;    		
        /* 存储器突发单次传输 */  
        DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 
    
        /* 初始化DMA Stream */		
        DMA_Init(DMA1_Stream4, &DMA_InitStructure);
        /* 开启传输完成中断  */		
        DMA_ITConfig(DMA1_Stream4,DMA_IT_TC,ENABLE);
    
        // 中断初始化 
        /* DMA发送中断源 */  
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn;	
        /* 抢断优先级 */  
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        /* 响应优先级 */  
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;				
        /* 使能外部中断通道 */ 
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						 
        /* 配置NVIC */		
        NVIC_Init(&NVIC_InitStructure);
    }
    static uint8_t sendFlg = 0;
    
    void DMA1_Stream4_IRQHandler(void)
    {
        // DMA 发送完成
        if(DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4))	
        {
            // 清除DMA发送完成标志
            DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);	
            // 片选拉高,数据发送完毕	
            LCD_CS_SET;	
    		sendFlg = 1;
        }
    }
    
    void DMA_Write_buf(uint32_t SizeLen)
    {   
    	// 关闭发送 DMA		
    	DMA_Cmd(DMA1_Stream4, DISABLE);	
    	// 设置发送的数据量    
    	DMA_SetCurrDataCounter(DMA1_Stream4, SizeLen);
    	// 清空数据
    	SPI2->DR;		
    	// 擦除DMA标志位	
    	DMA_ClearFlag(DMA1_Stream4, DMA_IT_TCIF4);
    	// 片选拉低,接收数据
    	LCD_CS_CLR;
    	LCD_DC_SET;
    	// 开启发送 DMA
    	DMA_Cmd(DMA1_Stream4, ENABLE);
    	
    	while(!sendFlg); 
    	sendFlg = 0;
    }
    
    #define SEND_BUF_SIZE 480*20
    
    uint16_t SendBuff[SEND_BUF_SIZE];	//发送数据缓冲区
    
    // 单颜色填满屏幕
    void Lcd_Dma_Full(uint16_t color)
    {
        int i = 0;
        for(i = 0 ; i < SEND_BUF_SIZE; i++)
        {
            SendBuff[i] = color; // 缓存区不够,分段来凑
        }
    	for(i = 0; i < 16; i++)
    	{
    		SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE); // 60ms 1 frame
    		DMA_Write_buf(SEND_BUF_SIZE);
    	}
    }
    
    int main(void)
    {
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
        delay_init(168);      //初始化延时函数
        uart_init(115200);		//初始化串口波特率为115200
    
        LED_Init();					  //初始化LED
    	
    	Lcd_Init();
    	
    	Lcd_Full(RED);
    	delay_ms(1000);
    
    	uint16_t num = 0;
    	
        while(1)
        {
    	#if 1
    		//num+=1;
    		Lcd_Dma_Full(num++);
    //		LED_ON;
    		//delay_ms(10);
    //		
    //		Lcd_Dma_Full(WHITE);
    //		LED_OFF;
    		delay_ms(50);
    //		
    //		Lcd_Dma_Full(BLUE);
    //		LED_ON;
    		delay_ms(50);
    //		
    //		Lcd_Dma_Full(WHITE);
    //		LED_OFF;
    	#else
    		Lcd_Full(GREEN);
    		LED_ON;
    //		delay_ms(50);
    		
    		Lcd_Full(WHITE);
    		LED_OFF;
    //		delay_ms(50);
    		
    		Lcd_Full(BLUE);
    		LED_ON;
    //		delay_ms(50);
    		
    		Lcd_Full(WHITE);
    		LED_OFF;		
    	#endif
    //		delay_ms(50);
    //		printf("Test \r\n");;
        }
    }
    
    

    总结

    分辨率:240*320
    颜色:RGB565

    方式 一帧耗时 帧率
    GPIO模拟SPI 220ms 4.5 fps
    硬件SPI 188ms 5.3 fps
    DMA+硬件SPI 60ms 16.6 fps
    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 SPI屏幕刷图总结:GPIO模拟、硬件SPI和DMA硬件SPI对比分析

    发表回复