• 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
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include
    17. #include
    18. #include
    19. #include
    20. #include
    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
    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
    271. {
    272. OLED_Set_Pos(x0,y);
    273. for(x=x0;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博客

  • 相关阅读:
    【可靠性测试】什么是可靠性测试:定义、方法和工具
    Util应用框架基础(五) - 异常处理
    .netCore .net5,6,7 存日志文件
    颜色标记txt和多根走线【Cadance进阶】
    路由菜单路径匹配方法
    【mysql 事件自启动】mysql重启开启事件
    SpringBoot 如何快速过滤出一次请求的所有日志?
    Unity3D 在做性能优化时怎么准确判断是内存、CPU、GPU瓶颈详解
    EWSD交换机加电过程
    activiti7.0工作流,举个例子实现自定义任务监听和执行监听代码
  • 原文地址:https://blog.csdn.net/qq8864/article/details/137080873