【树莓派5 测评】--6.OLED显示
本帖最后由 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;
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)))
{
ip=(inet_ntoa(((struct sockaddr_in*)(&buf.ifr_addr))->sin_addr));
break;
}
}
}
close (fd);
return ip;
}
}
//获取CPU温度
int get_cpu_temp()
{
FILE *fd;
int temp;
char buff;
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;
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(***ALRM, 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显示,且应用丰富。
页:
[1]