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