回答

收藏

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

#板卡评测 #板卡评测 2529 人阅读 | 0 人回复 | 2024-04-14

本帖最后由 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引脚

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屏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:工程目录

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:编译运行


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


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

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

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


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






分享到:
回复

使用道具 举报

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

本版积分规则

高级嵌入式软件工程师
949 积分
9 主题
+ 关注
热门推荐
关闭

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