- Navigation (`・ω・´)
- house主页
- 作品
- 暮光小猿图片管理器2
- av_timerSC 科学创造
- 记录
- videocam没事瞎折腾系列
- devices隔壁老王210
- devicesLinux嵌入式
- 关于
- archive更新日志
- restore历史版本
哔哩哔哩 - ( ゜- ゜)つロ 乾杯~
实验环境
- 硬件: Orange PI Zero 512M
- CPU: Allwiner H2+
- 内核版本: 3.4.113
- 操作系统: Armbian 版本忘了
- OLED: 常见的 0.96寸 I2C总线
相关资料
什么是I2C
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。
它只需要两根线即可在连接于总线上的器件之间传送信息。
I2C总线是一个真正的多主机总线,
每个连接到总线上的器件都有唯一的地址,
任何器件既可以作为主机也可以作为从机,
但同一时刻只允许有一个主机。
注册I2C设备
-
取出系统中的适配器,加入一个i2c设备,再把加过设备的适配器放回去。
// 注意3C这个地址,在地址选择的时候是78 // 但是因为最低位是用来标识读写的 所以linux中需要把地址右移一位 // 0x78 >> 1 = 0x3C static struct i2c_board_info info = { I2C_BOARD_INFO("oled_0.96", 0x3C), };
-
在入口取出系统中的适配器,加入一个i2c设备,再把加过设备的适配器放回去。出口里直接取消。
// 定义全局变量存放设备 static struct i2c_client *client; // 入口注册 { struct i2c_adapter *adapter; // 取出I2C-1总线 OrangePi有I2C-0和I2C-1两个总线 adapter = i2c_get_adapter(1); // 在这个总线里加入刚才定义的设备 client = i2c_new_device(adapter, &info); // 把加了设备的再放回去 i2c_put_adapter(adapter); } // 出口注销 { i2c_unregister_device(client); }
加入I2C设备驱动
-
定义设备被加载时的初始化和被卸载的退出动作
int OLED_Probe(struct i2c_client *client, const struct i2c_device_id *id) { WriteCmd(client, 0xAE); //display off WriteCmd(client, 0x20); //Set Memory Addressing Mode // ... 各种初始化 OLED_Cls(client); WriteCmd(client, 0xaf); //--turn on oled panel return 0; // 返回0成功,非0会在加载后给出错误提示 } int OLED_Remove(struct i2c_client *client) { printk("remove\n"); // 驱动卸载时关闭显示 WriteCmd(client, 0xAE); return 0; }
-
定义驱动
// 定义这个驱动会匹配什么名字的设备 static const struct i2c_device_id oled_id[] = { // 这个名字一定要和上边设备的名字一样 {"oled_0.96", 0}, {}, }; // 定义这个驱动的名字,匹配什么名字的设备,初始化和退出动作等 static struct i2c_driver driver = { .driver = { .name = "oled_0.96", .owner = THIS_MODULE, }, .probe = OLED_Probe, .remove = OLED_Remove, .id_table = oled_id, };
-
在入口中注册驱动,出口中注销驱动
// 入口注册 { int ret = i2c_add_driver(&driver); if (ret) { printk(KERN_ERR "I2C error\n"); return ret; } } // 出口注销 { i2c_del_driver(&driver); }
实现写入
按照第6篇,实现一个只写的杂项设备
// 实现写入
static ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *fpos)
{
// 申请一段空间,存放数据
char *str = vmalloc(count);
// 把数据从用户空间复制到内核空间
if (copy_from_user(str, buf, count))
{
vfree(str);
return -EFAULT;
}
// 字符串的结束
*(str + count) = '\0';
// 按照普通单片机实现一个设置屏幕上文字的方法
OLED_Set(client, str);
// 设置完成后释放空间
vfree(str);
// 返回已写入的长度
return count;
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.write = &dev_write,
};
static struct miscdevice mdev = {
// 动态申请从设备号
.minor = MISC_DYNAMIC_MINOR,
// 名字,会显示到/dev/oled
.name = "oled",
.fops = &dev_fops,
};
// 最终的入口出口
static __init int Module_Init(void)
{
struct i2c_adapter *adapter;
int ret;
adapter = i2c_get_adapter(1);
client = i2c_new_device(adapter, &info);
i2c_put_adapter(adapter);
ret = i2c_add_driver(&driver);
if (ret) {
printk(KERN_ERR "I2C error\n");
return ret;
}
ret = misc_register(&mdev);
if (ret)
{
printk(KERN_ERR "Mdev error\n");
}
return ret;
}
static __exit void Module_Exit(void)
{
misc_deregister(&mdev);
i2c_unregister_device(client);
i2c_del_driver(&driver);
}
测试
-
编译并加载模块,然后写入一段文字
root@orangepizero:~/i2cTest# make && make load root@orangepizero:~/i2cTest# echo "Hello World~ I'm Kuresaru!" > /dev/oled
-
把一段命令的输出写入到屏幕
root@orangepizero:~/i2cTest# ip addr show wlan0 | grep -i "inet" > /dev/oled
-
卸载模块,屏幕黑屏
root@orangepizero:~/i2cTest# make unload