TA的每日心情 | 奋斗 前天 11:38 |
---|
签到天数: 19 天 连续签到: 1 天 [LV.4]偶尔看看III
|
本帖最后由 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写命令函数
- void ssd1306_command(unsigned int c)
- {
- // I2C
- unsigned int control = 0x00; // Co = 0, D/C = 0
- wiringPiI2CWriteReg8(i2cd, control, c);
- }
复制代码 2.ssd1306初始化
- void ssd1306_begin(unsigned int vccstate, unsigned int i2caddr)
- {
- // I2C Init
- _vccstate = vccstate;
- i2cd = wiringPiI2CSetup(i2caddr);
- if (i2cd < 0) {
- fprintf(stderr, "ssd1306_i2c : Unable to initialise I2C:\n");
- return;
- }
- // Init sequence
- ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE
- ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
- ssd1306_command(0x80); // the suggested ratio 0x80
- ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8
- ssd1306_command(SSD1306_LCDHEIGHT - 1);
- ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3
- ssd1306_command(0x0); // no offset
- ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0
- ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D
- if (vccstate == SSD1306_EXTERNALVCC) {
- ssd1306_command(0x10);
- } else {
- ssd1306_command(0x14);
- }
- ssd1306_command(SSD1306_MEMORYMODE); // 0x20
- ssd1306_command(0x00); // 0x0 act like ks0108
- ssd1306_command(SSD1306_SEGREMAP | 0x1);
- ssd1306_command(SSD1306_COMSCANDEC);
- #if defined SSD1306_128_32
- ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
- ssd1306_command(0x02);
- ssd1306_command(SSD1306_SETCONTRAST); // 0x81
- ssd1306_command(0x8F);
- #elif defined SSD1306_128_64
- ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
- ssd1306_command(0x12);
- ssd1306_command(SSD1306_SETCONTRAST); // 0x81
- if (vccstate == SSD1306_EXTERNALVCC) {
- ssd1306_command(0x9F);
- } else {
- ssd1306_command(0xCF);
- }
- #elif defined SSD1306_96_16
- ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
- ssd1306_command(0x2); // ada x12
- ssd1306_command(SSD1306_SETCONTRAST); // 0x81
- if (vccstate == SSD1306_EXTERNALVCC) {
- ssd1306_command(0x10);
- } else {
- ssd1306_command(0xAF);
- }
- #endif
- ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9
- if (vccstate == SSD1306_EXTERNALVCC) {
- ssd1306_command(0x22);
- } else {
- ssd1306_command(0xF1);
- }
- ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB
- ssd1306_command(0x40);
- ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4
- ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6
- ssd1306_command(SSD1306_DEACTIVATE_SCROLL);
- ssd1306_command(SSD1306_DISPLAYON); // --turn on oled panel
- }
复制代码 oled驱动基本是在ssd1306_command函数配合寄存器命令构建。
3.关于界面显示数据源函数,这里选取部分
- //获取时间
- time(&now);
- timenow = localtime(&now);
复制代码- //获取IP
- char* GetLocalIp(void)
- {
- int MAXINTERFACES=16;
- char *ip;
- int fd, intrface, retn = 0;
- struct ifreq buf[MAXINTERFACES];
- struct ifconf ifc;
- if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
- {
- ifc.ifc_len = sizeof(buf);
- ifc.ifc_buf = (caddr_t)buf;
- if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc))
- {
- intrface = ifc.ifc_len / sizeof(struct ifreq);
- while (intrface-- > 0)
- {
- if (!(ioctl (fd, SIOCGIFADDR, (char *) &buf[intrface])))
- {
- ip=(inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr));
- break;
- }
- }
- }
- close (fd);
- return ip;
- }
- }
复制代码- //获取CPU温度
- int get_cpu_temp()
- {
- FILE *fd;
- int temp;
- char buff[256];
- fd = fopen("/sys/class/thermal/thermal_zone0/temp","r");
- fgets(buff,sizeof(buff),fd);
- sscanf(buff, "%d", &temp);
- fclose(fd);
- return temp/1000;
- }
复制代码- //获取内存占用
- MEM_PACK get_memocupy()
- {
- FILE *fd;
- int n;
- double mem_total,mem_used_rate;
- char buff[256];
- MEM_OCCUPY *m = (MEM_OCCUPY*)malloc(sizeof(MEM_OCCUPY));
- MEM_PACK p;
- fd = fopen("/proc/meminfo", "r");
- fgets(buff,sizeof (buff), fd);
- sscanf(buff, "%s %lu %s\n", m->name, &m->total, m->name2);
- mem_total = m->total;
- fgets(buff,sizeof (buff),fd);
- sscanf(buff, "%s %lu %s\n", m->name, &m->total,m->name2);
- mem_used_rate = (1 - m->total/mem_total)*100;
- mem_total = mem_total/(1024*1024);
- p.total = mem_total;
- p.used_rate = mem_used_rate;
- fclose(fd);
- free(m);
- return p;
- }
复制代码 4.界面设计
设计思想是在main函数初始化与参数轮询更新,在Display定时刷新显示。工程目录如下。
图3:工程目录
①main函数
- void main()
- {
- #if DHT11_USE
- printf("PIN:%d\n", pinNumber);
-
- if (-1 == wiringPiSetup()) {
- printf("Setup wiringPi failed!");
- return;
- }
- pinMode(pinNumber, OUTPUT); // set mode to output
- digitalWrite(pinNumber, 1); // output a high level
-
- #endif
-
- printf("Run OLED\n");
- //屏幕初始化
- ssd1306_begin(SSD1306_SWITCHCAPVCC, SSD1306_I2C_ADDRESS);
- //绑定定时器执行函数
- signal(SIGALRM, Display);
- //定时器启用中断 每0.5s执行一次,就是2帧
- set_timer();
- while (1)
- {
- #if DHT11_USE
- pinMode(pinNumber, OUTPUT); // set mode to output
- digitalWrite(pinNumber, 1); // output a high level
- //delay(3000);
- if (readSensorData())
- {
- printf("Sensor data read ok!\n");
- printf("RH:%d.%d\n", (databuf >> 24) & 0xff, (databuf >> 16) & 0xff);
- printf("TMP:%d.%d\n", (databuf >> 8) & 0xff, databuf & 0xff);
- //databuf = 0;
- }
- else
- {
- printf("Sensor dosent ans!\n");
- databuf = 0;
- }
- #endif
-
- //获取时间
- time(&now);
- timenow = localtime(&now);
- //获取IP
- IP_address=GetLocalIp();
- //获取CPU温度
- temp = get_cpu_temp();
- //获取CPU占用
- get_cpuoccupy((CPU_OCCUPY*)&cpu_stat1);
- sleep(10);
- get_cpuoccupy((CPU_OCCUPY*)&cpu_stat2);
- cpu = cal_cpuoccupy((CPU_OCCUPY *)&cpu_stat1,(CPU_OCCUPY*)&cpu_stat2);
- //获取内存占用
- mem = get_memocupy();
- //获取实时流量
- getCurrentDownloadRates(&start_download_rates); //获取当前流量,并保存在start_download_rates
- sleep(3); //休眠多少秒,这个值根据宏定义中的WAIT_SECOND的值来确定
- getCurrentDownloadRates(&end_download_rates); //获取当前流量,并保存在end_download_rates里
- Byte=(end_download_rates - start_download_rates) / 3;
- //获取当前磁盘的使用情况
- get_disk_occupy(&used);
- }
- }
复制代码
②界面设计刷新
- oid Display(int m)
- {
- //清屏
- ssd1306_clearDisplay();
-
- ssd1306_drawString("Hi,Raspberry_Pi5 !");
- ssd1306_drawString("\r\n");
-
-
- //显示时间
- ssd1306_drawString(" - - : :");
- OLED_ShowNum(0,8,timenow->tm_year+1900,4,1);
- OLED_ShowNum(30,8,timenow->tm_mon,2,1);
- OLED_ShowNum(48,8,timenow->tm_mday,2,1);
- OLED_ShowNum(66+12,8,timenow->tm_hour,2,1);
- OLED_ShowNum(82+12,8,timenow->tm_min,2,1);
- OLED_ShowNum(100+12,8,timenow->tm_sec,2,1);
- ssd1306_drawString("\r\n");
-
- //显示IP
- ssd1306_drawString("IP=");
- ssd1306_drawString(IP_address);
- ssd1306_drawString("\r\n");
- //显示CPU温度
- ssd1306_drawString("CPU Temperature= C");
- OLED_ShowNum(96,24,temp,2,0);
- ssd1306_drawString("\r\n");
- //显示CPU占用
- ssd1306_drawString("CPU usage= %");
- OLED_ShowNum(61,32,cpu,2,0);
- ssd1306_drawString("\r\n");
- //显示运存占用
- ssd1306_drawString("mem use rate= %");
- OLED_ShowNum(78,40,mem.used_rate,2,0);
- ssd1306_drawString("\r\n");
- #if DHT11_USE
- //DHT11 温湿度显示
- ssd1306_drawString("RH= . ,TEMP= . C");
- OLED_ShowNum(20,48,(databuf >> 24) & 0xff,2,1);
- OLED_ShowNum(35,48,(databuf >> 16) & 0xff,1,1);
- OLED_ShowNum(85,48,(databuf >> 8) & 0xff,2,1);
- OLED_ShowNum(105,48,databuf & 0xff,1,1);
- ssd1306_drawString("\r\n");
- //databuf = 0;
- #else
- //显示适时网速
- ssd1306_drawString("Download= B/s");
- OLED_ShowNum(54,48,Byte,6,1);
- ssd1306_drawString("\r\n");
- #endif
- //显示适时磁盘读写
- ssd1306_drawString("Disk used=");
- ssd1306_drawString(used);
- ssd1306_display();
- }
复制代码
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显示,且应用丰富。
|
|