当前位置:   article > 正文

linux系统下操作I2C总线外设(imx6ull的oled显示屏i2c驱动笔记)

linux系统下操作I2C总线外设(imx6ull的oled显示屏i2c驱动笔记)

SPI和I2C总线是在单片机中很常见的一种外设,通过普通IO口也可以模拟spi和i2c总线实现相关外接接口芯片的通信。在前面文章中总结分享过在linux中操作spi外设,这里总结分享下在linux系统中如何操作i2c总线设备,分享给有需要的小伙伴。

学习方法论

快乐的人意识到一天能做的有限,也不低估一年内的改变。在做事的时候 ,设定可实现的目标能带来快乐。如果做事没有得到反馈,没有成就,感觉没有尽头,人就特别容易放弃。为什么干农活容易给人满足感?因为一分耕耘一分收获,做多长时间农活就有多少成果。

在“心流”状态下做事处在“心流”状态下,人有一种自发的喜悦和兴奋的感觉。心流取决于定任务挑战和执行任务的技能之间微妙的平衡。给自己找件事情做,尽量去找那种全身投入的心流感,那样做事效率是最高的。

这里先介绍下博主的学习方法。如果用一个字总结的话,就是“练”。两个字总结的话就是“实战”,三个子总结的话就是“做项目”。项目从何而来?哪有那么多项目让你练手?自己给自己定小目标呗,比如同一个oled显示屏,既可以支持spi接口又可以支持i2c,那就反复换不同的方式去实现一遍。

linux之父Linus大佬说的一句名言 “ Talk is cheap, show me the code ,just read the fucking source code!” 。但是哪有那么多项目练?自己给自己找,权当是拿来玩的,just for fun。

Linus 在学习Minix操作系统的过程中,对自带的终端仿真程序非常不满,于是自己写了一个。然后他不想在Minix系统里写,想要在无系统的裸硬件上写,来更好的理解计算机的执行原理。于是他需要知道cpu的工作原理,知道如何写入屏幕,如何读取键盘的输入,如何读写modem,这就是操作系统的雏形。Linus 用这个终端程序登入学校的电脑,查阅电子邮件,参加Minix新闻组的讨论,但是他还想上传和下载文件,于是还需要磁盘驱动程序和文件系统驱动,这是巨大的工作量。他的日程从此变成了编程-睡觉-编程-睡觉的循环,在过程中,这个终端越来越像操作系统,于是Linus 干脆决定把这个终端做成操作系统。

要做成操作系统,还有一个巨大的挑战,那就是要实现Posix规范,也就是Unix系统里的系统调用,有了这些系统api,其他Unix的程序就可以在这个操作系统上运行。Linus 在电子邮件新闻组里求助未果后,找到Sun公司Unix系统的用户手册,然后根据基本版的系统调用标准,开始自己实现这些功能。在1991年9月17日,Linus把操作系统上传到FTP上,那个时候的Linux操作系统大概1w行左右,而现在Linux超过1000w行代码。所以,你如今看到的庞然大物,大名鼎鼎的linux系统起初也是源于Linus的《just for fun》. Linus的人生哲学,Linus认为驱动人类的是三点动机:生存,人在社会秩序中的位置,以及娱乐。Linus在那年夏天我做了两件事。第一件是什么都没做。第二件事是读完了七百一十九页的《操作系统:设计和执行》。那本红色的简装本教科书差不多等于睡在了我的床。

在软件世界中,一旦你解决了最根本的的问题,兴趣就容易很快地消失。一旦你遇到了不知道而想要了解的东西,兴趣就很容易上来。

我的学习三步法:

1.定一个小目标(不是挣它一个亿啊)

2.专注努力达成目标(不轻言放弃)

3.总结分享(学到的写出来也教会别人,费曼学习法)

linux下的i2c介绍

在硬件层中,I2C硬件总线只有两条线路,上面可以挂载多个I2C-device,这些I2C-device有的在I2C总线里充当主机的角色,一般情况该主机为板子上的主cpu中的I2C控制器,拿imx6ul板子来说,这个I2C主机就是imx6中的I2C控制器模块。

其他的I2C-device在I2C总线里充当从机的角色,通常这些从机是板子上完成特定功能的传感器外设,只不过该外设与主控cpu的通信方式是只需要两条线路的I2C总线,比如在imx6ul板子中就有eeprom和AP3216两个外设,以及本次实验用的oled硬件显示模块(i2c接口),它们在I2C总线中充当的都是I2C从机的角色,它们和主控芯片imx6中的I2C控制器都是以并联的方式挂在这个I2C总线上。

在内核中,驱动程序对下要完成I2C总线上的I2C通信协议,收集硬件外设的I2C数据并封装成标准的linux操作接口供用户空间的应用程序操作。对上要实现可以通过linux程序把数据流组织成I2C协议下发到硬件层的相应的外设传感器中。

在用户空间的应用程序中,应用工程师完全可以不必理会I2C协议的详细规定。只需要按照驱动层提供给我们的操作I2C外设的操作接口函数就可以像操作linux中其他普通设备文件那样轻松的操作I2C外设了。

下图是linux系统环境里操作i2c总线上的外设流程框图:

在应用程序中读写I2C设备

通过前面的介绍,我们已经知道站在cpu的角度来看,操作I2C外设实际上就是通过控制cpu中挂载该I2C外设的I2C控制器,而这个I2C控制器在linux系统中被称为“I2C适配器”,而且在linux系统中,每一个设备都是以文件的形式存在的,所以在linux中操作I2C外设就变成了操作I2C适配器设备文件。

Linux系统(也就是内核)为每个I2C适配器生成了一个主设备号为89的设备节点(次设备号为0-255),它并没有针对特定的I2C外设而设计,只是提供了通用的read(),write(),和ioctl()等文件操作接口,在用户空间的应用层就可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。 

操作流程

 1.确定I2C适配器的设备文件节点

i2c适配器的设备节点是/dev/i2c-x,其中x是数字。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。

  1. cat /sys/class/i2c-dev/i2c-0/name
  2. cat /sys/class/i2c-dev/i2c-1/name

然后查看硬件原理图外设是挂在cpu的哪个控制器中,或者借助i2ctools工具可以测试出来。
本次实验我的oled外设屏对应的I2C控制器的设备节点为:/dev/i2c-0。

2.打开适配器对应的设备节点

当用户打开适配器设备节点的时候,Kernel中的i2c-dev代码为其建立一个i2c_client,但是这个i2c_client并不加到i2c_adapter的client链表当中。当用户关闭设备节点时,它自动被释放。

3.IOCTL控制

这个可以参考内核源码中的include/linux/i2c-dev.h文件。下面举例说明主要的IOCTL命令:

  • I2C_SLAVE_FORCE    设置I2C从设备地址(只有在该地址空闲的
  • 情况下成功。)
  • I2C_SLAVE_FORCE    强制设置I2C从设备地址(无论内核中是否已有驱动在使用这个地址都会成功)
  • I2C_TENBIT    选择地址位长:
  • 0 表示是7bit地址 ;
  • 不等于0 就是10 bit的地址。只有适配器支持I2C_FUNC_10BIT_ADDR,这个请求才是有效的。
  • I2C_FUNCS    获取适配器支持的功能,详细的可以参考文件include/linux/i2c.h
  • I2C_RDWR    设置为可读写
  • I2C_RETRIES    设置收不到ACK时的重试次数
  • I2C_TIMEOUT    设置超时的时限

4.使用I2C协议和设备进行通信

代码:ioctl(file,I2C_RDWR,(struct i2c_rdwr_ioctl_data *)msgset); 它可以进行连续的读写,中间没有间歇。只有当适配器支持I2C_FUNC_I2C此命令才有效。参数msgset是一个指针,指向一个i2c_rdwr_ioctl_data类型的结构体,该结构体的功能就是让应用程序可以向内核传递消息。

其成员包括:struct i2c_msg __user *msgs; 和表示i2c_msgs 个数的__u32 nmsgs,它也决定了在硬件I2C总线的硬件通信中有多少个开始信号。由于I2C适配器与外设通信是以消息为单位的,所以struct i2c_msg对我们来说是非常重要的,它可以包含多条消息,而一条消息有可能包含多个数据,比如对于eeprom页写就包含多个数据。

下面介绍一下这个结构体的内容:

__u16 addr:     从设备地址
__u16 flags:     标志(读/写)
I2C_M_TEN:    这是一个10位芯片地址
I2C_M_RD:      从设备到适配器读数据
I2C_M_NOSTART:    不发送起始位
I2C_M_REV_DIR_ADDR:   翻转读写标志
I2C_M_IGNORE_NAK:    忽略I2C的NACK信号
I2C_M_NO_RD_ACK:    读操作的时候不发ACK信号
I2C_M_RECV_LEN :   第一次接收数据的长度
__u16 len:    写入或者读出数据的个数(字节)
__u8 *buf:   写入或者读出数据的地址 buf[0]。
(注:不要忘记给2c_rdwr_ioctl_data结构体中的最重要的结构i2c_msg中的buf分配内存。)

5.用read和write读写I2C设备

可以使用read()/write()来与I2C设备进行通信。
第一,打开I2C控制器文件节点: fd =open(“/dev/i2c-0”, O_RDWR);
第二,设置设备的设备地址:ioctl(fd,I2C_SLAVE, 0x50);
第三,向设备读写数据。

OLED显示屏介绍

SSD1306芯片框图

IIC 通讯

  • 地址: 0111100'R/W#'('R/W#'=0,写模式;'R/W#'=1,读模式)
  • 数据格式: (IIC地址) +控制字节 (1 Byte) +数据字 (n Byte)

命令/数据 标志位

控制字: 0'D/C'000000('D/C'=0,命令;'D/C'=1,数据)

i2c-tools介绍

i2cdetect -l 查看当前系统的I2C总线

总线挂载了I2C设备,可通过i2cdetect扫描每一个总线的所有设备。

i2cdetect -l


i2cdetect -y -r 1 :查看总线1上的所有从设备("--"表示地址被检测到了,但是没有芯片,"UU"表示地址正在被某一个驱动使用,而16进制的地址号3c)

i2cdetect -y -r 1


 

查询总线1(I2C -1)的功能,命令:

 i2cdetect -F 1

i2cget:获取某一个总线上某一个从设备的寄存器值。

如下:获取1总线从设备0x62寄存器00的值。

i2cget -f -y 1 0x62 0x00

-f:强制访问设备

-y:取消交互模式。默认情况下,i2cdetect将等待用户的确认,当使用此标志时,它将直接执行操作。

i2cset 设置某一个总线上某一个从设备的寄存器的值。

如下:设置1总线从设备0x62寄存器00的值为0x00

i2cset -f -y 1 0x62 0x00 0x00

设置i2c1上从地址为0x62的外设0x00寄存器的值为0x00

i2cdump :查看某一个总线上某一个从设备所有寄存器的值,寄存器地址为8位

如下:查看i2c1上0x62外设所有寄存器的值

i2cdump -f -y 1 0x62

i2ctranfer:向寄存器地址为16位的从设备读取或者写入数据

i2ctransfer -f -y 1 w2@0x62 0x00 0x00 r32  

读取

1:哪条总线

w2:写两个字节地址

0x00 0x00:寄存器地址

r32:往后32个寄存器所对应的寄存器值

i2ctransfer -f -y 1 w2@0x62 0x00 0x00 0x10 

1:哪条总线
w2:写两个字节地址
0x00 0x00:寄存器地址
0x10:0x00 0x00寄存器地址往后的寄存器写入0x10

OLED显示屏驱动demo

  1. /*
  2. # I2C testing utility (using iecdev driver)
  3. # test iicdev-oled
  4. # author: yangyongzhen
  5. # qq: 534117529
  6. # wx: yongzhen1111
  7. # Copyright (C) 2023 yangyongzhen <5234117529@qq.com>
  8. #
  9. */
  10. #include <stdint.h>
  11. #include <unistd.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <getopt.h>
  16. #include <fcntl.h>
  17. #include <sys/ioctl.h>
  18. #include <linux/types.h>
  19. #include <linux/i2c-dev.h>
  20. #include <linux/i2c.h>
  21. #include "oledfont.h"
  22. #include "bmp.h"
  23. #include "oled.h"
  24. /* I2C设备地址 */
  25. #define I2C_ADDR 0x3c
  26. #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
  27. static void pabort(const char *s)
  28. {
  29. perror(s);
  30. abort();
  31. }
  32. static const char *iic_device = "/dev/i2c-1";
  33. static int i2c_fd = -1;
  34. static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix)
  35. {
  36. int i = 0;
  37. const unsigned char *address = src;
  38. const unsigned char *line = address;
  39. unsigned char c;
  40. printf("%s | ", prefix);
  41. while (length-- > 0) {
  42. printf("%02X ", *address++);
  43. if (!(++i % line_size) || (length == 0 && i % line_size)) {
  44. if (length == 0) {
  45. while (i++ % line_size)
  46. printf("__ ");
  47. }
  48. printf(" | "); /* right close */
  49. while (line < address) {
  50. c = *line++;
  51. printf("%c", (c < 33 || c == 255) ? 0x2E : c);
  52. }
  53. printf("\n");
  54. if (length > 0)
  55. printf("%s | ", prefix);
  56. }
  57. }
  58. }
  59. /*
  60. * Unescape - process hexadecimal escape character
  61. * converts shell input "\x23" -> 0x23
  62. */
  63. static int unescape(char *_dst, char *_src, size_t len)
  64. {
  65. int ret = 0;
  66. char *src = _src;
  67. char *dst = _dst;
  68. unsigned int ch;
  69. while (*src) {
  70. if (*src == '\\' && *(src+1) == 'x') {
  71. sscanf(src + 2, "%2x", &ch);
  72. src += 4;
  73. *dst++ = (unsigned char)ch;
  74. } else {
  75. *dst++ = *src++;
  76. }
  77. ret++;
  78. }
  79. return ret;
  80. }
  81. void Write_IIC_Byte(unsigned char iic_byte)
  82. {
  83. u8 buf[2] = {0};
  84. buf[0] = iic_byte;
  85. if(i2c_fd){
  86. write(i2c_fd, buf, 1);
  87. }
  88. }
  89. //向SSD1106写入一个字节。
  90. //dat:要写入的数据/命令
  91. //cmd:数据/命令标志 0,表示命令;1,表示数据;
  92. void OLED_WR_Byte(u8 dat,u8 cmd)
  93. {
  94. if(cmd)
  95. {
  96. Write_IIC_Byte(I2C_ADDR); //D/C#=0; R/W#=0
  97. Write_IIC_Byte(0x40); //write data
  98. Write_IIC_Byte(dat);
  99. }
  100. else
  101. {
  102. Write_IIC_Byte(I2C_ADDR); //Slave address,SA0=0
  103. Write_IIC_Byte(0x00); //write command
  104. Write_IIC_Byte(dat);
  105. }
  106. }
  107. //初始化SSD1306
  108. void OLED_Init(void)
  109. {
  110. //OLED_RST_Set();
  111. usleep(100000);
  112. //OLED_RST_Clr();
  113. usleep(100000);
  114. //OLED_RST_Set();
  115. OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
  116. OLED_WR_Byte(0x02,OLED_CMD);//---set low column address
  117. OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
  118. OLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
  119. OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
  120. OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
  121. OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
  122. OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
  123. OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
  124. OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
  125. OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
  126. OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
  127. OLED_WR_Byte(0x00,OLED_CMD);//-not offset
  128. OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
  129. OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
  130. OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
  131. OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
  132. OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
  133. OLED_WR_Byte(0x12,OLED_CMD);
  134. OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
  135. OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
  136. OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
  137. OLED_WR_Byte(0x02,OLED_CMD);//
  138. OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
  139. OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
  140. OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
  141. OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
  142. OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
  143. OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
  144. OLED_Clear();
  145. OLED_Set_Pos(0,0);
  146. }
  147. void OLED_Set_Pos(unsigned char x, unsigned char y)
  148. {
  149. OLED_WR_Byte(0xb0+y,OLED_CMD);
  150. OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
  151. OLED_WR_Byte((x&0x0f)|0x01,OLED_CMD);
  152. }
  153. //开启OLED显示
  154. void OLED_Display_On(void)
  155. {
  156. OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
  157. OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON
  158. OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON
  159. }
  160. //关闭OLED显示
  161. void OLED_Display_Off(void)
  162. {
  163. OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
  164. OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF
  165. OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF
  166. }
  167. //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
  168. void OLED_Clear(void)
  169. {
  170. u8 i,n;
  171. for(i=0;i<8;i++)
  172. {
  173. OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
  174. OLED_WR_Byte (0x02,OLED_CMD); //设置显示位置—列低地址
  175. OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址
  176. for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
  177. } //更新显示
  178. }
  179. //在指定位置显示一个字符,包括部分字符
  180. //x:0~127
  181. //y:0~63
  182. //mode:0,反白显示;1,正常显示
  183. //size:选择字体 16/12
  184. void OLED_ShowChar(u8 x,u8 y,u8 chr)
  185. {
  186. unsigned char c=0,i=0;
  187. c=chr-' ';//得到偏移后的值
  188. if(x>Max_Column-1){x=0;y=y+2;}
  189. if(SIZE ==16)
  190. {
  191. OLED_Set_Pos(x,y);
  192. for(i=0;i<8;i++)
  193. OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
  194. OLED_Set_Pos(x,y+1);
  195. for(i=0;i<8;i++)
  196. OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
  197. }
  198. else {
  199. OLED_Set_Pos(x,y+1);
  200. for(i=0;i<6;i++)
  201. OLED_WR_Byte(F6x8[c][i],OLED_DATA);
  202. }
  203. }
  204. //m^n函数
  205. u32 oled_pow(u8 m,u8 n)
  206. {
  207. u32 result=1;
  208. while(n--)result*=m;
  209. return result;
  210. }
  211. //显示2个数字
  212. //x,y :起点坐标
  213. //len :数字的位数
  214. //size:字体大小
  215. //mode:模式 0,填充模式;1,叠加模式
  216. //num:数值(0~4294967295);
  217. void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size)
  218. {
  219. u8 t,temp;
  220. u8 enshow=0;
  221. for(t=0;t<len;t++)
  222. {
  223. temp=(num/oled_pow(10,len-t-1))%10;
  224. if(enshow==0&&t<(len-1))
  225. {
  226. if(temp==0)
  227. {
  228. OLED_ShowChar(x+(size/2)*t,y,' ');
  229. continue;
  230. }else enshow=1;
  231. }
  232. OLED_ShowChar(x+(size/2)*t,y,temp+'0');
  233. }
  234. }
  235. //显示一个字符号串
  236. void OLED_ShowString(u8 x,u8 y,u8 *chr)
  237. {
  238. unsigned char j=0;
  239. while (chr[j]!='\0')
  240. { OLED_ShowChar(x,y,chr[j]);
  241. x+=8;
  242. if(x>120){x=0;y+=2;}
  243. j++;
  244. }
  245. }
  246. //显示汉字
  247. void OLED_ShowCHinese(u8 x,u8 y,u8 no)
  248. {
  249. u8 t,adder=0;
  250. OLED_Set_Pos(x,y);
  251. for(t=0;t<16;t++)
  252. {
  253. OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
  254. adder+=1;
  255. }
  256. OLED_Set_Pos(x,y+1);
  257. for(t=0;t<16;t++)
  258. {
  259. OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
  260. adder+=1;
  261. }
  262. }
  263. /***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
  264. void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
  265. {
  266. unsigned int j=0;
  267. unsigned char x,y;
  268. if(y1%8==0) y=y1/8;
  269. else y=y1/8+1;
  270. for(y=y0;y<y1;y++)
  271. {
  272. OLED_Set_Pos(x0,y);
  273. for(x=x0;x<x1;x++)
  274. {
  275. OLED_WR_Byte(BMP[j++],OLED_DATA);
  276. }
  277. }
  278. }
  279. int i2cdev_init()
  280. {
  281. int ret = -1;
  282. printf("hello,this is i2cdev_init \n");
  283. /* 打开eeprom对应的I2C控制器文件 */
  284. i2c_fd = open(iic_device, O_RDWR);
  285. if (i2c_fd < 0)
  286. {
  287. printf("open i2c DEVICE failed \n");
  288. return ret;
  289. }
  290. /*设置I2C设备地址*/
  291. ret = ioctl(i2c_fd,I2C_SLAVE_FORCE, I2C_ADDR);
  292. if ( ret < 0)
  293. {
  294. printf("set slave address failed \n");
  295. }
  296. printf("i2cdev_init ok \n");
  297. return ret;
  298. }
  299. void delay_ms(int ms)
  300. {
  301. usleep(ms*1000);
  302. }
  303. int main(int argc, char *argv[])
  304. {
  305. //导出DC口,这里使用的是GPIO1管脚,作为DC口使用(命令数据选择管脚)
  306. //system("echo 1 > /sys/class/gpio/export");
  307. //system("echo out >/sys/class/gpio/gpio1/direction");
  308. u8 t;
  309. i2cdev_init();
  310. OLED_Init();
  311. while(1)
  312. {
  313. OLED_Clear();
  314. OLED_ShowCHinese(0,0,0);//中
  315. OLED_ShowCHinese(18,0,1);//景
  316. OLED_ShowCHinese(36,0,2);//园
  317. OLED_ShowCHinese(54,0,3);//电
  318. OLED_ShowCHinese(72,0,4);//子
  319. OLED_ShowCHinese(90,0,5);//科
  320. OLED_ShowCHinese(108,0,6);//技
  321. OLED_ShowString(0,3,"1.3' OLED TEST");
  322. //OLED_ShowString(8,2,"ZHONGJINGYUAN");
  323. // OLED_ShowString(20,4,"2014/05/01");
  324. OLED_ShowString(0,6,"ASCII:");
  325. OLED_ShowString(63,6,"CODE:");
  326. OLED_ShowChar(48,6,t);//显示ASCII字符
  327. t++;
  328. if(t>'~')t=' ';
  329. OLED_ShowNum(103,6,t,3,16);//显示ASCII字符的码值
  330. delay_ms(8000);
  331. OLED_Clear();
  332. delay_ms(8000);
  333. OLED_DrawBMP(0,0,128,8,BMP1); //图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)
  334. delay_ms(8000);
  335. OLED_DrawBMP(0,0,128,8,BMP2);
  336. delay_ms(8000);
  337. }
  338. return 0;
  339. }

交叉编译

简单的makefile模板文件如下:

  1. # test iicdev-oled
  2. # author: yangyongzhen
  3. # qq: 534117529
  4. # wx: yongzhen1111
  5. # Copyright (C) 2023 yangyongzhen <5234117529@qq.com>
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. CC ?= arm-linux-gnueabihf-gcc
  12. AR ?= arm-linux-gnueabihf-ar
  13. STRIP ?= strip
  14. CFLAGS ?= -O2
  15. # When debugging, use the following instead
  16. #CFLAGS := -O -g
  17. CFLAGS += -Wall
  18. SOCFLAGS := -fpic -D_REENTRANT $(CFLAGS)
  19. KERNELVERSION := $(shell uname -r)
  20. .PHONY: all strip clean
  21. all:
  22. $(CC) iicdev_oled.c -o iicdev_oled
  23. clean:
  24. rm -rf *.o

简单测试,直接执行make即可。

其他资源

0.96 OLED显示屏案例

https://download.csdn.net/download/qq8864/88117562

Linux下I2C-tools工具使用_i2cdetect-CSDN博客

正点原子IMX6UL 检测I2C上设备地址_imax415 iic地址-CSDN博客

Android i2cdetect i2cdump i2cget i2cset调试工具使用_i2cget命令详解-CSDN博客

模拟I2C/IIC协议_csi 模拟iic,i2c-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/485230
推荐阅读
相关标签
  

闽ICP备14008679号