0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

龙芯2K500先锋板试用体验,驱动OLED小屏播放视频

电子发烧友开源社区 来源:未知 2023-02-04 12:50 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

一、硬件准备

硬件部分主要包括:

  • 龙芯2K500先锋板
  • 0.96寸OLED

OLED屏幕参数:

开发板选择使用I2C1,和OLED屏接线参考下图:

efc7c83c-a446-11ed-bfe3-dac502259ad0.png

开发板和OLED小屏的连接关系为:

  • 3号针(I2C1_SCL)连接到OLED屏的SCL脚

  • 4号针(I2C1_SDA)连接到OLED屏的SDA脚

  • 23号针(GND)连接到OLED屏的GND脚

  • 24号针(P3V3)连接到OLED屏的VCC脚

二、背景知识

开始之前,先简单介绍一些背景知识。

2.1 Linux内核I2C驱动配置

龙芯2K0500内核默认已经打开了I2C驱动,启动后使用如下命令可以看到:

ls /dev/i2c-*

(左右移动查看全部内容)

已经有i2c设备了。

2.2 Linux用户空间I2C API

参考这个文档:https://www.kernel.org/doc/html/latest/i2c/dev-interface.html

用户空间使用I2C,首先需要包含头文件:

#include 
#include 

(左右移动查看全部内容)

然后,打开设备文件:

int file;
int adapter_nr = 2; /* probably dynamically determined */
char filename[20];


snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
file = open(filename, O_RDWR);
if (file < 0) {
 /* ERROR HANDLING; you can check errno to see what went wrong */
 exit(1);
}

(左右移动查看全部内容)

打开设备之后,需要指定需要通信的从设备地址:

int addr = 0x40; /* The I2C address */


if (ioctl(file, I2C_SLAVE, addr) < 0) {
 /* ERROR HANDLING; you can check errno to see what went wrong */
 exit(1);
}

(左右移动查看全部内容)

好了,接下来就可以进行I2C通信了:

/*
 * Using I2C Write, equivalent of
 * i2c_smbus_write_word_data(file, reg, 0x6543)
 */
buf[0] = reg;
buf[1] = 0x43;
buf[2] = 0x65;
if (write(file, buf, 3) != 3) {
 /* ERROR HANDLING: I2C transaction failed */
}


/* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */
if (read(file, buf, 1) != 1) {
 /* ERROR HANDLING: I2C transaction failed */
} else {
 /* buf[0] contains the read byte */
}

(左右移动查看全部内容)

以上几个代码段,都来自于kernel.org的文档。

三、移植SSD1306驱动库

3.1 选择SSD1306驱动库

之前移植过的一个STM32的SSD1306驱动库,原始开源项目链接:https://github.com/afiskon/stm32-ssd1306

移植后的开源项目连接:https://gitee.com/hihopeorg/harmonyos-ssd1306

这个移植版本主要修改包括:

  • 适配了OpenHarmony 1.0的WIFI_IOT硬件接口;

  • 添加了一个用于绘制矩形位图的接口,可用于绘制汉字;

  • 优化了I2C全屏刷新速率;

这里使用移植版本作为基础。

3.2 移植SSD1306驱动库

主要修改点包括:

  • 初始化函数ssd1306_Reset中,添加打开I2C设备的代码;

  • 发送数据函数ssd1306_SendData中,修改为使用I2C用户空间接口的代码;

  • 延时函数HAL_Delay,修改为使用ulseep实现;

  • 计时函数HAL_GetTick,修改为使用gettimeofday实现;

  • 添加了关闭函数ssd1306_Finish,用于关闭初始化时打开的I2C设备;

修改之后,这几个函数的主要代码为:

static int g_i2c = -1;


static uint64_t g_start_ms = 0;


#define TV2MS(tv) ((tv).tv_sec * 1000 + (tv).tv_usec / 1000)


void ssd1306_Reset(void)
{
  char path[128] = {0};
  snprintf(path, sizeof(path), "/dev/i2c-%d", SSD1306_DEV_NO);


  g_i2c = open(path, O_RDWR);
  if (g_i2c < 0) {
    printf("open %s failed, %s!
", path, strerror(errno));
  }


  if (ioctl(g_i2c, I2C_SLAVE, SSD1306_DEV_ADDR) < 0) {
    /* ERROR HANDLING; you can check errno to see what went wrong */
    printf("ioctl %s I2C_SLAVE failed, %s!
", path, strerror(errno));
    exit(1);
  }


  struct timeval start_tv = {0};
  if (gettimeofday(&start_tv, NULL) != 0) {
    printf("gettimeofday failed!
");
  }
  g_start_ms = TV2MS(start_tv);
}


void ssd1306_Finish(void)
{
  if (g_i2c >= 0) {
    close(g_i2c);
  }
}


void HAL_Delay(uint32_t ms)
{
  usleep(ms * 1000);
}


uint32_t HAL_GetTick(void)
{
  struct timeval now_tv = {0};
  if (gettimeofday(&now_tv, NULL) != 0) {
    printf("gettimeofday failed!
");
  }


  return TV2MS(now_tv) - g_start_ms;
}


uint32_t HAL_GetTickFreq(void)
{
  return 1000;
}


static uint32_t ssd1306_SendData(uint8_t* data, size_t size)
{
  struct i2c_msg msg = {0};
  msg.addr = SSD1306_DEV_ADDR;
  msg.buf = data;
  msg.len = size;


  if (g_i2c >= 0) {
    struct i2c_rdwr_ioctl_data data = {0};
    data.msgs = &msg;
    data.nmsgs = 1;
    return ioctl(g_i2c, I2C_RDWR, &data) < 0 ? -1 : 0;
  }
  return -1;
}

(左右移动查看全部内容)

3.3 添加CMake构建规则文件

接下来添加CMake构建规则CMakeLists.txt文件,分别到ssd1306目录和examples目录。

ssd1306目录的CMakeLists.txt用于编译驱动库,内容为:

set(sources
  ssd1306.c
  ssd1306_fonts.c
)


add_library(ssd1306 STATIC ${sources})
include_directories(.)

(左右移动查看全部内容)

3.4 移植SSD1306测试程序

之前移植版的测试程序适配的是OpenHarmony 1.0,这里也需要修改,主要修改点:

  • ssd1306_demo.c文件中,移除和OpenHarmony相关的代码;

  • 添加main函数作为入口;

examples目录的CMakeLists.txt用于编译测试程序,内容为:

set(sources
  ssd1306_demo.c
  ssd1306_tests.c
)


add_executable(oled_test ${sources})
add_definitions(-DUSE_MAIN)
target_link_libraries(oled_test ssd1306)
target_link_libraries(oled_test m)
include_directories(../ssd1306)

(左右移动查看全部内容)

3.5 LoongArch CMake构建参数

顶层的CMakeLists.txt文件内容如下:

cmake_minimum_required(VERSION 3.21.0) # 网上看到消息称这个版本的cmake才支持loongarch


set(CMAKE_SYSTEM_PROCESSOR loongarch)
set(CMAKE_C_COMPILER loongarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER loongarch64-linux-gnu-g++)
set(CMAKE_C_FLAGS -Wall)


project(ssd1306_oled)


add_subdirectory(ssd1306)
add_subdirectory(examples)

(左右移动查看全部内容)

由于这里我添加了CMAKE_SYSTEM_PROCESSOR、CMAKE_C_COMPILER、CMAKE_CXX_COMPILER三个参数,因此可以直接编译出LoongArch的可执行程序了。

如果不在CMakeLists.txt文件中指定这几个参数,通过命令行参数指定也是可以的:

cmake -B build -DCMAKE_SYSTEM_PROCESSOR=loongarch -DCMAKE_C_COMPILER=loongarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=loongarch64-linux-gnu-g++

(左右移动查看全部内容)

3.6 编译、运行SSD1306测试程序

完成以上步骤后,就可以编译SSD1306测试程序了。编译:

# 生成 Makefile ,build 为生成的目录
cmake -B build


# 编译 ,或者 cd build && make 也可以
cmake --build build

(左右移动查看全部内容)

编译完成后,build/examples目录下生成了oled_test二进制文件,将其拷贝到开发板上。运行测试程序:

./oeld_test

(左右移动查看全部内容)

不出意外的话,就可以看到OLED上正常显示各种测试画面了:

efdbb9dc-a446-11ed-bfe3-dac502259ad0.png

四、实现SSD1306播放视频

4.1 准备视频文件

首先需要准备一个视频文件,例如,我这里找的是蔡徐坤的“鸡你太美”视频;

4.2 转换视频格式

前面测试发现最大帧率接近 8 fps,接下来需要使用ffmpeg将视频转换为帧率 8 fps。转换命令为:

ffmpeg -i input.mp4 -r 10 output.mp4

(左右移动查看全部内容)

之后再使用Python脚本将视频转换为原始帧的二进制文件:

./video2bin.py output.mp4 out.bin

(左右移动查看全部内容)

这里的bin文件包含若干个连续的原始帧数据,每个原始帧占用1KB(128x64/8=1024);

完整的视频转换python脚本,

#!/usr/bin/env python3
import sys
import cv2 as cv


TARGET_WIDTH = 128
TARGET_HEIGHT = 64
PIXEL_PER_BYTE = 8
WIDTH_BYTES = int(TARGET_WIDTH/PIXEL_PER_BYTE)
PIXEL_THRESHOLD = 128.0


# 将多个灰度像素打包到一个整数中
def pack_pixels(pixels, threshold):
  value = 0
  for gray in pixels:
    bit = 1 if gray >= threshold else 0 # 二值化
    value = (value << 1) + bit # 多个二值化像素值拼接为一个字节值
  return value


frameCount = 0
def resize_and_binarize_image(frame, width, height, threshold):
  data = []
  # count = 0 # for debug
  frame = cv2.resize(frame, (width, height)) # 缩放
  frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) # 转为灰度图
  _, binary = cv2.threshold(frame, threshold, 255, cv2.THRESH_BINARY) # 二值化


  for r in range(height):
    for b in range(int(width / PIXEL_PER_BYTE)):
      colStart = b * PIXEL_PER_BYTE
      pixels = frame[r, colStart: colStart + PIXEL_PER_BYTE]
      byte = pack_pixels(pixels, threshold)
      data.append(byte)
  return bytes(data)


def convert_frame_to_bytes(frame):
  return resize_and_binarize_image(frame, TARGET_WIDTH, TARGET_HEIGHT, PIXEL_THRESHOLD)


def convert_video_to_bin(videoFile, binFile):
  cap = cv.VideoCapture(videoFile)
  frameCount = cap.get(cv.CAP_PROP_FRAME_COUNT)
  print('frame count:', frameCount)
  print('frame width:', cap.get(cv.CAP_PROP_FRAME_WIDTH))
  print('frame height:', cap.get(cv.CAP_PROP_FRAME_HEIGHT))
  lastPercent = 0
  with open(binFile, 'wb+') as f:
    while True:
      retval, frame = cap.read()
      if not retval:
        print('video done!')
        break
      bitmap = convert_frame_to_bytes(frame)
      f.write(bitmap)
      pos = cap.get(cv.CAP_PROP_POS_FRAMES)
      percent = pos / frameCount * 100
      if percent - lastPercent >= 1:
        lastPercent = percent
        sys.stdout.write('=')
        sys.stdout.flush()
  print('convert all frames done!')
  cap.release()


def main():
  if len(sys.argv) < 3:
    print("Usage: {} videoFile binFile
	".format(sys.argv[0]))
    exit(-1)


  try:
    videoFile = sys.argv[1]
    binFile = sys.argv[2]
    convert_video_to_bin(videoFile, binFile)
  except Exception as e:
    print('exception raised:', e)


if __name__ == "__main__":
  main()

(左右移动查看全部内容)

4.3 实现视频播放

在examples目录下,添加ssd1306_play.cpp文件,代码如下:

#include "ssd1306.h"


#include 
#include 
#include 
#include 


int play(char* video_bin)
{
  std::unique_ptrdecltype,>(&fclose)> fptr{fopen(video_bin, "rb"), fclose};


  uint32_t count = 0;
  uint8_t frame[SSD1306_BUFFER_SIZE] = {0};


  ssd1306_Init();
  uint32_t beg = HAL_GetTick();
  for(;;) {
    size_t nbytes = fread(frame, 1, sizeof(frame), fptr.get());
    if (ferror(fptr.get())) {
      printf("Error: %s
", strerror(errno));
      return -1;
    }
    if (feof(fptr.get())) {
      break;
    }


    ssd1306_Fill(Black);
    ssd1306_DrawBitmap(frame, sizeof(frame));
    ssd1306_UpdateScreen();
    count++;
  }
  uint32_t end = HAL_GetTick();


  ssd1306_Fill(Black);
  ssd1306_UpdateScreen();
  ssd1306_Finish();


  float cost = (end - beg) / (float) HAL_GetTickFreq();
  printf("Total frames : %d
", count);
  printf("Total time(s): %.3f
", cost);
  printf("Average FPS : %.3f
", count / cost);
  return 0;
}


int main(int argc, char* argv[])
{
  if (argc <= 1) {
    printf("Usage: %s video.bin
", argv[0]);
    return 1;
  }


  return play(argv[1]);
}

(左右移动查看全部内容)

这段代码实现了播放原始视频二进制文件;

4.4 添加构建规则

examples目录的CMakeLists.txt中添加:

add_executable(oled_play ssd1306_play.cpp)
target_link_libraries(oled_play ssd1306)
include_directories(../ssd1306)

(左右移动查看全部内容)

4.5 播放视频文件

完成以上操作后,重新编译,再次运行:

./oled_play ikun.bin

(左右移动查看全部内容)

效果如下:

五、源码仓库

本文所有代码均已在码云开源,链接为:https://gitee.com/swxu/linux-ssd1306

六、参考链接

  • Implementing I2C device drivers in userspace — The Linux Kernel documentation:https://www.kernel.org/doc/html/latest/i2c/dev-interface.html

  • 一个STM32 SSD1306驱动库:https://github.com/afiskon/stm32-ssd1306

  • 【只因太美】用龙芯2K0500驱动小屏放视频:https://www.bilibili.com/video/BV1Gv4y1i7nW/

更多热点文章阅读
  • 基于 OpenHarmony 的智能电磁炮
  • 【我的2022】坚果:我和OpenHarmony的这一年
  • OpenHarmony小型系统兼容性测试指南
  • 玩转OpenHarmony社交场景:即时通讯平台
  • 龙芯 2K500 开发板 Linux环境基础调教和使用

提示:本文由电子发烧友社区发布,转载请注明以上来源。如需社区合作及入群交流,请添加微信EEFans0806,或者发邮箱liuyong@huaqiu.com。


原文标题:龙芯2K500先锋板试用体验,驱动OLED小屏播放视频

文章出处:【微信公众号:电子发烧友开源社区】欢迎添加关注!文章转载请注明出处。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 电子发烧友
    +关注

    关注

    33

    文章

    584

    浏览量

    33980
  • 开源社区
    +关注

    关注

    0

    文章

    95

    浏览量

    775

原文标题:龙芯2K500先锋板试用体验,驱动OLED小屏播放视频

文章出处:【微信号:HarmonyOS_Community,微信公众号:电子发烧友开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    【RA4M2-SENSOR】+OLED显示驱动

    RA4M2-SENSOR开发是一款近于最小系统的开发,通过添加相应的外设,可丰富其功能。 这里就为它配置一个I2C接口的OLED
    发表于 09-02 18:28

    喜讯 | 眺望电子2K3000工控系列入选龙芯中科伙伴产品

    电子同时,眺望电子2K3000系列的核心,工控和工控机也成功入选龙芯2K3000芯片在工控应用领域的首款伙伴产品,为工控嵌入式市场用户提
    的头像 发表于 07-25 08:31 744次阅读
    喜讯 | 眺望电子<b class='flag-5'>2K</b>3000工控系列入选<b class='flag-5'>龙芯</b>中科伙伴产品

    全方位解析,眺望电子2K3000核心参数与设计

    前言:6月26日,广州眺望电子作为龙芯硬件生态在大湾区的核心合作伙伴,在北京中关村国际创新中心举行的2025龙芯产品发布会暨用户大会上登台展示基于龙芯2K3000核心
    的头像 发表于 07-04 14:02 1331次阅读
    全方位解析,眺望电子<b class='flag-5'>2K</b>3000核心<b class='flag-5'>板</b>参数与设计

    4K和8K显示器驱动的区别

    前面我们有讲到lhdh4k 4KUSB3.0显示方案以及驱动出现噪音应该如何解决?,今天我们来聊一聊有关
    的头像 发表于 06-06 11:18 1395次阅读

    龙芯2K0300蜂鸟试用】固件更新与测试

    龙芯2K0300蜂鸟试用】固件更新与测试 本文介绍了龙芯2K300蜂鸟开发板实现官方固件更新
    发表于 06-06 04:52

    飞凌嵌入式携手龙芯发布2K0300i核心,全国产自主可控新选择

    飞凌嵌入式作为龙芯合作伙伴,隆重推出FET-2K0300i-S全国产自主可控工业级核心!FET-2K0300i-S核心基于
    的头像 发表于 05-07 11:31 846次阅读
    飞凌嵌入式携手<b class='flag-5'>龙芯</b>发布<b class='flag-5'>2K</b>0300i核心<b class='flag-5'>板</b>,全国产自主可控新选择

    龙芯2K0300开发软件资料-新世界更新

    【资料更新】正点原子龙芯2K0300开发资料更新! 龙芯2K0300开发软件资料-新世界更新
    发表于 04-22 14:16

    基于龙芯2K0300-I的工业级核心

    格欣以龙芯工业级微处理器芯片LS2K0300-I为核心设计的工业级核心(AH2300)已经面向市场发布,受到广泛关注。 LS2K0300-I具有一些鲜明特点:1、LS
    发表于 04-19 18:24

    龙芯2K3000/3B6000M处理器流片成功

    近日,龙芯2K3000(3B6000M)完成初步功能和性能摸底,各项指标符合预期。龙芯2K3000和龙芯3B6000M是基于相同硅片的不同封
    的头像 发表于 04-07 15:32 928次阅读

    基于龙芯2K0300处理器 龙芯中科携手正点原子发布龙芯ATK-DL2K0300B开发

    龙芯中科携手正点原子发布龙芯ATK-DL2K0300B开发。该开发为正点原子发布的首款龙芯
    的头像 发表于 02-25 15:29 1258次阅读
    基于<b class='flag-5'>龙芯</b><b class='flag-5'>2K</b>0300处理器 <b class='flag-5'>龙芯</b>中科携手正点原子发布<b class='flag-5'>龙芯</b>ATK-DL<b class='flag-5'>2K</b>0300B开发<b class='flag-5'>板</b>

    龙芯2K0300开发及资料来袭,开启国产芯片新篇章!

    正点原子携手龙芯正式发布首款龙芯开发:ATK-DL2K0300开发!基于龙芯LS
    发表于 02-24 15:04

    广东龙芯2K300先锋派&amp;锋鸟新世界BSP

    /docs-2k0300 说明文档见这里 https://gitee.com/open-loongarch/build-2k0300 一键拉取源码和交叉工具链见这里 广东龙芯2K300
    发表于 02-14 17:03

    迅为电子推出基于龙芯2K0300的HMI产品

    2024年6月,龙芯中科发布了又一款重磅产品龙芯2K0300,龙芯2K0300芯片是一款基于LA264处理器核的多功能SoC芯片,可广泛适用
    的头像 发表于 02-13 14:30 917次阅读
    迅为电子推出基于<b class='flag-5'>龙芯</b><b class='flag-5'>2K</b>0300的HMI产品

    迅为龙芯2K1000开发/核心介绍

    视频教程 新增龙芯2K1000开发环境搭建系列、系统编译与烧写系列、PMON基础知识及启动流程分析系列、pmon移植教程系列视频教程。更多视频
    发表于 02-08 11:01

    自主可控的全国产方案,基于龙芯LS2K1000LA-i!

    龙芯LS2K1000LA-i产品简介 LS2K1000LA-i是龙芯双核LoongArch LA264自主架构处理器。创龙科技基于LS2K1
    的头像 发表于 01-24 09:26 904次阅读
    自主可控的全国产方案,基于<b class='flag-5'>龙芯</b>LS<b class='flag-5'>2K</b>1000LA-i!