众所周知,墨水屏卡片是最近非常火的一个词。那么墨水屏卡片到底是什么梗呢?相信大家对墨水屏卡片都很熟悉,墨水屏卡片就是我们每天都会经常遇到的,但是墨水屏卡片是怎么回事呢?下面就让小编带大家一起了解一下墨水屏卡片是怎么回事吧。 其实之前并没有太多人知道墨水屏卡片,但是最近由于热度的上升,墨水屏卡片受到了大家的关注。大家可能会感到很惊讶,墨水屏卡片为什么是这样的?墨水屏卡片究竟为什么火起来了呢?但事实就是这样,小编也感到非常惊讶。 希望小编精心整理的这篇内容能够帮助到你,本期教学结束了,如果大家喜欢,可以点赞表示对小编的支持。欢迎在下方评论和小编一起讨论喔~
咳咳,,,
演示视频:[https://www.bilibili.com/video/BV1MT4y1f7yF](https://www.bilibili.com/video/BV1MT4y1f7yF)
# 为什么有这个项目
![屏幕截图 2021-09-29 142238.png](//image.lceda.cn/pullimage/uTSTQexHSAnnhSs1FUFKrapNE7M9RpcFKGGKnNm7.png)
很久之前就很留意野生钢铁侠的这个NFC名片项目了([视频链接](https://www.bilibili.com/video/BV1Cf4y1y7KT?from=search&seid=9538469412435783180&spm_id_from=333.337.0.0)),可惜一直没有足够的知识储备已经原材料,项目一直停留在新建文件夹这一步(说白了就是懒~)
然后,几天前在老王家买来了一些还不错的墨水屏,成色如图,感觉还可以,买了6片亮了3片,投资成功率五五开:
![image.png](//image.lceda.cn/pullimage/iCZaj2rblBoXry6uYRBknLhIi8oIxDYMry6WG65x.png)
驱动板在开源平台找的,作为学习平台。
## 异同
原本打算是实现稚晖君的NFC卡片的100%功能的:模拟门禁卡,NFC上传数据等。可惜NFC传输数据实在找不到好的学习资料(有相关资料的大佬欢迎在评论区留言,共同学习,感谢!),NFC功能是实现不了了,这可咋整,怎么上传图片数据?
几番思考后,决定还是做一款不同于稚晖君的墨水屏卡片,放弃NFC方案,使用单片机虚拟U盘,将要显示的图片放入U盘内(FLASH),单片机读取bmp图像数据并显示在墨水屏上。
## 电路板设计
确定了需求,那么电路图也是胸有成竹了。
### 单片机
驱动墨水屏和FLASH都需要SPI,我这里用了两路SPI,为了防止数据传输有干扰,后面发现其实可以在编程上稍加注意,两路数据传输不会干扰,所以大家也可以只使用一路SPI,用两个CS脚分别控制就行,不再赘述:
![image.png](//image.lceda.cn/pullimage/QcF3fXCOSVafyyBLUvlw0I0NWjbc4Lf5AjmEI5Dd.png)
### 墨水屏
墨水屏驱动电路参考[这个链接](https://oshwhub.com/ludas/mo-shui-ping-qu-dong),感谢大佬!
![image.png](//image.lceda.cn/pullimage/a3Ic5gBTBXSHK32dpcSJobnRs9sblrGBZs7ceXOv.png)
这个方案还是比较成熟的。
### 电源
不同于稚晖君的纽扣电池,我这里选择了锂电池供电方案,搭配IP2312充电芯片,效果还是非常的棒。给U盘传输数据同时还可以给电池充电:
![image.png](//image.lceda.cn/pullimage/tkve9uQ8zxD6hGc5Ci25sA4X8z5UEl9sgQA996cV.png)
### FLASH
不赘述,可以参考我上一个开源工程:功率计[power-X](https://oshwhub.com/ZERO--0/usb-gong-shuai-ji_copy_copy_copy_copy_copy_copy_copy)
### 其他
其他LED,按键什么的不赘述。
## 焊接 组装
用solidworks设计了一个外壳,还是非常的nice,三围尺寸比稚晖君的要小:
![image.png](//image.lceda.cn/pullimage/BnetxWQzqkT5zTqj9f0Gq5M1hUnzot98dAIGdD6R.png)
电路板图:
![image.png](//image.lceda.cn/pullimage/dsMdm2HBhK7m2MNQ6FManXBPu7IGOic1RNvab0mH.png)
![image.png](//image.lceda.cn/pullimage/sdsqSslCDQcI0SyvyAlg7Ba3Pf0Sw7vTq3ongfwR.png)
组装好后:
![image.png](//image.lceda.cn/pullimage/Ro8GF8dcDO9mkQY3yPenwXAmGsKamaWNwSyGIRAF.png)
## 程序编写
烧录文件和最关键的bmp读取文件我放在附件,有需自取,欢迎纠错、指正!
下面稍微讲解一哈:
首先我们要了解bmp的格式,最关键就是:8位图用1个bytes存储一个像素,表示灰度值;24位图用3个bytes存储一个像素,按照BGR顺序存储。
下面两个函数首先读取bmp图,包括验证图片属性,读取图片位深,图片长宽等:
``` c++
uint8_t ImgReadHeader(BITMAPFILEHEADER*Header, FIL* fp)
void ImgReadInfo(BMP_INFOHEADER* INFO, FIL* fp)
```
稚晖君选择的是黑白双色墨水屏,我这里稍加改进,改为了黑白红三色墨水屏,缺点是不能局部刷新(快),只能全局刷新(很慢),但考虑到这个屏幕尺寸很小,一天也就刷新几次,刷新慢可以接受。
那么问题来了,如何将一张图片显示在3色屏上呢,由于墨水屏每一个"微胶囊"(显示的每一个小像素点)只能显示一种颜色:要么黑要么红要么白,所以我们有必要将一种图片分为两种颜色,,,这个我研究了老半天,尝试了很多方案:
**Q**:bmp不是储存BGR三种颜色的值嘛,红色的R值肯定比较大,比如说定R>200为红色不就好了吗?
**A** :确实,bmp储存是rgb,但是墨水屏显示不是rgb,准确来说是CMYK,类似于打印机,具体可以看[这篇博文](https://blog.csdn.net/xsb1815/article/details/103469254?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163289389916780255296144%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163289389916780255296144&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-3-103469254.first_rank_v2_pc_rank_v29&utm_term=CMYK+rgb&spm=1018.2226.3001.4187),如果之前没有了解过可能有点绕,仔细想想就明白了。所以,纯红和纯白的R值都是255,照你这么判断,他们都是“红色“了,存在bug。
**Q**:害,我举个例子嘛,改进一下,比如说 R>200 && G<50 && B<50 (这里看不懂可以仔细想一想),不就好了吗!
**A**:某些图片显示确实可以,但是遇到一些比较复杂的图片就不堪入目了,这样的阈值无法解决红到白的渐变图,明显可以看到分割线两边不均匀。
**Q**:。。。
几番尝试,效果依然不理想,主要是RGB颜色空间里对红色的约束不清晰,最终我决定使用HSV颜色空间来解决这个问题(因为HSV对红色有着比较清晰的区分),使用到RGB转HSV函数:
```
void RGB2HSV(uint8_t R, uint8_t G, uint8_t B,float * H, float * S, float * V)
{
float max, min, delta=0;
float r = R/255.0;
float g = G/255.0;
float b = B/255.0;
max = max3(r, g, b);
min = min3(r, g, b);
delta = (max - min);
if (fabs(delta)<1e-3) {
*H = 0;
} else {
if (fabs(r-max)<1e-3) {
*H = ((g-b)/delta)*60;
} else if (fabs(g-max)<1e-3) {
*H = 120+(((b-r)/delta)*60);
} else if (fabs(b-max)<1e-3) {
*H = 240 + (((r-g)/delta)*60);
}
if (*H < 0)
*H += 360;
}
if (fabs(max)<1e-3)
*S = 0;
else
*S = (float)(delta/max);
*V = max;
}
```
判断函数:
```
if( ((H>=0&&H<=10)||(H>=156&&H<=180)) && S>=43 && S<=255 && V>=46 && V<=255 )
```
**因为我这一款墨水屏没有灰阶**,所以我决定使用稚晖君同款抖动函数,opencv源代码图片:
![image.png](//image.lceda.cn/pullimage/dcaDY83KQroQzxS4Gb9mGUJBVNMV68mDTKwQ3PgO.png)
感谢我的队友把它移植到了一下:
```
uint8_t saturated_add(uint8_t val1, int8_t val2)
{
int16_t val1_int = val1;
int16_t val2_int = val2;
int16_t tmp = val1_int + val2_int;
if (tmp > 255)
{
return 255;
}
else if (tmp < 0)
{
return 0;
}
else
{
return tmp;
}
}
void dither(BMP_8 bmp8[][IMG_WIDTH])
{
int err;
int8_t a, b, c, d;
for (int i = 0; i < IMG_HEIGHT; i++)
{
for (int j = 0; j < IMG_WIDTH; j++)
{
if (bmp8[i][j].gray_val > 127)
{
err = bmp8[i][j].gray_val - 255;
bmp8[i][j].gray_val = 255;
}
else
{
err = bmp8[i][j].gray_val - 0;
bmp8[i][j].gray_val = 0;
}
a = (err * 7) / 16;
b = (err * 1) / 16;
c = (err * 5) / 16;
d = (err * 3) / 16;
if ((i != (IMG_HEIGHT - 1)) && (j != 0) && (j != (IMG_WIDTH - 1)))
{
bmp8[i+0][j+1].gray_val = saturated_add(bmp8[i+0][j+1].gray_val , a);
bmp8[i+1][j+1].gray_val = saturated_add(bmp8[i+1][j+1].gray_val, b);
bmp8[i+1][j+0].gray_val = saturated_add(bmp8[i+1][j+0].gray_val , c);
bmp8[i+1][j-1].gray_val = saturated_add( bmp8[i+1][j-1].gray_val, d);
}
}
}
}
```
还有取模函数,相当于我们使用的取模软件:
![image.png](//image.lceda.cn/pullimage/qWfmoymA58uTLBsE4DxPECh7XjWUJDRUoJxeUUDB.png)
```
void img2LCD(BMP_8 img_bmp8[IMG_HEIGHT][IMG_WIDTH],unsigned char gImage[]){
uint16_t num=0;
uint8_t data[8];
uint8_t out;
for (int i = 0; i < IMG_HEIGHT; i++) {
for (int j = 0; j < IMG_WIDTH; ) {
for(int k=0;k<8;k++){
if(img_bmp8[i][j].gray_val>128)
data[k]=1;
else
data[k]=0;
j++;
}
for(int k=0;k<8;k++)
out=out<<1|data[k];
gImage[num]=out;
num++;
out=0;
}
}
}
```
这么一来,我们就仅仅需要把图片放入虚拟U盘内,让单片机自动读取,抖动,取模,非常nice,不用关心其他。
然后是移植FATFS,这个不赘述,也可以参考我上一个开源工程:功率计[power-X](https://oshwhub.com/ZERO--0/usb-gong-shuai-ji_copy_copy_copy_copy_copy_copy_copy)。
这么一来,整个流程就很清晰了:
![image.png](//image.lceda.cn/pullimage/iosHXp5JqsYPhJsZHL98GAJm0nQIek7p8RKseBBO.png)
- - -
至此,项目完结~
整个项目和稚晖君的有不少不同,但是灵感来自于他,感谢大佬。然后放弃NFC使用FASH储存也有好处:实现简单,并且可以同时储存多张图片,一个4Mb的flash就可以储存上百张152*152的bmp图。
最后,还是希望有NFC的好的资料的同学可以在评论区交流,共同学习,感谢观看!
ZERO. 2021.9.29
ID |
Name |
Designator |
Footprint |
Quantity |
1 |
1uF |
C1,C4,C5,C6,C7,C10,C11,C13,C14 |
C0603 |
9 |
2 |
4.7uF |
C2,C16 |
C0603 |
2 |
3 |
100nF |
C3,C12,C15,C28,C29,C30 |
C0603 |
6 |
4 |
15pF |
C8,C9 |
C0603 |
2 |
5 |
1u |
C17,C18 |
C0603 |
2 |
6 |
10uF |
C20,C21,C23 |
C0805 |
3 |
7 |
10uF |
C22 |
C0603 |
1 |
8 |
22uF |
C24,C25,C26 |
C0805 |
3 |
9 |
MBR0530 |
D4,D5,D6 |
SOD-123 |
3 |
10 |
IP2312 |
IP2312 |
SOP-8_L4.9-W3.9-P1.27-LS6.0-BL-EP |
1 |
11 |
HDR-M-2.54_1x4 |
J1 |
HDR-M-2.54_1X4 |
1 |
12 |
HDR-M-2.54_1x2 |
J2,J3 |
HDR-M-2.54_1X2 |
2 |
13 |
1uH |
L1 |
IND-SMD_L4.4-W4.2 |
1 |
14 |
68uH |
L2 |
IND-SMD_L3.5-W3.0 |
1 |
15 |
E6C1204QBAC1UDA |
LED1,LED2,LED3,LED4 |
LED-SMD_L3.2-W1.0-R-RD |
4 |
16 |
FPC-0.5MM-LSSJ-H2.0-24P |
P3 |
FPC-0.5MM-LSSJ-H2.0-24P |
1 |
17 |
MOSFET-N+SI1304BDL |
Q1 |
SOT-323 |
1 |
18 |
10K |
R1,R8,R13 |
R0603 |
3 |
19 |
0.47 |
R2 |
R0603 |
1 |
20 |
1K |
R3,R4,RD1,RD2,RTST1 |
R0603 |
5 |
21 |
5.1k |
R5,R6 |
R0603 |
2 |
22 |
22 |
R14,R15 |
R0603 |
2 |
23 |
2 |
RIN1 |
R0603 |
1 |
24 |
CR0603F51K0P05Z |
RNTC1 |
R0603 |
1 |
25 |
0.5 |
ROUT1 |
R0603 |
1 |
26 |
TM-2023 |
SW1 |
SW-SMD_TM-2023-1 |
1 |
27 |
SK-3296S-01-L3 |
SW2 |
SW-SMD_SK-3296S-01-L3 |
1 |
28 |
RT9013-33GB |
U3 |
SOT-23-5_L3.0-W1.7-P0.95-LS2.8-BR |
1 |
29 |
W25Q32JVSSIQ |
U4 |
SOIC-8_L5.3-W5.3-P1.27-LS8.0-BL |
1 |
30 |
TYPE-C-31-M-14 |
USBC1 |
USB-C-SMD_12P-P0.50-H-F_TYPE-C-31-M-14 |
1 |
31 |
25MHz |
X2 |
OSC-SMD_4P-L3.2-W2.5-BL |
1 |
32 |
STM32F401CCU6 |
U1 |
UFQFPN-48_L7.0-W7.0-P0.50-BL-EP |
1 |
展开
65
138
收藏到专辑