书接上文:
最近在研究用低速、低RAM的单片机来驱动小LCD或TFT彩屏实现动画效果
首先我用一个16MHz晶振的m0内核的8位单片机nRF51822尝试驱动一个1.77寸的4线SPI屏(128X160),
发现,刷一屏大约要0.8s左右的时间,
具体收录在《1、一个简单的nRF51822驱动的天马4线SPI-1.77寸LCD彩屏DEMO》中
觉得,如果用72MHz的STM32也许效果会好很多
于是在stm32上做了个类似的版本,
具体收录在《一个简单的stm32vet6驱动的天马4线SPI-1.77寸LCD彩屏DEMO》中
发现刷一屏0.2s左右,
效果是有的,但是还不能达到支持播放流畅动画的效果!
于是,决定将串行数据改成并行数据传输
本节将带来一个用stm32驱动的2.4寸240X320的8位并口tft屏的刷屏效果
工程结构
main.c
1 /* Includes ------------------------------------------------------------------*/ 2 #include "stm32f10x.h" 3 #include "LCD2.h" 4 5 6 void RCC_Configuration(void); 7 /**************************************************************************** 8 * 名 称:int main(void) 9 * 功 能:主函数 10 * 入口参数:无 11 * 出口参数:无 12 * 说 明: 13 * 调用方法:无 14 ****************************************************************************/ 15 int main(void) 16 { 17 RCC_Configuration(); //系统时钟配置 18 LCD2_GPIO_Init(); 19 LCD2_Init(); 20 while (1) 21 { 22 Show_RGB(0,240,0,320,0xff0f); 23 DELAY_MS(1000); 24 Show_RGB(0,240,0,320,0x00fe); 25 DELAY_MS(1000); 26 } 27 } 28 29 /**************************************************************************** 30 * 名 称:void RCC_Configuration(void) 31 * 功 能:系统时钟配置为72MHZ 32 * 入口参数:无 33 * 出口参数:无 34 * 说 明: 35 * 调用方法:无 36 ****************************************************************************/ 37 void RCC_Configuration(void) 38 { 39 SystemInit(); 40 }
LCD2.c
1 #include "LCD2.h" 2 3 4 5 void LCD2_GPIO_Init() 6 { 7 GPIO_InitTypeDef GPIO_InitStructure; 8 9 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE); 10 11 12 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; 13 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 14 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //口线翻转速度为50MHz 15 GPIO_Init(GPIOB, &GPIO_InitStructure); 16 17 //8位数据输出 18 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; 19 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 20 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //口线翻转速度为50MHz 21 GPIO_Init(GPIOD, &GPIO_InitStructure); 22 } 23 24 ////////////////////////////////////////////////////////////////// 25 //最底层数据传输函数 26 ////////////////////////////////////////////////////////////////// 27 //写命令 28 void Write_Cmd(unsigned char DH,unsigned char DL) 29 { 30 LCD2_CS=0; 31 LCD2_RS=0; 32 33 DataPort=DH; 34 LCD2_RW=0; 35 LCD2_RW=1; 36 37 DataPort=DL; 38 39 LCD2_RW=0; 40 LCD2_RW=1; 41 LCD2_CS=1; 42 } 43 //写数据 双8位 44 void Write_Data(unsigned char DH,unsigned char DL) 45 { 46 LCD2_CS=0; 47 48 LCD2_RS=1; 49 DataPort=DH; 50 LCD2_RW=0; 51 LCD2_RW=1; 52 53 DataPort=DL; 54 LCD2_RW=0; 55 LCD2_RW=1; 56 LCD2_CS=1; 57 } 58 59 //写数据 双8位 60 void Write_Data2(unsigned char DH,unsigned char DL) 61 { 62 DataPort=DH; 63 LCD2_RW=0; 64 LCD2_RW=1; 65 66 DataPort=DL; 67 LCD2_RW=0; 68 LCD2_RW=1; 69 } 70 71 ////////////////////////////////////////////////////////////////// 72 //调用上面最底层实现稍高层写命令和数据函数 73 ////////////////////////////////////////////////////////////////// 74 /*---------------------------------------------------------------- 75 写命令、写数据 76 输入参数:x 需要输入的命令 16位 77 y 需要输入的数据 16位 78 ----------------------------------------------------------------*/ 79 void Write_Cmd_Data (unsigned char x,unsigned int y) 80 { 81 unsigned char m,n; 82 m=y>>8; 83 n=y; 84 Write_Cmd(0x00,x); 85 Write_Data(m,n); 86 } 87 /*---------------------------------------------------------------- 88 写16位数据 89 ----------------------------------------------------------------*/ 90 void Write_Data_U16(unsigned int y) 91 { 92 unsigned char m,n; 93 m=y>>8; 94 n=y; 95 Write_Data2(m,n); 96 } 97 98 /*---------------------------------------------------------------- 99 液晶初始化 100 ----------------------------------------------------------------*/ 101 void LCD2_Init(void) 102 { 103 LCD2_CS=1; 104 DELAY_MS(5); 105 LCD2_RES=0; 106 DELAY_MS(5); 107 LCD2_RES=1; 108 DELAY_MS(50); 109 Write_Cmd_Data(0x0001,0x0100); 110 Write_Cmd_Data(0x0002,0x0700); 111 Write_Cmd_Data(0x0003,0x1030); 112 Write_Cmd_Data(0x0004,0x0000); 113 Write_Cmd_Data(0x0008,0x0207); 114 Write_Cmd_Data(0x0009,0x0000); 115 Write_Cmd_Data(0x000A,0x0000); 116 Write_Cmd_Data(0x000C,0x0000); 117 Write_Cmd_Data(0x000D,0x0000); 118 Write_Cmd_Data(0x000F,0x0000); 119 //power on sequence VGHVGL 120 Write_Cmd_Data(0x0010,0x0000); 121 Write_Cmd_Data(0x0011,0x0007); 122 Write_Cmd_Data(0x0012,0x0000); 123 Write_Cmd_Data(0x0013,0x0000); 124 //vgh 125 Write_Cmd_Data(0x0010,0x1290); 126 Write_Cmd_Data(0x0011,0x0227); 127 //DELAY_MS(100); 128 //vregiout 129 Write_Cmd_Data(0x0012,0x001d); //0x001b 130 //DELAY_MS(100); 131 //vom amplitude 132 Write_Cmd_Data(0x0013,0x1500); 133 //DELAY_MS(100); 134 //vom H 135 Write_Cmd_Data(0x0029,0x0018); 136 Write_Cmd_Data(0x002B,0x000D); 137 138 //gamma 139 Write_Cmd_Data(0x0030,0x0004); 140 Write_Cmd_Data(0x0031,0x0307); 141 Write_Cmd_Data(0x0032,0x0002);// 0006 142 Write_Cmd_Data(0x0035,0x0206); 143 Write_Cmd_Data(0x0036,0x0408); 144 Write_Cmd_Data(0x0037,0x0507); 145 Write_Cmd_Data(0x0038,0x0204);//0200 146 Write_Cmd_Data(0x0039,0x0707); 147 Write_Cmd_Data(0x003C,0x0405);// 0504 148 Write_Cmd_Data(0x003D,0x0F02); 149 //ram 150 Write_Cmd_Data(0x0050,0x0000); 151 Write_Cmd_Data(0x0051,0x00EF); 152 Write_Cmd_Data(0x0052,0x0000); 153 Write_Cmd_Data(0x0053,0x013F); 154 Write_Cmd_Data(0x0060,0xA700); 155 Write_Cmd_Data(0x0061,0x0001); 156 Write_Cmd_Data(0x006A,0x0000); 157 // 158 Write_Cmd_Data(0x0080,0x0000); 159 Write_Cmd_Data(0x0081,0x0000); 160 Write_Cmd_Data(0x0082,0x0000); 161 Write_Cmd_Data(0x0083,0x0000); 162 Write_Cmd_Data(0x0084,0x0000); 163 Write_Cmd_Data(0x0085,0x0000); 164 // 165 Write_Cmd_Data(0x0090,0x0010); 166 Write_Cmd_Data(0x0092,0x0600); 167 Write_Cmd_Data(0x0093,0x0003); 168 Write_Cmd_Data(0x0095,0x0110); 169 Write_Cmd_Data(0x0097,0x0000); 170 Write_Cmd_Data(0x0098,0x0000); 171 Write_Cmd_Data(0x0007,0x0133); 172 173 // Write_Cmd_Data(0x0022);// 174 } 175 176 /*---------------------------------------------------------------- 177 设置坐标 178 ----------------------------------------------------------------*/ 179 /*---------------------------------------------------------------- 180 全局变量 181 ----------------------------------------------------------------*/ 182 #define WINDOW_XADDR_START 0x0050 // Horizontal Start Address Set 183 #define WINDOW_XADDR_END 0x0051 // Horizontal End Address Set 184 #define WINDOW_YADDR_START 0x0052 // Vertical Start Address Set 185 #define WINDOW_YADDR_END 0x0053 // Vertical End Address Set 186 #define GRAM_XADDR 0x0020 // GRAM Horizontal Address Set 187 #define GRAM_YADDR 0x0021 // GRAM Vertical Address Set 188 #define GRAMWR 0x0022 // memory write 189 void LCD_SetPos(unsigned int x0,unsigned int x1,unsigned int y0,unsigned int y1) 190 { 191 Write_Cmd_Data(WINDOW_XADDR_START,x0); 192 Write_Cmd_Data(WINDOW_XADDR_END,x1); 193 Write_Cmd_Data(WINDOW_YADDR_START,y0); 194 Write_Cmd_Data(WINDOW_YADDR_END,y1); 195 Write_Cmd_Data(GRAM_XADDR,x0); 196 Write_Cmd_Data(GRAM_YADDR,y0); 197 Write_Cmd (0x00,0x22);//LCD_WriteCMD(GRAMWR); 198 } 199 200 /*---------------------------------------------------------------- 201 显示RGB颜色 202 输入参数:x0,y0 起始坐标 203 x1,y1 结束坐标 204 Color 背景颜色 205 ----------------------------------------------------------------*/ 206 void Show_RGB (unsigned int x0,unsigned int x1,unsigned int y0,unsigned int y1,unsigned int Color) 207 { 208 unsigned int i,j; 209 LCD_SetPos(x0,x1,y0,y1); 210 LCD2_CS=0; 211 LCD2_RS=1; 212 // for (i=y0;i<=y1;i++) 213 // { 214 // for (j=x0;j<=x1;j++) 215 // Write_Data_U16(Color); 216 // } 217 218 for (i=0;i<=(y1-y0+1)*(x1-x0+1);i+=32) 219 { 220 Write_Data_U16(Color); 221 Write_Data_U16(Color); 222 Write_Data_U16(Color); 223 Write_Data_U16(Color); 224 Write_Data_U16(Color); 225 Write_Data_U16(Color); 226 Write_Data_U16(Color); 227 Write_Data_U16(Color); 228 Write_Data_U16(Color); 229 Write_Data_U16(Color); 230 Write_Data_U16(Color); 231 Write_Data_U16(Color); 232 Write_Data_U16(Color); 233 Write_Data_U16(Color); 234 Write_Data_U16(Color); 235 Write_Data_U16(Color); 236 Write_Data_U16(Color); 237 Write_Data_U16(Color); 238 Write_Data_U16(Color); 239 Write_Data_U16(Color); 240 Write_Data_U16(Color); 241 Write_Data_U16(Color); 242 Write_Data_U16(Color); 243 Write_Data_U16(Color); 244 Write_Data_U16(Color); 245 Write_Data_U16(Color); 246 Write_Data_U16(Color); 247 Write_Data_U16(Color); 248 Write_Data_U16(Color); 249 Write_Data_U16(Color); 250 Write_Data_U16(Color); 251 Write_Data_U16(Color); 252 } 253 LCD2_CS=1; 254 } 255 256 257 void Delay_ms(u16 time) 258 { 259 u16 i=0; 260 while(time--) 261 { 262 i=12000; 263 while(i--); 264 } 265 }
注:代码比较容易理解,不做详解
另外补上这三个小实验的连线图:
1、这个是本节的并行接口与屏幕的连接方式:
2、这是上两节串行接口的连线,上面对应的引脚连接是与nRF51822的(第一次试验),下面对应的连接是与stm32的(第二次试验)
小结
从效果图上看,即使采用stm32的8位并行来驱动屏幕速度还是达不到刷新动画的效果~
之后我也在传输数据的函数上做了些优化,可效果还是不明显——
如第一点:优化前RS等引脚的定义要通过宏展开,每次计算BitBand后面的式子~
1 #define BitBand(Addr, Bit) *((volatile int*)(((int)(Addr) & 0x60000000) + 0x02000000 + (int)(Addr) * 0x20 + (Bit) * 4)) 2 #define LCD2_CS BitBand(&GPIOB->ODR, 8) 3 #define LCD2_RES BitBand(&GPIOB->ODR, 9) 4 #define LCD2_RS BitBand(&GPIOB->ODR, 7) 5 #define LCD2_RW BitBand(&GPIOB->ODR, 6) 6 #define DataPort GPIOD->ODR
优化后采用直接把值赋给对应的引脚来减少运算量
1 #define BitBand(Addr, Bit) *((volatile int*)(((int)(Addr) & 0x60000000) + 0x02000000 + (int)(Addr) * 0x20 + (Bit) * 4)) 2 #define LCD2_CS (*((volatile int*)0x422181A0)) //BitBand(&GPIOB->ODR, 8) 3 #define LCD2_RES (*((volatile int*)0x422181A4)) //BitBand(&GPIOB->ODR, 9) 4 #define LCD2_RS (*((volatile int*)0x4221819C)) //BitBand(&GPIOB->ODR, 7) 5 #define LCD2_RW (*((volatile int*)0x42218198)) //BitBand(&GPIOB->ODR, 6) 6 #define DataPort (*((volatile int*)0x4001140C)) //GPIOD->ODR
如第二点:为了减少Show_RGB函数中循环中的Write_Data_U16的调用,直接将Write_Data_U16计算拆到最细放到循环内
如第三点:为了排除Write_Data_U16中4、5两行移位运算和类型转换所带来的时间花销,直接采用上图中全局变量color1、color2来直接赋值,查看效果有没有提升~
1 void Write_Data_U16(unsigned int y) 2 { 3 unsigned char m,n; 4 m=y>>8; 5 n=y; 6 Write_Data2(m,n); 7 }
本篇中资源链接:http://pan.baidu.com/s/1bnjw1Fh
注:其中未优化版工程比较简洁方便理解学习,优化测试版是为了提升传输速率做的几点优化(效果不大,代码稍乱)
@beautifulzzzz
2015-11-28 持续更新中~