香橙派zero3配个iic屏显示ip

参考

ESP32 波特律动oled.csdn

香橙派zero3 交叉编译和cmake配置.csdn

orangepizero3 硬件配置

bash 复制代码
orangepi@orangepizero3:~$ sudo  orangepi-config
# System->Hardware
# 使用 /dev/i2c-3
orangepi@orangepizero3:~$ ls /dev/i2c-*
/dev/i2c-1  /dev/i2c-2  /dev/i2c-3  /dev/i2c-4  /dev/i2c-5
# 扫描并探测 I2C 总线 3 上的所有设备 
# oled的地址是3c
orangepi@orangepizero3:~$ sudo  i2cdetect -y 3
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --

硬件连线

I2C3 连接 SSD1306 128*32 (0.91寸)

源码目录

bash 复制代码
orangepi@orangepizero3:~$ ls
build  
font.cpp  
font.h  
main.cpp  
Makefile  
oled.cpp   
oled.h 

源码

Makefile

bash 复制代码
CXX = g++
CXXFLAGS = -std=c++17 -Wall

TARGET = build/oled_demo

SRCS = main.cpp \
       oled.cpp \
       font.cpp

OBJS = $(patsubst %.cpp,build/%.o,$(SRCS))

all: $(TARGET)

# 创建 build 目录
build:
	mkdir -p build

# 链接
$(TARGET): build $(OBJS)
	$(CXX) -o $@ $(OBJS)

# 编译
build/%.o: %.cpp | build
	$(CXX) $(CXXFLAGS) -c $< -o $@

# 清理
clean:
	rm -rf build

.PHONY: all clean build

font.cpp和font.h

参考 ESP32 波特律动oled.csdn

main.cpp

c 复制代码
#include <arpa/inet.h>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <ifaddrs.h>
#include <net/if.h>
#include <unistd.h>

#include "oled.h"

extern const ASCIIFont afont24x12;
extern const ASCIIFont afont16x8;

static void GetLocalIP(char *ip, size_t len)
{
    strncpy(ip, "0.0.0.0", len);
    ip[len - 1] = '\0';

    struct ifaddrs *ifaddr = nullptr;

    if (getifaddrs(&ifaddr) == -1)
        return;

    for (struct ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next)
    {
        if (!ifa->ifa_addr)
            continue;

        if (ifa->ifa_addr->sa_family != AF_INET)
            continue;

        if (ifa->ifa_flags & IFF_LOOPBACK)
            continue;

        inet_ntop(AF_INET,
                  &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
                  ip,
                  len);
        break;
    }

    freeifaddrs(ifaddr);
}

static void MakeShortIP(const char *ip, char *shortIp, size_t len)
{
    const char *last = strrchr(ip, '.');

    if (!last)
    {
        strncpy(shortIp, ip, len);
        shortIp[len - 1] = '\0';
        return;
    }

    const char *prev = last - 1;

    while (prev > ip && *prev != '.')
        prev--;

    if (*prev == '.')
        prev++;

    strncpy(shortIp, prev, len);
    shortIp[len - 1] = '\0';
}

int main()
{
    char ip[64] = "0.0.0.0";
    char oldIp[64] = "";
    char ipShort[16] = "0.0.0.0";
    char msg[16];

    int refresh = 0;

    time_t lastUpdate = 0;

    OLED_Init();

    while (1)
    {
        // 每秒检查一次 IP 是否变化
        time_t now = time(nullptr);
        if (now != lastUpdate)
        {
            lastUpdate = now;

            GetLocalIP(ip, sizeof(ip));

            if (strcmp(ip, oldIp) != 0)
            {
                strcpy(oldIp, ip);
                MakeShortIP(ip, ipShort, sizeof(ipShort));

                printf("IP Changed: %s -> %s\n", ip, ipShort);
            }
        }

        OLED_NewFrame();

        // 左侧:大字显示最后两段 IP,例如 3.11
        OLED_PrintASCIIString(
            0,
            0,
            ipShort,
            &afont24x12,
            OLED_COLOR_NORMAL);

        // 右侧:小字刷新计数
        snprintf(msg, sizeof(msg), "%05d", refresh++);

        OLED_PrintASCIIString(
            68,
            8,
            msg,
            &afont16x8,
            OLED_COLOR_NORMAL);

        OLED_ShowFrame();

        usleep(100000);      // OLED 刷新 10Hz
    }

    return 0;
}

oled.h

c 复制代码
#ifndef __OLED_H__
#define __OLED_H__


#include "font.h"
#include "string.h"

typedef enum {
  OLED_COLOR_NORMAL = 0, // 正常模式 黑底白字
  OLED_COLOR_REVERSED    // 反色模式 白底黑字
} OLED_ColorMode;

void OLED_Init();
void OLED_DisPlay_On();
void OLED_DisPlay_Off();

void OLED_NewFrame();
void OLED_ShowFrame();
void OLED_SetPixel(uint8_t x, uint8_t y, OLED_ColorMode color);

void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, OLED_ColorMode color);
void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color);
void OLED_DrawFilledRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color);
void OLED_DrawTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color);
void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color);
void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color);
void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color);
void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b, OLED_ColorMode color);
void OLED_DrawImage(uint8_t x, uint8_t y, const Image *img, OLED_ColorMode color);

void OLED_PrintASCIIChar(uint8_t x, uint8_t y, char ch, const ASCIIFont *font, OLED_ColorMode color);
void OLED_PrintASCIIString(uint8_t x, uint8_t y, char *str, const ASCIIFont *font, OLED_ColorMode color);
void OLED_PrintString(uint8_t x, uint8_t y, char *str, const Font *font, OLED_ColorMode color);

#endif // __OLED_H__

oled.cpp

c 复制代码
#include "oled.h"
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include <cstring>

#define OLED_ADDR   0x3C
#define OLED_WIDTH  128
#define OLED_HEIGHT 32
#define OLED_PAGE   4
#define OLED_COLUMN 128             // OLED列数

static int g_fd = -1;
static uint8_t OLED_GRAM[OLED_PAGE][OLED_WIDTH];

static void sendCmd(uint8_t cmd)
{
    uint8_t buf[2] = {0x00, cmd};
    write(g_fd, buf, 2);
}

static void sendData(const uint8_t *data, int len)
{
    uint8_t buf[129];
    buf[0] = 0x40;

    while (len > 0)
    {
        int n = len > 128 ? 128 : len;
        memcpy(buf + 1, data, n);
        write(g_fd, buf, n + 1);
        data += n;
        len -= n;
    }
}

uint8_t _OLED_GetUTF8Len(char* string) {
    if ((string[0] & 0x80) == 0x00) {
        return 1;
    }
    else if ((string[0] & 0xE0) == 0xC0) {
        return 2;
    }
    else if ((string[0] & 0xF0) == 0xE0) {
        return 3;
    }
    else if ((string[0] & 0xF8) == 0xF0) {
        return 4;
    }
    return 0;
}


void OLED_Init()
{
    printf("OLED_Init\n");
    g_fd = open("/dev/i2c-3", O_RDWR);
    if (g_fd < 0)
    {
        perror("open");
        return;
    }

    printf("open ok\n");

    if (ioctl(g_fd, I2C_SLAVE, OLED_ADDR) < 0)
    {
        perror("ioctl");
        return;
    }

    printf("ioctl ok\n");

    ioctl(g_fd, I2C_SLAVE, OLED_ADDR);

    sendCmd(0xAE);
    sendCmd(0x20);
    sendCmd(0x00);
    sendCmd(0xB0);
    sendCmd(0xC8);
    sendCmd(0x00);
    sendCmd(0x10);
    sendCmd(0x40);
    sendCmd(0x81);
    sendCmd(0x7F);
    sendCmd(0xA1);
    sendCmd(0xA6);
    sendCmd(0xA8);
    sendCmd(0x1F);
    sendCmd(0xD3);
    sendCmd(0x00);
    sendCmd(0xD5);
    sendCmd(0x80);
    sendCmd(0xD9);
    sendCmd(0xF1);
    sendCmd(0xDA);
    sendCmd(0x02);
    sendCmd(0xDB);
    sendCmd(0x40);
    sendCmd(0x8D);
    sendCmd(0x14);
    sendCmd(0xAF);

    OLED_NewFrame();
    OLED_ShowFrame();
}

void OLED_NewFrame()
{
    memset(OLED_GRAM, 0, sizeof(OLED_GRAM));
}

void OLED_ShowFrame()
{
    for (int page = 0; page < OLED_PAGE; page++)
    {
        sendCmd(0xB0 + page);
        sendCmd(0x00);
        sendCmd(0x10);
        sendData(OLED_GRAM[page], OLED_WIDTH);
    }
}



void OLED_SetByte_Fine(uint8_t page, uint8_t column, uint8_t data, uint8_t start, uint8_t end, OLED_ColorMode color) {
    static uint8_t temp;
    if (page >= OLED_PAGE || column >= OLED_COLUMN)
        return;
    if (color)
        data = ~data;

    temp = data | (0xff << (end + 1)) | (0xff >> (8 - start));
    OLED_GRAM[page][column] &= temp;
    temp = data & ~(0xff << (end + 1)) & ~(0xff >> (8 - start));
    OLED_GRAM[page][column] |= temp;
}



void OLED_SetBits(uint8_t x, uint8_t y, uint8_t data, OLED_ColorMode color) {
    uint8_t page = y / 8;
    uint8_t bit = y % 8;
    OLED_SetByte_Fine(page, x, data << bit, bit, 7, color);
    if (bit) {
        OLED_SetByte_Fine(page + 1, x, data >> (8 - bit), 0, bit - 1, color);
    }
}

/**
@brief 设置显存中的一字节数据的某几位
**/
void OLED_SetBits_Fine(uint8_t x, uint8_t y, uint8_t data, uint8_t len, OLED_ColorMode color) {
    uint8_t page = y / 8;
    uint8_t bit = y % 8;
    if (bit + len > 8) {
        OLED_SetByte_Fine(page, x, data << bit, bit, 7, color);
        OLED_SetByte_Fine(page + 1, x, data >> (8 - bit), 0, len + bit - 1 - 8, color);
    }
    else {
        OLED_SetByte_Fine(page, x, data << bit, bit, bit + len - 1, color);
    }
}


/**
  @brief 设置显存中一字节长度的数据
**/
void OLED_SetBlock(uint8_t x, uint8_t y, const uint8_t* data, uint8_t w, uint8_t h, OLED_ColorMode color) {
    uint8_t fullRow = h / 8; // 完整的行数
    uint8_t partBit = h % 8; // 不完整的字节中的有效位数
    for (uint8_t i = 0; i < w; i++) {
        for (uint8_t j = 0; j < fullRow; j++) {
            OLED_SetBits(x + i, y + j * 8, data[i + j * w], color);
        }
    }
    if (partBit) {
        uint16_t fullNum = w * fullRow; // 完整的字节数
        for (uint8_t i = 0; i < w; i++) {
            OLED_SetBits_Fine(x + i, y + (fullRow * 8), data[fullNum + i], partBit, color);
        }
    }
 
}


void OLED_PrintString(uint8_t x, uint8_t y, char* str, const Font* font, OLED_ColorMode color) {
    uint16_t i = 0; // 字符串索引
    uint8_t oneLen = (((font->h + 7) / 8) * font->w) + 4; // 一个字模占多少字节
    while (str[i]) {
        uint8_t found = 0; // 是否找到字模
        uint8_t utf8Len = _OLED_GetUTF8Len(str + i); // UTF-8编码长度
        if (utf8Len == 0)
            break; // 有问题的UTF-8编码

        // 寻找字符  TODO 优化查找算法, 二分查找或者hash
        for (uint8_t j = 0; j < font->len; j++) {
            uint8_t* head = (uint8_t*)(font->chars) + (j * oneLen); // 字模头指针
            if (memcmp(str + i, head, utf8Len) == 0) {
                OLED_SetBlock(x, y, head + 4, font->w, font->h, color);
                // 移动光标
                x += font->w;
                i += utf8Len;
                found = 1;
                break;
            }
        }

        // 若未找到字模,且为ASCII字符, 则缺省显示ASCII字符
        if (found == 0) {
            if (utf8Len == 1) {
                OLED_PrintASCIIChar(x, y, str[i], font->ascii, color);
                // 移动光标
                x += font->ascii->w;
                i += utf8Len;
            }
            else {
                OLED_PrintASCIIChar(x, y, ' ', font->ascii, color);
                x += font->ascii->w;
                i += utf8Len;
            }
        }
    }
}


void OLED_PrintASCIIChar(uint8_t x, uint8_t y, char ch, const ASCIIFont* font, OLED_ColorMode color) {
    OLED_SetBlock(x, y, font->chars + (ch - ' ') * (((font->h + 7) / 8) * font->w), font->w, font->h, color);
}
void OLED_PrintASCIIString(uint8_t x, uint8_t y, char* str, const ASCIIFont* font, OLED_ColorMode color) {
    uint8_t x0 = x;
    while (*str) {
        OLED_PrintASCIIChar(x0, y, *str, font, color);
        x0 += font->w;
        str++;
    }
}

开机启动配置

bash 复制代码
orangepi@orangepizero3:~$ sudo  vim /etc/systemd/system/oled_demo.service
bash 复制代码
[Unit]
Description=OLED Demo
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=root
ExecStart=/home/orangepi/oled_demo
Restart=always
RestartSec=2

[Install]
WantedBy=multi-user.target

oled_demo相关脚本

bash 复制代码
# 添加执行权限
chmod +x /home/orangepi/oled_demo
# 修改 service 文件后,重新加载 systemd 配置
sudo systemctl daemon-reload
# 设置 oled_demo 为开机自动启动
sudo systemctl enable oled_demo

# 查看 oled_demo 当前运行状态
sudo systemctl status oled_demo
# 查看 oled_demo 的实时运行日志
journalctl -u oled_demo -f
# 查看是否已设置为开机自启动
systemctl is-enabled oled_demo
# 停止服务
sudo systemctl stop oled_demo
# 立即重启(重新启动)oled_demo 服务
sudo systemctl restart oled_demo
相关推荐
ShirleyWang0122 小时前
Day02 K3s NGF(Nginx Gateway Fabric)单 Worker 环境网关更新与故障处置 SOP
linux·服务器·python·k8s·k3s
微露清风2 小时前
模拟算法学习记录
学习·算法
酷可达拉斯2 小时前
Linux操作系统-shell编程(1)
linux·运维·服务器
j7~2 小时前
【Linux】二十四.线程篇一《一文吃透Linux线程全面解析:概念、内存管理、优缺点与用途》
linux·运维·服务器·页表·线程概念·分页式存储管理·缺页异常
ShirleyWang0122 小时前
DAY01 K3s 私有化部署排障复盘与 SOP
linux·服务器·k8s·k3s·企业部署
筱谙2 小时前
BES BLE CTKD 完整学习笔记
笔记·学习
AbandonForce3 小时前
Linux进程(WHAT HOW)
linux·运维·服务器
静默追光3 小时前
服务器——看门狗
linux·运维·服务器
无足鸟ICT3 小时前
【RHCA+】测试变量
linux