基于stm32 标准库spi驱动st7789(使用DMA)

DMA

DMA 直接内存搬运技术,使数据不经过cpu,直接从内存搬运到spi的发送的寄存器里面,这样做的好处是减少cpu的负担,而且能大大提升显示屏的刷新速率

使用spi直接驱动ST7789显示屏

最开始我是用spi直接驱动 显示屏幕,但我发现即使是使用spi的最大频率发送数据,刷屏的速率依旧很慢

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "delay.h"
#include "sys.h"
#include "st7789.h"
 int main(void)
 {
     delay_init();           //?óê±oˉêy3?ê??ˉ     
     initlcd();
     while(1){
     fillScreen(0xf800);
   //delay_us(100);
     fillScreen(0);
    // delay_us(100);
     }
}

1
2
3
4
5
6
7
8
9
#include "sys.h"
#define DC    PBout(11) //DC     
void initlcd();
void writeData(u8 data);
void writeCommand(u8 data);
void fillScreen(u16 color);
void SPI1_Init(void);
void SPI1_SetSpeed(u8 SpeedSet);  
u8 SPI1_ReadWriteByte(u8 TxData);

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "st7789.h"
#include "delay.h"

SPI_InitTypeDef  SPI_InitStructure;
//spi1的初始化
void SPI1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
   
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //?′ó?í?íìê?3?
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       //
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;       //
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;     //
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    //
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;      
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //
    SPI_InitStructure.SPI_CRCPolynomial = 7;    //
    SPI_Init(SPI1, &SPI_InitStructure);  //
 
    SPI_Cmd(SPI1, ENABLE); //
   
    SPI1_ReadWriteByte(0xff);//  
}  

 //设置spi的传输速率
void SPI1_SetSpeed(u8 SpeedSet)
{
    SPI_InitStructure.SPI_BaudRatePrescaler = SpeedSet ;
    SPI_Init(SPI1, &SPI_InitStructure);
    SPI_Cmd(SPI1,ENABLE);
}
//spi 读写
u8 SPI1_ReadWriteByte(u8 TxData)
{      
    u8 retry=0;                
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //
        {
        retry++;
        if(retry>200)return 0;
        }            
    SPI_I2S_SendData(SPI1, TxData);
    retry=0;

    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//
        {
        retry++;
        if(retry>200)return 0;
        }                              
    return SPI_I2S_ReceiveData(SPI1); //                   
}

//初始化显示屏
void initlcd(){
    RCC->APB2ENR|=1<<3;//时钟使能                            
    GPIOB->CRH&=0XFFFF0FFF;//设置为输出模式
    GPIOB->CRH|=0X00003000;
    GPIOB->ODR|=1<11;     //dc high

    SPI1_Init();
    SPI1_SetSpeed(SPI_BaudRatePrescaler_2);
   
   
    writeCommand(0x01);
    delay_us(150);
    writeCommand(0x11);
    delay_us(120);
    writeCommand(0x3A);
    writeData(0x55);
    writeCommand(0x36);
    writeData(0x00);
    writeCommand(0x21);
    writeCommand(0x13);
    writeCommand(0x29);
   
}
void writeData(u8 data){
   DC = 1;
   SPI1_ReadWriteByte(data);
   
}

void writeCommand(u8 cmd){
     DC = 0;
   SPI1_ReadWriteByte(cmd);
}

void fillScreen(u16 color){
  u16 i ,j;
  writeCommand(0x2A);
  writeData(0);
  writeData(0);
  writeData(0);
  writeData(240);
  writeCommand(0X2B);
  writeData(0);
  writeData(0);
  writeData(0X01);
  writeData(0X40);
  writeCommand(0X2C);
  for(i = 0 ; i<240 ; i++){
      for(j = 0 ; j<320 ; j++){
           writeData(color>>8);  
          writeData(color);        
    }
    }
}

结果

刷新频率约 1秒3帧
在这里插入图片描述

使用spi加dma驱动ST7789显示屏

在使dma搬运数据后刷新速率有了明显的提升

代码

1
2
3
4
5
6
7
8
9
10
11
12
#include "delay.h"
#include "sys.h"
#include "st7789.h"      
 int main(void)
 {
     delay_init();               
     initlcd();
     while(1){
       fillScreen(0xf800);
       fillScreen(0xffff);
     }
}

1
2
3
4
5
6
7
8
9
10
11
#include "sys.h"     
#define DC    PBout(11) //DC     
void initlcd();
void writeData(u8 data);
void writeCommand(u8 data);
void fillScreen(u16 color);
void SPI1_Init(void);  
void SPI1_SetSpeed(u8 SpeedSet);  
u8 SPI1_ReadWriteByte(u8 TxData);
void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include "st7789.h"
#include "delay.h"
#include "sys.h"
u8 SendBuff[480];
DMA_InitTypeDef DMA_InitStructure;
u16 DMA1_MEM_LEN;    
//配置dma
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  //ê1?üDMA′?ê?
   
    DMA_DeInit(DMA_CHx);   //将dma1的某通道
    DMA1_MEM_LEN=cndtr;
    DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //dma 要搬运到的外设地址
    DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //dma要搬运的内存的地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //搬运方向, 从内存到外设
    DMA_InitStructure.DMA_BufferSize = cndtr; //要搬运的内存的大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //  传输过程中外设的基地址不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //传输过程中内存地址递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为八位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//数据宽度为八位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //正常传输模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级设置
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //没有内存到内存的传输
    DMA_Init(DMA_CHx, &DMA_InitStructure);  //
       
}
//使能dma1的通道3,因为spi输出对应的是此通道
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
    DMA_Cmd(DMA_CHx, DISABLE );    
    DMA_SetCurrDataCounter(DMA1_Channel3,DMA1_MEM_LEN);
    DMA_Cmd(DMA_CHx, ENABLE);  
}    

 


SPI_InitTypeDef  SPI_InitStructure;
//spi1的初始化
void SPI1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
 
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;      
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;      
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;    
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;   
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;      
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;   
    SPI_Init(SPI1, &SPI_InitStructure); ′??÷
 
    SPI_Cmd(SPI1, ENABLE);
   
    SPI1_ReadWriteByte(0xff);    
}  
 
void SPI1_SetSpeed(u8 SpeedSet)
{
    SPI_InitStructure.SPI_BaudRatePrescaler = SpeedSet ;
    SPI_Init(SPI1, &SPI_InitStructure);
    SPI_Cmd(SPI1,ENABLE);
}


u8 SPI1_ReadWriteByte(u8 TxData)
{      
    u8 retry=0;                
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //
        {
        retry++;
        if(retry>200)return 0;
        }            
    SPI_I2S_SendData(SPI1, TxData);
    retry=0;

    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//
        {
        retry++;
        if(retry>200)return 0;
        }                              
    return SPI_I2S_ReceiveData(SPI1);                  
}


void initlcd(){

    RCC->APB2ENR|=1<<3;//?????IO PORTC??                             
    GPIOB->CRH&=0XFFFF0FFF;//PC11/12
    GPIOB->CRH|=0X00003000;
    GPIOB->ODR|=1<11;     //PC11,12 ???
 
  SPI1_Init();
    SPI1_SetSpeed(SPI_BaudRatePrescaler_2);
    //配置dma
    MYDMA_Config(DMA1_Channel3,(u32)&SPI1->DR,(u32)SendBuff,480);
   
  writeCommand(0x01);
    delay_us(150);
    writeCommand(0x11);
    delay_us(120);
    writeCommand(0x3A);
    writeData(0x55);
    writeCommand(0x36);
    writeData(0x00);
    writeCommand(0x21);
    writeCommand(0x13);
    writeCommand(0x29);
   
}
void writeData(u8 data){
   DC = 1;
   SPI1_ReadWriteByte(data);
   
}

void writeCommand(u8 cmd){
     DC = 0;
   SPI1_ReadWriteByte(cmd);
}

void fillScreen(u16 color){
    u16 i ,j;
    //DC = 0;
  writeCommand(0x2A);
  writeData(0);
  writeData(0);
    writeData(0);
    writeData(240);
  writeCommand(0X2B);
  writeData(0);
  writeData(0);
    writeData(0X01);
    writeData(0X40);
  writeCommand(0X2C);
    DC = 1;
    for(j=0 ;j<480;){
      SendBuff[j] = color>>8;
          SendBuff[j+1] = color;
          j += 2;
    }

  for(i = 0 ; i<320 ; i++){
       
          SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx,ENABLE); //????1?DMA??
          MYDMA_Enable(DMA1_Channel3);
       
  while(1){
        if(DMA_GetFlagStatus(DMA1_FLAG_TC3)!=RESET)//μè′yí¨μà4′?ê?íê3é
                {
                    DMA_ClearFlag(DMA1_FLAG_TC3);//??3yí¨μà4′?ê?íê3é±ê??
                    break;
                }
                    }          
   
    }

}

结果

在这里插入图片描述
刷新速率约一秒十多帧

连线

DC ------------ PB11
CLK----------- PA5
MISO--------- PA6
MOSI--------- PA7
CS------------ GND

引用

dma和spi部分参考正点原子代码,示例使用的主控芯片是stm32f103