目录
- 前言
- 引脚连接
- 代码示例
- 问题排查
- 1. sd卡无法读取
- 2. lcd无显示
前言
要问什么是测试单片机控制单色屏幕的最佳办法,我想badapple应该当之无愧,经典程度如helloworld。
这次,我用stm32f103c8t6+arduino框架读取sd卡,并控制jlx12864g-086液晶屏(3.3v版本)播放20fps(理论上)badapple动画。sd(tf)卡模块就是arduino上常用的那种,上面有卡槽和电平转换ic,将arduino常用的5v转成3.3v。液晶屏在某宝有,用u8g2库控制,可以看我以前的博客。
经过测试,发现该cog lcd实际帧率并不高,相较于oled,是肉眼可见的慢。和b站上的5110屏幕相比也慢。目前暂未能有效提升动画流畅度,也不知道是卡在sd的io上(可能性最大)还是lcd上还是别的什么地方,读者有兴趣可以在本文基础上进一步研究。
注意,本文需要结合其他的badapple实现文章进行阅读。本文代码皆为在他人基础上加以改动。在此表达感谢。
引脚连接
SD模块(硬SPI控制) | 单片机引脚 |
---|---|
VCC | 5V |
GND | GND |
MISO | PA6 |
MOSI | PA7 |
SCK | PA5 |
CS | PA4 |
12864(软SPI控制) | 单片机引脚 |
---|---|
CS | PA3 |
RST | PA2 |
RS | PA1 |
SDA | PA0 |
SCLK | PC15 |
VDD | 3.3V |
VSS | GND |
代码示例
badapple.ino
#include <SD.h>
#include <Arduino.h>
#include <U8g2lib.h>#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#define LCD_WIDTH 128
#define LCD_HEIGHT 64
//注意是count,乘以8=80,才是真实width
#define WIDTH_COUNT 10
#define HEIGHT 60
#define SIZE (WIDTH_COUNT*HEIGHT)
#define top (LCD_HEIGHT - HEIGHT) / 2
#define left (LCD_WIDTH - WIDTH_COUNT * 8) / 2
//液晶屏驱动
U8G2_UC1701_MINI12864_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ PC15, /* data=*/ PA0, /* cs=*/ PA3, /* dc=*/ PA1, /* reset=*/ PA2);
int sdCSPin=PA4;
char buf[SIZE];
File root;
File myFile;void draw() {u8g2.drawBitmap(left, top, WIDTH_COUNT, HEIGHT, (const uint8_t*)buf);
}void setup(){Serial.begin(9600);u8g2.begin();if (!SD.begin(sdCSPin)) {Serial.println("Initialization Failed");return;}Serial.println("Initialization OK");myFile = SD.open("BADAPP~2.BIN");
}void loop(){u8g2.firstPage();do {draw();} while(u8g2.nextPage());if (myFile && myFile.available()) {myFile.read(buf, SIZE);} else {myFile.close();myFile = SD.open("BADAPP~2.BIN");Serial.println("Replay.");delay(5000);}
}
下面是将视频转为图像数据的matlab代码。在安装matlab时需要装图像处理工具箱这个组件,否则报错。不想安装matlab的读者可以自己写。
%% Bad Apple 图像数据生成程序
% 需事先将视频文件放在代码所在文件夹内
% 本例最终生成图像尺寸为80*60,可按需修改
%该源码来源于网络,具体作者未知,棉花糖做了细微修改
%如果侵犯了你的权益,我会及时删除
%% 预处理
clc;clear;
fileName = 'badapple.mp4'; % 文件名可按实际修改
video = VideoReader(fileName); % matlab自带,读取视频文件
numOfFrames = video.Duration * video.frameRate; % 总帧数 = 总时长 * 帧率
guiWaitBar = waitbar(0,'Please wait'); % 新建进度条GUI
fid = fopen('badapple_20FPS.bin','a'); % 新建二进制文件,以添加方式打开
%% 图像处理
tic; % 计时开始
k = 1;
while hasFrame(video) % 只要视频还没读取完就循环frame = readFrame(video); % 每执行一次readframe,帧数减少1if rem(k,2)==0 % 每隔3帧处理一帧,即原视频30FPS,此处取15FPSD = im2bw(frame,0.5); % 对图像二值化,0.5代表阈值取中值rawData = imresize(D,[60 80]); % 保持原比例调整尺寸到80*60%fwrite(fid,imageData','ubit1','ieee-be'); % 按位,以大端模式写入二进制文件fwrite(fid,rawData','ubit1','ieee-be');endk = k+1;waitbar(k/numOfFrames,guiWaitBar); % 设置进度条当前值
end
close(guiWaitBar); % 关闭进度条
fclose(fid);
toc; % 计时停止
问题排查
1. sd卡无法读取
这个问题非常复杂,可能的原因有很多。
如果用的是stlink或jlink,那么请换成usb-ttl串口下载工具。我在测试时发现stlink调试sd卡是一直失败的,card.init通过,但卡在volume.init。换了usb-ttl可以正常使用。原因未可知,如果有知道原因的大佬麻烦解答一下。
先烧一个串口输出例程看看是否正常,检查波特率对不对。
运行cardinfo(官方例程)。官方例程是用硬件SPI1引脚。默认CS引脚是4,其实就是stm32f103上的PA4。也就是说只需要切换板型即可。引脚按照上文表格连接,保持不变。接下来看串口输出,是否能正常初始化、读取卡型、获取文件名等。
如果能成功获取文件名,那么要注意读取出的文件名是否是badapp~1.bin这种。如果是,请尝试把该名称替换掉代码里的文件名,再进行测试。
如果不能成功获取到文件名,那么有以下方法:
- 用avr版的arduino(比如uno)测试模块及sd卡是否正常
- 换更短的杜邦线
- 降低SPI速度。比如cardinfo例程中,有
if (!card.init(SPI_HALF_SPEED, chipSelect))
这一句,可以把
SPI_HALF_SPEED
换成SPI_QUARTER_SPEED
,或者除以1个值再进行测试。这里补充一下,如果读者遇到stm32+arduino i2c无法正常工作,也可以降低速度。
如果sd卡信息能够成功读取,那么恭喜你节省了99%的精力,并距离成功仅一步之遥(确信)。
2. lcd无显示
可以用我之前发的代码测试一下是否可以正常输出。
检查引脚是否正确、排针是否虚焊。如果买了多个屏幕那么换一个看看。