DMA
DMA 直接記憶體搬運技術,使資料不經過cpu,直接從記憶體搬運到spi的發送的寄存器裡面,這樣做的好處是減少cpu的負擔,而且能大大提升顯示屏的重新整理速率
使用spi直接驅動ST7789顯示屏
最開始我是用spi直接驅動 顯示螢幕,但我發現即使是使用spi的最大頻率發送資料,刷屏的速率依舊很慢
代碼:
#include "delay.h"
#include "sys.h"
#include "st7789.h"
int main(void)
{
delay_init(); //ÑÓʱº¯Êý³õʼ»¯
initlcd();
while(1){
fillScreen(0xf800);
//delay_us(100);
fillScreen(0);
// delay_us(100);
}
}
<st7789.h>
#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);
<st7789.c>
#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; //¸´ÓÃÍÆÍìÊä³ö
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搬運資料後重新整理速率有了明顯的提升
代碼
#include "delay.h"
#include "sys.h"
#include "st7789.h"
int main(void)
{
delay_init();
initlcd();
while(1){
fillScreen(0xf800);
fillScreen(0xffff);
}
}
<st7789.h>
#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);
<st7789.c>
#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); //ʹÄÜ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)//µÈ´ýͨµÀ4´«ÊäÍê³É
{
DMA_ClearFlag(DMA1_FLAG_TC3);//Çå³ýͨµÀ4´«ÊäÍê³É±êÖ¾
break;
}
}
}
}
結果
重新整理速率約一秒十多幀
連線
DC ------------ PB11
CLK----------- PA5
MISO--------- PA6
MOSI--------- PA7
CS------------ GND
引用
dma和spi部分參考正點原子代碼,示例使用的主要晶片是stm32f103