3线硬件SPI驱动 HX8347 TFT屏

3线(cs、mosi、sck)硬件SPI驱动HX8347 LCD

3线硬件SPI驱动 HX8347 TFT屏_第1张图片

参考:STM32的硬件SPI驱动LCD例子 - 21ic电子网 

一、源码

HX8347.h

#ifndef USER_HX8347_H_
#define USER_HX8347_H_

#define SPI_hardware

#define X_MAX_PIXEL 240
#define Y_MAX_PIXEL 320

#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

#ifdef SPI_hardware
    #define LCD_CS          GPIO_Pin_0  // CS:PA0
    #define LCD_SDA         GPIO_Pin_7  // SDA:PA7  SPI1 //硬件SPI实现,不用人工处理
    #define LCD_SCL         GPIO_Pin_5  // SCL:PA5  SPI1 //硬件SPI实现,不用人工处理
    #define LCD_RST         GPIO_Pin_4  // RST:PA4
#else
    #define LCD_CS          GPIO_Pin_0  // CS:PA0
    #define LCD_SDA         GPIO_Pin_1  // SDA:PA1
    #define LCD_SCL         GPIO_Pin_3  // SCL:PA3
    #define LCD_RST         GPIO_Pin_4  // RST:PA4
#endif

#define LCD_SCL_SET GPIO_WriteBit(GPIOA, LCD_SCL,Bit_SET)
#define LCD_SDA_SET GPIO_WriteBit(GPIOA, LCD_SDA,Bit_SET)
#define LCD_CS_SET  GPIO_WriteBit(GPIOA, LCD_CS,Bit_SET)
#define LCD_RST_SET GPIO_WriteBit(GPIOA, LCD_RST,Bit_SET)


#define LCD_SCL_CLR GPIO_WriteBit(GPIOA, LCD_SCL,Bit_RESET)
#define LCD_SDA_CLR GPIO_WriteBit(GPIOA, LCD_SDA,Bit_RESET)
#define LCD_CS_CLR  GPIO_WriteBit(GPIOA, LCD_CS,Bit_RESET)
#define LCD_RST_CLR GPIO_WriteBit(GPIOA, LCD_RST,Bit_RESET)


void LCD_GPIO_Init(void);
void Lcd_WriteIndex(unsigned char Index);
void Lcd_WriteData(unsigned char Data);
void LCD_WriteData_16Bit(unsigned int Data);
void Lcd_Write_REG(unsigned char Index,unsigned char Data);
void LCD_Init(void);
void Lcd_Clear(unsigned int Color);
void FillRect(u16 x1, u16 y1, u16 x2, u16 y2, u16 color);

void Gui_DrawFont_GBK16(unsigned int x, unsigned int y, unsigned int fc, unsigned int bc, unsigned char *s);

#endif /* USER_HX8347_H_ */

HX8347.C

#include "debug.h"
#include "HX8347.h"
#include "font.h"

void LCD_GPIO_Init(void)
{
   GPIO_InitTypeDef GPIO_InitStructure = {0};

   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
#ifdef SPI_hardware
   GPIO_InitStructure.GPIO_Pin = LCD_CS|LCD_RST;
#else
   GPIO_InitStructure.GPIO_Pin = LCD_SCL|LCD_SDA|LCD_CS|LCD_RST;
#endif
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(GPIOA, &GPIO_InitStructure);
}


//向SPI总线传输一个8位数据

void  SPI_WriteData(unsigned char Data)
{
#ifdef SPI_hardware

     SPI_I2S_SendData( SPI1, Data);
     while( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) == RESET );
     Delay_Us(1);

#else
    unsigned char i=0;
    for(i=8;i>0;i--)
    {
      if(Data&0x80)
          LCD_SDA_SET; //输出数据
      else LCD_SDA_CLR;

      LCD_SCL_CLR;
      LCD_SCL_SET;
      Data<<=1;
    }
#endif
}

//向液晶屏写一个8位指令

void Lcd_WriteIndex(unsigned char Index)
{
   //SPI 写命令时序开始
   LCD_CS_CLR;
   SPI_WriteData(0x70);
   SPI_WriteData(Index);

   LCD_CS_SET;
}



//向液晶屏写一个8位数据

void Lcd_WriteData(unsigned char Data)
{
   LCD_CS_CLR;

   SPI_WriteData(0x72);
   SPI_WriteData(Data);

   LCD_CS_SET;
}


//向液晶屏写一个16位数据
void LCD_WriteData_16Bit(unsigned int Data)
{
   LCD_CS_CLR;
   SPI_WriteData(0x72);
  // printf( "%d", 0x72);
   SPI_WriteData(Data>>8);
   SPI_WriteData(Data);
   LCD_CS_SET;
}

void Lcd_Write_REG(unsigned char Index,unsigned char Data)
{
    Lcd_WriteIndex(Index);
    Lcd_WriteData(Data);
}

void Lcd_Reset(void)
{
    LCD_RST_CLR;
    Delay_Ms(50);
    LCD_RST_SET;
    Delay_Ms(50);
}





//  set region to paint
void LCD_SetWindow(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2)
{

    //SC
    Lcd_Write_REG(0x02,x1>>8);       // Column address start2
    Lcd_Write_REG(0x03,(u8)x1);          // Column address start1
    //EC
    Lcd_Write_REG(0x04,x2>>8);       // Column address end2
    Lcd_Write_REG(0x05,(u8)x2);          // Column address end1
    //SP
    Lcd_Write_REG(0x06,y1>>8);       // Row address start2
    Lcd_Write_REG(0x07,(u8)y1);          // Row address start1
    //EP
    Lcd_Write_REG(0x08,y2>>8);       // Row address end2
    Lcd_Write_REG(0x09,(u8)y2);          // Row address end1
    //写0x22到index register,那么下次send data就会直接被写到graphic ram
    Lcd_WriteIndex(0x22);
}



void FillRect(u16 x1, u16 y1, u16 x2, u16 y2, u16 color)
{
    LCD_SetWindow(x1, y1,x2,y2);
    x2 = x2 - x1 + 1;
    y2 = y2 - y1 + 1;

    LCD_CS_CLR;
    SPI_WriteData(0x72);

    for(x1 = x2; x1 != 0 ; x1--)
    {
        for (y1 = y2;y1 != 0 ;y1--)
        {
            SPI_WriteData(color>>8);
            SPI_WriteData(color);
        }
    }

    LCD_CS_SET;
}

void LCD_Init(void)
{
    //LCD_GPIO_Init();
    Lcd_Reset();

    Lcd_Write_REG(0x18,0x88);        //UADJ 75Hz
    Lcd_Write_REG(0x19,0x01);        //OSC_EN='1', start Osc
    //Power Voltage Setting
    Lcd_Write_REG(0x1B,0x1e); //VRH=4.60V   0x1e
    Lcd_Write_REG(0x1C,0x04); //AP Crosstalk    04
    Lcd_Write_REG(0x1A,0x01); //BT (VGH~15V,VGL~-10V,DDVDH~5V)   0x01
    Lcd_Write_REG(0x24,0x21); //VMH 27   0x38
    Lcd_Write_REG(0x25,0x5F); //VML




    //VCOM offset
    Lcd_Write_REG(0x23,0x8C); //for Flicker adjust
    Lcd_Write_REG(0x1F,0x88);// GAS=1, VOMG=00, PON=0, DK=1, XDK=0, DVDH_TRI=0, STB=0
    Delay_Ms(50);
    Lcd_Write_REG(0x1F,0x80);// GAS=1, VOMG=00, PON=0, DK=0, XDK=0, DVDH_TRI=0, STB=0
    Delay_Ms(50);
    Lcd_Write_REG(0x1F,0x90);// GAS=1, VOMG=00, PON=1, DK=0, XDK=0, DVDH_TRI=0, STB=0
    Delay_Ms(50);
    Lcd_Write_REG(0x1F,0xD0);// GAS=1, VOMG=10, PON=1, DK=0, XDK=0, DDVDH_TRI=0, STB=0
    Delay_Ms(50);
    //Display ON Setting
    Lcd_Write_REG(0x28,0x38);   //GON=1, DTE=1, D=1000
    Delay_Ms(50);
    Lcd_Write_REG(0x28,0x3C);   //GON=1, DTE=1, D=1100
    Lcd_Write_REG(0x36,0x09);   //REV, BGR
    Lcd_Write_REG(0x17,0x05);  //16BIT/PIXEL


    //Gamma 2.2 Setting
    Lcd_Write_REG(0x40,0x00); //
    Lcd_Write_REG(0x41,0x00); //
    Lcd_Write_REG(0x42,0x00); //
    Lcd_Write_REG(0x43,0x11); //
    Lcd_Write_REG(0x44,0x0e); //
    Lcd_Write_REG(0x45,0x23); //
    Lcd_Write_REG(0x46,0x08); //
    Lcd_Write_REG(0x47,0x53); //
    Lcd_Write_REG(0x48,0x03); //
    Lcd_Write_REG(0x49,0x11); //
    Lcd_Write_REG(0x4A,0x18); //
    Lcd_Write_REG(0x4B,0x1a); //
    Lcd_Write_REG(0x4C,0x16); //
    Lcd_Write_REG(0x50,0x1c); //
    Lcd_Write_REG(0x51,0x31); //
    Lcd_Write_REG(0x52,0x2e); //
    Lcd_Write_REG(0x53,0x3f); //
    Lcd_Write_REG(0x54,0x3f); //
    Lcd_Write_REG(0x55,0x3f); //
    Lcd_Write_REG(0x56,0x2c); //
    Lcd_Write_REG(0x57,0x77); //
    Lcd_Write_REG(0x58,0x09); //
    Lcd_Write_REG(0x59,0x05); //
    Lcd_Write_REG(0x5A,0x07); //
    Lcd_Write_REG(0x5B,0x0e); //
    Lcd_Write_REG(0x5C,0x1c); //
    Lcd_Write_REG(0x5D,0x88); //



     Delay_Ms(100);
     FillRect(0, 0, 239, 319, RED );
}


/*************************************************
函数名:LCD_DrawPoint
功能:画一个点
入口参数:无
返回值:无
*************************************************/
void Gui_DrawPoint(unsigned int x,unsigned int y,unsigned int Data)
{
    LCD_SetWindow(x,y,x+1,y+1);
    LCD_WriteData_16Bit(Data);
}


void Gui_DrawFont_GBK16(unsigned int x, unsigned int y, unsigned int fc, unsigned int bc, unsigned char *s)
{
    unsigned char i,j;
    unsigned short k,x0;
    x0=x;

    while(*s)
    {
        if((*s) < 128)
        {
            k=*s;
            if (k==13)
            {
                x=x0;
                y+=16;
            }
            else
            {
                if (k>32) k-=32; else k=0;

                for(i=0;i<16;i++)
                for(j=0;j<8;j++)
                    {
                        if(asc16[k*16+i]&(0x80>>j)) Gui_DrawPoint(x+j,y+i,fc);
                        else
                        {
                            if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc);
                        }
                    }
                x+=8;
            }
            s++;
        }

        else
        {


            for (k=0;k>j))  Gui_DrawPoint(x+j,y+i,fc);
                                else {
                                    if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc);
                                }
                            }
                        for(j=0;j<8;j++)
                            {
                                if(hz16[k].Msk[i*2+1]&(0x80>>j))    Gui_DrawPoint(x+j+8,y+i,fc);
                                else
                                {
                                    if (fc!=bc) Gui_DrawPoint(x+j+8,y+i,bc);
                                }
                            }
                    }
                }
              }
            s+=2;x+=16;
        }

    }
}

main.c

/********************************** (C) COPYRIGHT *******************************
* File Name          : main.c
* Author             : WCH
* Version            : V1.0.0
* Date               : 2021/06/06
* Description        : Main program body.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for 
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/

/*
 *@Note
 GPIO routine:
 PA0 push-pull output.

*/

#include "debug.h"
#include "HX8347.h"

/* Global define */


/* Global Variable */

/*********************************************************************
 * @fn      GPIO_Toggle_INIT
 *
 * @brief   Initializes GPIOA.0
 *
 * @return  none
 */
void GPIO_Toggle_INIT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}


void SPI_1Lines_HalfDuplex_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure={0};
    SPI_InitTypeDef SPI_InitStructure={0};


    RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI1|RCC_APB2Periph_GPIOA, ENABLE );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init( GPIOA, &GPIO_InitStructure );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init( GPIOA, &GPIO_InitStructure );

    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;         //在空闲状态下,时钟线保持高电平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;        //数据在时钟的上升沿被传输。
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init( SPI1, &SPI_InitStructure );

    SPI_Cmd( SPI1, ENABLE );
}


/*********************************************************************
 * @fn      main
 *
 * @brief   Main program.
 *
 * @return  none
 */
int main(void)
{
    u8 i = 0;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);	
    printf("SystemClk:%d\r\n", SystemCoreClock);
    printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );

    printf("GPIO Toggle TEST\r\n");
    GPIO_Toggle_INIT();

    LCD_GPIO_Init();
#ifdef SPI_hardware
    printf("SPI_HARDWARE");
    SPI_1Lines_HalfDuplex_Init();
#else
    printf("SPI_SOFT");
#endif
    LCD_Init();

    //

    GPIO_WriteBit(GPIOB, GPIO_Pin_13,Bit_RESET);
    //GPIO_WriteBit(GPIOB, GPIO_Pin_14,Bit_SET);
    //Lcd_Clear(0xf800);


    while(1)
    {

        Gui_DrawFont_GBK16(15,5,BLACK,GRAY0,"HELLO world");
        Gui_DrawFont_GBK16(15,25,RED,GRAY0,"LCD OK DISPLAY");
        Gui_DrawFont_GBK16(15,45,RED,GRAY0,"实验");

        Delay_Ms(1000);
        GPIO_WriteBit(GPIOB, GPIO_Pin_13, (i == 0) ? (i = Bit_SET) : (i = Bit_RESET));
        printf( "GPIOB:%d\r\n", i);


        FillRect(0, 0, 239, 319, RED );
        Delay_Ms(1000);
        FillRect(0, 0, 239, 319, GREEN );
        Delay_Ms(1000);
        FillRect(0, 0, 239, 319, BLUE );
        FillRect(50, 50, 100, 100, RED );
        Delay_Ms(2000);
        FillRect(0, 0, 239, 319, WHITE );


    }
}

二、总结

1、硬件SPI初始化:

void SPI_1Lines_HalfDuplex_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure={0};
    SPI_InitTypeDef SPI_InitStructure={0};


    RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI1|RCC_APB2Periph_GPIOA, ENABLE );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init( GPIOA, &GPIO_InitStructure );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init( GPIOA, &GPIO_InitStructure );

    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;         //在空闲状态下,时钟线保持高电平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;        //数据在时钟的上升沿被传输。
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init( SPI1, &SPI_InitStructure );

    SPI_Cmd( SPI1, ENABLE );
}

2、SPI写数据函数

void  SPI_WriteData(unsigned char Data)
{
#ifdef SPI_hardware

     SPI_I2S_SendData( SPI1, Data);
     while( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) == RESET );//等待发送完成
     Delay_Us(1);        //一定要延时,否则无显示

#else
    unsigned char i=0;
    for(i=8;i>0;i--)
    {
      if(Data&0x80)
          LCD_SDA_SET; //输出数据
      else LCD_SDA_CLR;

      LCD_SCL_CLR;
      LCD_SCL_SET;
      Data<<=1;
    }
#endif
}

把上面的Delay_Us(1)改成:

 uint32_t i;

     SysTick->SR &= ~(1 << 0);
     i = 8;

     SysTick->CMP = i;
     SysTick->CTLR |= (1 << 4);
     SysTick->CTLR |= (1 << 5) | (1 << 0);

     while((SysTick->SR & (1 << 0)) != (1 << 0))
         ;
     SysTick->CTLR &= ~(1 << 0);

1us是12个周期,上面改成8个周期延时,应该还可以提高一些帧数。 

3、把#define SPI_hardware注释掉,可以启动模拟SPI。

显示效果

你可能感兴趣的:(c语言,单片机,开发语言)