模块来源
采购链接: 0.91寸OLED显示屏12832液晶屏显串口屏iic接口屏i2cssd1306
资料下载链接: https://pan.baidu.com/s/1fKmD5lr4bA0WB54ukonI-A
资料提取码:1111
规格参数
工作电压:3~5V
工作电流:最大16mA
模块尺寸:12(H) x 38(V) MM
像素大小:128(H) x 32(V)
驱动芯片:SSD1306
通信协议:IIC
以上信息见厂家资料文件
移植过程
我们的目标是将例程移植至衡山派开发板上。已经为大家提供了完整的驱动代码,按照以下步骤,即可完成移植。
代码移植
这里选择的引脚见引脚接线表
下载为大家准备的驱动代码文件夹,复制到自己工程中\luban-lite\application\rt-thread\helloworld\user-bsp
文件夹下
提示
如果未找到 user-bsp
这个文件夹,说明你未进行模块移植的前置操作。请转移到手册使用必要操作(点击跳转)中进行必要的配置操作!!!
接下来打开自己的工程,开始修改Kconfig文件。
1、在 VSCode 中打开 application\rt-thread\helloworld\Kconfig 文件
2、在该文件的 #endif
前面添加该模块的 Kconfig路径语句
# 0.91寸彩屏
source "application/rt-thread/helloworld/user-bsp/0-91-color-screen/Kconfig"
2
添加完成之后:
menuconfig操作
1、我们 双击 luban-lite
文件夹下的 win_env.bat
脚本打开env工具:
2、输入以下命令列出所有可用的默认配置:
scons --list-def
3、选择 d13x_JLC_rt-thread_helloworld
这个配置!这个是我们衡山派开发板的默认配置!输入以下命令即可:
scons --apply-def=7
或者
scons --apply-def=d13x_JLC_rt-thread_helloworld_defconfig
这两个命令作用是一样的,一个是 文件名 ,一个是 编号 !!!
4、输入以下命令进入menuconfig菜单
scons --menuconfig
进入以下界面:
5、选中 Porting code using the LCKFB module
按
Y
选中按
N
取消选中方向键
左右
调整 最下面菜单的选项方向键
上下
调整 列表的选项
回车
执行最下面菜单的选项
6、回车进入 Porting code using the LCKFB module
菜单
7、按方向键 上下
选中 Use 0.91 inch color screen
后按 Y
键,看到前面括号中出现一个 *
号,就可以下一步了。
8、按方向键 左右
选中 <Save>
然后一路回车
,然后 退出
即可
编译
我们 保存并退出menuconfig菜单 之后,输入以下命令进行编译:
scons
或
scons -j16
-j 用来选择参与编译的核心数: 我这里是选择16
大家可以根据自己的电脑来选择
核心越多编译越快
如果写的数量高于电脑本身,那么就自动按照最高可用的来运行!
镜像烧录
编译完成之后会在 \luban-lite\output\d13x_JLC_rt-thread_helloworld\images
文件夹下生成一个 d13x_JLC_v1.0.0.img
镜像文件!
然后我们烧录镜像,具体的教程请查看:镜像烧录(点击跳转🚀)
到这里完成了,请移步到 最后一节 进行移植验证。
工程代码解析
oled.c
#include "oled.h"
#include "oledfont.h"
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <rtthread.h>
#include "rtdevice.h"
#include "aic_core.h"
#include "hal_i2c.h"
#define I2C_BUS_NAME "i2c0" /* I2C总线设备名称 */
#define OLED_ADDR (0x78 >> 1) /* 从机地址 0011 1100 原来是0x78 记得右移1位 */
static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C总线设备句柄 */
u8 OLED_GRAM[144][4];
//延时 ms级别
static void delay_ms(int ms)
{
aicos_mdelay(ms);
}
//反显函数
void OLED_ColorTurn(u8 i)
{
if(i==0)
{
OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
}
if(i==1)
{
OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
}
}
//屏幕旋转180度
void OLED_DisplayTurn(u8 i)
{
if(i==0)
{
OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
OLED_WR_Byte(0xA1,OLED_CMD);
}
if(i==1)
{
OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
OLED_WR_Byte(0xA0,OLED_CMD);
}
}
//发送一个字节
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{
rt_uint8_t buf[2];
struct rt_i2c_msg msgs;
if(mode)
{
buf[0] = 0x40;
buf[1] = dat;
msgs.addr = OLED_ADDR;
msgs.flags = RT_I2C_WR;
msgs.buf = buf;
msgs.len = 2;
}
else
{
buf[0] = 0x00;
buf[1] = dat;
msgs.addr = OLED_ADDR;
msgs.flags = RT_I2C_WR;
msgs.buf = buf;
msgs.len = 2;
}
/* 调用I2C设备接口传输数据 */
if (rt_i2c_transfer(i2c_bus, &msgs, 1) == 1)
{
return;
}
else
{
LOG_E("rt_i2c_transfer failure!!");
return;
}
}
//开启OLED显示
void OLED_DisPlay_On(void)
{
OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
}
//关闭OLED显示
void OLED_DisPlay_Off(void)
{
OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
OLED_WR_Byte(0xAE,OLED_CMD);//关闭屏幕
}
//更新显存到OLED
void OLED_Refresh(void)
{
u8 i,n;
for(i=0;i<4;i++)
{
OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
OLED_WR_Byte(0x00,OLED_CMD); //设置低列起始地址
OLED_WR_Byte(0x10,OLED_CMD); //设置高列起始地址
for(n=0;n<128;n++)
{
OLED_WR_Byte(OLED_GRAM[n][i], OLED_DATA);
}
}
}
//清屏函数
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<4;i++)
{
for(n=0;n<128;n++)
{
OLED_GRAM[n][i]=0;//清除所有数据
}
}
OLED_Refresh();//更新显示
}
//画点
//x:0~127
//y:0~63
//t:1 填充 0,清空
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
u8 i,m,n;
i=y/8;
m=y%8;
n=1<<m;
if(t){OLED_GRAM[x][i]|=n;}
else
{
OLED_GRAM[x][i]=~OLED_GRAM[x][i];
OLED_GRAM[x][i]|=n;
OLED_GRAM[x][i]=~OLED_GRAM[x][i];
}
}
//画线
//x1,y1:起点坐标
//x2,y2:结束坐标
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode)
{
u16 t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;//画线起点坐标
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if (delta_x==0)incx=0;//垂直线
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if (delta_y==0)incy=0;//水平线
else {incy=-1;delta_y=-delta_x;}
if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
else distance=delta_y;
for(t=0;t<distance+1;t++)
{
OLED_DrawPoint(uRow,uCol,mode);//画点
xerr+=delta_x;
yerr+=delta_y;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
//x,y:圆心坐标
//r:圆的半径
void OLED_DrawCircle(u8 x,u8 y,u8 r)
{
int a, b,num;
a = 0;
b = r;
while(2 * b * b >= r * r)
{
OLED_DrawPoint(x + a, y - b,1);
OLED_DrawPoint(x - a, y - b,1);
OLED_DrawPoint(x - a, y + b,1);
OLED_DrawPoint(x + a, y + b,1);
OLED_DrawPoint(x + b, y + a,1);
OLED_DrawPoint(x + b, y - a,1);
OLED_DrawPoint(x - b, y - a,1);
OLED_DrawPoint(x - b, y + a,1);
a++;
num = (a * a + b * b) - r*r;//计算画的点离圆心的距离
if(num > 0)
{
b--;
a--;
}
}
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size1:选择字体 6x8/6x12/8x16/12x24
//mode:0,反色显示;1,正常显示
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode)
{
u8 i,m,temp,size2,chr1;
u8 x0=x,y0=y;
if(size1==8)size2=6;
else size2=(size1/8+((size1%8)?1:0))*(size1/2); //得到字体一个字符对应点阵集所占的字节数
chr1=chr-' '; //计算偏移后的值
for(i=0;i<size2;i++)
{
if(size1==8)
{temp=asc2_0806[chr1][i];} //调用0806字体
else if(size1==12)
{temp=asc2_1206[chr1][i];} //调用1206字体
else if(size1==16)
{temp=asc2_1608[chr1][i];} //调用1608字体
else if(size1==24)
{temp=asc2_2412[chr1][i];} //调用2412字体
else return;
for(m=0;m<8;m++)
{
if(temp&0x01)OLED_DrawPoint(x,y,mode);
else OLED_DrawPoint(x,y,!mode);
temp>>=1;
y++;
}
x++;
if((size1!=8)&&((x-x0)==size1/2))
{x=x0;y0=y0+8;}
y=y0;
}
}
//显示字符串
//x,y:起点坐标
//size1:字体大小
//*chr:字符串起始地址
//mode:0,反色显示;1,正常显示
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode)
{
while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
{
OLED_ShowChar(x,y,*chr,size1,mode);
if(size1==8)x+=6;
else x+=size1/2;
chr++;
}
}
//m^n
u32 OLED_Pow(u8 m,u8 n)
{
u32 result=1;
while(n--)
{
result*=m;
}
return result;
}
//显示数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:0,反色显示;1,正常显示
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode)
{
u8 t,temp,m=0;
if(size1==8)m=2;
for(t=0;t<len;t++)
{
temp=(num/OLED_Pow(10,len-t-1))%10;
if(temp==0)
{
OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode);
}
else
{
OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode);
}
}
}
//显示汉字
//x,y:起点坐标
//num:汉字对应的序号
//mode:0,反色显示;1,正常显示
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode)
{
u8 m,temp;
u8 x0=x,y0=y;
u16 i,size3=(size1/8+((size1%8)?1:0))*size1; //得到字体一个字符对应点阵集所占的字节数
for(i=0;i<size3;i++)
{
if(size1==16)
{temp=Hzk1[num][i];}//调用16*16字体
else if(size1==24)
{temp=Hzk2[num][i];}//调用24*24字体
else if(size1==32)
{temp=Hzk3[num][i];}//调用32*32字体
else if(size1==64)
{temp=Hzk4[num][i];}//调用64*64字体
else return;
for(m=0;m<8;m++)
{
if(temp&0x01)OLED_DrawPoint(x,y,mode);
else OLED_DrawPoint(x,y,!mode);
temp>>=1;
y++;
}
x++;
if((x-x0)==size1)
{x=x0;y0=y0+8;}
y=y0;
}
}
//num 显示汉字的个数
//space 每一遍显示的间隔
//mode:0,反色显示;1,正常显示
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode)
{
u8 i,n,t=0,m=0,r;
while(1)
{
if(m==0)
{
OLED_ShowChinese(128,8,t,16,mode); //写入一个汉字保存在OLED_GRAM[][]数组中
t++;
}
if(t==num)
{
for(r=0;r<16*space;r++) //显示间隔
{
for(i=1;i<144;i++)
{
for(n=0;n<4;n++)
{
OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
}
}
OLED_Refresh();
}
t=0;
}
m++;
if(m==16){m=0;}
for(i=1;i<144;i++) //实现左移
{
for(n=0;n<4;n++)
{
OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
}
}
OLED_Refresh();
}
}
//x,y:起点坐标
//sizex,sizey,图片长宽
//BMP[]:要写入的图片数组
//mode:0,反色显示;1,正常显示
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode)
{
u16 j=0;
u8 i,n,temp,m;
u8 x0=x,y0=y;
sizey=sizey/8+((sizey%8)?1:0);
for(n=0;n<sizey;n++)
{
for(i=0;i<sizex;i++)
{
temp=BMP[j];
j++;
for(m=0;m<8;m++)
{
if(temp&0x01)OLED_DrawPoint(x,y,mode);
else OLED_DrawPoint(x,y,!mode);
temp>>=1;
y++;
}
x++;
if((x-x0)==sizex)
{
x=x0;
y0=y0+8;
}
y=y0;
}
}
}
//OLED的初始化
void OLED_Init(void)
{
/* 查找I2C总线设备,获取I2C总线设备句柄 */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(I2C_BUS_NAME);
if(i2c_bus==RT_NULL)
{
rt_kprintf("no device: %s\n",I2C_BUS_NAME);
}
else
{
rt_kprintf("find device: %s\n",I2C_BUS_NAME);
}
delay_ms(200);
OLED_WR_Byte(0xAE,OLED_CMD); /*display off*/
OLED_WR_Byte(0x00,OLED_CMD); /*set lower column address*/
OLED_WR_Byte(0x10,OLED_CMD); /*set higher column address*/
OLED_WR_Byte(0x00,OLED_CMD); /*set display start line*/
OLED_WR_Byte(0xB0,OLED_CMD); /*set page address*/
OLED_WR_Byte(0x81,OLED_CMD); /*contract control*/
OLED_WR_Byte(0xff,OLED_CMD); /*128*/
OLED_WR_Byte(0xA1,OLED_CMD); /*set segment remap*/
OLED_WR_Byte(0xA6,OLED_CMD); /*normal / reverse*/
OLED_WR_Byte(0xA8,OLED_CMD); /*multiplex ratio*/
OLED_WR_Byte(0x1F,OLED_CMD); /*duty = 1/32*/
OLED_WR_Byte(0xC8,OLED_CMD); /*Com scan direction*/
OLED_WR_Byte(0xD3,OLED_CMD); /*set display offset*/
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xD5,OLED_CMD); /*set osc division*/
OLED_WR_Byte(0x80,OLED_CMD);
OLED_WR_Byte(0xD9,OLED_CMD); /*set pre-charge period*/
OLED_WR_Byte(0x1f,OLED_CMD);
OLED_WR_Byte(0xDA,OLED_CMD); /*set COM pins*/
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xdb,OLED_CMD); /*set vcomh*/
OLED_WR_Byte(0x40,OLED_CMD);
OLED_WR_Byte(0x8d,OLED_CMD); /*set charge pump enable*/
OLED_WR_Byte(0x14,OLED_CMD);
OLED_Clear();
OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
}
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
oled.h
#ifndef __OLED_H
#define __OLED_H
#include "stdlib.h"
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <finsh.h>
#include <rtdevice.h>
#include <aic_core.h>
#include "aic_core.h"
#ifndef u8
#define u8 uint8_t
#endif
#ifndef u16
#define u16 uint16_t
#endif
#ifndef u32
#define u32 uint32_t
#endif
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
void OLED_ClearPoint(u8 x,u8 y);
void OLED_ColorTurn(u8 i);
void OLED_DisplayTurn(u8 i);
void OLED_WR_Byte(u8 dat,u8 mode);
void OLED_DisPlay_On(void);
void OLED_DisPlay_Off(void);
void OLED_Refresh(void);
void OLED_Clear(void);
void OLED_DrawPoint(u8 x,u8 y,u8 t);
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode);
void OLED_DrawCircle(u8 x,u8 y,u8 r);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode);
void OLED_ShowChar6x8(u8 x,u8 y,u8 chr,u8 mode);
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode);
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode);
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode);
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode);
void OLED_Init(void);
#endif
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
Kconfig
这个是一个menuconfig中的选项,如果在菜单中选中该选项,就会在rtconfig.h
中定义一个语句,用来if判断条件编译之类的。
config LCKFB_0_91_COLOR_SCREEN
bool "Use 0.91 inch color screen"
select AIC_USING_I2C0
default n
help
More information is available at: https://wiki.lckfb.com/
2
3
4
5
6
SConscript
自动化构建文件,如果定义了 LCKFB_0_91_COLOR_SCREEN
和 USING_LCKFB_TRANSPLANT_CODE
就自动编译当前目录下的文件!!
Import('RTT_ROOT')
Import('rtconfig')
import rtconfig
from building import *
cwd = GetCurrentDir()
CPPPATH = [cwd]
src = []
if GetDepend('LCKFB_0_91_COLOR_SCREEN') and GetDepend('USING_LCKFB_TRANSPLANT_CODE'):
src = Glob(os.path.join(cwd, '*.c'))
group = DefineGroup('lckfb-0-91-color-screen', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
test_0_91_color_screen.c
这个文件定义了一个用于控制0.91寸单色OLED屏幕显示的线程,并设置了线程的优先级、栈大小和时间片。以下是文件中定义的主要功能:
OLED_Init
函数:初始化OLED屏幕。OLED_Clear
函数:清除OLED屏幕显示。OLED_ShowPicture
函数:在OLED屏幕上显示图片。OLED_ShowNum
函数:在OLED屏幕上显示数字。OLED_Refresh
函数:刷新OLED屏幕以显示更新。aicos_mdelay
函数:实现毫秒级的延时。
线程入口函数 sreen_thread_entry
的逻辑如下:
- 初始化OLED屏幕并清除屏幕。
- 在一个无限循环中,显示一个固定的图片。
- 显示一个动态增加的数字,该数字会在达到99后重置为0。
- 每次循环结束时,线程会挂起一段时间,这里是500毫秒,并刷新屏幕显示更新。
test_0_91_color_screen
函数是主测试函数,它创建并启动OLED显示线程。如果线程创建失败,将不会打印错误信息,但是可以通过检查返回值来处理这种情况。
在代码的最后,MSH_CMD_EXPORT
宏导出了 test_0_91_color_screen
函数,使其可以通过命令行接口访问。这样用户就可以通过输入命令来启动OLED屏幕的测试线程。
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <finsh.h>
#include <rtdevice.h>
#include <aic_core.h>
#include <drv_qspi.h>
#include "aic_core.h"
#include "aic_hal_gpio.h"
#include "lcd_init.h"
#include "lcd.h"
#include "pic.h"
#define THREAD_PRIORITY 25 // 线程优先级
#define THREAD_STACK_SIZE 4096 // 线程大小
#define THREAD_TIMESLICE 10 // 时间片
#define QPSI_BUS_NANE "qspi3" // qspi总线名称
#define QPSI_DEVICE_NAME "lcd" // qspi设备lcd名称
static struct rt_qspi_device *qspi_device = RT_NULL; // qspi设备结构体
static rt_thread_t lcd_thread = RT_NULL; // 线程控制块
/**********************************************************
* 函 数 名 称:lcd_qspi_attach
* 函 数 功 能:挂载qspi的lcd设备
* 传 入 参 数:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:LP
**********************************************************/
static int lcd_qspi_attach(void)
{
// 为spi结构体申请一片空间
qspi_device = (struct rt_qspi_device *)rt_malloc(sizeof(struct rt_qspi_device));
if(RT_NULL == qspi_device)
{
LOG_E("Failed to malloc the qspi device.");
LOG_E("file: %s",__FILE__);
LOG_E("line: %d",__LINE__);
return -RT_ENOMEM;
}
rt_kprintf("malloc the qspi succeed.\r\n");
qspi_device->enter_qspi_mode = RT_NULL;
qspi_device->exit_qspi_mode = RT_NULL;
qspi_device->config.qspi_dl_width = 1;
int ret = rt_spi_bus_attach_device(&qspi_device->parent, QPSI_DEVICE_NAME, QPSI_BUS_NANE, RT_NULL);
if(ret != RT_EOK)
{
LOG_E("Failed to rt_spi_bus_attach_device.");
LOG_E("file: %s",__FILE__);
LOG_E("line: %d",__LINE__);
return ret;
}
return RT_EOK;
}
/**********************************************************
* 函 数 名 称:lcd_qspi_init
* 函 数 功 能:配置qspi进行初始化
* 传 入 参 数:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:LP
**********************************************************/
static int lcd_qspi_init(void)
{
struct rt_qspi_device *g_qspi = RT_NULL;
struct rt_qspi_configuration qspi_cfg;
struct rt_device *dev = RT_NULL;
char *name = RT_NULL;
int ret = 0;
name = QPSI_DEVICE_NAME;
g_qspi = (struct rt_qspi_device *)rt_device_find(name);
if (!g_qspi)
{
LOG_E("Failed to get device in name %s\n", name);
LOG_E("file: %s",__FILE__);
LOG_E("line: %d",__LINE__);
return -1;
}
dev = (struct rt_device *)g_qspi;
if (dev->type != RT_Device_Class_SPIDevice)
{
g_qspi = NULL;
LOG_E("%s is not SPI device.\n", name);
LOG_E("file: %s",__FILE__);
LOG_E("line: %d",__LINE__);
return -1;
}
rt_memset(&qspi_cfg, 0, sizeof(qspi_cfg));
qspi_cfg.qspi_dl_width = 1; // QSPI 总线位宽,单线模式 1 位、双线模式 2 位,4 线模式 4 位
qspi_cfg.parent.mode = RT_SPI_MASTER | RT_SPI_MODE_1 | RT_SPI_MSB;
qspi_cfg.parent.data_width = 8;
qspi_cfg.parent.max_hz = 20 * 1000 * 1000; // 20M
ret = rt_qspi_configure(g_qspi, &qspi_cfg);
if (ret < 0)
{
LOG_E("qspi configure failure.");
LOG_E("file: %s",__FILE__);
LOG_E("line: %d",__LINE__);
return ret;
}
return RT_EOK;
}
// 线程入口函数
static void lcd_thread_entry(void *param)
{
float t = 0;
LCD_Init();//屏幕初始化
rt_kprintf("[ LCD_Init ] succeed.\r\n");
LCD_Fill(0,0,LCD_W,LCD_H,BLACK);//清全屏为黑色
rt_kprintf("[ LCD_Fill ] succeed.\r\n");
while(1)
{
if(t >= 99.88)
t = 0;
LCD_ShowString(0,16*0,(uint8_t *)"LCD_W:",WHITE,BLACK,16,0);
LCD_ShowIntNum(48,16*0,LCD_W,3,WHITE,BLACK,16);
LCD_ShowString(80,16*0,(uint8_t *)"LCD_H:",WHITE,BLACK,16,0);
LCD_ShowIntNum(128,16*0,LCD_H,3,WHITE,BLACK,16);
LCD_ShowString(0,16*1,(uint8_t *)"Nun:",WHITE,BLACK,16,0);
LCD_ShowFloatNum1(8*4,16*1,t,4,WHITE,BLACK,16);
LCD_ShowChinese(8*5,16*3,(uint8_t *)"中电子",RED,BLACK,32,0);
t+=0.11;
rt_thread_mdelay(500);
}
}
/**********************************************************
* 函 数 名 称:test_lckfb_0_96_color_screen
* 函 数 功 能:0.96寸彩屏测试程序
* 传 入 参 数:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:LP
**********************************************************/
static int test_lckfb_0_96_color_screen(void)
{
int ret = 0;
ret = lcd_qspi_attach();
if(ret != RT_EOK)
{
LOG_E("[ lcd_qspi_attach ] failure.");
LOG_E("file: %s",__FILE__);
LOG_E("line: %d",__LINE__);
return RT_ERROR;
}
rt_kprintf("[ lcd_qspi_attach ] succeed.\r\n");
ret = lcd_qspi_init();
if(ret != RT_EOK)
{
LOG_E("[ lcd_qspi_init ] failure.");
LOG_E("file: %s",__FILE__);
LOG_E("line: %d",__LINE__);
return RT_ERROR;
}
rt_kprintf("[ lcd_qspi_init ] succeed.\r\n");
/* 创建线程,名称是 lcd_thread,入口是 lcd_thread_entry */
lcd_thread = rt_thread_create("lcd_thread",
lcd_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (lcd_thread != RT_NULL)
rt_thread_startup(lcd_thread);
}
MSH_CMD_EXPORT(test_lckfb_0_96_color_screen, color screen test);
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
移植验证
我们使用串口调试,将 USB转TTL模块 连接到衡山派开发板上面!!
具体的教程查看:串口调试(点击跳转🚀)
串口波特率默认为
115200
我们在输入下面的命令运行该模块的线程:
输入的时候按下
TAB键
会进行命令补全!!
test_0_96_iic_single_screen
模块上电效果:
【特别注意】
使用该模块需要在menuconfig菜单中取消掉一些选项:
这些是冲突的配置
选中选项按
N
取消!!
Application options --->
[*] LVGL (official): powerful and easy-to-use embedded GUI library ----
[*] ArtInChip LVGL demo ----
Board options --->
[*] Using DVP
2
3
4
5
6