香蕉派 I2C点亮OLED
最近比赛项目需要在香蕉派上利用oled显示收到的数据,于是淘宝买了一块0.91寸、IIC驱动的oled屏幕,然而店家只提供了一份51单片机的驱动程序,需要自己移植到香蕉派上,踩了一些小坑,记录一波。
实验器材:
- 一块BPI M64的板子,系统为Ubuntu 16.04.3 LTS
- 0.91寸OLED模块,控制芯片为SSD1306
- 逻辑分析仪
实验步骤
- 首先测试香蕉派的IIC通信是否正常,系统dev目录下已有两个设备i2c-0、i2c-1。只需要写用户态的程序就可以了,查看M64的管脚图可以看到确实有两个,一个在3、5脚;一个在27、28脚。
那么只有分别尝试一下了,利用逻辑分析仪查看那个管脚有波形就可以了,不赘述。这里发现对i2c-1写入时3、5脚有波形,看来这两个是对应的。程序如下:
int fd = open("/dev/i2c-1", O_RDWR);
if (fd < 0)
{
printf("i2c open failed
");
return -1;
}
struct i2c_rdwr_ioctl_data data;
data.msgs = (struct i2c_msg *)malloc(2 * sizeof(struct i2c_msg));
uint8_t sendbuf[sizeof(uint8_t) + 1] = {0};
data.nmsgs = 1; //消息的数目
data.msgs[0].len = 2;//1个写入目标的reg地址和1个数据
data.msgs[0].addr = 0x78;//i2c设备地址
data.msgs[0].flags = 0;//flags为0:表示写;为1:表示读.
data.msgs[0].buf = sendbuf;//申请内存为2个Byte
data.msgs[0].buf[0] = 0x00;//i2c设备要操作的reg地址
data.msgs[0].buf[1] = 0x00;//向reg写入的值
ioctl(fd,I2C_RDWR,(uint64_t)&data);
}
2 然后测试香蕉派是否可以和oled通信。这里我犯了一个傻,直接将香蕉派的I2C管脚和逻辑分析仪连接,然后发现发送的信号永远只有地址,没有下文了。然后才反应过来,I2C每发送一位,都需要一个ack的确认信号,我没有和oled相连,就没有信号确认,香蕉派就不会继续发送数据。于是将香蕉派和oled连接上,然后在SCL和SDA中引出一根线连接逻辑分析仪。
3 接下来又是一个坑,具体是这样的:我查看卖家的51单片机程序,他是通过GPIO口模拟IIC通信协议实现的,他在发送每位时发送的从机地址位0x78,于是我在程序中也设置地址位0x78,结果oled并没有反应,逻辑分析仪显示波形如下:
4 折腾了一下午,直到看到这篇博客:为什么i2c从机地址要左移一位。i2c每次发送8位,最后一位为读写控制位,也就是说7位的地址需要左移一位才可以被正确的识别。那么问题又来了,为什么逻辑分析中显示发送的地址为0xF0呢。在网上查阅到SSD1306的IIC地址位0x3C,也就是说单片机程序已经将0x3C左移了一位变成了0x78,为什么从机还是不应答呢,根据逻辑分析仪可以发现,linux内核中肯定已经左移了一位,所以0x78变成了0xF0。那么只需要填入原来的地址即可,也就是0x3C。最后,成功通信:
5 后面就没什么好说的了,单片机程序没什么地方需要改动,直接复制过来,改一下代码风格就是自己的了,最后成功点亮。