用OLED屏幕播放视频(2): 为OLED屏幕开发I2C驱动
下面的系列文章记录了如何使用一块linux开发扳和一块OLED屏幕实现视频的播放:
这是此系列文章的第2篇, 主要总结和记录一个I2C从设备的驱动, 在linux内核中如何实现, 如何给用户态的程序暴露合适的接口, 让用户态有机会操作真实的硬件设备. 可以通过下面的视频快速了解最终达到的效果和实现的总体思路.
跳转到6:48, 直接观看演示
1). I2C驱动架构
I2C总线是一种主从, 同步, 半双工的低速通信总线, 硬件标准可以参考这里. 这篇文章只讨论I2C总线上从设备的驱动在linux平台下如何实现, 下图是linux中I2C总线相关的软件模块, 其中i2c core提供给驱动开发人员重要的数据结构和接口函数:
- i2c_adapter: 表示总线上的主设备, 或者说总线控制器
- i2c_algorithm: 当主设备想要通信时, 它负责具体硬件时序的实现, 比如, 在总线上产生开始/结束条件, 发送/接收数据
- i2c_client: 表示总线上的从设备
- i2c_driver: 表示从设备对应的驱动, 需要实现其中的接口函数之后, 把驱动注册到i2c core之中
- i2c_add_driver: 注册i2c_driver到i2c core, 一般在模块初始化函数中调用
- i2c_del_driver: 删除i2c_driver, 一般在模块退出函数中调用
- i2c_master_send/recv: 主设备发送/接收数据, 实际上为了驱动从设备, 需要让主设备向从设备发送合适的命令, 或者读取从设备的状态, 具体发送或者接收什么, 参考从设备的datasheet即可
2). 实现ssd1306屏幕的I2C驱动
- 注册i2c_driver
使用module_i2c_driver宏, 并传递我们实现的i2c_driver, 该宏能够为我们生成模块的init和exit函数, 在函数中自动注册和删除传递进来的i2c_driver. 如果需要在init和exit中做一些其他工作, 则需要自己实现, 不能使用这个宏.
module_i2c_driver(ssd130x_driver);
- 实现i2c_driver中的接口
static struct i2c_driver ssd130x_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "ssd130x_driver",
},
.probe = ssd130x_probe,
.remove = ssd130x_remove,
.id_table = ssd130x_id_table,
};
这里只实现了i2c_driver中的probe和remove. 当驱动和设备匹配成功时, probe函数被调用, 在probe函数中, 完成了字符设备的相关的操作, 包括:
- 分配设备号
- 初始化字符设备结构体
- 添加字符设备到内核
- 创建设备文件
- 实现字符设备接口, 暴露给用户态程序
static struct file_operations ssd130x_fops = {
.owner = THIS_MODULE,
.open = ssd130x_open,
.release = ssd130x_close,
.write = ssd130x_write,
};
用户态程序可以对设备文件进行打开, 关闭, 写入3种操作. 当打开设备文件时, ssd130x_open被调用, 完成OLED屏幕的初始化; 关闭设备文件时, ssd130x_close被调用, 屏幕被关闭; 当向设备文件写入数据时, ssd130x_write被调用, 一帧数据被发送到ssd1306的RAM上, 屏幕显示的内容被更新. 以上3种操作, 底层都是通过i2c_master_send向从设备发送特定的命令或者数据实现的.
2.1). 阅读数据手册
ssd1306的数据手册参考这里, 手册内容较多, 不宜通读, 主要关注以下几点:
- 基本硬件参数: 屏幕分辨率, 支持的通信接口, 支持哪些显示相关的功能(比如滚动, 反转等) ...
- 基本工作原理: 通过向RAM中写入数据, 控制屏幕像素点的亮灭
- 基本使用方法: 支持哪些命令? 分别能控制它的什么功能?
- Application Note: 典型硬件电路, 示例代码
2.2). 设备的初始化
在数据手册的Application Note中包含使用ssd1306时的初始化流程, 如下图所示. 在此基础上, 可以做一些调整, 比如我在驱动中关闭了屏幕滚动.
2.3). 调整I2C的频率
我在beaglebone black板子上刷入的debian系统, 其设备树中的i2c时钟频率是100kbits/s, 内核中的i2c_algorithm会根据这个频率计算在i2c总线上发送数据时使用的延时. 实际测试之后发现按照这个频率播放视频存在一些卡顿, 因此需要对i2c时钟频率做修改, 有两种方式:
- 在uboot启动时, 进入uboot的shell, 使用fdt相关的命令修改始终频率
- 备份原来的设备树文件, 使用dtc编译器从dtb得到dts, 在dts中修改始终频率, 再编译得到新的dtb, 替换原来的设备树文件
我这里采用的是dtc的方式, 这样就不需要每次系统启动都手动修改了, 修改之后的时钟频率为400kbits/s, 播放视频流畅很多.
3). 测试驱动功能
驱动代码编写完成之后, 需要实际测试一下功能, 下面代码首先打开OLED屏幕的设备文件, 写入一帧数据, 每个字节都填充为0x88, 这样屏幕上会显示出预期的条纹, sleep两秒之后, 关闭设备文件, 屏幕熄灭.
#define FRAME_SIZE (128 * 8)
int main(int argc, char **argv)
{
int device_fd = open("/dev/ssd130x0", O_WRONLY);
if (device_fd < 0) {
return -1;
}
char *frame = malloc(FRAME_SIZE);
memset(frame, 0x88, FRAME_SIZE);
write(device_fd, frame, FRAME_SIZE);
sleep(2);
free(frame);
close(device_fd);
return 0;
}
4). 文末推广
欢迎关注我的B站账号, 或者加入QQ群838923389, 一起研究计算机底层技术, 一起搞事情:P
其实还有很多实现的细节没有在博客中写出来, 只有自己在做的时候遇到了才能够体会的到, 需要完整代码的老铁直接在qq群中问一下.
用OLED屏幕播放视频(2): 为OLED屏幕开发I2C驱动的更多相关文章
- android SurfaceView中播放视频 按视频的原始比例播放
OnPreparedListener mediaPlayerOnPreparedListener = new OnPreparedListener() { @Override public void ...
- Android三种播放视频的方式
在Android中,我们有三种方式来实现视频的播放: 1.使用其自带的播放器.指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型. 2.使用VideoView来播放. ...
- Android MediaPlayer和SurfaceView播放视频
昨天介绍了VideoView播放视频,今天再介绍一种播放视频的方法MediaPlayer和SurfaceView,MediaPlayer播放音频,SurfaceView来显示图像,具体步骤如下: 1. ...
- 3D立体显示大屏幕拼接视频墙系统解决方案【转】
http://shop.souvr.com/thread-123416-1-1.html 随着3D立体视像.全息影像等技术不断取得突破性进展,国内外越来越多的公司投身3D显示领域,产品层出不穷.3D技 ...
- Windows Phone 7 播放视频
在Windows Phone 7中播放视频有两种方式,一种是使用MediaElement 控件来播放,一种是使用启动器MediaPlayerLanucher来实现视频的播放.用MediaElement ...
- [转]Android WebView播放视频(包括全屏播放),androidwebview
Android WebView播放视频(包括全屏播放),androidwebview 最近项目开发中用到了WebView播放视频的功能,总结了开发中犯过的错误,这些错误在开发是及容易遇到的,所以我这里 ...
- 使用MediaPlayer和SurfaceView播放视频
使用VideoView播放视频简单.方便,丹有些早期的开发者更喜欢使用MediaPlayer来播放视频,但由于MediaPlayer主要用于播放音频,因此它没有提供图像输出界面,此时 需要借助于Sur ...
- 运用surfaceView与MediaPlayer实现播放视频的功能
该程序运用了surfaceView与MediaPlayer结合,实现播放视频,surfaceView详情请见 SurfaceView的使用 使用了第三方包Volly里面的方法StringQueue下载 ...
- android 98 MediaPlayer+SurfaceView播放视频
package com.itheima.videoplayer; import java.io.IOException; import android.media.MediaPlayer; impor ...
- Windows Phone 之播放视频
在Windows Phone 7中播放视频有两种方式, (1)使用MediaElement 控件来播放:用MediaElement 控件来播放视频比较灵活,你需要自己去实现播放暂停进度条等等的功能,播 ...
随机推荐
- 执行pod setup 报错error: RPC failed; curl 18 transfer closed with outstanding read data remainin
执行pod setup 报错 error: RPC failed; curl 18 transfer closed with outstanding read data remaining fatal ...
- adb基本命令
adb基本命令 adb查看当前设备 adb devices adb覆盖安装app adb install -r 包地址 adb查看当前运行app的包名 adb shell "dumpsys ...
- 在EXCEL和WPS表格里实现邮件合并功能
在EXCEL和WPS表格里实现邮件合并功能 2020/3/21 22:06:09 0人评论 10635次 OFFICE邮件合并:在Office中,先建立两个文档:一个WORD包括所有文件共有内容的主文 ...
- python 学习之----time模块
# timeimport time# # #1 获取时间戳# # print(time.time())# # #2 获取格式化时间对象# # #获取默认参数是当前系统时间戳# # print(time ...
- Galaxy v-21.01 发布,新的流程和历史栏体验
Galaxy Project(https://galaxyproject.org/)是在云计算背景下诞生的一个生物信息学可视化分析开源项目. 该项目由美国国家科学基金会(NSF).美国国家人类基因组研 ...
- 白帽子讲web安全
世界安全观 Web安全筒史 起初,研究计算机系统和网络的人,被称为"Hacker","Hacker"在中国按照音译,被称为"黑客" 对于现代 ...
- ENVI5.6 安装教程,新手入门(超详细)附安装包和常见问题
ENVI是一个完整的遥感图像处理平台,广泛应用于科研.环境保护.气象.农业.林业.地球科学.遥感工程.水利.海洋等领域.目前ENVI已成为遥感影像处理的必备软件,包含辐射定标.大气校正.镶嵌裁剪.分类 ...
- Java输入三个班每班三个人,输入成绩,分别计算每个班级的总分和平均分
代码如下: public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int score; ...
- 工作中必备的12个Git命令
前言 以下是工作中必备的12个Git命令,包括创建和初始化仓库.克隆远程仓库.添加和提交文件.查看状态和历史记录.创建和切换分支.合并分支以及推送和拉取远程仓库等操作.掌握这些命令可以帮助你进行基本的 ...
- HCL 实验7:OSPF
拓扑图 R1配置 [R1]int g0/1 [R1-GigabitEthernet0/1]ip add 192.168.4.1 24 [R1-GigabitEthernet0/1]undo shutd ...