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