树莓派oled IP显示

1 实现效果

在这里插入图片描述

2 说明

问题: 在嵌入式开发中,经常遇到一些问题,比如收到一块开发板,没有屏幕,没有串口,需要调试,只能使用网口连接。
可是如果公式内网不是你管理,无法设置固定IP,那么怎么搞?我开发板IP都不知道怎么连接调试或者写代码?

为了解决这个问题,这便是我此次创作的目的!

看了网上很多方法,其中有的使用扬声器开机播报IP,确实也是可以,不过我最终选择的还是使用OLED屏幕显示IP,其中还可以显示cpu温度、MAC地址、时间等其他信息。

这里我是用的开发环境是用qt开发(虽然这个项目没有ui界面),iic驱动使用树莓派的wiringpi IO驱动库。

oled屏幕淘宝买的,8元包邮!!!(图片无卖家信息)
在这里插入图片描述

3 设计思路

软件整体包含三个部分,一个是oled的显示驱动,一个是树莓派需要显示的信息获取,最后就是软件开机运行的设置。
整体思路就是,使用一个定时器,1s 驱动一次,每次显示都需要刷新时间,每30s刷新一次cpu温度、IP地址、MAC地址信息。

我是用的oled显示屏是12832,即128 * 32个像素点,显示字符用的8*8大小的,所以能显示4行信息,最长显示16个字符,所以mac地址会将中间的“:”符号去掉显示。
显示的信息排布:

行号 显示内容 刷新间隔
第一行 系统时间 1s
第二行 cpu温度 30s
第三行 IP地址 30s
第四行 MAC地址(eth0) 30s

4 硬件连接

随便画的,绘制比较简单,树莓派的3、5引脚为IIC_1的SDA(数据)和SCL(时钟)脚,我们使用的wiringpi库也是使用的这个IIC外设,所以我们的iic屏幕也是挂在这上面。
在这里插入图片描述

5 代码设计

5.1 信息获取

(1)时间获取
获取时间是最简单的,使用QTime或者QDateTime类即可获取,代码如下:

#include <QTime>
 QString timeStr = QTime::currentTime().toString("    hh:mm:ss    ");

上面代码为什么只使用QTime获取时间,因为屏幕宽度有线,无法显示日期+时间,所以把日期去掉了。
“hh:mm:ss” 为显示 “ 时:分:秒 ”
如果需要显示日期,则使用QDateTime类,代码如下:

#include <QDateTime>
     QString timeStr = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");

(2)cpu温度获取

#include <QFile>
#define TEMP_PATH "/sys/class/thermal/thermal_zone0/temp"
QString OledIP::getCpuTemp()
{
    
    
    char buf[20];
    QFile tempFile(TEMP_PATH);
    if(tempFile.open(QFile::ReadOnly))
    {
    
    
        tempFile.read(buf,20);
        float temp = atoi(buf) / 1000.0;
        return QString::number(temp);
    }
    return "";
}

(3)IP地址获取

QString OledIP::getLocalIp()
{
    
    
    QString myIp;
    QList<QHostAddress> ipList = QNetworkInterface::allAddresses();
    for (int i = 0; i < ipList.size(); ++i)	 // 获取第一个本主机的IPv4地址
    {
    
    
           if (ipList.at(i) != QHostAddress::LocalHost && ipList.at(i).toIPv4Address())
           {
    
    
               myIp= ipList.at(i).toString();
               break;
           }
     }
     if (myIp.isEmpty())	 // 如果没有找到,则使用本地IP
        myIp= QHostAddress(QHostAddress::LocalHost).toString();
     return myIp;
}

(4)MAC地址获取

QString OledIP::getMAC(Qstring card = "eth0")
{
    
    
    QString myMAC;
    auto interfaces = QNetworkInterface::allInterfaces();

    for (int i = 0; i < interfaces.size(); i++)
    {
    
    
        if(interfaces.at(i).name().contains(card))
            if (interfaces.at(i).isValid())
            {
    
    
                myMAC= interfaces.at(i).hardwareAddress().replace(":","");
                break;
            }
    }
    return myMAC;
}

输入参数为网卡名字,不同的网卡存在不同的MAC地址,该参数一般传入 “eth0”。

5.2 驱动12832屏幕

(1)写入命令和数据

void oled12832::writeCmd(int fd,unsigned char I2C_Command)//写命令
{
    
    
    wiringPiI2CWriteReg8(fd,0x00, I2C_Command);
}
void oled12832::writeData(int fd,unsigned char I2C_Data)//写数据
{
    
    
    wiringPiI2CWriteReg8(fd,0x40, I2C_Data);
}

(2)初始化寄存器

void oled12832::regInit(int fd)
{
    
    
    writeCmd(fd,0xAE); //display off
    writeCmd(fd, 0x20);	//Set Memory Addressing Mode
    writeCmd(fd, 0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
    writeCmd(fd, 0xb0);	//Set Page Start Address for Page Addressing Mode,0-7
    writeCmd(fd, 0xc8);	//Set COM Output Scan Direction
    writeCmd(fd, 0x00); //---set low column address
    writeCmd(fd, 0x10); //---set high column address
    writeCmd(fd, 0x40); //--set start line address
    writeCmd(fd, 0x81); //--set contrast control register
    writeCmd(fd, 0xff); //亮度调节 0x00~0xff
    writeCmd(fd, 0xa1); //--set segment re-map 0 to 127
    writeCmd(fd, 0xa6); //--set normal display
    writeCmd(fd, 0xa8); //--set multiplex ratio(1 to 64)
    writeCmd(fd, 0x3F); //
    writeCmd(fd, 0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
    writeCmd(fd, 0xd3); //-set display offset
    writeCmd(fd, 0x00); //-not offset
    writeCmd(fd, 0xd5); //--set display clock divide ratio/oscillator frequency
    writeCmd(fd, 0xf0); //--set divide ratio
    writeCmd(fd, 0xd9); //--set pre-charge period
    writeCmd(fd, 0x22); //
    writeCmd(fd, 0xda); //--set com pins hardware configuration
    writeCmd(fd, 0x12);
    writeCmd(fd, 0xdb); //--set vcomh
    writeCmd(fd, 0x20); //0x20,0.77xVcc
    writeCmd(fd, 0x8d); //--set DC-DC enable
    writeCmd(fd, 0x14); //
    writeCmd(fd, 0xaf); //--turn on oled panel
}

(3)设置写入位置

void oled12832::oledSetPos(int fd,unsigned char x, unsigned char y) //设置起始点坐标
{
    
    
    writeCmd(fd, 0xb0 + x);
    writeCmd(fd,((y & 0x0f) | 0x00));//LOW
    writeCmd(fd,(((y & 0xf0) >> 4) | 0x10));//HIGHT
}

(4) 屏幕填充和清空

void oled12832::oledFill(unsigned char data)//全屏填充
{
    
    
    for (unsigned char i = 0; i < 8; i++)
    {
    
    
        oledSetPos(mOledHard, i, 0); //设置起始点坐标
        for (int j = 0; j < 128; j++)
            writeData(mOledHard, data);//写数据
    }
}
void oled12832::oledClear()//清屏
{
    
    
    unsigned char i, j;
    for (i = 0; i < 8; i++)
    {
    
    
        oledSetPos(mOledHard, i, 0); //设置起始点坐标
        for (j = 0; j < 128; j++)
            writeData(mOledHard, 0x00);//写数据
    }
}

(5)清空某行(0-3,一共4行)

void oled12832::clearLine(int row)
{
    
    
    oledSetPos(mOledHard,row*2, 0); //设置起始点坐标
    for (int j = 0; j < 128; j++)
        writeData(mOledHard, 0x00);//写数据
    oledSetPos(mOledHard,row*2+1, 0); //设置起始点坐标
    for (int j = 0; j < 128; j++)
        writeData(mOledHard, 0x00);//写数据
}

(6)显示字符

//显示字符
void oled12832::showASCLL(unsigned char row, unsigned char col, unsigned char ascii_char)
{
    
    
    unsigned char  i,j;
    i = ascii_char - ' ';	//获取字符偏移量,这是因为字库跟标准ASCII码表相差32,即一个空格
    writeCmd(mOledHard ,0xb0+row);	//设置页地址
    writeCmd(mOledHard ,col&0x0F);			//设置列地址
    writeCmd(mOledHard ,((col&0xF0)>>4)|0x10);

    for(j=0;j<8;j++)
        writeData(mOledHard ,oled_fonts1608[i][j]);

    writeCmd(mOledHard ,0xB0+(row&0x07)+1);	//设置下一页地址
    writeCmd(mOledHard ,col&0x0F);			//设置列地址
    writeCmd(mOledHard ,((col&0xF0)>>4)|0x10);
    for(j=0;j<8;j++)
        writeData(mOledHard ,oled_fonts1608[i][j+8]);
}

(7)显示字符串

//显示字符串
void oled12832::showString(unsigned char row, unsigned char col, unsigned char *ascii_string)
{
    
    
    row = row*2;
    while(*ascii_string != '\0')
    {
    
    
        if(col == 128)	//防止出现长度大于16的字符串在同一行显示的情况
        {
    
    
            col = 0;
            row += 2;
        }
        showASCLL(row, col, *ascii_string);
        col += 8;
        ascii_string++;
    }
}
void oled12832::showString(unsigned char row, unsigned char col, QString str)
{
    
    
    QByteArray buff = str.toLatin1();
    unsigned char *ptr = (unsigned char *)buff.data();
    showString(row,col,ptr);
}

5.3 开机启动脚本(保证该程序只运行在一个进程)

为什么还需要脚本启动,而不是直接运行oledIP可执行文件,因为我是将启动方式放在profile中,树莓派开机过程中,好像是会多次运行到此文件,所以会导致多个oledIP进程运行,因此运行该进程之前使用pgrep查询一下是否有oledIP进程,如果没有才执行,脚本内容如下:

if [ $(pgrep -f oledIP | wc -l) -eq 0 ];then
cd /home/pi/mApp
./oledIP &
fi

树莓派自定义进程开机启动的方式很多,可参考其他文章,个人做嵌入式linux开发习惯修改profile文件,其实不推荐此方法。

5.4整体代码

整体代码已经上传至gitee并开源,其中包含编译好的可执行文件,树莓派的ubuntu系统可直接运行。
项目链接: https://gitee.com/jiangtao008/raspi-oledip

猜你喜欢

转载自blog.csdn.net/weixin_42887343/article/details/120028955