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