查看: 288|回复: 0

[评测分享] 【树莓派5 测评】--6.OLED显示

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-20 11:34
  • 签到天数: 16 天

    连续签到: 2 天

    [LV.4]偶尔看看III

    发表于 2024-4-14 12:20:21 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 dirty123 于 2024-4-15 09:14 编辑

          树莓派5有硬件I2C接口,这里准备了128*64分辨率ssd1306 OLED屏,本篇讲述使用wringpi库,C语言代码驱动OLED做显示。
    一.准备工作
    1.安装wringpi库。前面以讲述不赘述。
    2.打开树莓派5的I2C接口。sudo raspi-config命令 或开始菜单路径下配置,也不赘述。
    3.查询引脚后,oled与开发板连接好。
    1_I2C引脚.jpg
    图1:查询I2C引脚

    oled模块   树莓派5
    VCC      3.3V
    GND      0V
    SCL      SCL.1
    SDA      SDA.1
          这里把前面实现过的DHT11也接在开发板上,oled上做温湿度显示。
    4.安装i2c工具
    sudo apt-get install i2c-toolssudo apt-get install i2c-tools
    5.测试oled连接情况,如连接好可看到设备地址
    sudo i2cdetect -y 1
    2_oled连接情况.jpg
    图2:OLED屏i2c连接情况


    二.代码编写,界面设计
    1.ssd1306写命令函数
    1. void ssd1306_command(unsigned int c)
    2. {
    3.         // I2C
    4.         unsigned int control = 0x00;    // Co = 0, D/C = 0
    5.         wiringPiI2CWriteReg8(i2cd, control, c);
    6. }
    复制代码
    2.ssd1306初始化
    1. void ssd1306_begin(unsigned int vccstate, unsigned int i2caddr)
    2. {
    3.         // I2C Init

    4.         _vccstate = vccstate;

    5.         i2cd = wiringPiI2CSetup(i2caddr);
    6.         if (i2cd < 0) {
    7.                 fprintf(stderr, "ssd1306_i2c : Unable to initialise I2C:\n");
    8.                 return;
    9.         }
    10.         // Init sequence
    11.         ssd1306_command(SSD1306_DISPLAYOFF);    // 0xAE
    12.         ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);    // 0xD5
    13.         ssd1306_command(0x80);  // the suggested ratio 0x80

    14.         ssd1306_command(SSD1306_SETMULTIPLEX);  // 0xA8
    15.         ssd1306_command(SSD1306_LCDHEIGHT - 1);

    16.         ssd1306_command(SSD1306_SETDISPLAYOFFSET);      // 0xD3
    17.         ssd1306_command(0x0);   // no offset
    18.         ssd1306_command(SSD1306_SETSTARTLINE | 0x0);    // line #0
    19.         ssd1306_command(SSD1306_CHARGEPUMP);    // 0x8D
    20.         if (vccstate == SSD1306_EXTERNALVCC) {
    21.                 ssd1306_command(0x10);
    22.         } else {
    23.                 ssd1306_command(0x14);
    24.         }
    25.         ssd1306_command(SSD1306_MEMORYMODE);    // 0x20
    26.         ssd1306_command(0x00);  // 0x0 act like ks0108
    27.         ssd1306_command(SSD1306_SEGREMAP | 0x1);
    28.         ssd1306_command(SSD1306_COMSCANDEC);

    29. #if defined SSD1306_128_32
    30.         ssd1306_command(SSD1306_SETCOMPINS);    // 0xDA
    31.         ssd1306_command(0x02);
    32.         ssd1306_command(SSD1306_SETCONTRAST);   // 0x81
    33.         ssd1306_command(0x8F);

    34. #elif defined SSD1306_128_64
    35.         ssd1306_command(SSD1306_SETCOMPINS);    // 0xDA
    36.         ssd1306_command(0x12);
    37.         ssd1306_command(SSD1306_SETCONTRAST);   // 0x81
    38.         if (vccstate == SSD1306_EXTERNALVCC) {
    39.                 ssd1306_command(0x9F);
    40.         } else {
    41.                 ssd1306_command(0xCF);
    42.         }

    43. #elif defined SSD1306_96_16
    44.         ssd1306_command(SSD1306_SETCOMPINS);    // 0xDA
    45.         ssd1306_command(0x2);   // ada x12
    46.         ssd1306_command(SSD1306_SETCONTRAST);   // 0x81
    47.         if (vccstate == SSD1306_EXTERNALVCC) {
    48.                 ssd1306_command(0x10);
    49.         } else {
    50.                 ssd1306_command(0xAF);
    51.         }

    52. #endif
    53.         ssd1306_command(SSD1306_SETPRECHARGE);  // 0xd9
    54.         if (vccstate == SSD1306_EXTERNALVCC) {
    55.                 ssd1306_command(0x22);
    56.         } else {
    57.                 ssd1306_command(0xF1);
    58.         }
    59.         ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB
    60.         ssd1306_command(0x40);
    61.         ssd1306_command(SSD1306_DISPLAYALLON_RESUME);   // 0xA4
    62.         ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6

    63.         ssd1306_command(SSD1306_DEACTIVATE_SCROLL);

    64.         ssd1306_command(SSD1306_DISPLAYON);     // --turn on oled panel
    65. }
    复制代码
         oled驱动基本是在ssd1306_command函数配合寄存器命令构建。
    3.关于界面显示数据源函数,这里选取部分
    1. //获取时间
    2. time(&now);
    3. timenow = localtime(&now);
    复制代码
    1. //获取IP
    2. char* GetLocalIp(void)
    3. {
    4.     int MAXINTERFACES=16;
    5.     char *ip;
    6.     int fd, intrface, retn = 0;
    7.     struct ifreq buf[MAXINTERFACES];
    8.     struct ifconf ifc;

    9.     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
    10.     {
    11.         ifc.ifc_len = sizeof(buf);
    12.         ifc.ifc_buf = (caddr_t)buf;
    13.         if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc))
    14.         {
    15.             intrface = ifc.ifc_len / sizeof(struct ifreq);

    16.             while (intrface-- > 0)
    17.             {
    18.                 if (!(ioctl (fd, SIOCGIFADDR, (char *) &buf[intrface])))
    19.                 {
    20.                     ip=(inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr));
    21.                     break;
    22.                 }
    23.             }
    24.         }
    25.         close (fd);
    26.         return ip;
    27.     }
    28. }
    复制代码
    1. //获取CPU温度
    2. int get_cpu_temp()
    3. {
    4.     FILE *fd;
    5.     int temp;
    6.     char buff[256];

    7.     fd = fopen("/sys/class/thermal/thermal_zone0/temp","r");
    8.     fgets(buff,sizeof(buff),fd);
    9.     sscanf(buff, "%d", &temp);

    10.     fclose(fd);

    11.     return temp/1000;
    12. }
    复制代码
    1. //获取内存占用
    2. MEM_PACK get_memocupy()
    3. {
    4.     FILE *fd;
    5.     int n;
    6.     double mem_total,mem_used_rate;
    7.     char buff[256];
    8.     MEM_OCCUPY *m = (MEM_OCCUPY*)malloc(sizeof(MEM_OCCUPY));
    9.     MEM_PACK p;

    10.     fd = fopen("/proc/meminfo", "r");

    11.     fgets(buff,sizeof (buff), fd);
    12.     sscanf(buff, "%s %lu %s\n", m->name, &m->total, m->name2);
    13.     mem_total = m->total;
    14.     fgets(buff,sizeof (buff),fd);
    15.     sscanf(buff, "%s %lu %s\n", m->name, &m->total,m->name2);
    16.     mem_used_rate = (1 - m->total/mem_total)*100;
    17.     mem_total = mem_total/(1024*1024);
    18.     p.total = mem_total;
    19.     p.used_rate = mem_used_rate;
    20.     fclose(fd);
    21.     free(m);
    22.     return p;
    23. }
    复制代码
    4.界面设计
          设计思想是在main函数初始化与参数轮询更新,在Display定时刷新显示。工程目录如下。
    3_工程目录.jpg
    图3:工程目录

    main函数
    1. void main()
    2. {
    3.     #if DHT11_USE
    4.     printf("PIN:%d\n", pinNumber);
    5.         
    6.         if (-1 == wiringPiSetup()) {
    7.                 printf("Setup wiringPi failed!");
    8.                 return;
    9.         }
    10.         pinMode(pinNumber, OUTPUT); // set mode to output
    11.         digitalWrite(pinNumber, 1); // output a high level
    12.    
    13.     #endif
    14.    
    15.     printf("Run OLED\n");
    16.     //屏幕初始化
    17.     ssd1306_begin(SSD1306_SWITCHCAPVCC, SSD1306_I2C_ADDRESS);
    18.     //绑定定时器执行函数
    19.     signal(SIGALRM, Display);
    20.     //定时器启用中断 每0.5s执行一次,就是2帧
    21.     set_timer();
    22.     while (1)
    23.     {
    24.         #if DHT11_USE
    25.         pinMode(pinNumber, OUTPUT); // set mode to output
    26.                 digitalWrite(pinNumber, 1); // output a high level
    27.                 //delay(3000);
    28.                 if (readSensorData())
    29.                 {
    30.                         printf("Sensor data read ok!\n");
    31.                         printf("RH:%d.%d\n", (databuf >> 24) & 0xff, (databuf >> 16) & 0xff);
    32.                         printf("TMP:%d.%d\n", (databuf >> 8) & 0xff, databuf & 0xff);
    33.                         //databuf = 0;
    34.                 }
    35.                 else
    36.                 {
    37.                         printf("Sensor dosent ans!\n");
    38.                         databuf = 0;
    39.                 }
    40.         #endif
    41.         
    42.         //获取时间
    43.         time(&now);
    44.         timenow = localtime(&now);

    45.         //获取IP
    46.         IP_address=GetLocalIp();

    47.         //获取CPU温度
    48.         temp = get_cpu_temp();

    49.         //获取CPU占用
    50.         get_cpuoccupy((CPU_OCCUPY*)&cpu_stat1);
    51.         sleep(10);
    52.         get_cpuoccupy((CPU_OCCUPY*)&cpu_stat2);
    53.         cpu = cal_cpuoccupy((CPU_OCCUPY *)&cpu_stat1,(CPU_OCCUPY*)&cpu_stat2);

    54.         //获取内存占用
    55.         mem = get_memocupy();

    56.         //获取实时流量
    57.         getCurrentDownloadRates(&start_download_rates);    //获取当前流量,并保存在start_download_rates
    58.         sleep(3);     //休眠多少秒,这个值根据宏定义中的WAIT_SECOND的值来确定
    59.         getCurrentDownloadRates(&end_download_rates);    //获取当前流量,并保存在end_download_rates里
    60.                 Byte=(end_download_rates - start_download_rates) / 3;


    61.         //获取当前磁盘的使用情况
    62.         get_disk_occupy(&used);

    63.     }
    64. }
    复制代码

    ②界面设计刷新
    1. oid Display(int m)
    2. {
    3.     //清屏
    4.     ssd1306_clearDisplay();
    5.    
    6.     ssd1306_drawString("Hi,Raspberry_Pi5 !");
    7.     ssd1306_drawString("\r\n");
    8.    
    9.    
    10.     //显示时间
    11.     ssd1306_drawString("    -  -       :  :");
    12.     OLED_ShowNum(0,8,timenow->tm_year+1900,4,1);
    13.     OLED_ShowNum(30,8,timenow->tm_mon,2,1);
    14.     OLED_ShowNum(48,8,timenow->tm_mday,2,1);
    15.     OLED_ShowNum(66+12,8,timenow->tm_hour,2,1);
    16.     OLED_ShowNum(82+12,8,timenow->tm_min,2,1);
    17.     OLED_ShowNum(100+12,8,timenow->tm_sec,2,1);
    18.     ssd1306_drawString("\r\n");

    19.    
    20.     //显示IP
    21.     ssd1306_drawString("IP=");
    22.     ssd1306_drawString(IP_address);
    23.     ssd1306_drawString("\r\n");

    24.     //显示CPU温度
    25.     ssd1306_drawString("CPU Temperature=   C");
    26.     OLED_ShowNum(96,24,temp,2,0);
    27.     ssd1306_drawString("\r\n");

    28.     //显示CPU占用
    29.     ssd1306_drawString("CPU usage=   %");
    30.     OLED_ShowNum(61,32,cpu,2,0);
    31.     ssd1306_drawString("\r\n");

    32.     //显示运存占用
    33.     ssd1306_drawString("mem use rate=  %");
    34.     OLED_ShowNum(78,40,mem.used_rate,2,0);
    35.     ssd1306_drawString("\r\n");

    36.     #if DHT11_USE
    37.     //DHT11 温湿度显示
    38.     ssd1306_drawString("RH=  .  ,TEMP=  .  C");
    39.     OLED_ShowNum(20,48,(databuf >> 24) & 0xff,2,1);
    40.     OLED_ShowNum(35,48,(databuf >> 16) & 0xff,1,1);
    41.     OLED_ShowNum(85,48,(databuf >> 8) & 0xff,2,1);
    42.     OLED_ShowNum(105,48,databuf & 0xff,1,1);
    43.     ssd1306_drawString("\r\n");
    44.     //databuf = 0;
    45.     #else
    46.     //显示适时网速   
    47.     ssd1306_drawString("Download=       B/s");
    48.     OLED_ShowNum(54,48,Byte,6,1);
    49.     ssd1306_drawString("\r\n");
    50.     #endif

    51.     //显示适时磁盘读写
    52.     ssd1306_drawString("Disk used=");
    53.     ssd1306_drawString(used);

    54.     ssd1306_display();
    55. }
    复制代码

    5.编译运行
    ①编译
    gcc oled.c ssd1306_i2c.c dht11.c  -lwiringPi -o oled
    这里将oled.c,ssd1306.c和dht11.c 分别编译后链接成oled可执行文件,-l用来指定依赖库
    ②运行
    ./oled
    4_编译运行.jpg
    图4:编译运行


    三.OLED显示效果与开机固化程序
    1.OLED显示效果
          执行上述,OLED开始显示运行,效果如下
    5.OLED显示效果.jpg
    图5:OLED显示效果


    2.将程序固化到开机启动
    ①pwd获取执行文件所在路径
    6_可执行文件路径.jpg
    图6:获取可执行文件路径

    ②添加开机自启
    sudo vim /etc/rc.local

    ③在exit()之前添加,格式nohup (编译好oled文件的全名--带路径的) &
    nohup /home/cool/project/code/oled_combine/oled &
    ④关机断电后上电,查看效果
    sudo reboot
    断电后开机,可以看到图5所示效果,说明固化开机程序生效。


          至此,实现了树莓派5驱动OLED显示,且应用丰富。






    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-5-9 12:44 , Processed in 0.125388 second(s), 16 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.