0.96英寸I2C,OLED 显示屏显示二维码 STM32 SSD1306 RT-Thread
- 关于
- 软、硬件环境
- 开启RT-Thread的终端打印二维码功能
- 思路
- 移植开肝
- 开始测试
关于
最近手头上有个0.96英寸的 128*64得单色显示屏,突发想法能不能在上面显示二维码,手机扫描出来呢,这样的功能对于拥有这种显示屏的物联网设备肯定大有用处啊。
首先 百度一下,发现还真有人已经实现了这样的功能,CSDN上也有资源,但是这特么的积分也要的太狠吧,我这个穷逼买不起啊,开源给大家看看会屎么?MP啊。
网上找了些资料,发现有开源的库,发现一个库https://github.com/fukuchi/libqrencode 研究了下,表示看的不太明白,放弃。
突然想到 卧槽,我这个用的RT-Thread,不是有在终端打印出二维码的功能么。。。。。
卧槽。。。。。。什么都⑧说了,开干。
软、硬件环境
首先给大家说明下,我的环境,免得后续出现一系列问题。
- 主控MCU ,STM32F103VE;
- 显示屏 ,0.96 英寸 128*64 OLED显示屏,控制芯片是SSD1306,接口I2C
- 操作系统,众所周知,RT-Thread。
显示的驱动已经移植好了。只需要可以正常显示字符就可以了。
开启RT-Thread的终端打印二维码功能
在生成工程之前不要忘记 输入命令 pkgs --update
生成工程 scons --target=mdk5
打开工程,编译。
一切正常的情况下,可以看到下图
RT-Thread 的这个QrCode的库也是移植这个的https://github.com/ricmoo/QRCode
思路
从源码可以看到如下示例:
for (uint8 y = 0; y < qrcode.size; y++) {for (uint8 x = 0; x < qrcode.size; x++) {if (qrcode_getModule(&qrcode, x, y) {Serial.print("**");} else {Serial.print(" ");}}Serial.print("\n");
}
这个是打印到终端的示例程序,根据代码可以判断,原理是根据生成的点阵,判断点阵的x,y是否为1,然后在再终端打印**或者空格。
函数 qrcode_getModule(&qrcode, x, y)
,获取指定位置是否有点的,返回1则表示该位置有点,返回0则改位置无点。
那么问题来了,这个得到的二维码的点阵大小是多少?
到刚才那个github项目大佬主页,可以看到
原来这个点阵和代码里面,定义的#define DEFAULT_QR_VERSION 3
有关,例如定义为4,那么生成的点阵就是 33*33 个点。
那么很简单了,把这些点阵 填充到显示的缓冲,然后不就ojbk了,是不是?
好了开始肝了。
移植开肝
这里首先我们要搞清楚,这个屏幕显示的原理
屏幕的主控用的是SSD1306:手册
可以看到点阵的显示扫描方式是,从左到右(也可从右到左),从上到下扫描显示的。每个页纵向8个点,横向127个点。纵向每8个点表示一页,如果是128*64的话就是8页(0~7)。
搞清了显示的原理,那么特么只要填点阵就可以了。
初始化显示屏
OLED_WR_Byte(0xAE,OLED_CMD);//--display offOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_WR_Byte(0x40,OLED_CMD);//--set start line address OLED_WR_Byte(0xB0,OLED_CMD);//--set page addressOLED_WR_Byte(0x81,OLED_CMD); // contract controlOLED_WR_Byte(0xFF,OLED_CMD);//--128 OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverseOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 dutyOLED_WR_Byte(0xC8,OLED_CMD);//Com scan directionOLED_WR_Byte(0xD3,OLED_CMD);//-set display offsetOLED_WR_Byte(0x00,OLED_CMD);//OLED_WR_Byte(0xD5,OLED_CMD);//set osc divisionOLED_WR_Byte(0x80,OLED_CMD);//OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode offOLED_WR_Byte(0x05,OLED_CMD);//OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge PeriodOLED_WR_Byte(0xF1,OLED_CMD);//OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartionOLED_WR_Byte(0x12,OLED_CMD);//OLED_WR_Byte(0xDB,OLED_CMD);//set VcomhOLED_WR_Byte(0x30,OLED_CMD);//OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enableOLED_WR_Byte(0x14,OLED_CMD);//OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panelOLED_Clear();
声明一个显示的二位数组
#define DEFAULT_QR_VERSION 6
#define Xsize 4*DEFAULT_QR_VERSION+17
#define Ysize (4*DEFAULT_QR_VERSION+17)/8+1
uint8_t qr_code_buff[Ysize][Xsize]={0}; // 显示点阵缓冲
对于显示的点阵,大家可以看如下解释:
-------------------------------->X
| 0 0 2 3 4 . . . . . 127
| 0 x x x x x . . . . . x
| 1 x x x x x . . . . . x
| 2 x x x x x . . . . . x
| . x x x x x . . . . . x
| . x x x x x . . . . . x
| . x x x x x . . . . . x
Y . x x x x x . . . . . x. x x x x x . . . . . x7 x x x x x . . . . . x每个X位 表示OLED 的纵向8个点阵的16进制数据
先搞个函数,方便后面操作。下面这个函数可以对上面矩阵任意位置填充一位16进制数。
//根据x,y填充纵向8个点阵的数据
void OLED_Draw_Vertical_Byte(u8 x,u8 y, u8 data)
{OLED_Set_Pos(x,y);OLED_WR_Byte(data,OLED_DATA);}
ojbk!!!
继续下一步
搞个方法,通过生成的二维码的点阵,来对显示的缓存进行相应位置填充,这里大家注意一下细节:
首先 起个函数名,然后第二步确定好传入的参数,可以了。。。。。。。。。。
接下来就像下面这样。。。。。。。。
void OLED_Draw_QRCode(char *qrstr) //绘制 二维码图片
{QRCode qrc;uint8_t x, y,z, *qrcodeBytes = (uint8_t *)rt_calloc(1, qrcode_getBufferSize(DEFAULT_QR_VERSION));int8_t result;uint8_t num=0;if (qrcodeBytes){result = qrcode_initText(&qrc, qrcodeBytes, DEFAULT_QR_VERSION, ECC_LOW, qrstr);if (result >= 0){num=qrc.size/8;//扫描方式:先每次扫y轴8个点,再扫描整个x轴for(x=0;x<qrc.size;x++) //根据生产的二维码点阵 填充到显示的缓冲矩阵重 先计算到 二维码点阵y坐标的8的整数倍 { for(z=0;z<num;z++) { for(y=0;y<8;y++) //y 坐标每次8个点 {if (qrcode_getModule(&qrc, x, 8*z+y)){setbit(qr_code_buff[z][x],y); }else{clrbit(qr_code_buff[z][x],y); } } } } for(x=0;x<qrc.size;x++) //计算填充y轴剩下的 点阵{ for(y=0;y<qrc.size%8;y++){if (qrcode_getModule(&qrc, x, num*8+y)){setbit(qr_code_buff[num][x],y); }else{clrbit(qr_code_buff[num][x],y); } } } for(y=0;y<Ysize;y++){for(x=0;x<Xsize;x++){ OLED_Draw_Vertical_Byte(x,y,qr_code_buff[y][x]); //将计算好点阵 填到屏幕上}} rt_free(qrcodeBytes);}else{rt_kprintf("Warning: no memory!\n");}}
}
好了,到此应该问题不大。。。。。。。。。。。
ojbk!!!!!!!!!!!!!
开始测试
用RT-Thread 的MSH控制台搞个测试命令
static void qrcode(uint8_t argc, char **argv)
{#define DEFAULT_QR_STRING "HELLO WORLD"char *qrstr = DEFAULT_QR_STRING;if (argc > 1){qrstr = argv[1];} OLED_Draw_QRCode(qrstr);
}
MSH_CMD_EXPORT(qrcode, qrcode generator: qrcode [string]);
everthing is OK!!!!!!
放图看下显示效果
看看大家扫出了什么?